Skip to content

feat(chat): decouple primitives from LangGraph via runtime-neutral ChatAgent contract#135

Merged
blove merged 40 commits into
mainfrom
feat/chat-runtime-decoupling-phase-1
Apr 22, 2026
Merged

feat(chat): decouple primitives from LangGraph via runtime-neutral ChatAgent contract#135
blove merged 40 commits into
mainfrom
feat/chat-runtime-decoupling-phase-1

Conversation

@blove
Copy link
Copy Markdown
Contributor

@blove blove commented Apr 22, 2026

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`).

  • New contract: `ChatAgent` with signal-based state (`messages`, `status`, `isLoading`, `toolCalls`, `state`) plus optional `interrupt`, `subagents`, and `customEvents$` (Observable). Ships with `mockChatAgent` + `runChatAgentConformance` test helpers.
  • Adapter: `toChatAgent(agentRef)` in `@cacheplane/langgraph` wraps a LangGraph `AgentRef` into a `ChatAgent`, bridging the `customEvents` signal into an RxJS Observable.
  • Primitives migrated (`@cacheplane/chat`): chat-messages, chat-input, chat-tool-calls, chat-typing-indicator, chat-error, chat-interrupt, chat-subagents — all switched from `[ref]` to `[agent]`.
  • Compositions migrated: `` (dropped `langgraphRef` escape hatch, subscribes to `customEvents$` for state updates), chat-interrupt-panel, chat-subagent-card.
  • Relocated to `@cacheplane/langgraph` (per ADR): chat-timeline, chat-timeline-slider, chat-debug and sub-components, mock-agent-ref. These render LangGraph-specific checkpoint/ThreadState UI and don't belong in a runtime-neutral package.
  • Cockpit demos (25 files): updated to bind `[agent]="toChatAgent(stream)"`.

Test Plan

  • `nx test chat` — passes
  • `nx test langgraph` — passes
  • `nx build chat` — passes (production)
  • `nx build langgraph` — passes (production)
  • Nx project graph confirms one-way dep: `langgraph → chat`, no edge back
  • Smoke-test a cockpit chat demo in the browser (reviewer)

🤖 Generated with Claude Code

blove and others added 30 commits April 21, 2026 12:35
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>
blove and others added 8 commits April 21, 2026 15:22
…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>
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cacheplane Ready Ready Preview, Comment Apr 22, 2026 0:57am
cacheplane-minting-service Error Error Apr 22, 2026 0:57am

Request Review

blove and others added 2 commits April 21, 2026 17:47
- 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 blove merged commit 96cb6d4 into main Apr 22, 2026
14 of 15 checks passed
@blove blove deleted the feat/chat-runtime-decoupling-phase-1 branch May 7, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant