From ce12d8d43fca15dfe718a14c5c2f6b61c2548a6a Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sun, 5 Apr 2026 16:49:52 -0700 Subject: [PATCH 1/5] docs: cockpit Angular examples chat integration spec (Phase 1) --- ...04-05-cockpit-examples-chat-integration.md | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-05-cockpit-examples-chat-integration.md diff --git a/docs/superpowers/specs/2026-04-05-cockpit-examples-chat-integration.md b/docs/superpowers/specs/2026-04-05-cockpit-examples-chat-integration.md new file mode 100644 index 000000000..1495b1c03 --- /dev/null +++ b/docs/superpowers/specs/2026-04-05-cockpit-examples-chat-integration.md @@ -0,0 +1,75 @@ +# Cockpit Angular Examples — Chat Integration (Phase 1) + +**Date:** 2026-04-05 +**Status:** Draft +**Scope:** Wire all 13 remaining cockpit Angular examples to correctly consume `@cacheplane/chat`, make each buildable and serveable as a standalone Angular app. + +--- + +## Overview + +Apply the streaming example's proven pattern to all 13 remaining cockpit Angular examples. Each becomes a standalone Angular app with correct `@cacheplane/chat` API usage, Angular CLI build/serve targets, and proxy configuration to its LangGraph backend. + +Phase 2 (separate spec) will customize each example with capability-specific content, docs, and backend integration. + +## Pattern (from streaming example) + +Each example gets: +1. Delete duplicate `src/app.component.ts` + `src/app.config.ts` +2. Rewrite capability component: `` (or `` for deep-agents) +3. `app.config.ts` with `provideStreamResource` + `provideChat` +4. `main.ts` bootstrapping the capability component +5. `project.json` with `@angular-devkit/build-angular:application` build + `dev-server` serve +6. `tsconfig.app.json` with `lib: ["es2022", "dom"]` + `emitDeclarationOnly: false` +7. `index.html` with `` and correct selector +8. Development environment using `/api` proxy + +## Capability → Chat Component Mapping + +| Example | Selector | Chat Component | Capability-Specific Additions | +|---|---|---|---| +| persistence | `app-persistence` | `` | `[threads]`, `[activeThreadId]`, `(threadSelected)` inputs for thread management | +| interrupts | `app-interrupts` | `` | `ChatInterruptPanelComponent` rendered when `stream.interrupt()` is defined | +| memory | `app-memory` | `` | `[threads]` for cross-thread state | +| time-travel | `app-time-travel` | `` | `ChatTimelineSliderComponent` for checkpoint navigation | +| subgraphs | `app-subgraphs` | `` | `ChatSubagentCardComponent` for nested agent tracking | +| durable-execution | `app-durable-execution` | `` | Error/retry patterns via `stream.error()` + `stream.reload()` | +| deployment-runtime | `app-deployment-runtime` | `` | Production config (different assistant ID) | +| planning | `app-planning` | `` | Full debug composition | +| filesystem | `app-filesystem` | `` | Full debug composition | +| subagents (DA) | `app-subagents` | `` | Full debug composition | +| memory (DA) | `app-da-memory` | `` | Full debug composition | +| skills | `app-skills` | `` | Full debug composition | +| sandboxes | `app-sandboxes` | `` | Full debug composition | + +## Port Assignment + +| Example | Port | Proxy Target | +|---|---|---| +| streaming | 4300 | localhost:8123 | +| persistence | 4301 | localhost:8124 | +| interrupts | 4302 | localhost:8125 | +| memory | 4303 | localhost:8126 | +| time-travel | 4304 | localhost:8127 | +| subgraphs | 4305 | localhost:8128 | +| durable-execution | 4306 | localhost:8129 | +| deployment-runtime | 4307 | localhost:8130 | +| planning | 4310 | localhost:8140 | +| filesystem | 4311 | localhost:8141 | +| subagents (DA) | 4312 | localhost:8142 | +| memory (DA) | 4313 | localhost:8143 | +| skills | 4314 | localhost:8144 | +| sandboxes | 4315 | localhost:8145 | + +## Files Changed Per Example (7 files each) + +For each of the 13 examples: +- Delete: `src/app.component.ts`, `src/app.config.ts` (root duplicates) +- Rewrite: `src/app/{capability}.component.ts` +- Rewrite: `src/app/app.config.ts` +- Rewrite: `src/main.ts` +- Rewrite: `project.json` +- Update: `tsconfig.app.json` +- Update: `src/index.html` +- Update: `src/environments/environment.development.ts` +- Add: `proxy.conf.json` (if missing) From c5f3622148030d893cc475902696c9ac2392f52d Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sun, 5 Apr 2026 16:51:49 -0700 Subject: [PATCH 2/5] docs: cockpit examples chat integration plan --- ...04-05-cockpit-examples-chat-integration.md | 326 ++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-05-cockpit-examples-chat-integration.md diff --git a/docs/superpowers/plans/2026-04-05-cockpit-examples-chat-integration.md b/docs/superpowers/plans/2026-04-05-cockpit-examples-chat-integration.md new file mode 100644 index 000000000..40cef68c5 --- /dev/null +++ b/docs/superpowers/plans/2026-04-05-cockpit-examples-chat-integration.md @@ -0,0 +1,326 @@ +# Cockpit Angular Examples — Chat Integration Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Wire all 13 remaining cockpit Angular examples to correctly consume `@cacheplane/chat`, making each a buildable standalone Angular app. + +**Architecture:** Each example follows the streaming reference pattern: standalone Angular app with `bootstrapApplication()`, `provideStreamResource()` + `provideChat()`, Angular CLI build/serve targets, proxy to LangGraph backend. LangGraph examples use ``, deep-agents examples use ``. + +**Tech Stack:** Angular 21, `@cacheplane/chat`, `@cacheplane/stream-resource`, `@angular-devkit/build-angular` + +**Spec:** `docs/superpowers/specs/2026-04-05-cockpit-examples-chat-integration.md` + +--- + +## Reference Pattern + +The streaming example (`cockpit/langgraph/streaming/angular/`) is the proven pattern. Each of the 13 remaining examples gets the same treatment. + +### Files changed per example: +1. **Delete** `src/app.component.ts` (duplicate) +2. **Delete** `src/app.config.ts` (duplicate at root level) +3. **Rewrite** `src/app/{capability}.component.ts` — use `` or `` +4. **Rewrite** `src/app/app.config.ts` — `provideStreamResource` + `provideChat` +5. **Rewrite** `src/main.ts` — bootstrap correct component +6. **Rewrite** `project.json` — `@angular-devkit/build-angular:application` + `dev-server` +7. **Update** `tsconfig.app.json` — add `lib`, `emitDeclarationOnly` +8. **Update** `src/index.html` — add `` +9. **Update** `src/environments/environment.development.ts` — use `/api` proxy path + +--- + +### Task 1: LangGraph Examples (7 examples) + +**Examples:** persistence, interrupts, memory, time-travel, subgraphs, durable-execution, deployment-runtime + +Each LangGraph example component uses `` as the template. The component creates `streamResource()` with the capability-specific `assistantId`. + +**Configuration per example:** + +| Example | Selector | AssistantId | Port | Proxy Target | +|---|---|---|---|---| +| persistence | `app-persistence` | `persistence` | 4301 | localhost:8124 | +| interrupts | `app-interrupts` | `interrupts` | 4302 | localhost:8125 | +| memory | `app-memory` | `memory` | 4303 | localhost:8126 | +| time-travel | `app-time-travel` | `time-travel` | 4304 | localhost:8127 | +| subgraphs | `app-subgraphs` | `subgraphs` | 4305 | localhost:8128 | +| durable-execution | `app-durable-execution` | `durable-execution` | 4306 | localhost:8129 | +| deployment-runtime | `app-deployment-runtime` | `deployment-runtime` | 4307 | localhost:8130 | + +**For each example, apply these changes:** + +- [ ] **Step 1: Delete duplicate files** + +```bash +rm cockpit/langgraph/{topic}/angular/src/app.component.ts +rm cockpit/langgraph/{topic}/angular/src/app.config.ts +``` + +- [ ] **Step 2: Rewrite capability component** + +Each component follows this template (replace `{Topic}`, `{topic}`, `{selector}`): + +```typescript +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Component } from '@angular/core'; +import { ChatComponent } from '@cacheplane/chat'; +import { streamResource } from '@cacheplane/stream-resource'; +import { environment } from '../environments/environment'; + +@Component({ + selector: '{selector}', + standalone: true, + imports: [ChatComponent], + template: ``, +}) +export class {Topic}Component { + protected readonly stream = streamResource({ + apiUrl: environment.langGraphApiUrl, + assistantId: environment.{topic}AssistantId, + }); +} +``` + +Where each example uses its existing environment config keys (e.g., `environment.persistenceAssistantId`). Check what the existing environment file exports and match it. + +- [ ] **Step 3: Rewrite app.config.ts** + +```typescript +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { ApplicationConfig } from '@angular/core'; +import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; +import { environment } from '../environments/environment'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), + ], +}; +``` + +- [ ] **Step 4: Rewrite main.ts** + +```typescript +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { {Topic}Component } from './app/{topic}.component'; + +bootstrapApplication({Topic}Component, appConfig).catch(console.error); +``` + +- [ ] **Step 5: Rewrite project.json** + +Replace with Angular CLI application pattern. Use the correct project name, paths, port, and smoke test values for each example. Pattern: + +```json +{ + "name": "cockpit-langgraph-{topic}-angular", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "cockpit/langgraph/{topic}/angular/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/cockpit/langgraph/{topic}/angular", + "index": "cockpit/langgraph/{topic}/angular/src/index.html", + "browser": "cockpit/langgraph/{topic}/angular/src/main.ts", + "tsConfig": "cockpit/langgraph/{topic}/angular/tsconfig.app.json", + "styles": ["cockpit/langgraph/{topic}/angular/src/styles.css"] + }, + "configurations": { + "production": { "outputHashing": "all" }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [{ + "replace": "cockpit/langgraph/{topic}/angular/src/environments/environment.ts", + "with": "cockpit/langgraph/{topic}/angular/src/environments/environment.development.ts" + }] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": {port}, + "proxyConfig": "cockpit/langgraph/{topic}/angular/proxy.conf.json" + }, + "configurations": { + "production": { "buildTarget": "cockpit-langgraph-{topic}-angular:build:production" }, + "development": { "buildTarget": "cockpit-langgraph-{topic}-angular:build:development" } + }, + "defaultConfiguration": "development" + }, + "smoke": { ... existing smoke target ... } + } +} +``` + +- [ ] **Step 6: Update tsconfig.app.json** + +Ensure it has: +```json +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../../dist/out-tsc", + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false + }, + "files": ["src/main.ts"], + "include": ["src/**/*.ts"] +} +``` + +- [ ] **Step 7: Update index.html** + +Add `` and correct body classes: +```html + + + + + LangGraph {Topic} — Angular + + + + + + <{selector}> + + +``` + +- [ ] **Step 8: Update environment.development.ts** + +Ensure it uses `/api` proxy (not full URL): +```typescript +export const environment = { + production: false, + langGraphApiUrl: '/api', + {topic}AssistantId: '{topic}', +}; +``` + +- [ ] **Step 9: Update proxy.conf.json target port** + +Each example's proxy should point to its backend port: +```json +{ "/api": { "target": "http://localhost:{backendPort}", "secure": false, "changeOrigin": true, "pathRewrite": { "^/api": "" }, "ws": true } } +``` + +- [ ] **Step 10: Build all 7 examples** + +```bash +npx nx build cockpit-langgraph-persistence-angular +npx nx build cockpit-langgraph-interrupts-angular +npx nx build cockpit-langgraph-memory-angular +npx nx build cockpit-langgraph-time-travel-angular +npx nx build cockpit-langgraph-subgraphs-angular +npx nx build cockpit-langgraph-durable-execution-angular +npx nx build cockpit-langgraph-deployment-runtime-angular +``` + +Fix any build errors. + +- [ ] **Step 11: Commit** + +```bash +git add cockpit/langgraph/ +git commit -m "feat(cockpit): wire 7 LangGraph Angular examples to @cacheplane/chat" +``` + +--- + +### Task 2: Deep Agents Examples (6 examples) + +**Examples:** planning, filesystem, subagents, memory, skills, sandboxes + +Each deep-agents example uses `` instead of ``. + +**Configuration per example:** + +| Example | Selector | AssistantId | Port | Proxy Target | +|---|---|---|---|---| +| planning | `app-planning` | `planning` | 4310 | localhost:8140 | +| filesystem | `app-filesystem` | `filesystem` | 4311 | localhost:8141 | +| subagents | `app-subagents` | `subagents` | 4312 | localhost:8142 | +| memory | `app-da-memory` | `memory` | 4313 | localhost:8143 | +| skills | `app-skills` | `skills` | 4314 | localhost:8144 | +| sandboxes | `app-sandboxes` | `sandboxes` | 4315 | localhost:8145 | + +**Same steps as Task 1, except:** + +- Component imports `ChatDebugComponent` instead of `ChatComponent` +- Template uses `` +- `app.config.ts` also includes `provideRender({})` from `@cacheplane/render` +- `$schema` path is 6 levels deep: `../../../../../../node_modules/nx/schemas/project-schema.json` +- All paths use `cockpit/deep-agents/{topic}/angular/` instead of `cockpit/langgraph/{topic}/angular/` +- Project names: `cockpit-deep-agents-{topic}-angular` + +Component template: +```typescript +import { ChatDebugComponent } from '@cacheplane/chat'; +// ... +template: ``, +``` + +- [ ] **Steps 1-9: Same as Task 1** but for deep-agents paths and ChatDebugComponent + +- [ ] **Step 10: Build all 6 examples** + +```bash +npx nx build cockpit-deep-agents-planning-angular +npx nx build cockpit-deep-agents-filesystem-angular +npx nx build cockpit-deep-agents-subagents-angular +npx nx build cockpit-deep-agents-memory-angular +npx nx build cockpit-deep-agents-skills-angular +npx nx build cockpit-deep-agents-sandboxes-angular +``` + +- [ ] **Step 11: Commit** + +```bash +git add cockpit/deep-agents/ +git commit -m "feat(cockpit): wire 6 Deep Agents Angular examples to @cacheplane/chat" +``` + +--- + +### Task 3: Full Verification + +- [ ] **Step 1: Run library tests** + +```bash +npx nx test render && npx nx test chat && npx nx test stream-resource +``` + +Expected: All pass. + +- [ ] **Step 2: Build all 14 examples (including streaming)** + +```bash +npx nx run-many -t build --projects='cockpit-*-angular' +``` + +Expected: All 14 build. + +- [ ] **Step 3: Commit any fixes** + +--- + +## Summary + +| Task | Description | Examples | +|------|-------------|---------| +| 1 | LangGraph examples → `` | 7 examples | +| 2 | Deep Agents examples → `` | 6 examples | +| 3 | Full verification | All 14 | From 1fb7a2610f55eab1be6f82355c26a95cbf1e4be2 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sun, 5 Apr 2026 16:54:54 -0700 Subject: [PATCH 3/5] docs: add narrative redesign and white paper pipeline specs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec 1 (2026-04-05-narrative-redesign): Four new landing page sections — ProblemSection (gap animation + 3 stats), FullStackSection (3-layer stack with SVG particle connectors), ChatFeaturesSection (interactive 4-tab chat scenarios), FairComparisonSection (honest LangChain comparison table). Includes animation detail, correct fix for border-radius fill artifact, and FeatureStrip copy update. Spec 2 (2026-04-05-whitepaper-pipeline): One-time Anthropic SDK generation script producing a static PDF from six assessment-framework chapters. Free download with optional lead-gen form. No gate. --- .../specs/2026-04-05-narrative-redesign.md | 255 ++++++++++ .../specs/2026-04-05-whitepaper-pipeline.md | 440 ++++++++++++++++++ 2 files changed, 695 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-05-narrative-redesign.md create mode 100644 docs/superpowers/specs/2026-04-05-whitepaper-pipeline.md diff --git a/docs/superpowers/specs/2026-04-05-narrative-redesign.md b/docs/superpowers/specs/2026-04-05-narrative-redesign.md new file mode 100644 index 000000000..b0e5990e6 --- /dev/null +++ b/docs/superpowers/specs/2026-04-05-narrative-redesign.md @@ -0,0 +1,255 @@ +# Narrative Redesign — Implementation Spec + +## Goal + +Add four new landing page sections that reframe StreamResource around the "last mile" production narrative: the problem (why AI projects stall), the full stack architecture, the @cacheplane/chat UI layer in depth, and a fair comparison table. Update one existing section (FeatureStrip) to remove an inaccurate claim. Existing sections are preserved as-is. + +## Architecture + +All new sections are standalone React components added to `apps/website/src/components/landing/`. The landing page (`apps/website/src/app/page.tsx`) is updated to insert them in a specific sequence within the existing section order. Each component uses Framer Motion with `whileInView + viewport={{ once: true }}` for scroll-triggered animations, design tokens from `@cacheplane/design-tokens`, and the established glass-morphism visual language. + +SVG animations use `animateMotion` with `mpath` for particle flows and CSS `@keyframes` for sequential reveal. No new dependencies required — Framer Motion and all tokens are already available. + +## Tech Stack + +- Next.js 16, React 19, TypeScript +- Framer Motion (already in workspace) +- `@cacheplane/design-tokens` for all color/glass values +- Inline SVG for particle animations (no library needed) +- EB Garamond for headlines, Inter for body, JetBrains Mono for labels/code + +--- + +## File Map + +| Action | Path | +|--------|------| +| Create | `apps/website/src/components/landing/ProblemSection.tsx` | +| Create | `apps/website/src/components/landing/FullStackSection.tsx` | +| Create | `apps/website/src/components/landing/ChatFeaturesSection.tsx` | +| Create | `apps/website/src/components/landing/FairComparisonSection.tsx` | +| Modify | `apps/website/src/app/page.tsx` | + +--- + +## Section 1: ProblemSection + +**Placement in page.tsx:** After ``, before ``. + +**Narrative:** The last-mile gap. Three stats establish the problem. An animated progress bar shows teams stalling at 77% and StreamResource closing the gap to 100%. + +### Component structure + +```tsx +'use client'; +import { useRef, useEffect, useState } from 'react'; +import { motion, useInView } from 'framer-motion'; +import { tokens } from '@cacheplane/design-tokens'; + +const STATS = [ + { num: '66%', label: 'of AI solutions are almost right — not quite production-ready' }, + { num: '31%', label: 'of prioritized AI use cases actually reach production' }, + { num: '75%', label: 'of developers still want a human in the loop when trust breaks down' }, +]; +``` + +### Stat cards + +Three glass cards in a 3-column grid. The number uses `tokens.colors.angularRed` with EB Garamond at `3.4rem`. Staggered `whileInView` entrance with `delay: i * 0.1`. + +### Gap animation + +A single `
` track bar with `overflow: hidden` and `borderRadius: 8`. **Critical:** `overflow: hidden` on the track container is what makes the fill clips work correctly — no two-piece border-radius artifact. The fill is one element that transitions in two phases: + +**Phase 1** (triggered by `useInView`): fill width transitions from `0%` → `77%` over `1.7s cubic-bezier(.4,0,.2,1)`. A counter animates from 0 → 77 using `requestAnimationFrame`. + +**Phase 2** (after 1s pause): stall marker (red vertical line at 77%) and hatch overlay appear. Track shakes with a CSS keyframe. + +**Phase 3** (after another 1s): hatch fades out, fill continues `77%` → `100%` over `1s`. The background gradient shifts from red → blue as it crosses the 77% threshold: `linear-gradient(90deg, rgba(221,0,49,.5) 0%, rgba(221,0,49,.38) 70%, rgba(0,64,144,.8) 82%, #004090 100%)`. Counter animates 77 → 100. + +**Phase 4**: "100%" label, "✓ Production" label, and tagline fade in. + +```tsx +// Trigger: useInView with once:true, 30% threshold +// Phase timings (ms from trigger): +// 150 → start fill to 77% +// 2100 → show stall marker + hatch + shake +// 3200 → close gap to 100% +// 4400 → show end labels + tagline +``` + +The tagline: *"Your backend agent may already work. The frontend and production path is what slips the schedule."* + +--- + +## Section 2: FullStackSection + +**Placement in page.tsx:** After ``, before ``. + +**Narrative:** Three-layer architecture. Particles animate downward through each connector, showing data flowing from LangGraph → stream-resource → chat → render. + +### Component structure + +A centered vertical stack, max-width 520px, centered on the page. Above the stack: eyebrow, headline, subtitle. Below the stack: a three-column roadmap strip. + +### LangGraph source node + +Dark card (`#1a1b26`), centered, width 220px. Shows "LangGraph Cloud" label and a pulsing "stream active" live badge. + +### Layer cards + +Three cards with the established color scheme: +- `@cacheplane/stream-resource` — blue (`#004090`), tag "Primitives" +- `@cacheplane/chat` — purple (`#5a00c8`), tag "UI Layer" +- `@cacheplane/render` — green (`#1a7a40`), tag "Gen UI" + +Each card shows the package name and 4–5 chip tags for key APIs. + +### SVG connectors + +Each of the three connectors (LG→SR, SR→chat, chat→render) is a `` element 4px wide, 52px tall. Contains: +- A dashed vertical line +- Two `` elements with `` and `` — staggered `begin` offsets so they flow continuously +- A `` element (display:none) defining the motion path + +Connector labels float to the right of each SVG: "AIMessage stream", "Signal", "Spec · JSON patch". + +### Roadmap strip + +Below the stack, a glass card with three columns: +- **Available now** (green): Text streaming, Tool-call cards, Interrupt flows, Generative UI specs, Thread persistence, Deterministic testing +- **Coming soon** (amber, with "Planned" badge): File attachments, Image inputs & rendering, Audio input, Multi-modal messages +- **On the horizon** (gray): Voice UI primitives, Video stream rendering, Collaborative agents + +--- + +## Section 3: ChatFeaturesSection + +**Placement in page.tsx:** After ``, before ``. + +**Narrative:** @cacheplane/chat in depth. Four interactive tabs (Streaming, Generative UI, Tool Calls, Interrupt) each play a focused chat scenario. Left/right callouts light up as the relevant component activates. + +### Layout + +Three-column: `1fr 440px 1fr`. Center column is the dark chat window. Left/right columns hold callout cards that illuminate when their component appears in the chat sequence. + +### Tab buttons + +Four pill buttons: Streaming, Generative UI, Tool Calls, Interrupt. Clicking a tab clears the chat window, updates the callouts, and runs the scenario. + +### Chat window + +Dark window (`#12131f`) with macOS traffic-light dots, window title, and a colored feature badge. Fixed height 460px with scrollable message list. + +### Scenarios (correct sequencing) + +**Streaming:** User message → typing dots → AI tokens stream in word by word. Callouts light: `` (left) while streaming, `isStreaming()` signal (right) throughout. + +**Generative UI:** User message → typing dots → AI text partial → then inline `` block appears with header "@cacheplane/render · DataTable", rows stream in one at a time. Callouts: `` left, `` right. + +**Tool Calls:** User message → tool card appears immediately (no AI text first — correct LangGraph execution order) with `● running` status. Steps appear sequentially. Card transitions to `✓ done`. Then AI response text streams in. Callouts: `` left, `` right. + +**Interrupt:** User message → typing dots → AI starts a sentence mid-stream → pauses → interrupt panel appears with approve/edit/cancel buttons. Callouts: `` left, `` right. + +### State management + +Each tab scenario is an `async` function using a local `wait(ms)` helper. Tab switch cancels any in-progress animation by checking a `currentScenario` ref that is incremented on each tab change. Each async step checks `if (scenario !== currentScenarioRef.current) return` before proceeding. + +--- + +## Section 4: FairComparisonSection + +**Placement in page.tsx:** After ``, before ``. + +**Narrative:** Honest comparison of LangChain alone vs StreamResource. No attacks. Just what StreamResource adds on top. + +### Component structure + +Eyebrow: "A fair comparison". Headline: "What StreamResource adds". Subtitle: "LangChain and LangGraph are excellent. This is what the Angular production layer provides on top." + +### Comparison table + +Two columns: "LangChain + Angular (without StreamResource)" and "LangChain + Angular + StreamResource". + +| Capability | Without | With | +|---|---|---| +| Token streaming | Custom SSE wiring + zone management | `streamResource()` signal, zero boilerplate | +| Thread persistence | Manual localStorage + API calls | `threadId` signal + `onThreadId` callback | +| Interrupt flows | Custom polling or WebSocket | `interrupt()` signal + resume built in | +| Tool-call rendering | Custom event parsing | `` or headless `` | +| Generative UI | No established pattern | `` + `` + registry | +| Deterministic testing | Mock HTTP + tick management | `MockStreamTransport` + writable signals | +| Human approval UI | Build from scratch | `` | +| Prebuilt full chat | Build from scratch | `` drop-in | + +Each "With" cell uses `tokens.colors.accent` text with a checkmark. Each "Without" cell uses `tokens.colors.textMuted`. + +--- + +## FeatureStrip Update + +**File:** `apps/website/src/components/landing/FeatureStrip.tsx` + +Remove the feature entry that reads "Generative UI — no established Angular pattern exists anywhere" or equivalent language that implies StreamResource is the only solution. Replace with: `{ icon: '🎨', title: 'Generative UI', desc: 'Agent-emitted Angular components via @cacheplane/render. Your component registry, your design — rendered inline from a JSON spec.' }`. + +--- + +## page.tsx Changes + +Insert sections in this order: + +```tsx +// After StatsStrip: + + + +// (then ValueProps, CodeBlock, LangGraphShowcase, DeepAgentsShowcase as before) +// After DeepAgentsShowcase: + +// (then ArchDiagram, FeatureStrip, CockpitCTA as before) +``` + +Add four new imports at the top. + +--- + +## Animation Details + +### Gap fill — no border-radius artifact fix + +The track container uses `overflow: hidden` with `borderRadius: 8`. The fill `
` has no border-radius of its own — it is simply `position: absolute, inset: 0, height: 100%`. The container clips it. This is the only correct approach; separate left/right border-radius on two fill elements always produces a visual seam. + +### SVG particle connectors + +```tsx +// Example connector SVG (copy pattern for all three, change fill color and timing) + + + + + + + + + + + + + + + + +``` + +Use unique `id` values for each connector path (`path-sr`, `path-chat`, `path-render`). + +--- + +## Testing + +Each component can be verified visually by running the dev server (`npm run dev` from `apps/website`). No unit tests required for animation components. The FairComparisonSection has no interactive state — verify it renders the correct table content. + +Scroll-triggered animations: verified by scrolling past each section in the browser. The `viewport={{ once: true }}` flag means each animation plays exactly once per page load. diff --git a/docs/superpowers/specs/2026-04-05-whitepaper-pipeline.md b/docs/superpowers/specs/2026-04-05-whitepaper-pipeline.md new file mode 100644 index 000000000..9a61fb735 --- /dev/null +++ b/docs/superpowers/specs/2026-04-05-whitepaper-pipeline.md @@ -0,0 +1,440 @@ +# White Paper Generation Pipeline — Implementation Spec + +## Goal + +Build a one-time Anthropic SDK generation script that produces a static PDF white paper saved to `apps/website/public/whitepaper.pdf`. The white paper chapter structure is shaped by a six-dimension "production readiness assessment framework" that mirrors the six last-mile pain points. The website change is minimal: remove the current gate (if any), add a free download button, keep an optional lightweight lead-gen form. + +## Architecture + +**Script:** `apps/website/scripts/generate-whitepaper.ts` — runs once via `npx tsx`. Calls the Anthropic API to generate chapter content, then uses Puppeteer to render HTML → PDF. Output saved to `apps/website/public/whitepaper.pdf`. + +**Command:** Added to root `package.json` scripts as `"generate-whitepaper": "npx tsx apps/website/scripts/generate-whitepaper.ts"` + +**Website:** A new `WhitePaperSection` component added to the landing page. Free download link to `/whitepaper.pdf`. Optional lead-gen form (name + email, not required to download). + +No new packages beyond Puppeteer. The Anthropic SDK (`@anthropic-ai/sdk`) is already in `devDependencies`. Puppeteer is installed as a dev dependency. + +## Tech Stack + +- `@anthropic-ai/sdk` (already present at `^0.79.0`) +- `puppeteer` (add as devDependency) +- `npx tsx` for script execution (same pattern as `generate-narrative-docs.ts`) +- Model: `claude-opus-4-5` (or env override via `ANTHROPIC_MODEL`) + +--- + +## File Map + +| Action | Path | +|--------|------| +| Create | `apps/website/scripts/generate-whitepaper.ts` | +| Create | `apps/website/src/components/landing/WhitePaperSection.tsx` | +| Modify | `package.json` (root) — add `generate-whitepaper` script | +| Modify | `apps/website/src/app/page.tsx` — add `` | + +--- + +## Assessment Framework — Six Dimensions + +These six dimensions map directly to the last-mile pain points described in the narrative. Each dimension becomes one chapter of the white paper. + +| # | Dimension | Pain it addresses | +|---|-----------|-------------------| +| 1 | Streaming State Management | Wiring SSE streams into Angular without zone thrashing or custom glue | +| 2 | Thread Persistence | Resuming conversations across refreshes without custom API calls | +| 3 | Tool-Call Rendering | Showing tool execution state inline without parsing raw events | +| 4 | Human Approval Flows | Building interrupt UI that connects back to LangGraph resume | +| 5 | Generative UI | Agent-emitted Angular components without a framework for it | +| 6 | Deterministic Testing | Testing agent-driven components without a real server | + +--- + +## Script: generate-whitepaper.ts + +### Structure + +```typescript +import Anthropic from '@anthropic-ai/sdk'; +import fs from 'fs'; +import path from 'path'; +import puppeteer from 'puppeteer'; + +const client = new Anthropic(); +const MODEL = process.env['ANTHROPIC_MODEL'] ?? 'claude-opus-4-5'; +const OUTPUT_PDF = 'apps/website/public/whitepaper.pdf'; +const OUTPUT_HTML = 'apps/website/public/whitepaper-preview.html'; +``` + +### Chapter definitions + +```typescript +const CHAPTERS = [ + { + id: 'streaming-state', + title: 'Streaming State Management', + dimension: 'Streaming State', + prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + +Chapter topic: Streaming State Management + +Context: Angular teams building LangGraph-powered agents must wire SSE event streams into reactive UI. Without the right primitives, they end up with custom zone-patching, manual subscription management, and brittle token accumulation logic that breaks under load. + +Cover: +- Why streaming state is hard in Angular (zone.js, change detection, timing) +- The signals-native approach: how streamResource() exposes messages() as Signal +- How isStreaming() lets developers drive loading UI without polling +- Code example: minimal streamResource() setup (TypeScript snippet, 8-12 lines) +- Production checklist item: "Are your message signals OnPush-compatible?" + +Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engineers.`, + }, + { + id: 'thread-persistence', + title: 'Thread Persistence', + dimension: 'Thread Persistence', + prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + +Chapter topic: Thread Persistence + +Context: Demos work with ephemeral state. Production agents need conversation history that survives page refreshes, tab switches, and navigation — wired to LangGraph's MemorySaver backend. + +Cover: +- Why stateless agent UIs fail in production +- The threadId signal and onThreadId callback pattern +- How to persist threadId to localStorage and restore on mount +- Thread list UI and switching between conversations +- Code example: provideStreamResource() with threadId (8-12 lines) +- Production checklist item: "Does your agent UI resume threads correctly after a browser refresh?" + +Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engineers.`, + }, + { + id: 'tool-call-rendering', + title: 'Tool-Call Rendering', + dimension: 'Tool-Call Rendering', + prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + +Chapter topic: Tool-Call Rendering + +Context: LangGraph agents invoke tools mid-stream. The UI needs to show tool execution state in real time — steps appearing as the tool runs, a final result, and collapsible history — without parsing raw SSE events by hand. + +Cover: +- What tool call events look like in the raw stream +- Why hand-parsing is fragile and hard to test +- The headless primitive and prebuilt option +- Progressive disclosure: showing steps live, collapsing on completion +- Code example: binding (8-12 lines of Angular template) +- Production checklist item: "Do your tool call cards handle partial step state during streaming?" + +Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engineers.`, + }, + { + id: 'human-approval-flows', + title: 'Human Approval Flows', + dimension: 'Human Approval Flows', + prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + +Chapter topic: Human Approval Flows (Interrupts) + +Context: Production agents that take consequential actions — sending emails, deploying services, modifying data — must pause for human approval before proceeding. This requires a tight loop between LangGraph's interrupt() primitive and Angular UI. + +Cover: +- The LangGraph interrupt() and Command.RESUME pattern +- Why polling and custom websocket approaches are brittle +- The interrupt() signal in streamResource() and how it maps to approval state +- headless and prebuilt +- The three approval actions: approve, edit, cancel — and how each maps to a resume command +- Code example: interrupt signal binding (8-12 lines) +- Production checklist item: "Can your agent UI recover gracefully if a user cancels an interrupt?" + +Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engineers.`, + }, + { + id: 'generative-ui', + title: 'Generative UI', + dimension: 'Generative UI', + prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + +Chapter topic: Generative UI + +Context: The most advanced production agents emit structured UI specs — not just text. A data analysis agent might render a live table. A booking agent might render a reservation form. Without a framework for this, teams either hardcode component logic into the agent or skip the feature entirely. + +Cover: +- The onCustomEvent pattern in LangGraph: how agents emit structured data +- The @cacheplane/render approach: json-render specs, defineAngularRegistry(), +- How JSON patch streaming enables progressive UI updates (rows appearing as data arrives) +- The registry pattern: decoupling agent from component implementation +- Code example: defineAngularRegistry() registration (8-12 lines) +- Production checklist item: "Can your agent emit UI components without tight coupling to the frontend codebase?" + +Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engineers.`, + }, + { + id: 'deterministic-testing', + title: 'Deterministic Testing', + dimension: 'Deterministic Testing', + prompt: `Write a 400-600 word chapter for an engineering white paper titled "From Prototype to Production: The Angular Agent Readiness Guide". + +Chapter topic: Deterministic Testing + +Context: Agent UIs are notoriously hard to test because they depend on live LLM responses. Flaky tests, slow CI, and inability to reproduce edge cases are the main reasons agent UIs ship with low confidence. + +Cover: +- Why testing agent components against real LLM APIs is impractical +- The MockStreamTransport approach: scripted event sequences, no server needed +- createMockStreamResourceRef(): writable signals you control directly in tests +- How to test streaming, interrupts, tool calls, and generative UI in isolation +- Code example: createMockStreamResourceRef() test pattern (10-14 lines) +- Production checklist item: "Do your agent component tests run offline and complete in under 100ms each?" + +Tone: Direct, technical, peer-to-peer. No fluff. Audience is senior Angular engineers.`, + }, +]; +``` + +### Generation function + +```typescript +async function generateChapter(chapter: typeof CHAPTERS[0]): Promise { + console.log(` Generating chapter: ${chapter.title}...`); + const message = await client.messages.create({ + model: MODEL, + max_tokens: 1500, + messages: [{ role: 'user', content: chapter.prompt }], + }); + const content = message.content[0]; + if (content.type !== 'text') throw new Error('Unexpected content type'); + return content.text; +} +``` + +### HTML template + +The script builds a single HTML string with all chapters, then renders to PDF via Puppeteer. + +```typescript +function buildHTML(chapters: Array<{ title: string; content: string }>): string { + const chapterHTML = chapters.map((ch, i) => ` +
+
Chapter ${i + 1}
+

${ch.title}

+
${markdownToHTML(ch.content)}
+
+ `).join('\n'); + + return ` + + + + + + + +
+
StreamResource · Production Readiness Guide
+

From Prototype
to Production

+

The Angular Agent Readiness Guide

+
cacheplane.io · ${new Date().getFullYear()}
+
+ + +
+

Contents

+ ${chapters.map((ch, i) => ` +
+ ${String(i + 1).padStart(2, '0')} + ${ch.title} +
+ `).join('')} +
+ + + ${chapterHTML} + +`; +} +``` + +### markdownToHTML helper + +A minimal converter for the subset of Markdown the API will produce (headings, paragraphs, bold, code blocks, bullet lists). No external library needed: + +```typescript +function markdownToHTML(md: string): string { + return md + .replace(/```[\w]*\n([\s\S]*?)```/g, '
$1
') + .replace(/^### (.+)$/gm, '

$1

') + .replace(/^## (.+)$/gm, '

$1

') + .replace(/\*\*(.+?)\*\*/g, '$1') + .replace(/^- (.+)$/gm, '
  • $1
  • ') + .replace(/(
  • [\s\S]+?<\/li>)/g, '
      $1
    ') + .replace(/\n\n/g, '

    ') + .replace(/^(?!<[hup]|$1

    ') + .replace(/

    <\/p>/g, ''); +} +``` + +### Puppeteer PDF export + +```typescript +async function renderPDF(html: string, outputPath: string): Promise { + console.log(' Launching browser...'); + const browser = await puppeteer.launch({ headless: true }); + const page = await browser.newPage(); + await page.setContent(html, { waitUntil: 'networkidle0' }); + await page.pdf({ + path: outputPath, + format: 'A4', + printBackground: true, + margin: { top: '0', right: '0', bottom: '0', left: '0' }, + }); + await browser.close(); +} +``` + +### main() + +```typescript +async function main() { + console.log('Generating white paper...\n'); + const generatedChapters: Array<{ title: string; content: string }> = []; + + for (const chapter of CHAPTERS) { + const content = await generateChapter(chapter); + generatedChapters.push({ title: chapter.title, content }); + } + + console.log('\nBuilding HTML...'); + const html = buildHTML(generatedChapters); + fs.writeFileSync(OUTPUT_HTML, html, 'utf8'); + + console.log('Rendering PDF...'); + fs.mkdirSync(path.dirname(OUTPUT_PDF), { recursive: true }); + await renderPDF(html, OUTPUT_PDF); + + console.log(`\nDone. PDF saved to ${OUTPUT_PDF}`); + console.log(`Preview HTML saved to ${OUTPUT_HTML}`); +} + +main().catch(err => { console.error(err); process.exit(1); }); +``` + +--- + +## Website: WhitePaperSection Component + +**Placement in page.tsx:** After ``, before ``. + +**File:** `apps/website/src/components/landing/WhitePaperSection.tsx` + +### Design + +Glass card, full-width within a max-width container, two columns: +- Left: eyebrow "Free Download", headline "From Prototype to Production", subtitle (1-2 sentences), download button linking to `/whitepaper.pdf` +- Right: optional lead-gen form — name field, email field, "Get notified of updates" submit button. The form is clearly marked optional with copy like "Optional — download above doesn't require this." + +The download button uses `tokens.colors.accent` background. It is an `` — no server round-trip, no gate. + +### Lead-gen form behavior + +Client component (`'use client'`). On submit, POST to a simple API route `apps/website/src/app/api/whitepaper-signup/route.ts` that writes to a JSON file at `apps/website/data/whitepaper-signups.json` (append-only, NDJSON format). No external service required. The API route returns 200 and the form shows a thank-you message. + +### API route: whitepaper-signup + +```typescript +// apps/website/src/app/api/whitepaper-signup/route.ts +import { NextRequest, NextResponse } from 'next/server'; +import fs from 'fs'; +import path from 'path'; + +const FILE = path.join(process.cwd(), 'data/whitepaper-signups.json'); + +export async function POST(req: NextRequest) { + const { name, email } = await req.json(); + if (!email) return NextResponse.json({ error: 'Email required' }, { status: 400 }); + + const entry = JSON.stringify({ name, email, ts: new Date().toISOString() }) + '\n'; + fs.mkdirSync(path.dirname(FILE), { recursive: true }); + fs.appendFileSync(FILE, entry, 'utf8'); + + return NextResponse.json({ ok: true }); +} +``` + +--- + +## package.json Changes + +Add to root `package.json` scripts: + +```json +"generate-whitepaper": "npx tsx apps/website/scripts/generate-whitepaper.ts" +``` + +Add to root `devDependencies`: + +```json +"puppeteer": "^22.0.0" +``` + +--- + +## page.tsx Changes + +```tsx +import { WhitePaperSection } from '../components/landing/WhitePaperSection'; +// ... after FairComparisonSection, before ArchDiagram: + +``` + +--- + +## Running the Script + +```bash +# From repo root +npm install # picks up puppeteer +npm run generate-whitepaper +# Output: apps/website/public/whitepaper.pdf +``` + +The script is run once by the team. The resulting PDF is committed to the repo and served statically. It does not run on every build. To regenerate, run the script again and commit the new PDF. + +--- + +## Testing + +1. Run `npm run generate-whitepaper` — verify PDF appears at `apps/website/public/whitepaper.pdf` +2. Open `apps/website/public/whitepaper-preview.html` in a browser — verify chapter formatting +3. Start dev server, navigate to landing page, verify `` renders with correct download link +4. Click download button — verify PDF downloads +5. Submit optional form — verify entry appears in `apps/website/data/whitepaper-signups.json` From 7052feab6d1e25d447a1c5fd8e593bc7db5b9cbd Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sun, 5 Apr 2026 17:01:03 -0700 Subject: [PATCH 4/5] feat(cockpit): wire 7 LangGraph Angular examples to @cacheplane/chat Co-Authored-By: Claude Opus 4.6 (1M context) --- .../deployment-runtime/angular/project.json | 47 ++++++++-- .../angular/src/app.component.ts | 46 ---------- .../angular/src/app.config.ts | 13 --- .../angular/src/app/app.config.ts | 15 +--- .../src/app/deployment-runtime.component.ts | 76 +--------------- .../environments/environment.development.ts | 2 +- .../deployment-runtime/angular/src/index.html | 5 +- .../deployment-runtime/angular/src/main.ts | 6 +- .../angular/tsconfig.app.json | 4 +- .../durable-execution/angular/project.json | 47 ++++++++-- .../durable-execution/angular/proxy.conf.json | 2 +- .../angular/src/app.component.ts | 39 -------- .../angular/src/app.config.ts | 13 --- .../angular/src/app/app.config.ts | 14 +-- .../src/app/durable-execution.component.ts | 82 +---------------- .../environments/environment.development.ts | 2 +- .../durable-execution/angular/src/index.html | 5 +- .../durable-execution/angular/src/main.ts | 6 +- .../angular/tsconfig.app.json | 4 +- .../langgraph/interrupts/angular/project.json | 47 ++++++++-- .../interrupts/angular/src/app.component.ts | 34 ------- .../interrupts/angular/src/app.config.ts | 13 --- .../interrupts/angular/src/app/app.config.ts | 14 +-- .../angular/src/app/interrupts.component.ts | 69 +------------- .../environments/environment.development.ts | 2 +- .../interrupts/angular/src/index.html | 5 +- .../langgraph/interrupts/angular/src/main.ts | 6 +- .../interrupts/angular/tsconfig.app.json | 4 +- cockpit/langgraph/memory/angular/project.json | 47 ++++++++-- .../memory/angular/src/app.component.ts | 90 ------------------- .../memory/angular/src/app.config.ts | 13 --- .../memory/angular/src/app/app.config.ts | 14 +-- .../angular/src/app/memory.component.ts | 70 +-------------- .../environments/environment.development.ts | 2 +- .../langgraph/memory/angular/src/index.html | 5 +- cockpit/langgraph/memory/angular/src/main.ts | 6 +- .../memory/angular/tsconfig.app.json | 4 +- .../persistence/angular/project.json | 47 ++++++++-- .../persistence/angular/src/app.component.ts | 84 ----------------- .../persistence/angular/src/app.config.ts | 13 --- .../persistence/angular/src/app/app.config.ts | 14 +-- .../angular/src/app/persistence.component.ts | 74 +-------------- .../environments/environment.development.ts | 2 +- .../persistence/angular/src/index.html | 5 +- .../langgraph/persistence/angular/src/main.ts | 6 +- .../persistence/angular/tsconfig.app.json | 4 +- .../langgraph/subgraphs/angular/project.json | 47 ++++++++-- .../subgraphs/angular/src/app.component.ts | 37 -------- .../subgraphs/angular/src/app.config.ts | 13 --- .../subgraphs/angular/src/app/app.config.ts | 14 +-- .../angular/src/app/subgraphs.component.ts | 54 +---------- .../environments/environment.development.ts | 2 +- .../subgraphs/angular/src/index.html | 5 +- .../langgraph/subgraphs/angular/src/main.ts | 6 +- .../subgraphs/angular/tsconfig.app.json | 4 +- .../time-travel/angular/project.json | 47 ++++++++-- .../time-travel/angular/proxy.conf.json | 2 +- .../time-travel/angular/src/app.component.ts | 37 -------- .../time-travel/angular/src/app.config.ts | 13 --- .../time-travel/angular/src/app/app.config.ts | 14 +-- .../angular/src/app/time-travel.component.ts | 70 +-------------- .../environments/environment.development.ts | 2 +- .../time-travel/angular/src/index.html | 5 +- .../langgraph/time-travel/angular/src/main.ts | 6 +- .../time-travel/angular/tsconfig.app.json | 4 +- 65 files changed, 403 insertions(+), 1101 deletions(-) delete mode 100644 cockpit/langgraph/deployment-runtime/angular/src/app.component.ts delete mode 100644 cockpit/langgraph/deployment-runtime/angular/src/app.config.ts delete mode 100644 cockpit/langgraph/durable-execution/angular/src/app.component.ts delete mode 100644 cockpit/langgraph/durable-execution/angular/src/app.config.ts delete mode 100644 cockpit/langgraph/interrupts/angular/src/app.component.ts delete mode 100644 cockpit/langgraph/interrupts/angular/src/app.config.ts delete mode 100644 cockpit/langgraph/memory/angular/src/app.component.ts delete mode 100644 cockpit/langgraph/memory/angular/src/app.config.ts delete mode 100644 cockpit/langgraph/persistence/angular/src/app.component.ts delete mode 100644 cockpit/langgraph/persistence/angular/src/app.config.ts delete mode 100644 cockpit/langgraph/subgraphs/angular/src/app.component.ts delete mode 100644 cockpit/langgraph/subgraphs/angular/src/app.config.ts delete mode 100644 cockpit/langgraph/time-travel/angular/src/app.component.ts delete mode 100644 cockpit/langgraph/time-travel/angular/src/app.config.ts diff --git a/cockpit/langgraph/deployment-runtime/angular/project.json b/cockpit/langgraph/deployment-runtime/angular/project.json index 7eb4b9fac..6a8f11507 100644 --- a/cockpit/langgraph/deployment-runtime/angular/project.json +++ b/cockpit/langgraph/deployment-runtime/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-langgraph-deployment-runtime-angular", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/langgraph/deployment-runtime/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/deployment-runtime/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/langgraph/deployment-runtime/angular", - "main": "cockpit/langgraph/deployment-runtime/angular/src/index.ts", - "tsConfig": "cockpit/langgraph/deployment-runtime/angular/tsconfig.json" - } + "index": "cockpit/langgraph/deployment-runtime/angular/src/index.html", + "browser": "cockpit/langgraph/deployment-runtime/angular/src/main.ts", + "tsConfig": "cockpit/langgraph/deployment-runtime/angular/tsconfig.app.json", + "styles": ["cockpit/langgraph/deployment-runtime/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/langgraph/deployment-runtime/angular/src/environments/environment.ts", + "with": "cockpit/langgraph/deployment-runtime/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4307, + "proxyConfig": "cockpit/langgraph/deployment-runtime/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-langgraph-deployment-runtime-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-langgraph-deployment-runtime-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/langgraph/deployment-runtime/angular/src/app.component.ts b/cockpit/langgraph/deployment-runtime/angular/src/app.component.ts deleted file mode 100644 index a043b8cc5..000000000 --- a/cockpit/langgraph/deployment-runtime/angular/src/app.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Component, inject, Injector, InjectionToken, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -/** Provide via environment.ts / environment.prod.ts for zero-code env switching. */ -export const LANGGRAPH_CONFIG = new InjectionToken<{ - apiUrl: string; - assistantId: string; -}>('LANGGRAPH_CONFIG', { - factory: () => ({ - apiUrl: 'http://localhost:2024', - assistantId: 'chat_agent', - }), -}); - -@Component({ - selector: 'app-deployment-runtime', - standalone: true, - imports: [ChatComponent], - template: ` -

    - `, -}) -export class DeploymentRuntimeAppComponent implements OnInit { - private readonly injector = inject(Injector); - private readonly config = inject(LANGGRAPH_CONFIG); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ - apiUrl: this.config.apiUrl, - assistantId: this.config.assistantId, - }); - }); - } -} diff --git a/cockpit/langgraph/deployment-runtime/angular/src/app.config.ts b/cockpit/langgraph/deployment-runtime/angular/src/app.config.ts deleted file mode 100644 index 6ac3b924c..000000000 --- a/cockpit/langgraph/deployment-runtime/angular/src/app.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - ], -}; diff --git a/cockpit/langgraph/deployment-runtime/angular/src/app/app.config.ts b/cockpit/langgraph/deployment-runtime/angular/src/app/app.config.ts index 9ae6eaaf5..b52a916ac 100644 --- a/cockpit/langgraph/deployment-runtime/angular/src/app/app.config.ts +++ b/cockpit/langgraph/deployment-runtime/angular/src/app/app.config.ts @@ -1,19 +1,12 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; import { environment } from '../environments/environment'; -/** - * Application configuration for the LangGraph Deployment Runtime demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * In production this URL points to a LangGraph Cloud deployment. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), ], }; diff --git a/cockpit/langgraph/deployment-runtime/angular/src/app/deployment-runtime.component.ts b/cockpit/langgraph/deployment-runtime/angular/src/app/deployment-runtime.component.ts index cd8f0a6d6..05410c24f 100644 --- a/cockpit/langgraph/deployment-runtime/angular/src/app/deployment-runtime.component.ts +++ b/cockpit/langgraph/deployment-runtime/angular/src/app/deployment-runtime.component.ts @@ -1,90 +1,18 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { Component } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -/** - * DeploymentRuntimeComponent demonstrates production deployment patterns. - * - * Shows how streamResource() connects to a deployed LangGraph Cloud - * instance. The sidebar displays connection info and deployment status. - */ @Component({ selector: 'app-deployment-runtime', standalone: true, imports: [ChatComponent], - template: ` - - -

    Deployment

    - -
    -
    API URL
    -
    - {{ apiUrl }} -
    -
    - -
    -
    Assistant ID
    -
    - {{ assistantId }} -
    -
    - -
    -
    Status
    - - {{ stream.status() }} - -
    - - @if (currentThreadId) { -
    -
    Thread ID
    -
    - {{ currentThreadId }} -
    -
    - } -
    -
    - `, + template: ``, }) export class DeploymentRuntimeComponent { protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.deploymentRuntimeAssistantId, - onThreadId: (id: string) => { - this.currentThreadId = id; - }, }); - - readonly apiUrl = environment.langGraphApiUrl; - readonly assistantId = environment.deploymentRuntimeAssistantId; - currentThreadId = ''; - - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } - - statusBadgeBackground(): string { - const status = this.stream.status(); - if (status === 'loading') return 'rgba(0,160,80,0.12)'; - if (status === 'error') return 'rgba(200,40,40,0.1)'; - return 'rgba(0,64,144,0.08)'; - } - - statusBadgeColor(): string { - const status = this.stream.status(); - if (status === 'loading') return '#00802a'; - if (status === 'error') return '#c82828'; - return '#004090'; - } } diff --git a/cockpit/langgraph/deployment-runtime/angular/src/environments/environment.development.ts b/cockpit/langgraph/deployment-runtime/angular/src/environments/environment.development.ts index 14f57678a..9dfed1292 100644 --- a/cockpit/langgraph/deployment-runtime/angular/src/environments/environment.development.ts +++ b/cockpit/langgraph/deployment-runtime/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4307/api', + langGraphApiUrl: '/api', deploymentRuntimeAssistantId: 'deployment-runtime', }; diff --git a/cockpit/langgraph/deployment-runtime/angular/src/index.html b/cockpit/langgraph/deployment-runtime/angular/src/index.html index fde252ce3..5290cf4af 100644 --- a/cockpit/langgraph/deployment-runtime/angular/src/index.html +++ b/cockpit/langgraph/deployment-runtime/angular/src/index.html @@ -2,11 +2,12 @@ - Deployment Runtime - LangGraph Angular Example + LangGraph Deployment & Runtime — Angular + - + diff --git a/cockpit/langgraph/deployment-runtime/angular/src/main.ts b/cockpit/langgraph/deployment-runtime/angular/src/main.ts index 57d8f1c7f..45a47e1ed 100644 --- a/cockpit/langgraph/deployment-runtime/angular/src/main.ts +++ b/cockpit/langgraph/deployment-runtime/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { DeploymentRuntimeAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { DeploymentRuntimeComponent } from './app/deployment-runtime.component'; -bootstrapApplication(DeploymentRuntimeAppComponent, appConfig).catch(console.error); +bootstrapApplication(DeploymentRuntimeComponent, appConfig).catch(console.error); diff --git a/cockpit/langgraph/deployment-runtime/angular/tsconfig.app.json b/cockpit/langgraph/deployment-runtime/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/langgraph/deployment-runtime/angular/tsconfig.app.json +++ b/cockpit/langgraph/deployment-runtime/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/langgraph/durable-execution/angular/project.json b/cockpit/langgraph/durable-execution/angular/project.json index dbc9cc575..a98d4b82a 100644 --- a/cockpit/langgraph/durable-execution/angular/project.json +++ b/cockpit/langgraph/durable-execution/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-langgraph-durable-execution-angular", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/langgraph/durable-execution/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/durable-execution/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/langgraph/durable-execution/angular", - "main": "cockpit/langgraph/durable-execution/angular/src/index.ts", - "tsConfig": "cockpit/langgraph/durable-execution/angular/tsconfig.json" - } + "index": "cockpit/langgraph/durable-execution/angular/src/index.html", + "browser": "cockpit/langgraph/durable-execution/angular/src/main.ts", + "tsConfig": "cockpit/langgraph/durable-execution/angular/tsconfig.app.json", + "styles": ["cockpit/langgraph/durable-execution/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/langgraph/durable-execution/angular/src/environments/environment.ts", + "with": "cockpit/langgraph/durable-execution/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4306, + "proxyConfig": "cockpit/langgraph/durable-execution/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-langgraph-durable-execution-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-langgraph-durable-execution-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/langgraph/durable-execution/angular/proxy.conf.json b/cockpit/langgraph/durable-execution/angular/proxy.conf.json index 36c6b6749..4b45f71fb 100644 --- a/cockpit/langgraph/durable-execution/angular/proxy.conf.json +++ b/cockpit/langgraph/durable-execution/angular/proxy.conf.json @@ -1,6 +1,6 @@ { "/api": { - "target": "http://localhost:8127", + "target": "http://localhost:8129", "secure": false, "changeOrigin": true, "pathRewrite": { "^/api": "" }, diff --git a/cockpit/langgraph/durable-execution/angular/src/app.component.ts b/cockpit/langgraph/durable-execution/angular/src/app.component.ts deleted file mode 100644 index af24a6ac6..000000000 --- a/cockpit/langgraph/durable-execution/angular/src/app.component.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Component, inject, Injector, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { ChatComponent, ChatErrorComponent } from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -@Component({ - selector: 'app-durable-execution', - standalone: true, - imports: [ChatComponent, ChatErrorComponent], - template: ` -
    - - - - -
    - `, -}) -export class DurableExecutionAppComponent implements OnInit { - private readonly injector = inject(Injector); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ - assistantId: 'chat_agent', - retry: { maxAttempts: 5, baseDelayMs: 500 }, - }); - }); - } -} diff --git a/cockpit/langgraph/durable-execution/angular/src/app.config.ts b/cockpit/langgraph/durable-execution/angular/src/app.config.ts deleted file mode 100644 index 6ac3b924c..000000000 --- a/cockpit/langgraph/durable-execution/angular/src/app.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - ], -}; diff --git a/cockpit/langgraph/durable-execution/angular/src/app/app.config.ts b/cockpit/langgraph/durable-execution/angular/src/app/app.config.ts index 3ef0ea1e9..b52a916ac 100644 --- a/cockpit/langgraph/durable-execution/angular/src/app/app.config.ts +++ b/cockpit/langgraph/durable-execution/angular/src/app/app.config.ts @@ -1,18 +1,12 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; import { environment } from '../environments/environment'; -/** - * Application configuration for the LangGraph Durable Execution demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), ], }; diff --git a/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts b/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts index e4cb26b4a..784eafcb8 100644 --- a/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts +++ b/cockpit/langgraph/durable-execution/angular/src/app/durable-execution.component.ts @@ -1,96 +1,18 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { Component } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -/** - * DurableExecutionComponent demonstrates fault-tolerant multi-step execution - * with `streamResource()`. - * - * This example shows how a graph checkpoints at each node, enabling it to - * resume after failures. The sidebar shows execution status in real time: - * - `stream.status()` as a badge (idle/loading/resolved/error) - * - `stream.hasValue()` indicator for received data - * - A "Retry" button that calls `stream.reload()` when `stream.error()` is set - * - * The backend processes each request through three nodes: - * analyze → plan → generate - * Each node updates `state.step` so the UI can track progress. - */ @Component({ selector: 'app-durable-execution', standalone: true, imports: [ChatComponent], - template: ` - - -

    Execution Status

    - -
    - Status -
    - - {{ stream.status() }} - -
    -
    - -
    - Data Received -
    - - {{ stream.hasValue() ? 'Yes' : 'No' }} -
    -
    - - @if (stream.error()) { -
    -
    Execution Failed
    - -
    - } -
    -
    - `, + template: ``, }) export class DurableExecutionComponent { - /** - * The streaming resource backing this durable-execution demo. - * - * The graph runs three nodes (analyze → plan → generate), checkpointing - * after each one. If the graph fails partway through, `stream.reload()` - * re-submits the last input so the run can resume from the last checkpoint. - */ protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - /** - * Submit a message to be processed through the multi-node graph. - */ - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } - - /** - * Returns a colour for the status badge based on the current stream status. - */ - statusBadgeColor(): string { - switch (this.stream.status()) { - case 'loading': - case 'reloading': return '#2563eb'; - case 'resolved': return '#16a34a'; - case 'error': return '#dc2626'; - default: return '#6b7280'; - } - } } diff --git a/cockpit/langgraph/durable-execution/angular/src/environments/environment.development.ts b/cockpit/langgraph/durable-execution/angular/src/environments/environment.development.ts index 3e05fdd69..3fd5889eb 100644 --- a/cockpit/langgraph/durable-execution/angular/src/environments/environment.development.ts +++ b/cockpit/langgraph/durable-execution/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4304/api', + langGraphApiUrl: '/api', streamingAssistantId: 'durable-execution', }; diff --git a/cockpit/langgraph/durable-execution/angular/src/index.html b/cockpit/langgraph/durable-execution/angular/src/index.html index 35a132130..014c5c510 100644 --- a/cockpit/langgraph/durable-execution/angular/src/index.html +++ b/cockpit/langgraph/durable-execution/angular/src/index.html @@ -2,11 +2,12 @@ - Durable Execution - LangGraph Angular Example + LangGraph Durable Execution — Angular + - + diff --git a/cockpit/langgraph/durable-execution/angular/src/main.ts b/cockpit/langgraph/durable-execution/angular/src/main.ts index b4e7384ec..d309624fb 100644 --- a/cockpit/langgraph/durable-execution/angular/src/main.ts +++ b/cockpit/langgraph/durable-execution/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { DurableExecutionAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { DurableExecutionComponent } from './app/durable-execution.component'; -bootstrapApplication(DurableExecutionAppComponent, appConfig).catch(console.error); +bootstrapApplication(DurableExecutionComponent, appConfig).catch(console.error); diff --git a/cockpit/langgraph/durable-execution/angular/tsconfig.app.json b/cockpit/langgraph/durable-execution/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/langgraph/durable-execution/angular/tsconfig.app.json +++ b/cockpit/langgraph/durable-execution/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/langgraph/interrupts/angular/project.json b/cockpit/langgraph/interrupts/angular/project.json index c34cc71a0..94f888503 100644 --- a/cockpit/langgraph/interrupts/angular/project.json +++ b/cockpit/langgraph/interrupts/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-langgraph-interrupts-angular", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/langgraph/interrupts/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/interrupts/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/langgraph/interrupts/angular", - "main": "cockpit/langgraph/interrupts/angular/src/index.ts", - "tsConfig": "cockpit/langgraph/interrupts/angular/tsconfig.json" - } + "index": "cockpit/langgraph/interrupts/angular/src/index.html", + "browser": "cockpit/langgraph/interrupts/angular/src/main.ts", + "tsConfig": "cockpit/langgraph/interrupts/angular/tsconfig.app.json", + "styles": ["cockpit/langgraph/interrupts/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/langgraph/interrupts/angular/src/environments/environment.ts", + "with": "cockpit/langgraph/interrupts/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4302, + "proxyConfig": "cockpit/langgraph/interrupts/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-langgraph-interrupts-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-langgraph-interrupts-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/langgraph/interrupts/angular/src/app.component.ts b/cockpit/langgraph/interrupts/angular/src/app.component.ts deleted file mode 100644 index 9e55364ff..000000000 --- a/cockpit/langgraph/interrupts/angular/src/app.component.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Component, inject, Injector, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { - ChatComponent, - ChatInterruptPanelComponent, -} from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -@Component({ - selector: 'app-interrupts', - standalone: true, - imports: [ChatComponent, ChatInterruptPanelComponent], - template: ` -
    - - - - -
    - `, -}) -export class InterruptsAppComponent implements OnInit { - private readonly injector = inject(Injector); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ assistantId: 'interrupt_agent' }); - }); - } -} diff --git a/cockpit/langgraph/interrupts/angular/src/app.config.ts b/cockpit/langgraph/interrupts/angular/src/app.config.ts deleted file mode 100644 index 6ac3b924c..000000000 --- a/cockpit/langgraph/interrupts/angular/src/app.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - ], -}; diff --git a/cockpit/langgraph/interrupts/angular/src/app/app.config.ts b/cockpit/langgraph/interrupts/angular/src/app/app.config.ts index b9b7bea96..b52a916ac 100644 --- a/cockpit/langgraph/interrupts/angular/src/app/app.config.ts +++ b/cockpit/langgraph/interrupts/angular/src/app/app.config.ts @@ -1,18 +1,12 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; import { environment } from '../environments/environment'; -/** - * Application configuration for the LangGraph Interrupts demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), ], }; diff --git a/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts b/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts index 96c9225ae..c7bb89986 100644 --- a/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts +++ b/cockpit/langgraph/interrupts/angular/src/app/interrupts.component.ts @@ -1,83 +1,18 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { Component } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -/** - * InterruptsComponent demonstrates human-in-the-loop with `streamResource()`. - * - * The LangGraph backend pauses execution when it needs human approval. - * The `stream.interrupt()` signal provides the interrupt data, and - * `stream.submit()` resumes execution with the human's decision. - * - * Key integration points: - * - `stream.interrupt()` — current pause data - * - `stream.submit({ resume: true })` — resume after approval - * - The graph uses LangGraph's `interrupt()` function to pause - */ @Component({ selector: 'app-interrupts', standalone: true, imports: [ChatComponent], - template: ` - - -

    Approvals

    - @if (stream.interrupt()) { -
    -

    {{ stream.interrupt() }}

    - - -
    - } @else { -

    No pending approvals

    - } -
    -
    - `, + template: ``, }) export class InterruptsComponent { - /** - * The streaming resource with interrupt support. - * - * When the LangGraph backend calls `interrupt()`, the `stream.interrupt()` - * signal emits the interrupt payload for display in the sidebar. - */ protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - /** - * Submit a message to the assistant. - */ - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } - - /** - * Approve the pending action and resume execution. - * Submitting null continues the graph (LangGraph convention). - */ - approve(): void { - this.stream.submit(null); - } - - /** - * Reject the pending action. Sends a resume value of false - * so the graph can handle rejection logic. - */ - reject(): void { - this.stream.submit({ resume: false }); - } } diff --git a/cockpit/langgraph/interrupts/angular/src/environments/environment.development.ts b/cockpit/langgraph/interrupts/angular/src/environments/environment.development.ts index eb08a2bc6..f897735e1 100644 --- a/cockpit/langgraph/interrupts/angular/src/environments/environment.development.ts +++ b/cockpit/langgraph/interrupts/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4302/api', + langGraphApiUrl: '/api', streamingAssistantId: 'interrupts', }; diff --git a/cockpit/langgraph/interrupts/angular/src/index.html b/cockpit/langgraph/interrupts/angular/src/index.html index 850914943..d32fdb39e 100644 --- a/cockpit/langgraph/interrupts/angular/src/index.html +++ b/cockpit/langgraph/interrupts/angular/src/index.html @@ -2,11 +2,12 @@ - Interrupts - LangGraph Angular Example + LangGraph Interrupts — Angular + - + diff --git a/cockpit/langgraph/interrupts/angular/src/main.ts b/cockpit/langgraph/interrupts/angular/src/main.ts index aad2c72e0..16589a6b1 100644 --- a/cockpit/langgraph/interrupts/angular/src/main.ts +++ b/cockpit/langgraph/interrupts/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { InterruptsAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { InterruptsComponent } from './app/interrupts.component'; -bootstrapApplication(InterruptsAppComponent, appConfig).catch(console.error); +bootstrapApplication(InterruptsComponent, appConfig).catch(console.error); diff --git a/cockpit/langgraph/interrupts/angular/tsconfig.app.json b/cockpit/langgraph/interrupts/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/langgraph/interrupts/angular/tsconfig.app.json +++ b/cockpit/langgraph/interrupts/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/langgraph/memory/angular/project.json b/cockpit/langgraph/memory/angular/project.json index bf746a477..8f9ed1a7f 100644 --- a/cockpit/langgraph/memory/angular/project.json +++ b/cockpit/langgraph/memory/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-langgraph-memory-angular", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/langgraph/memory/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/memory/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/langgraph/memory/angular", - "main": "cockpit/langgraph/memory/angular/src/index.ts", - "tsConfig": "cockpit/langgraph/memory/angular/tsconfig.json" - } + "index": "cockpit/langgraph/memory/angular/src/index.html", + "browser": "cockpit/langgraph/memory/angular/src/main.ts", + "tsConfig": "cockpit/langgraph/memory/angular/tsconfig.app.json", + "styles": ["cockpit/langgraph/memory/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/langgraph/memory/angular/src/environments/environment.ts", + "with": "cockpit/langgraph/memory/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4303, + "proxyConfig": "cockpit/langgraph/memory/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-langgraph-memory-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-langgraph-memory-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/langgraph/memory/angular/src/app.component.ts b/cockpit/langgraph/memory/angular/src/app.component.ts deleted file mode 100644 index b61309b77..000000000 --- a/cockpit/langgraph/memory/angular/src/app.component.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Component, inject, Injector, OnInit, signal } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { ChatComponent } from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -interface Thread { - id: string; - label: string; -} - -@Component({ - selector: 'app-memory', - standalone: true, - imports: [CommonModule, ChatComponent], - template: ` -
    - - - - -
    - -
    -
    - `, -}) -export class MemoryAppComponent implements OnInit { - private readonly injector = inject(Injector); - /** Stable user identity so the memory store scopes memories correctly. */ - private readonly userId = 'demo-user'; - - chat!: StreamResourceRef; - threads = signal([ - { id: 'thread-1', label: 'Session 1' }, - { id: 'thread-2', label: 'Session 2' }, - ]); - activeThreadId = signal('thread-1'); - - ngOnInit(): void { - this.initChat(this.activeThreadId()); - } - - selectThread(threadId: string): void { - this.activeThreadId.set(threadId); - this.initChat(threadId); - } - - newThread(): void { - const id = `thread-${Date.now()}`; - this.threads.update((ts) => [ - ...ts, - { id, label: `Session ${ts.length + 1}` }, - ]); - this.selectThread(id); - } - - private initChat(threadId: string): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ - assistantId: 'memory_agent', - threadId, - metadata: { userId: this.userId }, - }); - }); - } -} diff --git a/cockpit/langgraph/memory/angular/src/app.config.ts b/cockpit/langgraph/memory/angular/src/app.config.ts deleted file mode 100644 index 6ac3b924c..000000000 --- a/cockpit/langgraph/memory/angular/src/app.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - ], -}; diff --git a/cockpit/langgraph/memory/angular/src/app/app.config.ts b/cockpit/langgraph/memory/angular/src/app/app.config.ts index 0fb41954c..b52a916ac 100644 --- a/cockpit/langgraph/memory/angular/src/app/app.config.ts +++ b/cockpit/langgraph/memory/angular/src/app/app.config.ts @@ -1,18 +1,12 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; import { environment } from '../environments/environment'; -/** - * Application configuration for the LangGraph Memory demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), ], }; diff --git a/cockpit/langgraph/memory/angular/src/app/memory.component.ts b/cockpit/langgraph/memory/angular/src/app/memory.component.ts index 8f3bb9b89..0c6c171ca 100644 --- a/cockpit/langgraph/memory/angular/src/app/memory.component.ts +++ b/cockpit/langgraph/memory/angular/src/app/memory.component.ts @@ -1,82 +1,18 @@ -import { Component, computed } from '@angular/core'; +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Component } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -/** - * MemoryComponent demonstrates cross-thread persistent context with `streamResource()`. - * - * This example shows how an agent can learn and remember facts about the user - * across separate conversations. The graph maintains a `memory` dict in its - * state that is updated as new facts are extracted from the conversation. - * - * Key integration points: - * - `stream.value()` exposes the full graph state, including the `memory` field - * - `memory()` signal is derived from `stream.value()` for reactive sidebar rendering - * - Facts appear in the sidebar as the agent learns them during conversation - */ @Component({ selector: 'app-memory', standalone: true, imports: [ChatComponent], - template: ` - - -

    Agent Memory

    - @if (memoryEntries().length === 0) { -

    - No facts learned yet. Start chatting! -

    - } - @for (entry of memoryEntries(); track entry.key) { -
    -
    - {{ entry.key }} -
    -
    - {{ entry.value }} -
    -
    - } -
    -
    - `, + template: ``, }) export class MemoryComponent { - /** - * The streaming resource connected to the memory graph. - * - * The graph returns a `memory` dict alongside messages in its state. - * We expose it via `stream.value()` and derive a reactive signal for display. - */ protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - /** - * Reactive list of key-value memory entries derived from the graph state. - * - * The graph updates `memory` as it learns facts from the conversation. - * This signal re-computes whenever the stream state changes. - */ - protected readonly memoryEntries = computed(() => { - const state = this.stream.value() as { memory?: Record } | null; - const memory = state?.memory ?? {}; - return Object.entries(memory).map(([key, value]) => ({ - key, - value: typeof value === 'string' ? value : JSON.stringify(value), - })); - }); - - /** - * Submit a message to the agent. - */ - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } } diff --git a/cockpit/langgraph/memory/angular/src/environments/environment.development.ts b/cockpit/langgraph/memory/angular/src/environments/environment.development.ts index 4524766dd..42fd0147d 100644 --- a/cockpit/langgraph/memory/angular/src/environments/environment.development.ts +++ b/cockpit/langgraph/memory/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4303/api', + langGraphApiUrl: '/api', streamingAssistantId: 'memory', }; diff --git a/cockpit/langgraph/memory/angular/src/index.html b/cockpit/langgraph/memory/angular/src/index.html index f78ad2ca0..ffaedb18d 100644 --- a/cockpit/langgraph/memory/angular/src/index.html +++ b/cockpit/langgraph/memory/angular/src/index.html @@ -2,11 +2,12 @@ - Memory - LangGraph Angular Example + LangGraph Memory — Angular + - + diff --git a/cockpit/langgraph/memory/angular/src/main.ts b/cockpit/langgraph/memory/angular/src/main.ts index 180df289a..e53854a71 100644 --- a/cockpit/langgraph/memory/angular/src/main.ts +++ b/cockpit/langgraph/memory/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { MemoryAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { MemoryComponent } from './app/memory.component'; -bootstrapApplication(MemoryAppComponent, appConfig).catch(console.error); +bootstrapApplication(MemoryComponent, appConfig).catch(console.error); diff --git a/cockpit/langgraph/memory/angular/tsconfig.app.json b/cockpit/langgraph/memory/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/langgraph/memory/angular/tsconfig.app.json +++ b/cockpit/langgraph/memory/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/langgraph/persistence/angular/project.json b/cockpit/langgraph/persistence/angular/project.json index 0c363e462..44aa263c8 100644 --- a/cockpit/langgraph/persistence/angular/project.json +++ b/cockpit/langgraph/persistence/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-langgraph-persistence-angular", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/langgraph/persistence/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/persistence/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/langgraph/persistence/angular", - "main": "cockpit/langgraph/persistence/angular/src/index.ts", - "tsConfig": "cockpit/langgraph/persistence/angular/tsconfig.json" - } + "index": "cockpit/langgraph/persistence/angular/src/index.html", + "browser": "cockpit/langgraph/persistence/angular/src/main.ts", + "tsConfig": "cockpit/langgraph/persistence/angular/tsconfig.app.json", + "styles": ["cockpit/langgraph/persistence/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/langgraph/persistence/angular/src/environments/environment.ts", + "with": "cockpit/langgraph/persistence/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4301, + "proxyConfig": "cockpit/langgraph/persistence/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-langgraph-persistence-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-langgraph-persistence-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/langgraph/persistence/angular/src/app.component.ts b/cockpit/langgraph/persistence/angular/src/app.component.ts deleted file mode 100644 index 7d22076cb..000000000 --- a/cockpit/langgraph/persistence/angular/src/app.component.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Component, inject, Injector, OnInit, signal } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { ChatComponent } from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -interface Thread { - id: string; - label: string; -} - -@Component({ - selector: 'app-persistence', - standalone: true, - imports: [CommonModule, ChatComponent], - template: ` -
    - - - - -
    - -
    -
    - `, -}) -export class PersistenceAppComponent implements OnInit { - private readonly injector = inject(Injector); - - chat!: StreamResourceRef; - threads = signal([ - { id: 'thread-1', label: 'Conversation 1' }, - { id: 'thread-2', label: 'Conversation 2' }, - ]); - activeThreadId = signal('thread-1'); - - ngOnInit(): void { - this.initChat(this.activeThreadId()); - } - - selectThread(threadId: string): void { - this.activeThreadId.set(threadId); - this.initChat(threadId); - } - - newThread(): void { - const id = `thread-${Date.now()}`; - this.threads.update((ts) => [ - ...ts, - { id, label: `Conversation ${ts.length + 1}` }, - ]); - this.selectThread(id); - } - - private initChat(threadId: string): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ assistantId: 'chat_agent', threadId }); - }); - } -} diff --git a/cockpit/langgraph/persistence/angular/src/app.config.ts b/cockpit/langgraph/persistence/angular/src/app.config.ts deleted file mode 100644 index 6ac3b924c..000000000 --- a/cockpit/langgraph/persistence/angular/src/app.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - ], -}; diff --git a/cockpit/langgraph/persistence/angular/src/app/app.config.ts b/cockpit/langgraph/persistence/angular/src/app/app.config.ts index cb72fb5ee..b52a916ac 100644 --- a/cockpit/langgraph/persistence/angular/src/app/app.config.ts +++ b/cockpit/langgraph/persistence/angular/src/app/app.config.ts @@ -1,18 +1,12 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; import { environment } from '../environments/environment'; -/** - * Application configuration for the LangGraph Persistence demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), ], }; diff --git a/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts b/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts index ad9e87d79..2318a4363 100644 --- a/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts +++ b/cockpit/langgraph/persistence/angular/src/app/persistence.component.ts @@ -1,88 +1,18 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { Component } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -/** - * PersistenceComponent demonstrates thread persistence with `streamResource()`. - * - * This example shows how conversations persist across browser refreshes. - * Each thread has a unique ID that can be stored and resumed later. - * Use `stream.switchThread(id)` to load a previous conversation, - * or `stream.switchThread(null)` to start fresh. - * - * Key integration points: - * - `onThreadId` callback captures new thread IDs for storage - * - `stream.switchThread(id)` resumes a previous conversation - * - `stream.messages()` loads the full history when switching threads - */ @Component({ selector: 'app-persistence', standalone: true, imports: [ChatComponent], - template: ` - - -

    Threads

    - @for (id of threadIds; track id) { - - } - -
    -
    - `, + template: ``, }) export class PersistenceComponent { - /** - * The streaming resource with thread persistence. - * - * The `onThreadId` callback fires when a new thread is created, - * allowing us to track thread IDs for the sidebar picker. - */ protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, - onThreadId: (id: string) => { - this.currentThreadId = id; - if (!this.threadIds.includes(id)) this.threadIds.push(id); - }, }); - - threadIds: string[] = []; - currentThreadId = ''; - - /** - * Submit a message to the current thread. - */ - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } - - /** - * Switch to an existing thread, loading its full message history. - */ - selectThread(id: string): void { - this.currentThreadId = id; - this.stream.switchThread(id); - } - - /** - * Start a new conversation thread. - */ - newThread(): void { - this.currentThreadId = ''; - this.stream.switchThread(null); - } } diff --git a/cockpit/langgraph/persistence/angular/src/environments/environment.development.ts b/cockpit/langgraph/persistence/angular/src/environments/environment.development.ts index 5eb9e480c..f7f36ff36 100644 --- a/cockpit/langgraph/persistence/angular/src/environments/environment.development.ts +++ b/cockpit/langgraph/persistence/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4301/api', + langGraphApiUrl: '/api', streamingAssistantId: 'persistence', }; diff --git a/cockpit/langgraph/persistence/angular/src/index.html b/cockpit/langgraph/persistence/angular/src/index.html index fe09aaa9e..60367d98e 100644 --- a/cockpit/langgraph/persistence/angular/src/index.html +++ b/cockpit/langgraph/persistence/angular/src/index.html @@ -2,11 +2,12 @@ - Persistence - LangGraph Angular Example + LangGraph Persistence — Angular + - + diff --git a/cockpit/langgraph/persistence/angular/src/main.ts b/cockpit/langgraph/persistence/angular/src/main.ts index 8ac088e84..0e464b52c 100644 --- a/cockpit/langgraph/persistence/angular/src/main.ts +++ b/cockpit/langgraph/persistence/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { PersistenceAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { PersistenceComponent } from './app/persistence.component'; -bootstrapApplication(PersistenceAppComponent, appConfig).catch(console.error); +bootstrapApplication(PersistenceComponent, appConfig).catch(console.error); diff --git a/cockpit/langgraph/persistence/angular/tsconfig.app.json b/cockpit/langgraph/persistence/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/langgraph/persistence/angular/tsconfig.app.json +++ b/cockpit/langgraph/persistence/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/langgraph/subgraphs/angular/project.json b/cockpit/langgraph/subgraphs/angular/project.json index dec341cfa..27870cd91 100644 --- a/cockpit/langgraph/subgraphs/angular/project.json +++ b/cockpit/langgraph/subgraphs/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-langgraph-subgraphs-angular", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/langgraph/subgraphs/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/subgraphs/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/langgraph/subgraphs/angular", - "main": "cockpit/langgraph/subgraphs/angular/src/index.ts", - "tsConfig": "cockpit/langgraph/subgraphs/angular/tsconfig.json" - } + "index": "cockpit/langgraph/subgraphs/angular/src/index.html", + "browser": "cockpit/langgraph/subgraphs/angular/src/main.ts", + "tsConfig": "cockpit/langgraph/subgraphs/angular/tsconfig.app.json", + "styles": ["cockpit/langgraph/subgraphs/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/langgraph/subgraphs/angular/src/environments/environment.ts", + "with": "cockpit/langgraph/subgraphs/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4305, + "proxyConfig": "cockpit/langgraph/subgraphs/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-langgraph-subgraphs-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-langgraph-subgraphs-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/langgraph/subgraphs/angular/src/app.component.ts b/cockpit/langgraph/subgraphs/angular/src/app.component.ts deleted file mode 100644 index 9582d1972..000000000 --- a/cockpit/langgraph/subgraphs/angular/src/app.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Component, inject, Injector, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { - ChatComponent, - ChatSubagentCardComponent, -} from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -@Component({ - selector: 'app-subgraphs', - standalone: true, - imports: [ChatComponent, ChatSubagentCardComponent], - template: ` -
    - - - - -
    - `, -}) -export class SubgraphsAppComponent implements OnInit { - private readonly injector = inject(Injector); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ assistantId: 'orchestrator_agent' }); - }); - } -} diff --git a/cockpit/langgraph/subgraphs/angular/src/app.config.ts b/cockpit/langgraph/subgraphs/angular/src/app.config.ts deleted file mode 100644 index 6ac3b924c..000000000 --- a/cockpit/langgraph/subgraphs/angular/src/app.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - ], -}; diff --git a/cockpit/langgraph/subgraphs/angular/src/app/app.config.ts b/cockpit/langgraph/subgraphs/angular/src/app/app.config.ts index 7ab539a84..b52a916ac 100644 --- a/cockpit/langgraph/subgraphs/angular/src/app/app.config.ts +++ b/cockpit/langgraph/subgraphs/angular/src/app/app.config.ts @@ -1,18 +1,12 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; import { environment } from '../environments/environment'; -/** - * Application configuration for the LangGraph Subgraphs demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), ], }; diff --git a/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts b/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts index 647acf0a9..42be99684 100644 --- a/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts +++ b/cockpit/langgraph/subgraphs/angular/src/app/subgraphs.component.ts @@ -1,66 +1,18 @@ -import { Component, computed } from '@angular/core'; +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Component } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -/** - * SubgraphsComponent demonstrates nested agent delegation with `streamResource()`. - * - * This example shows how a parent orchestrator delegates tasks to child subgraphs. - * The sidebar tracks active subagents in real time using `stream.subagents()`, - * a Map of running child graph executions and their current status. - * - * Key integration points: - * - `stream.subagents()` returns a Map of active subagents - * - Each entry has a unique run ID (key) and a `status()` signal ('running' | 'done' | 'error') - * - `subagentEntries` is a `computed()` signal derived from the map for iteration in the template - */ @Component({ selector: 'app-subgraphs', standalone: true, imports: [ChatComponent], - template: ` - - -

    Subagents

    - @for (entry of subagentEntries(); track entry[0]) { -
    - {{ entry[0].substring(0, 8) }}: {{ entry[1].status() }} -
    - } - @empty { -

    No active subagents

    - } -
    -
    - `, + template: ``, }) export class SubgraphsComponent { - /** - * The streaming resource that tracks subgraph (child agent) activity. - * - * `stream.subagents()` is a Signal> that updates - * as the parent orchestrator dispatches work to child subgraphs. - */ protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - /** - * Derived signal: converts the subagents Map to an array of entries for template iteration. - * Using `computed()` ensures the template re-renders whenever the Map changes. - */ - subagentEntries = computed(() => Array.from(this.stream.subagents().entries())); - - /** - * Submit a message to the orchestrator graph. - */ - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } } diff --git a/cockpit/langgraph/subgraphs/angular/src/environments/environment.development.ts b/cockpit/langgraph/subgraphs/angular/src/environments/environment.development.ts index 1c3f1a801..016b0c585 100644 --- a/cockpit/langgraph/subgraphs/angular/src/environments/environment.development.ts +++ b/cockpit/langgraph/subgraphs/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4305/api', + langGraphApiUrl: '/api', streamingAssistantId: 'subgraphs', }; diff --git a/cockpit/langgraph/subgraphs/angular/src/index.html b/cockpit/langgraph/subgraphs/angular/src/index.html index bec6836d7..f38475630 100644 --- a/cockpit/langgraph/subgraphs/angular/src/index.html +++ b/cockpit/langgraph/subgraphs/angular/src/index.html @@ -2,11 +2,12 @@ - Subgraphs - LangGraph Angular Example + LangGraph Subgraphs — Angular + - + diff --git a/cockpit/langgraph/subgraphs/angular/src/main.ts b/cockpit/langgraph/subgraphs/angular/src/main.ts index a6de3d6d2..2412a5fa0 100644 --- a/cockpit/langgraph/subgraphs/angular/src/main.ts +++ b/cockpit/langgraph/subgraphs/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { SubgraphsAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { SubgraphsComponent } from './app/subgraphs.component'; -bootstrapApplication(SubgraphsAppComponent, appConfig).catch(console.error); +bootstrapApplication(SubgraphsComponent, appConfig).catch(console.error); diff --git a/cockpit/langgraph/subgraphs/angular/tsconfig.app.json b/cockpit/langgraph/subgraphs/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/langgraph/subgraphs/angular/tsconfig.app.json +++ b/cockpit/langgraph/subgraphs/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/langgraph/time-travel/angular/project.json b/cockpit/langgraph/time-travel/angular/project.json index 63c2675be..a01fd0ed0 100644 --- a/cockpit/langgraph/time-travel/angular/project.json +++ b/cockpit/langgraph/time-travel/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-langgraph-time-travel-angular", "$schema": "../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/langgraph/time-travel/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/langgraph/time-travel/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/langgraph/time-travel/angular", - "main": "cockpit/langgraph/time-travel/angular/src/index.ts", - "tsConfig": "cockpit/langgraph/time-travel/angular/tsconfig.json" - } + "index": "cockpit/langgraph/time-travel/angular/src/index.html", + "browser": "cockpit/langgraph/time-travel/angular/src/main.ts", + "tsConfig": "cockpit/langgraph/time-travel/angular/tsconfig.app.json", + "styles": ["cockpit/langgraph/time-travel/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/langgraph/time-travel/angular/src/environments/environment.ts", + "with": "cockpit/langgraph/time-travel/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4304, + "proxyConfig": "cockpit/langgraph/time-travel/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-langgraph-time-travel-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-langgraph-time-travel-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/langgraph/time-travel/angular/proxy.conf.json b/cockpit/langgraph/time-travel/angular/proxy.conf.json index 4b45f71fb..36c6b6749 100644 --- a/cockpit/langgraph/time-travel/angular/proxy.conf.json +++ b/cockpit/langgraph/time-travel/angular/proxy.conf.json @@ -1,6 +1,6 @@ { "/api": { - "target": "http://localhost:8129", + "target": "http://localhost:8127", "secure": false, "changeOrigin": true, "pathRewrite": { "^/api": "" }, diff --git a/cockpit/langgraph/time-travel/angular/src/app.component.ts b/cockpit/langgraph/time-travel/angular/src/app.component.ts deleted file mode 100644 index 0236d2361..000000000 --- a/cockpit/langgraph/time-travel/angular/src/app.component.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Component, inject, Injector, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { - ChatComponent, - ChatTimelineSliderComponent, -} from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -@Component({ - selector: 'app-time-travel', - standalone: true, - imports: [ChatComponent, ChatTimelineSliderComponent], - template: ` -
    - - - - -
    - `, -}) -export class TimeTravelAppComponent implements OnInit { - private readonly injector = inject(Injector); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ assistantId: 'chat_agent' }); - }); - } -} diff --git a/cockpit/langgraph/time-travel/angular/src/app.config.ts b/cockpit/langgraph/time-travel/angular/src/app.config.ts deleted file mode 100644 index 6ac3b924c..000000000 --- a/cockpit/langgraph/time-travel/angular/src/app.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - ], -}; diff --git a/cockpit/langgraph/time-travel/angular/src/app/app.config.ts b/cockpit/langgraph/time-travel/angular/src/app/app.config.ts index e0b1d8ecc..b52a916ac 100644 --- a/cockpit/langgraph/time-travel/angular/src/app/app.config.ts +++ b/cockpit/langgraph/time-travel/angular/src/app/app.config.ts @@ -1,18 +1,12 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; import { environment } from '../environments/environment'; -/** - * Application configuration for the LangGraph Time Travel demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), ], }; diff --git a/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts b/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts index 902114944..57a2d1dd1 100644 --- a/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts +++ b/cockpit/langgraph/time-travel/angular/src/app/time-travel.component.ts @@ -1,84 +1,18 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { Component } from '@angular/core'; import { ChatComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -/** - * TimeTravelComponent demonstrates replaying and branching conversation history. - * - * Key integration points: - * - `stream.history()` — array of ThreadState snapshots - * - `stream.branch()` — current branch identifier - * - `stream.setBranch(id)` — switch to a different checkpoint - */ @Component({ selector: 'app-time-travel', standalone: true, imports: [ChatComponent], - template: ` - - -

    History

    - @for (state of stream.history(); track $index) { - - } - @if (stream.history().length === 0) { -

    No history yet. Send a message to begin.

    - } -
    -
    - `, + template: ``, }) export class TimeTravelComponent { - /** - * The streaming resource with checkpointing enabled. - * - * `stream.history()` provides an array of ThreadState snapshots for - * the current thread. `stream.branch()` tracks the active checkpoint. - * Call `stream.setBranch(checkpointId)` to replay from a past state. - */ protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - /** - * Submit a message to the current thread. - */ - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } - - /** - * Branch the conversation from the selected checkpoint. - * After calling setBranch, the next submit will fork from that point. - */ - selectCheckpoint(state: { checkpoint_id?: string }): void { - if (state.checkpoint_id) { - this.stream.setBranch(state.checkpoint_id); - } - } - - /** - * Format a checkpoint for display in the sidebar. - */ - formatCheckpoint(state: { checkpoint_id?: string; created_at?: string }): string { - const id = state.checkpoint_id ?? 'unknown'; - const short = id.substring(0, 8); - if (state.created_at) { - const ts = new Date(state.created_at).toLocaleTimeString(); - return `${short}... @ ${ts}`; - } - return `${short}...`; - } } diff --git a/cockpit/langgraph/time-travel/angular/src/environments/environment.development.ts b/cockpit/langgraph/time-travel/angular/src/environments/environment.development.ts index 4fb57585f..c53e973f9 100644 --- a/cockpit/langgraph/time-travel/angular/src/environments/environment.development.ts +++ b/cockpit/langgraph/time-travel/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4306/api', + langGraphApiUrl: '/api', streamingAssistantId: 'time-travel', }; diff --git a/cockpit/langgraph/time-travel/angular/src/index.html b/cockpit/langgraph/time-travel/angular/src/index.html index 870877931..1c99aad7e 100644 --- a/cockpit/langgraph/time-travel/angular/src/index.html +++ b/cockpit/langgraph/time-travel/angular/src/index.html @@ -2,11 +2,12 @@ - Time Travel - LangGraph Angular Example + LangGraph Time Travel — Angular + - + diff --git a/cockpit/langgraph/time-travel/angular/src/main.ts b/cockpit/langgraph/time-travel/angular/src/main.ts index 266196d27..336b5e7a4 100644 --- a/cockpit/langgraph/time-travel/angular/src/main.ts +++ b/cockpit/langgraph/time-travel/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { TimeTravelAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { TimeTravelComponent } from './app/time-travel.component'; -bootstrapApplication(TimeTravelAppComponent, appConfig).catch(console.error); +bootstrapApplication(TimeTravelComponent, appConfig).catch(console.error); diff --git a/cockpit/langgraph/time-travel/angular/tsconfig.app.json b/cockpit/langgraph/time-travel/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/langgraph/time-travel/angular/tsconfig.app.json +++ b/cockpit/langgraph/time-travel/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] From b06fb1596183ae637acf490eea7f43d3a2f8d654 Mon Sep 17 00:00:00 2001 From: Brian Love Date: Sun, 5 Apr 2026 17:08:23 -0700 Subject: [PATCH 5/5] feat(cockpit): wire 6 Deep Agents Angular examples to @cacheplane/chat-debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace placeholder component implementations with ChatDebugComponent pattern - Delete src/app.component.ts and src/app.config.ts root duplicates from all 6 - Rewrite capability components to use - Rewrite app.config.ts to include provideRender({}) alongside provideChat({}) - Rewrite main.ts to bootstrap from src/app/ (not root src/) - Switch project.json from @nx/js:tsc library to @angular-devkit/build-angular:application - Fix tsconfig.json extends path (6 levels → 4 levels to workspace root) - Update tsconfig.app.json to add lib and emitDeclarationOnly: false - Add to all index.html files; fix memory selector app-da-memory - Set environment.development.ts langGraphApiUrl to '/api' (proxy-relative) - Fix proxy.conf.json targets to correct ports (8140–8145) - All 6 production builds pass with no errors Co-Authored-By: Claude Sonnet 4.6 --- .../filesystem/angular/project.json | 47 ++++++++-- .../filesystem/angular/proxy.conf.json | 2 +- .../filesystem/angular/src/app.component.ts | 38 -------- .../filesystem/angular/src/app.config.ts | 15 ---- .../filesystem/angular/src/app/app.config.ts | 16 ++-- .../angular/src/app/filesystem.component.ts | 81 ++--------------- .../environments/environment.development.ts | 2 +- .../filesystem/angular/src/index.html | 5 +- .../filesystem/angular/src/main.ts | 6 +- .../filesystem/angular/tsconfig.app.json | 4 +- .../filesystem/angular/tsconfig.json | 2 +- .../deep-agents/memory/angular/project.json | 47 ++++++++-- .../memory/angular/proxy.conf.json | 2 +- .../memory/angular/src/app.component.ts | 38 -------- .../memory/angular/src/app.config.ts | 15 ---- .../memory/angular/src/app/app.config.ts | 16 ++-- .../angular/src/app/memory.component.ts | 49 ++--------- .../environments/environment.development.ts | 2 +- .../deep-agents/memory/angular/src/index.html | 7 +- .../deep-agents/memory/angular/src/main.ts | 6 +- .../memory/angular/tsconfig.app.json | 4 +- .../deep-agents/memory/angular/tsconfig.json | 2 +- .../deep-agents/planning/angular/project.json | 47 ++++++++-- .../planning/angular/proxy.conf.json | 2 +- .../planning/angular/src/app.component.ts | 35 -------- .../planning/angular/src/app.config.ts | 15 ---- .../planning/angular/src/app/app.config.ts | 16 ++-- .../angular/src/app/planning.component.ts | 57 ++---------- .../environments/environment.development.ts | 2 +- .../planning/angular/src/index.html | 5 +- .../deep-agents/planning/angular/src/main.ts | 6 +- .../planning/angular/tsconfig.app.json | 4 +- .../planning/angular/tsconfig.json | 2 +- .../sandboxes/angular/project.json | 47 ++++++++-- .../sandboxes/angular/proxy.conf.json | 2 +- .../sandboxes/angular/src/app.component.ts | 39 -------- .../sandboxes/angular/src/app.config.ts | 15 ---- .../sandboxes/angular/src/app/app.config.ts | 16 ++-- .../angular/src/app/sandboxes.component.ts | 88 ++----------------- .../environments/environment.development.ts | 2 +- .../sandboxes/angular/src/index.html | 5 +- .../deep-agents/sandboxes/angular/src/main.ts | 6 +- .../sandboxes/angular/tsconfig.app.json | 4 +- .../sandboxes/angular/tsconfig.json | 2 +- .../deep-agents/skills/angular/project.json | 47 ++++++++-- .../skills/angular/proxy.conf.json | 2 +- .../skills/angular/src/app.component.ts | 36 -------- .../skills/angular/src/app.config.ts | 15 ---- .../skills/angular/src/app/app.config.ts | 16 ++-- .../angular/src/app/skills.component.ts | 84 ++---------------- .../environments/environment.development.ts | 2 +- .../deep-agents/skills/angular/src/index.html | 5 +- .../deep-agents/skills/angular/src/main.ts | 6 +- .../skills/angular/tsconfig.app.json | 4 +- .../deep-agents/skills/angular/tsconfig.json | 2 +- .../subagents/angular/project.json | 47 ++++++++-- .../subagents/angular/proxy.conf.json | 2 +- .../subagents/angular/src/app.component.ts | 38 -------- .../subagents/angular/src/app.config.ts | 15 ---- .../subagents/angular/src/app/app.config.ts | 16 ++-- .../angular/src/app/subagents.component.ts | 59 ++----------- .../environments/environment.development.ts | 2 +- .../subagents/angular/src/index.html | 5 +- .../deep-agents/subagents/angular/src/main.ts | 6 +- .../subagents/angular/tsconfig.app.json | 4 +- .../subagents/angular/tsconfig.json | 2 +- 66 files changed, 385 insertions(+), 853 deletions(-) delete mode 100644 cockpit/deep-agents/filesystem/angular/src/app.component.ts delete mode 100644 cockpit/deep-agents/filesystem/angular/src/app.config.ts delete mode 100644 cockpit/deep-agents/memory/angular/src/app.component.ts delete mode 100644 cockpit/deep-agents/memory/angular/src/app.config.ts delete mode 100644 cockpit/deep-agents/planning/angular/src/app.component.ts delete mode 100644 cockpit/deep-agents/planning/angular/src/app.config.ts delete mode 100644 cockpit/deep-agents/sandboxes/angular/src/app.component.ts delete mode 100644 cockpit/deep-agents/sandboxes/angular/src/app.config.ts delete mode 100644 cockpit/deep-agents/skills/angular/src/app.component.ts delete mode 100644 cockpit/deep-agents/skills/angular/src/app.config.ts delete mode 100644 cockpit/deep-agents/subagents/angular/src/app.component.ts delete mode 100644 cockpit/deep-agents/subagents/angular/src/app.config.ts diff --git a/cockpit/deep-agents/filesystem/angular/project.json b/cockpit/deep-agents/filesystem/angular/project.json index a60898d46..77ab4b9ae 100644 --- a/cockpit/deep-agents/filesystem/angular/project.json +++ b/cockpit/deep-agents/filesystem/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-deep-agents-filesystem-angular", "$schema": "../../../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/deep-agents/filesystem/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/filesystem/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/deep-agents/filesystem/angular", - "main": "cockpit/deep-agents/filesystem/angular/src/index.ts", - "tsConfig": "cockpit/deep-agents/filesystem/angular/tsconfig.json" - } + "index": "cockpit/deep-agents/filesystem/angular/src/index.html", + "browser": "cockpit/deep-agents/filesystem/angular/src/main.ts", + "tsConfig": "cockpit/deep-agents/filesystem/angular/tsconfig.app.json", + "styles": ["cockpit/deep-agents/filesystem/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/deep-agents/filesystem/angular/src/environments/environment.ts", + "with": "cockpit/deep-agents/filesystem/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4311, + "proxyConfig": "cockpit/deep-agents/filesystem/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-deep-agents-filesystem-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-deep-agents-filesystem-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/deep-agents/filesystem/angular/proxy.conf.json b/cockpit/deep-agents/filesystem/angular/proxy.conf.json index b406fbb2b..578c65888 100644 --- a/cockpit/deep-agents/filesystem/angular/proxy.conf.json +++ b/cockpit/deep-agents/filesystem/angular/proxy.conf.json @@ -1,6 +1,6 @@ { "/api": { - "target": "http://localhost:8132", + "target": "http://localhost:8141", "secure": false, "changeOrigin": true, "pathRewrite": { "^/api": "" }, diff --git a/cockpit/deep-agents/filesystem/angular/src/app.component.ts b/cockpit/deep-agents/filesystem/angular/src/app.component.ts deleted file mode 100644 index 1c9ea78ac..000000000 --- a/cockpit/deep-agents/filesystem/angular/src/app.component.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Component, inject, Injector, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { ChatDebugComponent } from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -@Component({ - selector: 'app-filesystem', - standalone: true, - imports: [ChatDebugComponent], - template: ` -
    - - -
    - `, -}) -export class FilesystemAppComponent implements OnInit { - private readonly injector = inject(Injector); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ assistantId: 'filesystem_agent' }); - }); - } -} diff --git a/cockpit/deep-agents/filesystem/angular/src/app.config.ts b/cockpit/deep-agents/filesystem/angular/src/app.config.ts deleted file mode 100644 index d64820e1e..000000000 --- a/cockpit/deep-agents/filesystem/angular/src/app.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; -import { provideRender } from '@cacheplane/render'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - provideRender({}), - ], -}; diff --git a/cockpit/deep-agents/filesystem/angular/src/app/app.config.ts b/cockpit/deep-agents/filesystem/angular/src/app/app.config.ts index f3845b19f..08e9180d0 100644 --- a/cockpit/deep-agents/filesystem/angular/src/app/app.config.ts +++ b/cockpit/deep-agents/filesystem/angular/src/app/app.config.ts @@ -1,18 +1,14 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; +import { provideRender } from '@cacheplane/render'; import { environment } from '../environments/environment'; -/** - * Application configuration for the Deep Agents Filesystem demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), + provideRender({}), ], }; diff --git a/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts b/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts index c909d7e5f..7603d956e 100644 --- a/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts +++ b/cockpit/deep-agents/filesystem/angular/src/app/filesystem.component.ts @@ -1,89 +1,18 @@ -import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Component } from '@angular/core'; +import { ChatDebugComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -interface ToolCallEntry { - name: string; - args: string; - result?: string; -} - -/** - * FilesystemComponent demonstrates agent file operations. - * - * The agent can read and write files using tool calls. The sidebar - * shows a real-time log of each file operation as it happens. - * - * Key integration points: - * - `stream.messages()` contains all messages including tool call results - * - `computed()` derives tool call entries from AI messages - * - Tool calls update reactively as the agent performs file operations - */ @Component({ selector: 'app-filesystem', standalone: true, - imports: [ChatComponent], - template: ` - - -

    File Operations

    - @for (entry of toolCallEntries(); track $index) { -
    - - {{ entry.name === 'read_file' ? '📖' : '✏️' }} - -
    -
    - {{ getFilePath(entry.args) }} -
    -
    - {{ entry.name === 'read_file' ? 'read' : 'write' }} - {{ entry.result ? ' · done' : ' · running…' }} -
    -
    -
    - } - @empty { -

    Ask the agent to read or write a file.

    - } -
    -
    - `, + imports: [ChatDebugComponent], + template: ``, }) export class FilesystemComponent { protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - toolCallEntries = computed(() => { - const msg = this.stream.messages(); - const calls: ToolCallEntry[] = []; - for (const m of msg) { - if ((m as any).tool_calls) { - for (const tc of (m as any).tool_calls) { - calls.push({ name: tc.name, args: JSON.stringify(tc.args), result: tc.output }); - } - } - } - return calls; - }); - - getFilePath(args: string): string { - try { - const parsed = JSON.parse(args); - return parsed.path ?? args; - } catch { - return args; - } - } - - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } } diff --git a/cockpit/deep-agents/filesystem/angular/src/environments/environment.development.ts b/cockpit/deep-agents/filesystem/angular/src/environments/environment.development.ts index 5b01daa5e..f76d58c7f 100644 --- a/cockpit/deep-agents/filesystem/angular/src/environments/environment.development.ts +++ b/cockpit/deep-agents/filesystem/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4311/api', + langGraphApiUrl: '/api', streamingAssistantId: 'filesystem', }; diff --git a/cockpit/deep-agents/filesystem/angular/src/index.html b/cockpit/deep-agents/filesystem/angular/src/index.html index 20be6aea4..f385242c9 100644 --- a/cockpit/deep-agents/filesystem/angular/src/index.html +++ b/cockpit/deep-agents/filesystem/angular/src/index.html @@ -2,11 +2,12 @@ - Filesystem - Deep Agents Angular Example + Filesystem — Deep Agents Angular + - + diff --git a/cockpit/deep-agents/filesystem/angular/src/main.ts b/cockpit/deep-agents/filesystem/angular/src/main.ts index af525927a..3ff5c10b4 100644 --- a/cockpit/deep-agents/filesystem/angular/src/main.ts +++ b/cockpit/deep-agents/filesystem/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { FilesystemAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { FilesystemComponent } from './app/filesystem.component'; -bootstrapApplication(FilesystemAppComponent, appConfig).catch(console.error); +bootstrapApplication(FilesystemComponent, appConfig).catch(console.error); diff --git a/cockpit/deep-agents/filesystem/angular/tsconfig.app.json b/cockpit/deep-agents/filesystem/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/deep-agents/filesystem/angular/tsconfig.app.json +++ b/cockpit/deep-agents/filesystem/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/deep-agents/filesystem/angular/tsconfig.json b/cockpit/deep-agents/filesystem/angular/tsconfig.json index 90497de60..d9e29392d 100644 --- a/cockpit/deep-agents/filesystem/angular/tsconfig.json +++ b/cockpit/deep-agents/filesystem/angular/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../../../tsconfig.base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { "module": "preserve" }, diff --git a/cockpit/deep-agents/memory/angular/project.json b/cockpit/deep-agents/memory/angular/project.json index 6d8960f96..8dab77f3d 100644 --- a/cockpit/deep-agents/memory/angular/project.json +++ b/cockpit/deep-agents/memory/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-deep-agents-memory-angular", "$schema": "../../../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/deep-agents/memory/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/memory/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/deep-agents/memory/angular", - "main": "cockpit/deep-agents/memory/angular/src/index.ts", - "tsConfig": "cockpit/deep-agents/memory/angular/tsconfig.json" - } + "index": "cockpit/deep-agents/memory/angular/src/index.html", + "browser": "cockpit/deep-agents/memory/angular/src/main.ts", + "tsConfig": "cockpit/deep-agents/memory/angular/tsconfig.app.json", + "styles": ["cockpit/deep-agents/memory/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/deep-agents/memory/angular/src/environments/environment.ts", + "with": "cockpit/deep-agents/memory/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4313, + "proxyConfig": "cockpit/deep-agents/memory/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-deep-agents-memory-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-deep-agents-memory-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/deep-agents/memory/angular/proxy.conf.json b/cockpit/deep-agents/memory/angular/proxy.conf.json index d03db391c..10f842806 100644 --- a/cockpit/deep-agents/memory/angular/proxy.conf.json +++ b/cockpit/deep-agents/memory/angular/proxy.conf.json @@ -1,6 +1,6 @@ { "/api": { - "target": "http://localhost:8134", + "target": "http://localhost:8143", "secure": false, "changeOrigin": true, "pathRewrite": { "^/api": "" }, diff --git a/cockpit/deep-agents/memory/angular/src/app.component.ts b/cockpit/deep-agents/memory/angular/src/app.component.ts deleted file mode 100644 index d30c0fc8d..000000000 --- a/cockpit/deep-agents/memory/angular/src/app.component.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Component, inject, Injector, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { ChatDebugComponent } from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -@Component({ - selector: 'app-memory', - standalone: true, - imports: [ChatDebugComponent], - template: ` -
    - - -
    - `, -}) -export class MemoryAppComponent implements OnInit { - private readonly injector = inject(Injector); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ assistantId: 'memory_agent' }); - }); - } -} diff --git a/cockpit/deep-agents/memory/angular/src/app.config.ts b/cockpit/deep-agents/memory/angular/src/app.config.ts deleted file mode 100644 index d64820e1e..000000000 --- a/cockpit/deep-agents/memory/angular/src/app.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; -import { provideRender } from '@cacheplane/render'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - provideRender({}), - ], -}; diff --git a/cockpit/deep-agents/memory/angular/src/app/app.config.ts b/cockpit/deep-agents/memory/angular/src/app/app.config.ts index 7e2215715..08e9180d0 100644 --- a/cockpit/deep-agents/memory/angular/src/app/app.config.ts +++ b/cockpit/deep-agents/memory/angular/src/app/app.config.ts @@ -1,18 +1,14 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; +import { provideRender } from '@cacheplane/render'; import { environment } from '../environments/environment'; -/** - * Application configuration for the Deep Agents Memory demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), + provideRender({}), ], }; diff --git a/cockpit/deep-agents/memory/angular/src/app/memory.component.ts b/cockpit/deep-agents/memory/angular/src/app/memory.component.ts index cbccb77e3..8d644d50d 100644 --- a/cockpit/deep-agents/memory/angular/src/app/memory.component.ts +++ b/cockpit/deep-agents/memory/angular/src/app/memory.component.ts @@ -1,57 +1,18 @@ -import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Component } from '@angular/core'; +import { ChatDebugComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -/** - * MemoryComponent demonstrates persistent agent memory across sessions. - * - * The agent extracts facts about the user from each conversation turn - * and stores them in `agent_memory` state. The sidebar shows all learned - * facts in real time as the agent updates its memory. - * - * Key integration points: - * - `stream.value()` contains the agent state including `agent_memory` - * - `computed()` derives key/value pairs for the sidebar - * - Memory entries update reactively as the agent learns new facts - */ @Component({ selector: 'app-da-memory', standalone: true, - imports: [ChatComponent], - template: ` - - -

    Learned Facts

    - @for (entry of memoryEntries(); track entry[0]) { -
    -
    {{ entry[0] }}
    -
    {{ entry[1] }}
    -
    - } - @empty { -

    Tell the agent something about yourself to see it remember.

    - } -
    -
    - `, + imports: [ChatDebugComponent], + template: ``, }) export class MemoryComponent { protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - memoryEntries = computed(() => { - const val = this.stream.value() as { agent_memory?: Record } | undefined; - return Object.entries(val?.agent_memory ?? {}); - }); - - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } } diff --git a/cockpit/deep-agents/memory/angular/src/environments/environment.development.ts b/cockpit/deep-agents/memory/angular/src/environments/environment.development.ts index 7b02d4419..a93d56296 100644 --- a/cockpit/deep-agents/memory/angular/src/environments/environment.development.ts +++ b/cockpit/deep-agents/memory/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4313/api', + langGraphApiUrl: '/api', streamingAssistantId: 'da-memory', }; diff --git a/cockpit/deep-agents/memory/angular/src/index.html b/cockpit/deep-agents/memory/angular/src/index.html index b9a5a9c10..2c0da9ab7 100644 --- a/cockpit/deep-agents/memory/angular/src/index.html +++ b/cockpit/deep-agents/memory/angular/src/index.html @@ -2,11 +2,12 @@ - Memory - Deep Agents Angular Example + Memory — Deep Agents Angular + - - + + diff --git a/cockpit/deep-agents/memory/angular/src/main.ts b/cockpit/deep-agents/memory/angular/src/main.ts index 180df289a..e53854a71 100644 --- a/cockpit/deep-agents/memory/angular/src/main.ts +++ b/cockpit/deep-agents/memory/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { MemoryAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { MemoryComponent } from './app/memory.component'; -bootstrapApplication(MemoryAppComponent, appConfig).catch(console.error); +bootstrapApplication(MemoryComponent, appConfig).catch(console.error); diff --git a/cockpit/deep-agents/memory/angular/tsconfig.app.json b/cockpit/deep-agents/memory/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/deep-agents/memory/angular/tsconfig.app.json +++ b/cockpit/deep-agents/memory/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/deep-agents/memory/angular/tsconfig.json b/cockpit/deep-agents/memory/angular/tsconfig.json index 90497de60..d9e29392d 100644 --- a/cockpit/deep-agents/memory/angular/tsconfig.json +++ b/cockpit/deep-agents/memory/angular/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../../../tsconfig.base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { "module": "preserve" }, diff --git a/cockpit/deep-agents/planning/angular/project.json b/cockpit/deep-agents/planning/angular/project.json index 6410ab68e..6dff8ce2f 100644 --- a/cockpit/deep-agents/planning/angular/project.json +++ b/cockpit/deep-agents/planning/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-deep-agents-planning-angular", "$schema": "../../../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/deep-agents/planning/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/planning/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/deep-agents/planning/angular", - "main": "cockpit/deep-agents/planning/angular/src/index.ts", - "tsConfig": "cockpit/deep-agents/planning/angular/tsconfig.json" - } + "index": "cockpit/deep-agents/planning/angular/src/index.html", + "browser": "cockpit/deep-agents/planning/angular/src/main.ts", + "tsConfig": "cockpit/deep-agents/planning/angular/tsconfig.app.json", + "styles": ["cockpit/deep-agents/planning/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/deep-agents/planning/angular/src/environments/environment.ts", + "with": "cockpit/deep-agents/planning/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4310, + "proxyConfig": "cockpit/deep-agents/planning/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-deep-agents-planning-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-deep-agents-planning-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/deep-agents/planning/angular/proxy.conf.json b/cockpit/deep-agents/planning/angular/proxy.conf.json index d89393efe..2a7969d46 100644 --- a/cockpit/deep-agents/planning/angular/proxy.conf.json +++ b/cockpit/deep-agents/planning/angular/proxy.conf.json @@ -1,6 +1,6 @@ { "/api": { - "target": "http://localhost:8131", + "target": "http://localhost:8140", "secure": false, "changeOrigin": true, "pathRewrite": { "^/api": "" }, diff --git a/cockpit/deep-agents/planning/angular/src/app.component.ts b/cockpit/deep-agents/planning/angular/src/app.component.ts deleted file mode 100644 index 69585febe..000000000 --- a/cockpit/deep-agents/planning/angular/src/app.component.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Component, inject, Injector, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { ChatDebugComponent } from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -@Component({ - selector: 'app-planning', - standalone: true, - imports: [ChatDebugComponent], - template: ` -
    - - -
    - `, -}) -export class PlanningAppComponent implements OnInit { - private readonly injector = inject(Injector); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ assistantId: 'planning_agent' }); - }); - } -} diff --git a/cockpit/deep-agents/planning/angular/src/app.config.ts b/cockpit/deep-agents/planning/angular/src/app.config.ts deleted file mode 100644 index d64820e1e..000000000 --- a/cockpit/deep-agents/planning/angular/src/app.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; -import { provideRender } from '@cacheplane/render'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - provideRender({}), - ], -}; diff --git a/cockpit/deep-agents/planning/angular/src/app/app.config.ts b/cockpit/deep-agents/planning/angular/src/app/app.config.ts index 75dca3bf3..08e9180d0 100644 --- a/cockpit/deep-agents/planning/angular/src/app/app.config.ts +++ b/cockpit/deep-agents/planning/angular/src/app/app.config.ts @@ -1,18 +1,14 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; +import { provideRender } from '@cacheplane/render'; import { environment } from '../environments/environment'; -/** - * Application configuration for the Deep Agents Planning demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), + provideRender({}), ], }; diff --git a/cockpit/deep-agents/planning/angular/src/app/planning.component.ts b/cockpit/deep-agents/planning/angular/src/app/planning.component.ts index b2deedcd7..6a802b7e1 100644 --- a/cockpit/deep-agents/planning/angular/src/app/planning.component.ts +++ b/cockpit/deep-agents/planning/angular/src/app/planning.component.ts @@ -1,65 +1,18 @@ -import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Component } from '@angular/core'; +import { ChatDebugComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -interface PlanStep { - title: string; - status: 'pending' | 'running' | 'complete'; -} - -/** - * PlanningComponent demonstrates agent task decomposition. - * - * The agent receives a complex task, breaks it into ordered steps, - * and executes them. The sidebar shows each step's status in real time. - * - * Key integration points: - * - `stream.value()` contains the plan state with step list - * - `computed()` derives the plan steps for the sidebar - * - Steps update reactively as the agent works through them - */ @Component({ selector: 'app-planning', standalone: true, - imports: [ChatComponent], - template: ` - - -

    Task Plan

    - @for (step of planSteps(); track $index) { -
    - - - {{ step.title }} - -
    - } - @empty { -

    Ask a complex question to see the plan.

    - } -
    -
    - `, + imports: [ChatDebugComponent], + template: ``, }) export class PlanningComponent { protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - planSteps = computed(() => { - const val = this.stream.value() as { plan?: PlanStep[] } | undefined; - return val?.plan ?? []; - }); - - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } } diff --git a/cockpit/deep-agents/planning/angular/src/environments/environment.development.ts b/cockpit/deep-agents/planning/angular/src/environments/environment.development.ts index b0133086b..839f63cfa 100644 --- a/cockpit/deep-agents/planning/angular/src/environments/environment.development.ts +++ b/cockpit/deep-agents/planning/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4310/api', + langGraphApiUrl: '/api', streamingAssistantId: 'planning', }; diff --git a/cockpit/deep-agents/planning/angular/src/index.html b/cockpit/deep-agents/planning/angular/src/index.html index cc6217adc..564ce18e9 100644 --- a/cockpit/deep-agents/planning/angular/src/index.html +++ b/cockpit/deep-agents/planning/angular/src/index.html @@ -2,11 +2,12 @@ - Planning - Deep Agents Angular Example + Planning — Deep Agents Angular + - + diff --git a/cockpit/deep-agents/planning/angular/src/main.ts b/cockpit/deep-agents/planning/angular/src/main.ts index cdeac74d0..33851aef1 100644 --- a/cockpit/deep-agents/planning/angular/src/main.ts +++ b/cockpit/deep-agents/planning/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { PlanningAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { PlanningComponent } from './app/planning.component'; -bootstrapApplication(PlanningAppComponent, appConfig).catch(console.error); +bootstrapApplication(PlanningComponent, appConfig).catch(console.error); diff --git a/cockpit/deep-agents/planning/angular/tsconfig.app.json b/cockpit/deep-agents/planning/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/deep-agents/planning/angular/tsconfig.app.json +++ b/cockpit/deep-agents/planning/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/deep-agents/planning/angular/tsconfig.json b/cockpit/deep-agents/planning/angular/tsconfig.json index 90497de60..d9e29392d 100644 --- a/cockpit/deep-agents/planning/angular/tsconfig.json +++ b/cockpit/deep-agents/planning/angular/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../../../tsconfig.base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { "module": "preserve" }, diff --git a/cockpit/deep-agents/sandboxes/angular/project.json b/cockpit/deep-agents/sandboxes/angular/project.json index e36edfe37..ec70bd648 100644 --- a/cockpit/deep-agents/sandboxes/angular/project.json +++ b/cockpit/deep-agents/sandboxes/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-deep-agents-sandboxes-angular", "$schema": "../../../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/deep-agents/sandboxes/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/sandboxes/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/deep-agents/sandboxes/angular", - "main": "cockpit/deep-agents/sandboxes/angular/src/index.ts", - "tsConfig": "cockpit/deep-agents/sandboxes/angular/tsconfig.json" - } + "index": "cockpit/deep-agents/sandboxes/angular/src/index.html", + "browser": "cockpit/deep-agents/sandboxes/angular/src/main.ts", + "tsConfig": "cockpit/deep-agents/sandboxes/angular/tsconfig.app.json", + "styles": ["cockpit/deep-agents/sandboxes/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/deep-agents/sandboxes/angular/src/environments/environment.ts", + "with": "cockpit/deep-agents/sandboxes/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4315, + "proxyConfig": "cockpit/deep-agents/sandboxes/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-deep-agents-sandboxes-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-deep-agents-sandboxes-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/deep-agents/sandboxes/angular/proxy.conf.json b/cockpit/deep-agents/sandboxes/angular/proxy.conf.json index 7cef15fe7..e9267a7bd 100644 --- a/cockpit/deep-agents/sandboxes/angular/proxy.conf.json +++ b/cockpit/deep-agents/sandboxes/angular/proxy.conf.json @@ -1,6 +1,6 @@ { "/api": { - "target": "http://localhost:8136", + "target": "http://localhost:8145", "secure": false, "changeOrigin": true, "pathRewrite": { "^/api": "" }, diff --git a/cockpit/deep-agents/sandboxes/angular/src/app.component.ts b/cockpit/deep-agents/sandboxes/angular/src/app.component.ts deleted file mode 100644 index 408dd027a..000000000 --- a/cockpit/deep-agents/sandboxes/angular/src/app.component.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Component, inject, Injector, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { ChatDebugComponent } from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -@Component({ - selector: 'app-sandboxes', - standalone: true, - imports: [ChatDebugComponent], - template: ` -
    - - -
    - `, -}) -export class SandboxesAppComponent implements OnInit { - private readonly injector = inject(Injector); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ assistantId: 'sandbox_agent' }); - }); - } -} diff --git a/cockpit/deep-agents/sandboxes/angular/src/app.config.ts b/cockpit/deep-agents/sandboxes/angular/src/app.config.ts deleted file mode 100644 index d64820e1e..000000000 --- a/cockpit/deep-agents/sandboxes/angular/src/app.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; -import { provideRender } from '@cacheplane/render'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - provideRender({}), - ], -}; diff --git a/cockpit/deep-agents/sandboxes/angular/src/app/app.config.ts b/cockpit/deep-agents/sandboxes/angular/src/app/app.config.ts index c92129d5c..08e9180d0 100644 --- a/cockpit/deep-agents/sandboxes/angular/src/app/app.config.ts +++ b/cockpit/deep-agents/sandboxes/angular/src/app/app.config.ts @@ -1,18 +1,14 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; +import { provideRender } from '@cacheplane/render'; import { environment } from '../environments/environment'; -/** - * Application configuration for the Deep Agents Sandboxes demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), + provideRender({}), ], }; diff --git a/cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts b/cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts index 47f63a985..a07ae694e 100644 --- a/cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts +++ b/cockpit/deep-agents/sandboxes/angular/src/app/sandboxes.component.ts @@ -1,96 +1,18 @@ -import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Component } from '@angular/core'; +import { ChatDebugComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -interface ExecutionLog { - code: string; - stdout: string; - exitStatus: number; -} - -/** - * SandboxesComponent demonstrates a coding agent that executes Python code. - * - * The agent writes and runs code snippets to solve problems using a - * `run_code` tool. The sidebar shows execution logs — code input, stdout - * output, and exit status — for each sandbox execution. - * - * Key integration points: - * - `stream.messages()` contains all messages including tool call results - * - `computed()` derives execution log entries from tool calls in AI messages - * - Logs update reactively as the agent writes and runs code - */ @Component({ selector: 'app-sandboxes', standalone: true, - imports: [ChatComponent], - template: ` - - -

    Execution Logs

    - @for (log of executionLogs(); track $index) { -
    -
    - - exit {{ log.exitStatus }} - -
    -
    {{ log.code }}
    - @if (log.stdout) { -
    stdout
    -
    {{ log.stdout }}
    - } -
    - } - @empty { -

    Ask the agent to write and run Python code.

    - } -
    -
    - `, + imports: [ChatDebugComponent], + template: ``, }) export class SandboxesComponent { protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - executionLogs = computed(() => { - const msgs = this.stream.messages(); - const logs: ExecutionLog[] = []; - for (const m of msgs) { - if ((m as any).tool_calls) { - for (const tc of (m as any).tool_calls) { - if (tc.name === 'run_code' && tc.output) { - try { - const parsed = JSON.parse(tc.output); - logs.push({ - code: tc.args?.code ?? '', - stdout: parsed.stdout ?? '', - exitStatus: parsed.exit_status ?? 0, - }); - } catch { - logs.push({ - code: tc.args?.code ?? '', - stdout: tc.output, - exitStatus: 0, - }); - } - } - } - } - } - return logs; - }); - - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } } diff --git a/cockpit/deep-agents/sandboxes/angular/src/environments/environment.development.ts b/cockpit/deep-agents/sandboxes/angular/src/environments/environment.development.ts index b727a59a3..64828de16 100644 --- a/cockpit/deep-agents/sandboxes/angular/src/environments/environment.development.ts +++ b/cockpit/deep-agents/sandboxes/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4315/api', + langGraphApiUrl: '/api', streamingAssistantId: 'sandboxes', }; diff --git a/cockpit/deep-agents/sandboxes/angular/src/index.html b/cockpit/deep-agents/sandboxes/angular/src/index.html index 36bcecc82..a6a5ba118 100644 --- a/cockpit/deep-agents/sandboxes/angular/src/index.html +++ b/cockpit/deep-agents/sandboxes/angular/src/index.html @@ -2,11 +2,12 @@ - Sandboxes - Deep Agents Angular Example + Sandboxes — Deep Agents Angular + - + diff --git a/cockpit/deep-agents/sandboxes/angular/src/main.ts b/cockpit/deep-agents/sandboxes/angular/src/main.ts index 6ea997dcb..0175071cb 100644 --- a/cockpit/deep-agents/sandboxes/angular/src/main.ts +++ b/cockpit/deep-agents/sandboxes/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { SandboxesAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { SandboxesComponent } from './app/sandboxes.component'; -bootstrapApplication(SandboxesAppComponent, appConfig).catch(console.error); +bootstrapApplication(SandboxesComponent, appConfig).catch(console.error); diff --git a/cockpit/deep-agents/sandboxes/angular/tsconfig.app.json b/cockpit/deep-agents/sandboxes/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/deep-agents/sandboxes/angular/tsconfig.app.json +++ b/cockpit/deep-agents/sandboxes/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/deep-agents/sandboxes/angular/tsconfig.json b/cockpit/deep-agents/sandboxes/angular/tsconfig.json index 90497de60..d9e29392d 100644 --- a/cockpit/deep-agents/sandboxes/angular/tsconfig.json +++ b/cockpit/deep-agents/sandboxes/angular/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../../../tsconfig.base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { "module": "preserve" }, diff --git a/cockpit/deep-agents/skills/angular/project.json b/cockpit/deep-agents/skills/angular/project.json index 4f85e59a5..7bdf63a8d 100644 --- a/cockpit/deep-agents/skills/angular/project.json +++ b/cockpit/deep-agents/skills/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-deep-agents-skills-angular", "$schema": "../../../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/deep-agents/skills/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/skills/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/deep-agents/skills/angular", - "main": "cockpit/deep-agents/skills/angular/src/index.ts", - "tsConfig": "cockpit/deep-agents/skills/angular/tsconfig.json" - } + "index": "cockpit/deep-agents/skills/angular/src/index.html", + "browser": "cockpit/deep-agents/skills/angular/src/main.ts", + "tsConfig": "cockpit/deep-agents/skills/angular/tsconfig.app.json", + "styles": ["cockpit/deep-agents/skills/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/deep-agents/skills/angular/src/environments/environment.ts", + "with": "cockpit/deep-agents/skills/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4314, + "proxyConfig": "cockpit/deep-agents/skills/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-deep-agents-skills-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-deep-agents-skills-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/deep-agents/skills/angular/proxy.conf.json b/cockpit/deep-agents/skills/angular/proxy.conf.json index 4dfc6c35a..c83583dc7 100644 --- a/cockpit/deep-agents/skills/angular/proxy.conf.json +++ b/cockpit/deep-agents/skills/angular/proxy.conf.json @@ -1,6 +1,6 @@ { "/api": { - "target": "http://localhost:8135", + "target": "http://localhost:8144", "secure": false, "changeOrigin": true, "pathRewrite": { "^/api": "" }, diff --git a/cockpit/deep-agents/skills/angular/src/app.component.ts b/cockpit/deep-agents/skills/angular/src/app.component.ts deleted file mode 100644 index 249563587..000000000 --- a/cockpit/deep-agents/skills/angular/src/app.component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Component, inject, Injector, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { ChatDebugComponent } from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -@Component({ - selector: 'app-skills', - standalone: true, - imports: [ChatDebugComponent], - template: ` -
    - - -
    - `, -}) -export class SkillsAppComponent implements OnInit { - private readonly injector = inject(Injector); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ assistantId: 'skills_agent' }); - }); - } -} diff --git a/cockpit/deep-agents/skills/angular/src/app.config.ts b/cockpit/deep-agents/skills/angular/src/app.config.ts deleted file mode 100644 index d64820e1e..000000000 --- a/cockpit/deep-agents/skills/angular/src/app.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; -import { provideRender } from '@cacheplane/render'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - provideRender({}), - ], -}; diff --git a/cockpit/deep-agents/skills/angular/src/app/app.config.ts b/cockpit/deep-agents/skills/angular/src/app/app.config.ts index 0a688b9c2..08e9180d0 100644 --- a/cockpit/deep-agents/skills/angular/src/app/app.config.ts +++ b/cockpit/deep-agents/skills/angular/src/app/app.config.ts @@ -1,18 +1,14 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; +import { provideRender } from '@cacheplane/render'; import { environment } from '../environments/environment'; -/** - * Application configuration for the Deep Agents Skills demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), + provideRender({}), ], }; diff --git a/cockpit/deep-agents/skills/angular/src/app/skills.component.ts b/cockpit/deep-agents/skills/angular/src/app/skills.component.ts index ffa41616a..c2a65c2f7 100644 --- a/cockpit/deep-agents/skills/angular/src/app/skills.component.ts +++ b/cockpit/deep-agents/skills/angular/src/app/skills.component.ts @@ -1,92 +1,18 @@ -import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Component } from '@angular/core'; +import { ChatDebugComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -interface SkillInvocation { - skillName: string; - args: string; - result?: string; -} - -/** - * SkillsComponent demonstrates a multi-skill agent with specialized tools. - * - * The agent can calculate math expressions, count words, and summarize text - * by selecting the appropriate skill tool for each user request. The sidebar - * shows each skill invocation as a card with the skill name, input args, - * and result. - * - * Key integration points: - * - `stream.messages()` contains all messages including tool call data - * - `computed()` derives skill invocation cards from tool calls in AI messages - * - Invocations update reactively as the agent calls and receives tool results - */ @Component({ selector: 'app-skills', standalone: true, - imports: [ChatComponent], - template: ` - - -

    Skill Invocations

    - @for (inv of skillInvocations(); track $index) { -
    -
    - - {{ inv.skillName }} - - @if (inv.result) { - done - } @else { - running… - } -
    -
    - {{ inv.args }} -
    - @if (inv.result) { -
    - {{ inv.result }} -
    - } -
    - } - @empty { -

    Ask the agent to calculate, count words, or summarize text.

    - } -
    -
    - `, + imports: [ChatDebugComponent], + template: ``, }) export class SkillsComponent { protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - skillInvocations = computed(() => { - const msgs = this.stream.messages(); - const invocations: SkillInvocation[] = []; - for (const m of msgs) { - if ((m as any).tool_calls) { - for (const tc of (m as any).tool_calls) { - invocations.push({ - skillName: tc.name, - args: JSON.stringify(tc.args), - result: tc.output, - }); - } - } - } - return invocations; - }); - - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } } diff --git a/cockpit/deep-agents/skills/angular/src/environments/environment.development.ts b/cockpit/deep-agents/skills/angular/src/environments/environment.development.ts index da7ba04a9..36224d46c 100644 --- a/cockpit/deep-agents/skills/angular/src/environments/environment.development.ts +++ b/cockpit/deep-agents/skills/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4314/api', + langGraphApiUrl: '/api', streamingAssistantId: 'skills', }; diff --git a/cockpit/deep-agents/skills/angular/src/index.html b/cockpit/deep-agents/skills/angular/src/index.html index bcd6ec7f5..1565f3166 100644 --- a/cockpit/deep-agents/skills/angular/src/index.html +++ b/cockpit/deep-agents/skills/angular/src/index.html @@ -2,11 +2,12 @@ - Skills - Deep Agents Angular Example + Skills — Deep Agents Angular + - + diff --git a/cockpit/deep-agents/skills/angular/src/main.ts b/cockpit/deep-agents/skills/angular/src/main.ts index eb3fab282..86bcf6de8 100644 --- a/cockpit/deep-agents/skills/angular/src/main.ts +++ b/cockpit/deep-agents/skills/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { SkillsAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { SkillsComponent } from './app/skills.component'; -bootstrapApplication(SkillsAppComponent, appConfig).catch(console.error); +bootstrapApplication(SkillsComponent, appConfig).catch(console.error); diff --git a/cockpit/deep-agents/skills/angular/tsconfig.app.json b/cockpit/deep-agents/skills/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/deep-agents/skills/angular/tsconfig.app.json +++ b/cockpit/deep-agents/skills/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/deep-agents/skills/angular/tsconfig.json b/cockpit/deep-agents/skills/angular/tsconfig.json index 90497de60..d9e29392d 100644 --- a/cockpit/deep-agents/skills/angular/tsconfig.json +++ b/cockpit/deep-agents/skills/angular/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../../../tsconfig.base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { "module": "preserve" }, diff --git a/cockpit/deep-agents/subagents/angular/project.json b/cockpit/deep-agents/subagents/angular/project.json index 255d66722..7e8e2f20b 100644 --- a/cockpit/deep-agents/subagents/angular/project.json +++ b/cockpit/deep-agents/subagents/angular/project.json @@ -2,16 +2,51 @@ "name": "cockpit-deep-agents-subagents-angular", "$schema": "../../../../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "cockpit/deep-agents/subagents/angular/src", - "projectType": "library", + "projectType": "application", "targets": { "build": { - "executor": "@nx/js:tsc", - "outputs": ["{workspaceRoot}/dist/cockpit/deep-agents/subagents/angular"], + "executor": "@angular-devkit/build-angular:application", + "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/cockpit/deep-agents/subagents/angular", - "main": "cockpit/deep-agents/subagents/angular/src/index.ts", - "tsConfig": "cockpit/deep-agents/subagents/angular/tsconfig.json" - } + "index": "cockpit/deep-agents/subagents/angular/src/index.html", + "browser": "cockpit/deep-agents/subagents/angular/src/main.ts", + "tsConfig": "cockpit/deep-agents/subagents/angular/tsconfig.app.json", + "styles": ["cockpit/deep-agents/subagents/angular/src/styles.css"] + }, + "configurations": { + "production": { + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true, + "fileReplacements": [ + { + "replace": "cockpit/deep-agents/subagents/angular/src/environments/environment.ts", + "with": "cockpit/deep-agents/subagents/angular/src/environments/environment.development.ts" + } + ] + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "options": { + "port": 4312, + "proxyConfig": "cockpit/deep-agents/subagents/angular/proxy.conf.json" + }, + "configurations": { + "production": { + "buildTarget": "cockpit-deep-agents-subagents-angular:build:production" + }, + "development": { + "buildTarget": "cockpit-deep-agents-subagents-angular:build:development" + } + }, + "defaultConfiguration": "development" }, "smoke": { "executor": "nx:run-commands", diff --git a/cockpit/deep-agents/subagents/angular/proxy.conf.json b/cockpit/deep-agents/subagents/angular/proxy.conf.json index e7220a509..79df6bcdc 100644 --- a/cockpit/deep-agents/subagents/angular/proxy.conf.json +++ b/cockpit/deep-agents/subagents/angular/proxy.conf.json @@ -1,6 +1,6 @@ { "/api": { - "target": "http://localhost:8133", + "target": "http://localhost:8142", "secure": false, "changeOrigin": true, "pathRewrite": { "^/api": "" }, diff --git a/cockpit/deep-agents/subagents/angular/src/app.component.ts b/cockpit/deep-agents/subagents/angular/src/app.component.ts deleted file mode 100644 index bd285ad7e..000000000 --- a/cockpit/deep-agents/subagents/angular/src/app.component.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Component, inject, Injector, OnInit } from '@angular/core'; -import { runInInjectionContext } from '@angular/core'; -import { ChatDebugComponent } from '@cacheplane/chat'; -import { streamResource, StreamResourceRef } from '@cacheplane/stream-resource'; - -@Component({ - selector: 'app-subagents', - standalone: true, - imports: [ChatDebugComponent], - template: ` -
    - - -
    - `, -}) -export class SubagentsAppComponent implements OnInit { - private readonly injector = inject(Injector); - chat!: StreamResourceRef; - - ngOnInit(): void { - runInInjectionContext(this.injector, () => { - this.chat = streamResource({ assistantId: 'orchestrator_agent' }); - }); - } -} diff --git a/cockpit/deep-agents/subagents/angular/src/app.config.ts b/cockpit/deep-agents/subagents/angular/src/app.config.ts deleted file mode 100644 index d64820e1e..000000000 --- a/cockpit/deep-agents/subagents/angular/src/app.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 -import { ApplicationConfig } from '@angular/core'; -import { provideStreamResource } from '@cacheplane/stream-resource'; -import { provideChat } from '@cacheplane/chat'; -import { provideRender } from '@cacheplane/render'; - -export const appConfig: ApplicationConfig = { - providers: [ - provideStreamResource({ - apiUrl: 'http://localhost:2024', - }), - provideChat({}), - provideRender({}), - ], -}; diff --git a/cockpit/deep-agents/subagents/angular/src/app/app.config.ts b/cockpit/deep-agents/subagents/angular/src/app/app.config.ts index a76914e8e..08e9180d0 100644 --- a/cockpit/deep-agents/subagents/angular/src/app/app.config.ts +++ b/cockpit/deep-agents/subagents/angular/src/app/app.config.ts @@ -1,18 +1,14 @@ +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { ApplicationConfig } from '@angular/core'; import { provideStreamResource } from '@cacheplane/stream-resource'; +import { provideChat } from '@cacheplane/chat'; +import { provideRender } from '@cacheplane/render'; import { environment } from '../environments/environment'; -/** - * Application configuration for the Deep Agents Subagents demo. - * - * Uses `provideStreamResource()` to set the global LangGraph API URL. - * All `streamResource()` calls in this app inherit this URL unless - * overridden at the call site. - */ export const appConfig: ApplicationConfig = { providers: [ - provideStreamResource({ - apiUrl: environment.langGraphApiUrl, - }), + provideStreamResource({ apiUrl: environment.langGraphApiUrl }), + provideChat({}), + provideRender({}), ], }; diff --git a/cockpit/deep-agents/subagents/angular/src/app/subagents.component.ts b/cockpit/deep-agents/subagents/angular/src/app/subagents.component.ts index 18cd9ca91..4dd419283 100644 --- a/cockpit/deep-agents/subagents/angular/src/app/subagents.component.ts +++ b/cockpit/deep-agents/subagents/angular/src/app/subagents.component.ts @@ -1,67 +1,18 @@ -import { Component, computed } from '@angular/core'; -import { ChatComponent } from '@cacheplane/chat'; +// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 +import { Component } from '@angular/core'; +import { ChatDebugComponent } from '@cacheplane/chat'; import { streamResource } from '@cacheplane/stream-resource'; import { environment } from '../environments/environment'; -/** - * SubagentsComponent demonstrates the Deep Agents subagent delegation pattern. - * - * The orchestrator agent receives a task and delegates subtasks to specialist - * subagents via tool calls. Each tool call spawns a child agent that streams - * its own progress independently. - * - * Key integration points: - * - `stream.subagents()` returns a Map - * - `subagentEntries` derives a sorted array for sidebar rendering - * - Each entry shows the tool call ID (truncated), status badge, and message count - * - Subagent statuses update reactively: pending → running → complete - */ @Component({ selector: 'app-subagents', standalone: true, - imports: [ChatComponent], - template: ` - - -

    Subagents

    - @for (entry of subagentEntries(); track entry[0]) { -
    -
    - - {{ entry[1].status() }} - - - {{ entry[0].slice(0, 8) }}… - -
    -
    - {{ entry[1].messages().length }} message{{ entry[1].messages().length === 1 ? '' : 's' }} -
    -
    - } - @empty { -

    Ask a question to see subagent activity.

    - } -
    -
    - `, + imports: [ChatDebugComponent], + template: ``, }) export class SubagentsComponent { protected readonly stream = streamResource({ apiUrl: environment.langGraphApiUrl, assistantId: environment.streamingAssistantId, }); - - subagentEntries = computed(() => Array.from(this.stream.subagents().entries())); - - send(text: string): void { - this.stream.submit({ messages: [{ role: 'human', content: text }] }); - } } diff --git a/cockpit/deep-agents/subagents/angular/src/environments/environment.development.ts b/cockpit/deep-agents/subagents/angular/src/environments/environment.development.ts index 9c8246785..2fb0e4bed 100644 --- a/cockpit/deep-agents/subagents/angular/src/environments/environment.development.ts +++ b/cockpit/deep-agents/subagents/angular/src/environments/environment.development.ts @@ -6,6 +6,6 @@ */ export const environment = { production: false, - langGraphApiUrl: 'http://localhost:4312/api', + langGraphApiUrl: '/api', streamingAssistantId: 'subagents', }; diff --git a/cockpit/deep-agents/subagents/angular/src/index.html b/cockpit/deep-agents/subagents/angular/src/index.html index caa1129f2..412e312c3 100644 --- a/cockpit/deep-agents/subagents/angular/src/index.html +++ b/cockpit/deep-agents/subagents/angular/src/index.html @@ -2,11 +2,12 @@ - Subagents - Deep Agents Angular Example + Subagents — Deep Agents Angular + - + diff --git a/cockpit/deep-agents/subagents/angular/src/main.ts b/cockpit/deep-agents/subagents/angular/src/main.ts index e51976092..869cb2212 100644 --- a/cockpit/deep-agents/subagents/angular/src/main.ts +++ b/cockpit/deep-agents/subagents/angular/src/main.ts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app.config'; -import { SubagentsAppComponent } from './app.component'; +import { appConfig } from './app/app.config'; +import { SubagentsComponent } from './app/subagents.component'; -bootstrapApplication(SubagentsAppComponent, appConfig).catch(console.error); +bootstrapApplication(SubagentsComponent, appConfig).catch(console.error); diff --git a/cockpit/deep-agents/subagents/angular/tsconfig.app.json b/cockpit/deep-agents/subagents/angular/tsconfig.app.json index 5d05a9e61..64731b107 100644 --- a/cockpit/deep-agents/subagents/angular/tsconfig.app.json +++ b/cockpit/deep-agents/subagents/angular/tsconfig.app.json @@ -2,7 +2,9 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../../dist/out-tsc", - "types": [] + "lib": ["es2022", "dom"], + "types": [], + "emitDeclarationOnly": false }, "files": ["src/main.ts"], "include": ["src/**/*.ts"] diff --git a/cockpit/deep-agents/subagents/angular/tsconfig.json b/cockpit/deep-agents/subagents/angular/tsconfig.json index 90497de60..d9e29392d 100644 --- a/cockpit/deep-agents/subagents/angular/tsconfig.json +++ b/cockpit/deep-agents/subagents/angular/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../../../../../tsconfig.base.json", + "extends": "../../../../tsconfig.base.json", "compilerOptions": { "module": "preserve" },