feat(chat): decouple primitives from LangGraph via runtime-neutral ChatAgent contract#135
Merged
Merged
Conversation
Introduces ChatAgent contract (AG-UI-shaped, chat-owned) with LangGraph and optional AG-UI adapters. Phased delivery; website/docs treated as a first-class deliverable per phase. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Covers workstreams A (contract) through G (website/docs). Includes LangGraph adapter, AG-UI adapter scaffold, primitive migrations, package rename, and documentation alignment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…d fallback Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… @cacheplane/langgraph Nx project: agent -> langgraph npm package: @cacheplane/angular -> @cacheplane/langgraph Directory: libs/agent -> libs/langgraph Updates tsconfig.base.json path mapping, project.json, ng-package.json, package.json, CI workflows, all code imports, docs, and website content. api-docs.json is a generated artifact and will regenerate. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rrowing Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the required `ref: AgentRef` input with `agent: ChatAgent` on the chat composition. Adds an optional `langgraphRef: AgentRef | undefined` escape hatch for chat-interrupt and customEvents (phase-1 only) guarded by @if. Updates onA2uiAction to use ChatSubmitInput.message shape. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…atAgent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds buildCustomEvents$() which uses an effect + cursor to emit only newly-appended CustomStreamEvent items from the Signal<CustomStreamEvent[]>, mapping name → type to satisfy the ChatCustomEvent contract. Handles session resets by detecting when the array length shrinks. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…scription Remove the AgentRef escape hatch from ChatComponent: delete the langgraphRef input and its old customEvents effect that polled ref.customEvents(). Wire custom events through agent.customEvents$ (Observable) with a one-shot guard effect in the constructor, and update the interrupt binding to [agent]="agent()". Guard both constructor effects against NG0950 in Angular 21 zoneless mode. Add two runInInjectionContext tests that exercise the routing logic and verify state_update events update the store while non-matching events are ignored. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Swaps SubagentStreamRef from @cacheplane/langgraph to the runtime-neutral ChatSubagent type from libs/chat/src/lib/agent. Aligns with Phase-1 decoupling objective. Also simplifies status type by importing ChatSubagentStatus directly. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Replace AgentRef with ChatAgent input; add getInterruptFromAgent helper for testability. Update specs to test the helper and component definition using mockChatAgent with withInterrupt option. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…cacheplane/langgraph
Per ADR docs/superpowers/specs/2026-04-21-langgraph-specific-primitives-location.md,
components that render checkpoint_id / ThreadState / fork-replay UI belong in
@cacheplane/langgraph, not in the runtime-neutral @cacheplane/chat.
Files moved (git mv, history preserved):
- libs/chat/src/lib/primitives/chat-timeline/ → libs/langgraph/src/lib/primitives/chat-timeline/
- libs/chat/src/lib/compositions/chat-timeline-slider/ → libs/langgraph/src/lib/compositions/chat-timeline-slider/
- libs/chat/src/lib/compositions/chat-debug/ → libs/langgraph/src/lib/compositions/chat-debug/
- libs/chat/src/lib/testing/mock-agent-ref.{ts,spec.ts} → libs/langgraph/src/lib/testing/
Import fixes in moved files:
- @cacheplane/langgraph self-imports → relative ../agent.types
- Internal chat primitives (ChatMessagesComponent, CHAT_THEME_STYLES, etc.) → @cacheplane/chat
- messageContent() added to @cacheplane/chat public-api to support the peer import
Package.json updates:
- libs/chat/package.json: removed unused @langchain/langgraph-sdk, added missing rxjs
- libs/langgraph/package.json: added @angular/common and @angular/platform-browser
Note: chat→langgraph circular dep in build graph persists until Task 6f drops
@cacheplane/langgraph from libs/chat/package.json peerDependencies.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cular edge Task 6f of Phase-1 decoupling. After Task 6e relocated LangGraph-specific primitives to @cacheplane/langgraph, no source in libs/chat imports from the langgraph package, so the peer-dep can be dropped. Dependency direction is now strictly one-way: langgraph -> chat. Also updates the langgraph lint config to accept 'chat' and 'debug' component-selector prefixes (needed for the moved chat-timeline, chat-debug, debug-* components to keep their public selectors stable), and softens a docstring mention of @cacheplane/langgraph in the ChatAgent contract so @nx/dependency-checks no longer flags it. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
All @cacheplane/chat component bindings ([ref]="stream") updated to
[agent]="chatAgent" using toChatAgent(this.stream) direct-call idiom.
Fixes wrong import sources for ChatDebugComponent and
ChatTimelineSliderComponent (moved from @cacheplane/chat to
@cacheplane/langgraph in Phase 6). LangGraph-specific chat-debug and
chat-timeline-slider retain [ref]="stream" bindings throughout.
langgraph/interrupts: migrated submit call to chatAgent.submit({resume:null}).
Affects 25 cockpit components across chat/, langgraph/, and deep-agents/ trees.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Export ChatCustomEvent from @cacheplane/chat public-api (used by toChatAgent's signature in @cacheplane/langgraph). - Import describe/it/expect from vitest in chat-agent-conformance.ts so the helper compiles under ng-packagr's production build (previously relied on Vitest globals, which aren't in the lib tsconfig types). - Loosen AgentRef's second type argument in buildCustomEvents$ from unknown to any so it satisfies the BagTemplate constraint. - Wire chat-debug composition (now in @cacheplane/langgraph) to toChatAgent for its chat-messages/chat-typing-indicator/chat-error/ chat-input child bindings, which moved from [ref] to [agent] in Phase-1. The AgentRef-shaped [ref] binding is kept for chat-debug-summary and chat-debug-controls, which still consume AgentRef directly. With this, `nx build chat`, `nx build langgraph`, `nx test chat`, and `nx test langgraph` all pass. Dependency direction is strictly one-way: langgraph -> chat. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
- Drop unused ref inputs on DebugControlsComponent and DebugSummaryComponent - Consolidate fragmented @cacheplane/chat imports in chat-debug - Route cockpit messages/input demos through chatAgent.submit Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CI's Library job failed lint on these pre-existing stub methods. The empty bodies are intentional — they implement the AgentRef surface for the adapter under test. Scope the disable to the stub helpers. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
blove
added a commit
that referenced
this pull request
Jun 9, 2026
…atAgent contract (#135) * docs(specs): add chat runtime decoupling design Introduces ChatAgent contract (AG-UI-shaped, chat-owned) with LangGraph and optional AG-UI adapters. Phased delivery; website/docs treated as a first-class deliverable per phase. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(specs): define ChatContentBlock in decoupling design Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs(plans): add Phase 1 implementation plan for chat runtime decoupling Covers workstreams A (contract) through G (website/docs). Includes LangGraph adapter, AG-UI adapter scaffold, primitive migrations, package rename, and documentation alignment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(chat): add ChatAgent data types (message, tool call, content block, status) * feat(chat): add ChatInterrupt, ChatSubagent, submit input/options types * feat(chat): define ChatAgent runtime-neutral contract * feat(chat): export ChatAgent contract from public-api * docs(chat): document runtime-neutral ChatAgent and adapters * chore(chat): add SPDX header to agent spec files * docs(chat): clarify ChatSubmitInput.message JSDoc Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(chat): add mockChatAgent() test helper Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(chat): add ChatAgent conformance test suite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): rename submit param to avoid shadowing in mockChatAgent Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(agent): add toChatAgent() adapter to runtime-neutral ChatAgent contract * feat(agent): export toChatAgent * test(agent): verify toChatAgent satisfies ChatAgent conformance * refactor(agent): rename cryptoRandom to randomId; use for interrupt id fallback Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor: rename libs/agent to libs/langgraph; @cacheplane/angular to @cacheplane/langgraph Nx project: agent -> langgraph npm package: @cacheplane/angular -> @cacheplane/langgraph Directory: libs/agent -> libs/langgraph Updates tsconfig.base.json path mapping, project.json, ng-package.json, package.json, CI workflows, all code imports, docs, and website content. api-docs.json is a generated artifact and will regenerate. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(chat): migrate chat-input to ChatAgent contract Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): migrate chat-messages to ChatAgent contract Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): migrate chat-tool-calls to ChatAgent contract Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): migrate chat-typing-indicator to ChatAgent contract Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): migrate chat-error to ChatAgent contract Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): drop redundant tool_use cast; rely on discriminant narrowing Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(chat): migrate chat composition core path to ChatAgent Replaces the required `ref: AgentRef` input with `agent: ChatAgent` on the chat composition. Adds an optional `langgraphRef: AgentRef | undefined` escape hatch for chat-interrupt and customEvents (phase-1 only) guarded by @if. Updates onA2uiAction to use ChatSubmitInput.message shape. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore(chat): mark remaining primitives as phase-2/phase-3 migration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(chat): add ChatCustomEvent type and optional customEvents$ to ChatAgent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(chat): extend mock + conformance for customEvents\$ * feat(langgraph): bridge customEvents signal to Observable in toChatAgent Adds buildCustomEvents$() which uses an effect + cursor to emit only newly-appended CustomStreamEvent items from the Signal<CustomStreamEvent[]>, mapping name → type to satisfy the ChatCustomEvent contract. Handles session resets by detecting when the array length shrinks. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): migrate chat-interrupt from AgentRef to ChatAgent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): replace langgraphRef with ChatAgent.customEvents$ subscription Remove the AgentRef escape hatch from ChatComponent: delete the langgraphRef input and its old customEvents effect that polled ref.customEvents(). Wire custom events through agent.customEvents$ (Observable) with a one-shot guard effect in the constructor, and update the interrupt binding to [agent]="agent()". Guard both constructor effects against NG0950 in Angular 21 zoneless mode. Add two runInInjectionContext tests that exercise the routing logic and verify state_update events update the store while non-matching events are ignored. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(chat): migrate chat-subagents from AgentRef to ChatAgent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): migrate chat-subagent-card to ChatSubagent contract Swaps SubagentStreamRef from @cacheplane/langgraph to the runtime-neutral ChatSubagent type from libs/chat/src/lib/agent. Aligns with Phase-1 decoupling objective. Also simplifies status type by importing ChatSubagentStatus directly. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> * refactor(chat): migrate chat-interrupt-panel to ChatAgent contract Replace AgentRef with ChatAgent input; add getInterruptFromAgent helper for testability. Update specs to test the helper and component definition using mockChatAgent with withInterrupt option. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(chat,langgraph): relocate LangGraph-specific primitives to @cacheplane/langgraph Per ADR docs/superpowers/specs/2026-04-21-langgraph-specific-primitives-location.md, components that render checkpoint_id / ThreadState / fork-replay UI belong in @cacheplane/langgraph, not in the runtime-neutral @cacheplane/chat. Files moved (git mv, history preserved): - libs/chat/src/lib/primitives/chat-timeline/ → libs/langgraph/src/lib/primitives/chat-timeline/ - libs/chat/src/lib/compositions/chat-timeline-slider/ → libs/langgraph/src/lib/compositions/chat-timeline-slider/ - libs/chat/src/lib/compositions/chat-debug/ → libs/langgraph/src/lib/compositions/chat-debug/ - libs/chat/src/lib/testing/mock-agent-ref.{ts,spec.ts} → libs/langgraph/src/lib/testing/ Import fixes in moved files: - @cacheplane/langgraph self-imports → relative ../agent.types - Internal chat primitives (ChatMessagesComponent, CHAT_THEME_STYLES, etc.) → @cacheplane/chat - messageContent() added to @cacheplane/chat public-api to support the peer import Package.json updates: - libs/chat/package.json: removed unused @langchain/langgraph-sdk, added missing rxjs - libs/langgraph/package.json: added @angular/common and @angular/platform-browser Note: chat→langgraph circular dep in build graph persists until Task 6f drops @cacheplane/langgraph from libs/chat/package.json peerDependencies. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(chat): drop @cacheplane/langgraph peer-dep, breaking the circular edge Task 6f of Phase-1 decoupling. After Task 6e relocated LangGraph-specific primitives to @cacheplane/langgraph, no source in libs/chat imports from the langgraph package, so the peer-dep can be dropped. Dependency direction is now strictly one-way: langgraph -> chat. Also updates the langgraph lint config to accept 'chat' and 'debug' component-selector prefixes (needed for the moved chat-timeline, chat-debug, debug-* components to keep their public selectors stable), and softens a docstring mention of @cacheplane/langgraph in the ChatAgent contract so @nx/dependency-checks no longer flags it. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(cockpit): bind cockpit demos to ChatAgent via toChatAgent All @cacheplane/chat component bindings ([ref]="stream") updated to [agent]="chatAgent" using toChatAgent(this.stream) direct-call idiom. Fixes wrong import sources for ChatDebugComponent and ChatTimelineSliderComponent (moved from @cacheplane/chat to @cacheplane/langgraph in Phase 6). LangGraph-specific chat-debug and chat-timeline-slider retain [ref]="stream" bindings throughout. langgraph/interrupts: migrated submit call to chatAgent.submit({resume:null}). Affects 25 cockpit components across chat/, langgraph/, and deep-agents/ trees. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(chat,langgraph): final Phase-1 build fixes - Export ChatCustomEvent from @cacheplane/chat public-api (used by toChatAgent's signature in @cacheplane/langgraph). - Import describe/it/expect from vitest in chat-agent-conformance.ts so the helper compiles under ng-packagr's production build (previously relied on Vitest globals, which aren't in the lib tsconfig types). - Loosen AgentRef's second type argument in buildCustomEvents$ from unknown to any so it satisfies the BagTemplate constraint. - Wire chat-debug composition (now in @cacheplane/langgraph) to toChatAgent for its chat-messages/chat-typing-indicator/chat-error/ chat-input child bindings, which moved from [ref] to [agent] in Phase-1. The AgentRef-shaped [ref] binding is kept for chat-debug-summary and chat-debug-controls, which still consume AgentRef directly. With this, `nx build chat`, `nx build langgraph`, `nx test chat`, and `nx test langgraph` all pass. Dependency direction is strictly one-way: langgraph -> chat. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(chat,langgraph): polish Phase-1 review nits - Drop unused ref inputs on DebugControlsComponent and DebugSummaryComponent - Consolidate fragmented @cacheplane/chat imports in chat-debug - Route cockpit messages/input demos through chatAgent.submit Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(langgraph): silence no-empty-function on AgentRef test stubs CI's Library job failed lint on these pre-existing stub methods. The empty bodies are intentional — they implement the AgentRef surface for the adapter under test. Scope the disable to the stub helpers. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase-1 of the chat-runtime decoupling: introduces a runtime-neutral `ChatAgent` contract in `@cacheplane/chat` and migrates every primitive and composition off LangGraph's `AgentRef`. Breaks the `chat ↔ langgraph` circular build-graph edge — dependency direction is now strictly one-way (`langgraph → chat`).
Test Plan
🤖 Generated with Claude Code