# AgentsKit Chat — canonical documentation corpus # Typed action confirmation Without persistent storage, an actionable choice moves from `pending` directly to `approved`, `rejected`, or `expired` after upstream success. Persistent cross-client sessions first claim `approving`, `rejecting`, or `expiring` through storage CAS, delegate only from the winning client, then persist the matching terminal status. | Field | Meaning | |---|---| | `token` | Opaque session correlation handle; not authentication | | `sessionId` | Session allowed to approve or reject | | `action` | Registered upstream tool name | | `input` | Validated, immutable argument snapshot | | `toolCallId` | Canonical AgentsKit tool-call identity | | `expiresAt` | Absolute expiry checked before approval | | `status` | `pending`; processing `approving`, `rejecting`, `expiring`; or terminal `approved`, `rejected`, `expired` | Persistent resolution is claimed before delegation, so concurrent replay cannot execute twice. A failure or crash after the claim leaves an accurate processing record for host reconciliation; processing and terminal records are inert on resume. In-memory resolution preserves retryable behavior by marking the outcome only after upstream success. A token from another session is inert. This slice does not claim to be an audit ledger. Policy work may observe these fields and the upstream tool lifecycle, attach approver identity, and persist audit events without changing the execution path. # Action policy Compose trusted capability policy into the same `ChatConfig` used by every renderer: ```ts const policy = createCapabilityPolicy({ sessionId, getContext: () => authenticatedSession, requirements: { 'email.send': ['email:send'], 'docs.open': [], }, onTrace: trace => auditBuffer.push(trace), }) const chat = withActionPolicy(baseChat, policy) ``` `getContext` is a trusted host seam. Never populate it from prompts, messages, component props, or tool arguments. Unknown actions and missing context are denied before confirmation. Capabilities are checked again immediately before execution, so revocation remains effective after a user has seen a confirmation. Policy traces are immutable and replayable but are not a durable audit ledger. Persist them in the host if compliance or incident investigation requires retention. # API reference All packages use named exports. Runtime input must enter through the exported schemas, decoders, or resolvers; TypeScript types alone are not a trust boundary. | Package | Primary public API | |---|---| | `@agentskit/chat/protocol` | Turn/session/component schemas and codecs, ordered assistant content, Ask events, deterministic answer contracts, conformance fixtures via `/protocol/fixtures`. | | `@agentskit/chat` | `defineChat`, sessions, controlled-session driver, deterministic routes, conversation composition, component manifest/resolution, semantic theme/fallback, action confirmation/policy, Ask adapter/memory, deterministic answer adapter. | | `@agentskit/chat/server` | `createChatHandler`, `ChatHandlerOptions`, `ChatHandlerError`; standard `Request` → streaming `Response`. | | `@agentskit/chat/react` | `AgentChat`, `ChoiceList`, `StandardComponent`, native slots, `toChatCssVariables`. | | `@agentskit/chat/react-native` | `AgentChatNative`, `ChoiceListNative`, `StandardComponentNative`, native slots/styles, `toChatNativeStyles`. | | `@agentskit/chat/ink` | `AgentChat`, `ChoiceList`, `StandardComponent`, `SemanticFallback`, native slots, `toChatInkTheme`. | | `@agentskit/chat/vue` | `AgentChat`, `ChoiceList`, `StandardComponent`, scoped slots, `toChatCssVariables`. | | `@agentskit/chat/svelte` | `AgentChat`, `ChoiceList`, `StandardComponent`, typed snippets, `toChatCssVariables`, `toChatStyle`. | | `@agentskit/chat/solid` | `AgentChat`, `ChoiceList`, `StandardComponent`, render props, `toChatCssVariables`. | | `@agentskit/chat/angular` | `AgentChatComponent`, `ChoiceListComponent`, `StandardComponentComponent`, content templates, `toChatCssVariables`. | | `@agentskit/chat-cli` | `CHAT_RENDERERS`, `detectRenderer`, `initChatProject`, `addChatComponent`; binary `agentskit-chat`. | | `@agentskit/chat/devtools` | Trace capture/projection, replay fixture codecs, and `compareRendererOutcomes`. Model recording/replay remains in `@agentskit/eval/replay`. | Renderer shells accept the same `ChatDefinition` while exposing native slots, templates, snippets, render props, or styles. They delegate controller state, streaming, messages, tools, cancellation, retry/edit/regenerate, and base confirmation behavior to the corresponding AgentsKit binding. `createControlledChatDriver` validates an external serializable snapshot and projects host lifecycle callbacks through the canonical AgentsKit `ChatReturn` shape. The React and Ink `AgentChat` renderers accept that source through their optional `controlled` prop; their existing definition-owned modes are unchanged. The generated declarations in each package's published `dist` directory are the exact signature reference. See the package README and the matching [quick start](./getting-started/README.md) for executable composition. # ADR-0001: Framework-neutral core with native renderers **Status:** Accepted **Date:** 2026-07-10 ## Context AgentsKit supports React, Vue, Svelte, Solid, Angular, React Native, and Ink through a shared chat controller. AgentsKit Chat must add application-level behavior without choosing one renderer as the canonical implementation or weakening platform-native developer experience. ## Decision Chat definitions, turn events, actions, policies, component manifests, conversation state, sessions, and compatibility fixtures are framework-neutral and runtime validated. Every renderer is a native adapter over its corresponding AgentsKit binding. Custom components share identity, props schemas, actions, and semantic fallbacks, but their visual implementation may differ by platform. React, React Native, and Ink are the initial architecture-proof targets. The remaining web renderers follow the validated contract. ## Alternatives considered 1. React-first core with wrappers for other frameworks. Rejected because it leaks JSX, hooks, DOM assumptions, and React lifecycle semantics into the public contract. 2. Web Components as the only renderer. Rejected because they do not provide a native experience in React Native or Ink and weaken framework-specific composition. 3. Fully independent implementations. Rejected because behavior and security would drift across platforms. ## Consequences - Behavior, sessions, actions, and tests can be shared across interfaces. - Renderer packages remain native and independently installable. - Visual components cannot always be source-compatible across platforms. - A conformance suite and semantic fallback contract become mandatory. # ADR-0002: Upstream-first and no reimplementation **Status:** Accepted **Date:** 2026-07-10 ## Context AgentsKit Chat is an application framework built on AgentsKit. AgentsKit already owns the chat controller, lifecycle, framework bindings, generative UI primitives, tools, confirmation, memory, RAG, runtime, replay, eval, and observability contracts. Without an explicit dependency rule, AgentsKit Chat could accidentally create parallel implementations with slightly different types and behavior. That would weaken interoperability, split fixes between repositories, and make the dogfood claim false. ## Decision AgentsKit Chat follows an **upstream-first** rule: 1. Search the current AgentsKit source and public API before designing or implementing a primitive. 2. Reuse an existing AgentsKit contract directly when it satisfies the requirement. 3. Adapt or compose the contract inside AgentsKit Chat when only application-level policy or developer experience is missing. 4. If a framework-neutral primitive is missing or defective, change it in [`AgentsKit-io/agentskit`](https://github.com/AgentsKit-io/agentskit) first, with its own issue, ADR when required, tests, release, and compatibility path. 5. Consume the released upstream API from AgentsKit Chat. Do not copy source, fork behavior, vendor private files, or depend on unpublished workspace paths. AgentsKit Chat may own a new contract only when it is specific to the application-framework layer and cannot be useful independently of that layer. The issue or ADR introducing it must document why composition of existing AgentsKit APIs is insufficient. ## Ownership matrix | Concern | Owner | AgentsKit Chat rule | |---|---|---| | Model adapters and model invocation | AgentsKit | Configure and consume; never wrap provider protocols independently. | | Chat state and lifecycle | AgentsKit `ChatController` | `defineChat` compiles to or derives `ChatConfig`; never create a parallel controller. | | Streaming, stop, retry, edit, regenerate | AgentsKit | Transport existing lifecycle semantics; do not redefine them. | | Tools and execution loop | AgentsKit `ToolDefinition` and runtime | Application actions compile to tools plus trusted policy metadata; no second executor. | | Tool argument validation | AgentsKit validation seam | Supply validators and schemas; fix generic validation gaps upstream. | | Human confirmation | AgentsKit tool confirmation and HITL primitives | Add product policy, identity, expiry, and audit by composition. | | Generative UI primitives | AgentsKit `UIMessage` and `UIElement` | Reuse for the standard portable element set; extend only through an application component registry. | | Chat message memory | AgentsKit `ChatMemory` | Reuse for messages; session envelopes store only application metadata not represented upstream. | | RAG and retrieval | AgentsKit | Configure retrievers and render retrieved-source metadata. | | Replay and eval primitives | AgentsKit | Compose application traces and renderer conformance around upstream fixtures. | | Framework hooks and stores | AgentsKit renderer packages | Each native renderer consumes its corresponding package directly. | | Deterministic application routes | AgentsKit Chat | Own because they coordinate application behavior before model dispatch. | | Conversation statecharts | AgentsKit Chat unless generalized upstream | Own application transitions; promote a generally useful primitive upstream before reuse. | | Application authorization and policy | Host application + AgentsKit Chat seam | Keep authority outside the model and outside low-level AgentsKit primitives. | | Custom component registry | AgentsKit Chat | Own semantic component identity, runtime props validation, native implementations, and fallback. | | Client-server application protocol | AgentsKit Chat | Transport upstream lifecycle plus application events; do not rename or alter upstream semantics. | | CLI and project scaffolding | AgentsKit Chat | Generate composition around published AgentsKit packages. | ## Required issue evidence Every implementation issue must include an **Upstream adoption** section containing: - AgentsKit source and exports inspected; - existing primitives reused; - application-layer behavior added; - upstream gap, if any, with a linked AgentsKit issue or PR; - explicit confirmation that no upstream source is copied or reimplemented. Code review must reject a change when equivalent behavior already exists upstream or when a generally useful primitive is added locally without an upstream decision. ## Source-change protocol When an upstream change is required: 1. Open or link an issue in `AgentsKit-io/agentskit` describing the primitive-level gap. 2. Implement and test the fix in AgentsKit under its compatibility and ADR rules. 3. Publish or otherwise make the supported upstream version available. 4. Update AgentsKit Chat's declared compatibility range. 5. Implement only the application-layer composition in this repository. Temporary copied implementations and private cross-repository imports are forbidden, even behind feature flags. Work remains blocked until the supported upstream contract exists. ## Consequences - AgentsKit remains the single source of truth for reusable agent and chat primitives. - AgentsKit Chat dogfoods published APIs rather than a privileged internal path. - Some AgentsKit Chat issues may block on upstream releases. - Cross-repository work is more explicit, but fixes benefit every AgentsKit consumer. - The application framework stays smaller and focuses on composition, policy, protocol, and developer experience. # ADR-0003: Framework-neutral semantic fallback envelope **Status:** Accepted **Date:** 2026-07-11 ## Context ADR-0001 requires portable components to share semantic fallbacks while allowing each renderer to use native visual primitives. The first Ink slice needs a stable text representation before the component registry is introduced. Defining the fallback format in the Ink package would make application behavior renderer-specific. Accepting unchecked values would also violate the runtime-validation boundary. ## Decision `@agentskit/chat` owns a minimal, runtime-validated semantic fallback envelope with two non-empty strings: `kind` identifies the unsupported visual and `summary` preserves its human-readable meaning. The shared formatter produces `[unsupported visual: ] `. Native renderer packages may expose components that present this shared value, but they do not redefine its schema or formatting. The future component registry may associate this envelope with component manifests. It must reuse or compatibly version this contract rather than create a renderer-specific alternative. ## Consequences - Fallback meaning and text remain identical across renderers. - Untrusted fallback values are validated before rendering. - Ink owns only native terminal presentation. - The contract is intentionally small and does not pre-empt component registry identity, props, or actions. # ADR-0004: Snapshot-first v1 turn protocol **Status:** Accepted **Date:** 2026-07-11 ## Context React, React Native, and Ink need one versioned application protocol that can cross future client/server boundaries without redefining the AgentsKit controller or adapter stream. Transporting raw `StreamChunk` values would expose adapter-level details and require clients to implement another lifecycle reducer. Reusing `AgentEvent` would confuse observer telemetry with durable application state. ## Decision `@agentskit/chat-protocol` owns a versioned, runtime-validated event envelope, inert typed decoder diagnostics, and committed conformance fixtures. Version 1 begins with three events: - `client.turn.submit` carries validated user input; - `server.turn.snapshot` carries the wire projection of canonical AgentsKit messages, status, usage, and optional safe diagnostic; - `server.turn.diagnostic` carries a safe typed failure. Streaming uses successive full snapshots with increasing sequence numbers and stable message ids. AgentsKit remains the lifecycle authority. Stateful sequence rejection, transport, sessions, actions, and components remain in their owning issues. Within v1, additive optional fields are compatible and stripped by older decoders. Unknown events and versions are inert. Required-field or semantic changes require v2, migrations, compatibility fixtures, and an ADR. ## Alternatives considered 1. Raw `StreamChunk` transport — rejected because it creates a second reducer. 2. `AgentEvent` transport — rejected because it is observer telemetry. 3. Per-renderer stores — rejected because behavior would drift. 4. Defining future action and session events now — rejected as speculative. ## Consequences - All proof renderers consume the same schema and fixture corpus. - Full snapshots are larger than deltas but materially simpler and safer for v0. - Protocol diagnostics never expose raw validation trees or untrusted payloads. - Future protocol evolution is explicit and compatibility-tested. # ADR-0005: Upstream memory-record validation **Status:** Accepted **Date:** 2026-07-11 ## Context The snapshot envelope needs runtime validation for serialized AgentsKit messages. Recreating message, content-part, tool-call, metadata, or memory schemas in this repository would duplicate canonical AgentsKit contracts and allow the framework to drift from its foundation. ## Decision `@agentskit/chat-protocol` delegates canonical message-record validation to the published `@agentskit/core/memory-validation` subpath. The protocol package owns only its application envelope, lifecycle fields, diagnostics, and compatibility policy. If the upstream validator cannot express a required canonical record, change and release AgentsKit first. Do not add a parallel validator, type assertion, compatibility cast, or local copy of the upstream schema. ## Consequences - AgentsKit remains the single source of truth for message records. - The framework receives upstream validation fixes through a dependency update. - Protocol tests must prove that hostile, cyclic, deep, and non-JSON records fail inertly. - A protocol release may wait for an upstream AgentsKit patch when a canonical contract is missing. # ADR-0006: Session-scoped deterministic conversation **Status:** Accepted **Date:** 2026-07-11 ## Context Known application workflows must execute without model interpretation, while unresolved input must retain the complete AgentsKit chat lifecycle. Conversation progress cannot live on a shared `ChatDefinition`, because the same definition may mount in multiple users, renderers, or tests. ## Decision `createChatSession(definition)` creates a fresh application session and derives an upstream `ChatConfig` by wrapping only its `AdapterFactory`. Routes are evaluated in declaration order. A route is eligible only when its state restriction matches and its event is a declared transition from the current state. A match emits an upstream-compatible deterministic `StreamSource`, commits the declared transition, and records its turn classification. A miss delegates the unchanged request to the original adapter and records an `agentic` turn. Conversation state, allowed events, and allowed actions are projected from the active state. The trace taxonomy is `deterministic`, `agentic`, `repaired`, and `fallback`; traces contain decision metadata, never prompt content. Deterministic decisions are cached by user-message identity and input. Retry and regenerate replay the same decision without model dispatch; a subsequent dispatch rebuilds conversation state from the retained user-message history before resolving an edited turn and prunes removed decisions. History mutations that do not dispatch a turn do not themselves transition the application machine. Route callback failures return an upstream error stream without committing a transition. Trace observers are best-effort and cannot affect dispatch. Every renderer compiles one session-scoped config per definition mount and continues to use its official AgentsKit binding. ## Alternatives considered 1. A second chat controller — rejected because AgentsKit owns lifecycle, streaming, tools, memory, and cancellation. 2. Route matching inside each renderer — rejected because behavior would drift across platforms. 3. Mutable state on `ChatDefinition` — rejected because concurrent mounts would share conversation progress. 4. A general statechart dependency — rejected because explicit transition records satisfy the current finite workflow without another runtime. ## Consequences - Known commands bypass model dispatch without bypassing the upstream controller. - Each mount receives isolated conversation progress. - Retry and regenerate remain deterministic; edited/truncated history is reconciled on its next dispatch. - Same-id `ChatConfig` updates reach the upstream binding without resetting conversation progress. - Route handlers cannot choose arbitrary target states; the machine owns transitions. - Persistence, async routing, protocol transport, and action execution remain separate future concerns. # ADR-0007: Closed application component manifest **Status:** Accepted **Date:** 2026-07-11 ## Context AgentsKit owns the portable standard `UIElement` union and its validator. Applications also need custom semantic components whose model-produced props are untrusted and whose native implementations differ across React, React Native, and Ink. Extending or copying the upstream union would create a competing generative-UI protocol. Letting renderers accept arbitrary component names or unchecked props would make model output executable UI configuration. ## Decision `@agentskit/chat` owns a closed application component manifest. Each entry has a stable key and a Zod props schema. A component render frame is usable only when its v1 envelope is valid, its key is registered, and its props pass that registered schema. Otherwise resolution is inert and returns a typed, non-retryable diagnostic. `@agentskit/chat-protocol` owns the framework-neutral render frame, semantic selection event, safe decoder, shared fixtures, and the wire schema for the semantic fallback established by ADR-0003. `@agentskit/chat` delegates its existing fallback parser to that schema and retains its public formatting API. This supersedes ADR-0003 only where it originally assigned schema ownership to `@agentskit/chat`; it avoids defining the same boundary twice. Native renderer packages own presentation and interaction mechanics only; they emit the same validated event. `ChoiceList` is the first registered application component. Its props contain a prompt and 1–20 uniquely identified choices. Selection can reference only an id in the validated frame. ## Consequences - AgentsKit remains the sole owner of standard generative UI, controller lifecycle, and framework bindings. - Unknown keys and invalid frames or props cannot call application callbacks. - React, React Native, Ink, and future renderers share identity, props, fallback, and event semantics without sharing UI primitives. - Adding a component is an explicit registry and documentation change; changing required v1 semantics requires a new protocol version and ADR. # ADR-0008: Typed actions use upstream confirmation **Status:** Accepted **Date:** 2026-07-11 ## Context A deterministic component choice may represent application intent with a side effect. AgentsKit already owns tool registration, runtime argument validation, canonical tool-call state, confirmation UI, approval, denial, and exactly-once execution. AgentsKit Chat must add session binding without creating a second executor or confirmation protocol. ## Decision An actionable `ChoiceList` choice carries `{ name, input }`. The framework sends it to the released AgentsKit `ChatReturn.proposeToolCall` API. Only registered executable tools with `requiresConfirmation` can become pending calls; AgentsKit applies the configured `ArgsValidator` and snapshots arguments. `createActionConfirmation` binds an opaque handle to session id, canonical tool-call id, registered action name, validated argument snapshot, and absolute expiry. Approval and rejection claim the local record before delegating exclusively to AgentsKit `approve` or `deny`. Replays return the terminal record. Expiry delegates denial and cannot execute. React, React Native, and Ink render their official AgentsKit `ToolConfirmation` component. The framework adds no alternative confirmation widget. The handle is a correlation and idempotency capability, not authentication. Authorization, approver identity, durable audit storage, and cryptographic bearer tokens belong to the policy/server slice. ## Consequences - Tool execution and schema validation remain single-sourced in AgentsKit. - All proof renderers share one framework-neutral lifecycle. - In-memory confirmation records are session-scoped; durable recovery remains future server work. - An approval is safety-biased: once claimed it is never automatically retried after an upstream failure. ## Upstream adoption - Inspected: `@agentskit/core` tool/controller contracts and React, React Native, and Ink confirmation components. - Reused: `ChatConfig.tools`, `ArgsValidator`, `ChatReturn.proposeToolCall`, `approve`, `deny`, `ToolCall`, and official `ToolConfirmation` components. - Upstream work: [AgentsKit issue #1144](https://github.com/AgentsKit-io/agentskit/issues/1144), released by [PR #1145](https://github.com/AgentsKit-io/agentskit/pull/1145) in `@agentskit/core@1.11.0` and corresponding framework releases. - Added locally: choice-to-action declaration and application-session metadata only. # ADR-0009: Trusted capability policy composes upstream authorization **Status:** Accepted **Date:** 2026-07-11 ## Context Confirmation proves user intent, not authority. Capability claims embedded in messages, component props, or tool arguments are untrusted. Policy must apply equally to model-originated and deterministic application actions without wrapping the AgentsKit executor. ## Decision `createCapabilityPolicy` resolves context from a host-owned callback on every proposal and execution check. It default-denies missing context, session mismatch, unregistered actions, and missing capabilities. Requirements explicitly list every allowed action; an empty list is an explicit public action. `withActionPolicy` composes this decision with any existing AgentsKit authorizer. It delegates enforcement to the released `ChatConfig.authorizeToolCall` contract from `@agentskit/core@1.12.0`. Every application decision creates an immutable, replayable trace containing action, canonical tool-call id, phase, required capabilities, decision, reason, and timestamp. Trusted context and tool arguments are deliberately excluded. Trace observers are isolated from enforcement. ## Consequences - Messages cannot grant capabilities or select a trusted session. - Capability revocation is observed at execution time. - The framework ships no RBAC engine, network policy service, or executor wrapper. - Durable audit storage and identity-provider adapters remain host/server responsibilities. ## Upstream adoption - Upstream issue: [AgentsKit #1147](https://github.com/AgentsKit-io/agentskit/issues/1147). - Released implementation: [AgentsKit PR #1148](https://github.com/AgentsKit-io/agentskit/pull/1148), `@agentskit/core@1.12.0`. - Reused: canonical tool registry, proposal lifecycle, execution-time authorization, typed errors, confirmation, and framework bindings. # ADR-0010: Lifecycle lineage and monotonic snapshot cursor **Status:** Accepted **Date:** 2026-07-11 ## Context ADR-0004 chose complete turn snapshots and deferred stateful sequence rejection. Retry, edit, and regeneration now need explicit ancestry, while reconnect and duplicate delivery must not roll a client backward. AgentsKit already owns streaming, abort, retry, edit, regeneration, message mutation, and late-chunk rejection. Reimplementing those behaviors in this framework would create a competing lifecycle. ## Decision The optional v1 snapshot `lineage` field records the operation and optional parent turn/source message identities. Because it is additive and older v1 decoders discard unknown fields, the envelope version remains 1. `createSnapshotEvent` projects canonical AgentsKit messages and explicit operation ancestry. `createTurnSnapshotCursor(sessionId)` fixes the trusted session before delivery, accepts its first validated complete snapshot as reconnect state, then accepts only a greater sequence. Duplicate, stale, foreign-session, malformed, and non-snapshot events are inert. Accepted state is deeply frozen. The cursor does not merge messages or interpret stream chunks. Renderers delegate lifecycle operations directly to their published AgentsKit binding. They may provide native controls, but cannot own lifecycle state or message mutation. ## Alternatives considered 1. A second renderer lifecycle store — rejected because AgentsKit is authoritative. 2. Delta events — rejected because they require another reducer and weaken reconnect simplicity. 3. Protocol v2 — rejected because optional lineage is backward-compatible in v1. 4. Deduplication by event id only — rejected because it would still permit older snapshots to roll state backward. ## Consequences - Lifecycle ancestry is transportable without changing canonical AgentsKit messages. - Reconnect and duplicate delivery have one framework-neutral monotonic rule. - Sequence numbers are session-monotonic; a new session begins with a new cursor. - A future delta protocol or cross-session merge requires a new ADR and protocol version. # ADR-0011: Application session metadata over AgentsKit ChatMemory **Status:** Accepted **Date:** 2026-07-11 ## Context Cross-client resume needs deterministic application state, protocol cursor, and confirmation bindings in addition to the canonical message history. AgentsKit already owns message persistence through `ChatMemory`; duplicating messages in an application envelope would create conflicting authorities. ## Decision AgentsKit Chat defines a runtime-validated `agentskit.chat.session` v1 envelope containing only application metadata: session and definition identity, definition revision, monotonic cursor, deterministic route decisions, and confirmation bindings. Messages remain exclusively in the `ChatMemory` configured on `ChatDefinition.chat`. `SessionStorage` is a host port with `load`, compare-and-set `save(snapshot, expectedCursor)`, and optional `delete`. A save returns `false` on conflict; last-write-wins storage is not conformant. `resumeChatSession` accepts missing state as a new session, explicitly migrates the supported v0 envelope, and rejects corrupt, unsupported, foreign-session, or incompatible-definition state before constructing a session. Renderers verify the prepared session identity and revision; they continue to use their official AgentsKit binding. Pending, processing, and terminal confirmation records are persisted. A pending record remains bound to its original session and canonical tool-call id. Resolution first claims `approving`, `rejecting`, or `expiring` through CAS; only the winner delegates to the upstream controller and then advances to the matching terminal status through a second CAS. A crash leaves an accurate processing record for host reconciliation rather than falsely claiming completion or permitting replay. ## Alternatives considered 1. Store messages inside the session envelope — rejected because `ChatMemory` already owns them. 2. Bundle localStorage, filesystem, and database adapters — rejected because upstream Core and Memory already provide message adapters and hosts have different metadata stores. 3. Silently accept any snapshot version or definition — rejected because partial hydration can corrupt state or weaken confirmation binding. ## Consequences - Cross-client hosts share two coordinated concerns: upstream `ChatMemory` for messages and `SessionStorage` for application metadata. - Definition revisions make compatibility explicit. - The v0-to-v1 migration is narrow and tested; future incompatible formats require a new migration and ADR. - Storage failures reject explicit persistence calls but never introduce a second chat lifecycle. ## Upstream adoption Inspected AgentsKit Core memory types, serialization, controller hydration and confirmation execution, plus Memory filesystem, SQLite, Redis, and Turso adapters. Reused `ChatMemory`, canonical messages/tool calls, and controller `approve`/`deny`. Added only application metadata and host storage composition. No upstream source or behavior is copied and no upstream gap exists. # ADR-0012: Web-standard snapshot handler **Status:** Accepted **Date:** 2026-07-11 ## Context AgentsKit Chat definitions need one portable server boundary for Node, serverless, and edge hosts. AgentsKit already owns the controller, adapters, stream consumption, tools, message memory, and cancellation. The application protocol already defines complete snapshots and safe diagnostics. ## Decision `@agentskit/chat-server` exposes `createChatHandler`, returning `(Request) => Promise`. It accepts validated `client.turn.submit` events and streams newline-delimited, encoded `server.turn.snapshot` events using Web `ReadableStream`. Authentication runs before body parsing. Its host-owned context is passed only to definition and storage resolvers; client fields cannot populate it. The handler loads and saves messages through upstream `ChatMemory`, resumes application metadata through CAS `SessionStorage`, creates the upstream `ChatController`, observes canonical state, and projects snapshots with the existing protocol helper. It never transports raw `StreamChunk` or creates another reducer. Persistent storage is required. Before controller creation, the handler claims an expiring active-turn lease through session CAS, rejecting concurrent work before any model or tool side effect. A bounded terminal-turn history rejects recent sequential replay and distinguishes completed work from an indeterminate memory-save outcome. The lease covers the request plus every bounded cleanup phase. Cleanup settles the controller, durably saves canonical messages, and releases the lease after request abort or response cancellation, including when response backpressure prevents another pull. Pending full snapshots are coalesced under backpressure. The request signal is combined with `AbortSignal.timeout`. Authentication, definition resolution, session load, message load, and the active turn share that deadline. Abort calls `controller.stop()`; stream cancellation also stops the controller. Boundary failures return safe versioned JSON diagnostics, and active-turn timeout/cancellation is represented by a safe diagnostic event. ## Alternatives considered 1. Framework-specific Next/Express handlers — rejected because the Web contract already runs across target hosts. 2. Stream adapter chunks directly — rejected by ADR-0004 because clients would need another lifecycle reducer. 3. Add a custom message store — rejected because AgentsKit `ChatMemory` owns canonical history. 4. Bundle authentication — rejected because identity and tenancy are host concerns. ## Consequences - Host adapters are thin request bridges. - Full snapshots use more bandwidth than deltas but retain deterministic reconnect behavior. - Hosts must provide atomic CAS session storage when persistence is enabled. - The 64-turn dedupe window is application replay protection, not crash-proof exactly-once execution; side-effecting tools that require stronger guarantees still need their own durable idempotency key or transactional outbox. - Provider-specific resilience remains inside the selected AgentsKit adapter; the handler enforces the outer request deadline and cancellation boundary. ## Upstream adoption Inspected AgentsKit Core controller, adapter, stream, chat, and memory contracts plus published Memory adapters. Reused `createChatController`, `ChatMemory`, canonical state/messages/tool calls, and `StreamSource.abort`. The cancellable `ChatMemory` seam was added upstream by AgentsKit #1155/#1156 and released in Core 1.12.2 before integration. Added locally only the Web application boundary, trusted-context composition, active-turn application lease, and protocol projection; no upstream source or behavior is copied. # ADR-0013: Semantic application themes and native slots **Status:** Accepted — HITL approved 2026-07-11 **Date:** 2026-07-11 ## Context AgentsKit Chat needs one visual intent across DOM, native, and terminal hosts without moving framework primitives into the shared definition or recreating AgentsKit UI bindings. ## Decision `@agentskit/chat` owns a small runtime-validated semantic theme: colors, spacing, radius, and font family. Renderer packages translate it only through published AgentsKit seams: - React maps to upstream `--ak-*` CSS custom properties and stable `data-ak-*` hooks. - React Native maps to upstream `style` pass-throughs and native styles for application components. - Ink maps supported colors to a complete upstream `InkTheme` and uses `InkThemeProvider`; spatial and typography tokens are intentionally unsupported by terminal capabilities. Each renderer exposes native component slots for its container, message, input, thinking indicator, confirmation, and `ChoiceList`. Slots never enter the shared definition. Hosts needing fully headless state import the published `useChat` hook from their AgentsKit binding directly. Default shells retain their documented accessibility and keyboard behavior. A host replacing a slot owns equivalent semantics. ## Alternatives considered 1. A universal component tree — rejected because it would leak DOM/React Native/Ink primitives into core. 2. A second headless chat hook — rejected because AgentsKit already owns `useChat`, state, lifecycle, and cancellation. 3. A local design system — rejected because AgentsKit already publishes CSS variables, native style seams, and Ink theming. ## Consequences - One shared definition can be restyled without renderer conditionals. - Mapping is capability-aware rather than pretending every platform supports every token. - Slots remain idiomatic and type-safe per renderer. - Replacing defaults transfers accessibility responsibility to the host. ## Upstream adoption Inspected AgentsKit React theme CSS and component attributes, React Native component style/testID pass-throughs, and Ink `InkThemeProvider`/`InkTheme`. Reused all of them directly. The missing React Native content/input text-style seams were added at the source in AgentsKit #1158/#1159 and released as `@agentskit/react-native@0.4.4` via #1160 before integration. Added here only application token validation, mapping, and renderer-local slots. No upstream primitive or behavior is copied. # ADR-0014: Safe application scaffolding in a dedicated CLI **Status:** Accepted **Date:** 2026-07-11 ## Context AgentsKit already scaffolds general AgentsKit starters and extension packages. AgentsKit Chat needs complete application slices containing its shared definition, server boundary, renderer, tests, and guidance without copying upstream templates or overwriting host projects. ## Decision `@agentskit/chat-cli` owns the `agentskit-chat init` application command. It uses Node standard-library parsing, prompting, and filesystem APIs; detects React, Expo/React Native, or Ink from package dependencies; prompts only on an interactive TTY; and atomically promotes a staged project only when the target path does not exist. Templates import published AgentsKit and AgentsKit Chat packages. They add only application composition. No upstream controller, adapter, renderer primitive, or general AgentsKit scaffold is reproduced. ## Consequences - CI has a fully flag-driven command and JSON success output. - Initialization is idempotent by refusal rather than risky file merging. - Templates are tested by installing, type-checking, and running their tests as real workspace fixtures. - Future renderers extend the closed renderer enum and conformance matrix. ## Upstream adoption Inspected AgentsKit revision `main` on 2026-07-11: `@agentskit/cli@0.13.10` exports `writeStarterProject` via `packages/cli/src/index.ts` and implements it in `packages/cli/src/init.ts` (command adapter: `packages/cli/src/commands/init.ts`); `@agentskit/templates@0.4.3` exports `scaffold` via `packages/templates/src/index.ts` and implements it in `packages/templates/src/scaffold.ts`. Those APIs own general starters and extension packages and do not express a safe AgentsKit Chat application slice. Generated code composes only their underlying published AgentsKit contracts; no upstream source is copied. # ADR-0015: Closed standard application component catalog **Status:** Accepted **Date:** 2026-07-11 ## Context ADR-0007 established a closed application component manifest while AgentsKit retained ownership of the portable `UIElement` union. A cross-framework baseline now needs richer application semantics—forms, approvals, progress, sources, tables, attachments, and status notices—without extending or copying upstream generative UI. ## Decision `@agentskit/chat` publishes 12 immutable component definitions. Each definition owns a strict props schema, declared interaction names and value shape, accessibility contract, capability list, and semantic fallback builder. Custom definitions remain source compatible because catalog metadata is optional outside the standard catalog. `@agentskit/chat-protocol` adds the v1 `interact` event beside the existing ChoiceList `select` event. The closed manifest validates the frame and declared event before an interaction is emitted. Display-only components declare no events. Interactions express intent; authorization, confirmation, navigation, downloads, and other effects remain host responsibilities. Every native renderer exposes one generic standard-component surface plus its existing specialized ChoiceList. Renderer support declarations generate the parity report and conformance fixtures exercise every entry. ## Alternatives considered 1. Extend AgentsKit `UIElement` — rejected because application workflows and events are outside its small portable presentation union. 2. Twelve independent APIs per renderer — rejected because it multiplies public surface and drift. 3. Treat every component as formatted text — rejected because forms, tables, progress, alerts, and controls require native accessibility semantics. ## Consequences - Schemas and events remain framework-neutral and runtime validated. - Visual implementations stay native while parity is mechanically checked. - Hosts own effects triggered by interaction events. - A new standard component requires catalog metadata, fixtures, all renderer presentations, documentation, and parity regeneration in one change. # ADR-0016: Seven-renderer CLI and semantic component generation **Status:** Accepted **Date:** 2026-07-11 ## Context The initial CLI supported React, React Native, and Ink. The framework now has seven native renderers and a closed semantic component catalog. Scaffolding must preserve one framework-neutral contract while allowing native presentation without turning the CLI into a second runtime or copying AgentsKit starters. ## Decision The CLI owns a closed seven-value renderer enum used by detection, `init`, help, completion, and tests. `init` retains its staged atomic project promotion. `add component` accepts an explicit comma-separated renderer list, validates a portable component name, checks every destination and symbolic-link boundary before writing, rolls back its created files on failure, and creates one strict Zod definition plus selected native source files. Generated component definitions contain schema, semantic key, event metadata, accessibility, capabilities, and fallback. Native files contain presentation only. Hosts explicitly register the definition and connect native files through renderer slots. ## Alternatives considered 1. Generate from framework components — rejected because framework source is not portable or safely serializable. 2. Introduce a template engine — rejected because small deterministic strings and Node filesystem APIs cover the closed matrix. 3. Extend upstream AgentsKit scaffolds — rejected because upstream owns general AgentsKit starters, not AgentsKit Chat application composition. ## Consequences - Detection and generated layouts are auditable as one closed matrix. - Adding a renderer requires updating templates, golden tests, install/typecheck tests, help, and completion together. - Component generation never edits a manifest automatically; explicit registration avoids unsafe source rewriting. - Existing files cause refusal before writes; the command does not merge or overwrite. ## Upstream adoption Inspected AgentsKit revision `978ce3d77be7bbf76094b5919d240e50091bc824`: `@agentskit/cli` exports `writeStarterProject`, `@agentskit/templates` exports `scaffold`, and `@agentskit/core/generative-ui` owns generic `UIElement` validation. This CLI composes published AgentsKit bindings and AgentsKit Chat contracts only. No upstream primitive is copied or reimplemented, and no upstream gap is required for this decision. # ADR-0017: Application traces compose upstream replay **Status:** Accepted **Date:** 2026-07-11 ## Context AgentsKit Chat already emits deterministic-route and capability-policy decisions. Developers need one committed fixture explaining application outcomes and comparing them across native renderers. AgentsKit already owns adapter recording, cassettes, playback, time travel, and eval reporting. ## Decision `@agentskit/chat-devtools` captures application-only records in causal append order. Records have a monotonic sequence, optional prior parent, category, timestamp, and bounded JSON detail. Configured field names are recursively redacted before storage. Returned snapshots and reports are deeply immutable. A versioned replay fixture stores these records beside an upstream `Cassette`. Cassette serialization and validation delegate to `@agentskit/eval/replay`; model requests and chunks are never independently recorded here. Semantic renderer parity compares ordered outcomes by turn identity and reports missing or different values without comparing pixels or framework internals. ## Alternatives considered 1. Build a second recording adapter — rejected because AgentsKit owns replay. 2. Add traces to the turn wire protocol — rejected because developer fixtures are not client/server state. 3. Snapshot native markup — rejected because DOM, mobile, and terminal layouts are intentionally different. ## Consequences - Hosts connect existing route/policy callbacks to one capture without changing execution. - Redaction covers application trace detail only; upstream cassettes may contain prompts and require separate handling. - A fixture can be committed and replayed without live model access. - Renderer diagnostics prove semantic equivalence, not visual identity. ## Upstream adoption Inspected AgentsKit revision `978ce3d77be7bbf76094b5919d240e50091bc824` and `@agentskit/eval@0.4.17`. Reused `createRecordingAdapter`, `createReplayAdapter`, `Cassette`, `serializeCassette`, and `parseCassette` from `@agentskit/eval/replay`. No upstream source or behavior is copied and no upstream gap blocks this application-only composition. # ADR-0018: Deterministic routes receive stable turn identity **Status:** Accepted **Date:** 2026-07-11 ## Context ADR-0006 caches deterministic responses by user-message identity, but route response callbacks previously received only input text. Application components need a stable identity that is unique per turn: a constant `instanceId` makes a later instance inert, while a process-global counter makes replay depend on unrelated sessions. ## Decision `DeterministicRoute.response` receives an additive second context argument containing the application `sessionId` and optional upstream user `messageId`. Existing one-argument callbacks remain source-compatible. Component-producing routes derive their instance identity from the message identity, so replay/regenerate returns the cached frame and separate turns remain independent. The context contains identity metadata only. It does not expose mutable session state, messages, model internals, or a second execution controller. ## Alternatives considered 1. Reuse a constant component identity — rejected because renderers intentionally keep resolved instances inert. 2. Increment a definition-global counter — rejected because concurrent sessions and replay become order-dependent. 3. Generate a random ID in the callback — rejected because retries and regenerated decisions must be deterministic. ## Consequences - Repeated component routes remain interactive within one session. - Deterministic replay preserves the original component identity. - Direct adapter calls without a user message can fall back to session identity. - This remains an AgentsKit Chat application-routing concern and does not duplicate an upstream AgentsKit primitive. # ADR-0019: Session confirmation observers are isolated **Status:** Accepted **Date:** 2026-07-12 ## Context Application audit traces need canonical confirmation records, including tool-call identity and terminal status. Renderers create confirmation coordinators through `ChatSession`, so application definitions previously had no supported observation seam. ## Decision `createChatSession` accepts an optional `onConfirmationChange` observer. It receives immutable canonical records after session persistence succeeds. Observer failure is isolated and cannot change authorization, confirmation, execution, or persistence outcomes. Durable storage remains the session store's responsibility; audit sinks consume the observer separately. ## Alternatives considered 1. Reconstruct approval from tool execution — rejected because denial, expiry, and canonical correlation are lost. 2. Add renderer-specific callbacks — rejected because behavior would drift across frameworks. 3. Build another confirmation coordinator — rejected because AgentsKit Chat already composes the upstream lifecycle. ## Consequences - All renderers expose the same application audit seam through prepared sessions. - Audit failure cannot turn a committed mutation into a retryable tool failure. - Observers must deduplicate records if they only want status transitions. # ADR-0020: Public alpha artifacts enable external dogfood **Status:** Accepted **Date:** 2026-07-13 ## Context The Docs, Registry, and Playbook dogfood migrations run in repositories outside AgentsKit Chat. ADR-0002 forbids private source imports and unpublished workspace paths, while the complete stable v0 publication remains gated by dogfood and conformance work in issue #30. The repository has no npm publishing credential yet. ## Decision AgentsKit Chat publishes `v0.1.0-alpha.0` as an immutable GitHub prerelease containing npm-compatible tarballs for the minimum Web dogfood graph: - `@agentskit/chat-protocol`; - `@agentskit/chat`; - `@agentskit/chat-react`; - `@agentskit/chat-server`. All four manifests carry the same prerelease version, and pnpm rewrites their `workspace:*` edges to that version when packing. Until stable npm publication, external consumers declare the required asset URLs and override the internal `@agentskit/chat-protocol` and `@agentskit/chat` semver edges to those same assets. The workflow rejects a tag commit that is not contained in `main`. Release assets are built from that reviewed commit, accompanied by SHA-256 checksums, and installed through the documented override graph plus lockfile integrity. A clean-room ESM/CJS import and production Vite build prove that no workspace path is required. GitHub Immutable Releases is enabled for the repository so the published tag and assets cannot be altered after release. This channel is supported for dogfood compatibility only. Issue #30 still owns the complete renderer matrix, npm provenance/trusted publishing, stable compatibility policy, public v0 documentation, and stable tags. ## Alternatives considered 1. Import source across repositories — rejected by ADR-0002 and because it would make dogfood privileged. 2. Move issue #30 before dogfood — rejected because the stable release criteria explicitly require dogfood evidence. 3. Publish directly to npm from a developer machine — rejected because no authenticated provenance path exists and local credentials must not be introduced. 4. Copy or bundle framework code into each host — rejected as reimplementation and a future upgrade fork. ## Consequences - External hosts consume a public, immutable, reviewable framework artifact before stable v0. - Alpha consumers must pin release assets and deliberately upgrade. - Only the Web graph is promised by this prerelease; other renderers remain workspace-tested until #30. - The alpha does not weaken or pre-approve the stable release gates. # ADR-0021: Ordered assistant content uses versioned JSON records **Status:** Accepted **Date:** 2026-07-13 ## Context An assistant turn may need to stream grounded prose and then present a portable application component such as `source-list`. AgentsKit correctly stores and streams canonical message content as a string. The existing AgentsKit Chat component envelope renders only when that complete string is one component frame. A host otherwise has to create a second timeline or renderer to combine prose and components, duplicating framework behavior. The composition boundary must remain incremental, inert under untrusted model text, bounded, compatible with existing messages, and independent of any transport or controller implementation. ## Decision `@agentskit/chat-protocol` defines `agentskit.chat.content/1`, an ordered record stream stored inside one canonical AgentsKit assistant-message string. The stream starts with a reserved record-separator prefix. Each following newline-delimited JSON record is either bounded text, encoded by the host through `createAssistantContentEncoder`, or an existing fully validated `ComponentRenderFrame`. The encoder JSON-escapes text, so newlines, JSON, and protocol-like values from a model remain inert. The decoder exposes only complete records while a trailing record is still streaming and enforces UTF-8 total-byte, record-count, text, props, fallback, and frame bounds. Renderers may coalesce adjacent validated text records for native message presentation. Malformed complete records and unsupported versions produce fixed safe diagnostics. Renderers resolve every component through the existing closed manifest and semantic fallback rules. Plain strings and whole-message component frames keep their existing behavior. The protocol does not replace AgentsKit messages, adapter chunks, controller state, cancellation, persistence, or transport. ## Alternatives considered 1. One JSON document containing all parts — rejected because incomplete JSON would expose transport text or defer all rendering until completion. 2. Raw textual delimiters — rejected because model output could inject delimiters and manufacture component records. 3. A host-owned parallel timeline — rejected because it duplicates canonical state, streaming, lifecycle, and renderer coordination. 4. Change AgentsKit `Message.content` to a custom union — rejected because ordered application components are an application-framework concern and AgentsKit already provides the correct canonical string and stream contracts. ## Consequences - Grounded prose can visibly stream before a validated `source-list` in the same assistant turn. - Hosts must encode every text chunk; concatenating raw model text to an envelope is invalid usage. - A reserved record-separator prefix is no longer rendered as ordinary assistant text. - Other native shells must adopt this protocol before stable cross-renderer parity is claimed by issue #30. ## Upstream adoption Inspected AgentsKit Core `Message`, `StreamChunk`, controller, and framework bindings. Reused canonical string messages, adapter streaming, lifecycle, and renderer message primitives without modification. AgentsKit Chat adds only application-level ordering of its already-owned closed components. No generic AgentsKit gap or upstream reimplementation exists. # ADR-0022: Ask service integration is shared application infrastructure **Status:** Accepted **Date:** 2026-07-13 ## Context AgentsKit Docs, Registry, and Playbook consume the same Ask NDJSON service. Their first dogfood implementations independently repeated the event schema, stream decoder, message projection, citation safety, connection deadline, cancellation, and browser-session migration. That drift already produced a whole-stream timeout in two hosts and storage behavior that differed from the canonical AgentsKit memory contract. The Ask wire protocol is application-specific, so it does not belong in AgentsKit Core. Browser message persistence is generic and already belongs to AgentsKit Memory. ## Decision `@agentskit/chat-protocol` owns the versioned, bounded Ask event schema and NDJSON decoder. `@agentskit/chat` owns one public integration composed of: - an `AdapterFactory` that maps Ask events into the ordered assistant-content protocol; - safe `cite` projection into the standard `source-list` component; - a validated callback for host-specific presentation tools; - a connection-only deadline, upstream stream cancellation, and plain-text fallback; - wire projection from canonical AgentsKit messages; and - legacy Ask-session migration composed over `createWebStorageMemory` from `@agentskit/memory@0.11.0`. Hosts configure only endpoint, corpus/persona, canonical and legacy storage keys, and an optional native application-tool projector. They must not own another Ask schema, decoder, fetch loop, citation projector, or message store. The connection deadline is cleared as soon as response headers arrive. A healthy long-running response body remains governed by user cancellation rather than an arbitrary whole-stream timer. ## Alternatives considered 1. Keep one adapter in every host — rejected because the three implementations had already diverged in timeout, validation, and persistence behavior. 2. Put Ask in AgentsKit Core — rejected because Ask is a product/application protocol, not a provider-neutral chat primitive. 3. Add another browser memory implementation here — rejected by ADR-0002; the generic gap was fixed in AgentsKit #1191/#1192 and released as `@agentskit/memory@0.11.0` first. 4. Hard-code Docs generative tools in the shared adapter — rejected because those components are host presentation policy. A validated projector preserves the generic boundary. ## Consequences - Docs, Registry, and Playbook consume one published implementation and can vary only at explicit configuration and presentation seams. - Unknown, malformed, oversized, and unsafe records stay inert. - Canonical messages, controller lifecycle, persistence contract, and cancellation remain owned by AgentsKit. - Changes to the Ask service contract require conformance tests and a new immutable artifact before host adoption. ## Upstream adoption AgentsKit Core continues to own `AdapterFactory`, `StreamSource`, `Message`, ordered controller reduction, and `ChatMemory`. AgentsKit Memory owns validated Web Storage persistence. AgentsKit Chat Protocol owns versioned wire validation; AgentsKit Chat adds only the Ask integration and projection required by its dogfood hosts. No generic upstream primitive is copied. # ADR-0023: Conversation transitions use AgentsKit statechart **Status:** Accepted **Date:** 2026-07-13 ## Context ADR-0006 introduced the smallest finite transition implementation needed for deterministic application routes. At that time AgentsKit did not expose a general, framework-neutral interaction-state primitive, so adding another dependency was rejected. AgentsKit now publishes `@agentskit/statechart@0.2.0`. It owns immutable definitions and instances, definition validation, typed transition results, and host-supplied identity and time. Keeping equivalent validation and transition assignment in AgentsKit Chat would violate ADR-0002 and split generic fixes between repositories. AgentsKit Chat still owns application routes, route precedence, adapter fallback, trace projection, retained deterministic decisions, session persistence, and the projection of application actions. Those concerns coordinate a chat application and do not belong to the reusable statechart package. ## Decision `@agentskit/chat` consumes the published `@agentskit/statechart` package. For each `ConversationDefinition`, `createChatSession` compiles the declared states and event targets into an upstream `StatechartDefinition`. It creates one immutable upstream instance per application session and advances or rebuilds that instance only through `transitionStatechart`. The public `ConversationDefinition`, `ConversationSnapshot`, `ChatSession`, and session wire envelope remain unchanged. State actions remain application metadata projected from the active state. Route declarations remain local because they match user input, produce application responses, and decide when to delegate to the AgentsKit chat adapter. AgentsKit Chat validates only its application-specific route constraints: route identity, route precedence, optional state restrictions, and references from routes to declared states. Generic initial-state and transition-target validation is delegated upstream. Upstream diagnostics are mapped to the existing public `ConfigError` boundary. Session persistence continues to store application decisions and the current application state, not an additional upstream snapshot. On resume and history repair, retained route decisions are replayed through the upstream statechart so persisted metadata cannot bypass its transition contract. ## Alternatives considered 1. Keep the local finite transition implementation — rejected because it duplicates a now-published AgentsKit primitive. 2. Expose `StatechartDefinition` directly from `ChatDefinition` — rejected because route composition and application actions would leak into a lower-level contract and break the existing public API. 3. Persist a second statechart snapshot beside the application session — rejected because it would create two authorities for the same conversation progress. 4. Import unpublished source or a workspace path — rejected by ADR-0002; only the supported npm release is an integration boundary. ## Consequences - AgentsKit is the single source of truth for reusable transition validation and execution. - Existing Chat definitions, renderers, and stored v1 session envelopes remain compatible. - A session rebuild performs small synchronous statechart transitions for retained deterministic decisions. - Statechart breaking changes require an upstream release and a downstream compatibility review. ## Upstream adoption record - Inspected public exports: `defineStatechart`, `createStatechartInstance`, `transitionStatechart`, `StatechartDefinition`, `StatechartEvent`, immutable instances, and typed diagnostics. - Reused upstream behavior: definition validation, immutable session state, and accepted/rejected transition execution. - Local behavior: deterministic route matching, adapter composition, application traces/actions, decision replay, and session persistence. - Upstream delivery: [AgentsKit #1199](https://github.com/AgentsKit-io/agentskit/issues/1199), [PR #1210](https://github.com/AgentsKit-io/agentskit/pull/1210), and `@agentskit/statechart@0.2.0`. - No upstream or private source is copied or reimplemented. ## Privacy boundary This decision is derived exclusively from public AgentsKit contracts and synthetic Chat tests. It contains no private source, identifier, schema, fixture, product policy, or business rule. # ADR-0024: Deterministic answers precede backend adapters **Status:** Accepted — HITL approved 2026-07-13 **Date:** 2026-07-13 ## Context AgentsKit Docs, Registry, Playbook, and future ecosystem sites need to answer exact questions such as commands, package identities, navigation targets, contribution links, restricted FAQs, and document titles without network latency or model cost. Reasoning, comparisons, recommendations, and other open-ended questions still require the configured backend. If each host invents matching, confidence, artifact, and escalation rules, the same question can produce incompatible behavior across the ecosystem. If AgentsKit Chat implements another controller, adapter lifecycle, retrieval engine, or message model, it duplicates AgentsKit. ## Decision `@agentskit/chat-protocol` owns three public v1 runtime boundaries: - `agentskit.chat.site`: per-site artifact location, expected content hash, and fallback policy; - `agentskit.chat.knowledge`: immutable local entries containing only declared exact aliases and bounded answers; and - `agentskit.chat.answer`: answer, choices, or escalation with citations, provenance, suggestions, and confidence. Artifacts accept only `command`, `package`, `navigation`, `contribution`, `ecosystem`, `restricted-faq`, and `document` entries. Equality uses Unicode NFKC normalization, trimming, collapsed whitespace, and locale-stable case folding. There is no fuzzy, semantic, embedding, prefix, keyword, or model match. `@agentskit/chat` builds a bounded index once and composes it before a host-supplied AgentsKit `AdapterFactory`: 1. one exact entry returns a high-confidence local answer; 2. multiple exact entries return medium-confidence choices; 3. miss, stale, corrupt, or offline state returns low-confidence escalation; and 4. when a backend exists, the original request, lifecycle, streaming latency, and cancellation remain owned by that adapter. The escalation envelope is added to `AdapterRequest.context.metadata`, while bounded streamed text is observed only to attach the unified backend-answer envelope to the final chunk. Artifact production, cache policy, fetching, and backend operation remain host responsibilities. The protocol package owns canonical serialization and SHA-256 verification so producers and consumers cannot drift. Doc Bridge may generate artifacts, but AgentsKit Chat does not depend on its implementation. Existing ordered assistant content and standard `source-list`/`choice-list` components project local decisions without a new renderer protocol. Deterministic choices reuse the visible `description` field. The host wires the adapter-owned submission resolver into the chat definition; the session wrapper supplies identity through an optional adapter extension while preserving the upstream request, and the resolver creates an exact, session-scoped reservation that commits after successful send or releases on failure. Generic frames cannot authorize themselves, retries remain functional, and sessions cannot consume each other's choices. Authorization state is bounded per session and globally with claimed reservations protected from eviction; headless hosts can explicitly release abandoned sessions. Headless consumers may resolve an offered entry ID against its original ambiguous query. ## Alternatives considered 1. Ask the backend for every input — rejected because exact local facts need neither network latency nor model work. 2. Add fuzzy or semantic local routing — rejected because uncertainty would be presented as deterministic confidence. 3. Put site artifacts in AgentsKit Core — rejected because ecosystem knowledge and application routing are not provider-neutral chat primitives. 4. Add another controller or stream reducer — rejected by ADR-0002; the published AgentsKit adapter boundary already composes this behavior. 5. Couple the runtime to Doc Bridge — rejected because artifact consumers must remain framework- and generator-neutral. ## Consequences - Exact ecosystem facts can resolve synchronously and offline. - Ambiguity is visible instead of silently selecting an answer. - Unknown or untrusted input never becomes a high-confidence local answer. - Hosts must cryptographically verify artifacts before constructing the adapter; corrupt programmatic input remains an inert escalation rather than a startup failure. - `fallback.mode` is enforced by the adapter, even if a backend factory is accidentally supplied while disabled. - Required fields or semantic changes require v2, compatibility fixtures, and a new ADR. - The human approval gate in issue #69 was satisfied on 2026-07-13 before this contract was marked ready. ## Upstream adoption record Inspected public `@agentskit/core@1.12.3` contracts for `AdapterFactory`, `AdapterRequest`, `AdapterContext.metadata`, `StreamSource`, canonical messages, capabilities, and cancellation. The proposal reuses those contracts unchanged. AgentsKit Chat adds only an application artifact schema, conservative exact lookup, response metadata, and projection into its already accepted ordered-content/component protocols. No upstream source or generic primitive is copied. ## Privacy boundary The proposal uses only public package contracts and synthetic fixtures. It contains no private site content, identifier, policy, source, or production artifact. # ADR-0025: Release conformance is a versioned evidence gate **Status:** Accepted **Date:** 2026-07-13 ## Context AgentsKit Chat supports seven native renderer packages. Shared protocol fixtures, standard-component support declarations, renderer tests, browser E2E, and Ink PTY tests already exist, but release automation cannot currently explain which renderer, component, event, or platform requirement drifted. A passing package test command alone is not a published compatibility promise, and a generated parity table alone does not prove interaction or accessibility behavior. ## Decision The repository owns one versioned, runtime-validated conformance manifest. It distinguishes universal requirements from DOM, native-mobile, and terminal requirements and maps every renderer to executable evidence. A repository-level conformance gate validates the manifest, compares every renderer support declaration with the standard component catalog, verifies required evidence files, and emits stable findings containing a code, renderer, optional component or event, and remediation. Meta-tests use synthetic broken manifests and support declarations to prove that diagnostics identify the exact failure. The generated public matrix is derived from the same manifest and standard catalog used by the gate. It is documentation, not a second source of truth. Graphical renderer suites remain responsible for native semantic roles, accessible names, live/error announcements, interaction state, and framework lifecycle behavior. Ink remains responsible for readable semantic fallback, keyboard interaction, Escape cancellation, and graceful process exit through its PTY suite. The gate orchestrates this existing evidence; it does not implement a cross-framework renderer, accessibility engine, or test runner. Pull-request CI runs the conformance gate and relevant package/platform suites. Release workflows run the same gate before packaging or publication. ## Alternatives considered 1. Treat the generated catalog table as the gate — rejected because a list of supported components does not prove accessibility or interaction evidence. 2. Build a shared virtual renderer — rejected because it would erase native semantics and duplicate the framework bindings owned by AgentsKit. 3. Require only manual accessibility review — rejected because semantic drift would not block releases. 4. Put the gate in AgentsKit — rejected because this matrix describes AgentsKit Chat application components, renderer packages, examples, and release policy rather than a reusable AgentsKit primitive. ## Consequences - Release failures identify the renderer and remediation instead of failing as an opaque aggregate test. - Universal and platform-specific promises are public and reviewable. - Adding a renderer, component, event, or exception requires updating one validated manifest and executable evidence. - Exceptions must be explicit, scoped, owned, and documented; missing evidence fails closed. - The gate remains application-framework orchestration over AgentsKit packages and native renderer tests. ## Upstream adoption Inspected the public AgentsKit core generative-UI contract and React, React Native, Vue, Svelte, Solid, Angular, and Ink bindings already consumed by the corresponding AgentsKit Chat renderers. AgentsKit continues to own controller lifecycle, framework bindings, confirmation, and portable generative UI. This decision adds only application-catalog conformance evidence and repository release orchestration. No upstream primitive is copied or reimplemented, and no generic upstream gap blocks the work. # ADR-0026: Trusted Ask backend vertical **Status:** Accepted **Date:** 2026-07-13 ## Context The deterministic answer plane resolves exact site facts without a network call, but semantic questions need a shared backend. Registry, Playbook, and Docs must be able to use that backend without allowing a browser to select another tenant, assistant, corpus, component, or action. Hosted and self-hosted operators also need the same public wire contract and enough privacy-safe measurements to establish a baseline before optimization. AgentsKit already owns adapters, provider execution, RAG retrieval, canonical chat memory, controller lifecycle, token usage, and cancellation. AgentsKit Chat must compose those capabilities rather than implement alternatives. ## Decision `@agentskit/chat-server` exposes `createAskServiceHandler`, a Web-standard `Request` to `Response` handler. Authentication and validated site resolution happen before body parsing. The authenticated server context selects the complete site configuration: site and assistant identity, corpus and retrieval mode, suggestions, registered components/actions, deadlines, source limit, and persistence policy. Client `corpus` and `persona` query parameters are compatibility hints only; a mismatch fails closed. The client sends the additive `agentskit.chat.ask` v1 request through `createAskAdapter`. A resumed application session contributes its validated session ID. When the deterministic plane escalates, its low-confidence envelope is included as bounded context so the server can measure fallback without receiving local artifacts or allowing it to grant authority. Hosts inject local or federated retrieval through an upstream AgentsKit RAG/Retriever adapter, generation through an AgentsKit provider/adapter, a CAS session store when persistence is required, rate limiting, authentication, and a metric observer. The server passes only the trusted corpus configuration to retrieval. Successful answers require at least one runtime-validated safe citation and stream through the existing Ask NDJSON events. It does not accept generated actions or components; the only server-projected component in this vertical is the standard citation list. Persistence loads a trusted `(site, subject, session)` record and appends only the latest submitted user turn to canonical stored history. Save uses an expected revision and reports a typed conflict. Histories, sources, answers, requests, and streams are bounded. Request, retrieval, generation, response cancellation, and persistence share abortable deadlines. Metrics contain only site/corpus/request identifiers, a fixed metric name, numeric value/unit/outcome, and time. They cover first event/token, total latency, bytes, Ask events, snapshot count, deterministic fallback, retrieval, persistence, cancellation, conflicts, errors, tokens, and cost. Prompts, answers, sources, credentials, subject/session IDs, and provider errors are never fields. Ask emits no snapshot events, so `stream.snapshots` is truthfully recorded as zero; the existing snapshot handler retains its separate turn protocol. No snapshot/delta or persistence-frequency optimization is part of this decision. Any such change needs baseline evidence and explicit HITL approval. ## Alternatives considered 1. Trust browser corpus/persona parameters — rejected because they are an authorization bypass. 2. Bundle a vector store, model provider, authentication, rate limiter, or database — rejected because these are upstream or host responsibilities. 3. Return uncited model output when retrieval is empty — rejected because it would misrepresent semantic recommendations as grounded. 4. Create a second streaming protocol — rejected because the published Ask NDJSON protocol and source-list projection already serve every renderer. 5. Put prompts or answers in telemetry — rejected because aggregate performance and cost baselines do not require user content. ## Consequences - Hosted and self-hosted deployments mount the same handler and use the same request, event, site-config, diagnostic, and metric schemas. - Operators must provide durable CAS storage when a site declares required persistence. - Retrieval and generation providers remain replaceable and keep their own provider-specific resilience. - A deterministic miss can be measured end-to-end without weakening corpus isolation. - Empty or invalid source sets fail safely instead of producing an uncited answer. ## Upstream adoption Inspected published AgentsKit Core adapter/controller/cancellation/usage contracts, AgentsKit RAG `createRAG`, `RAG`, `Retriever`, and `RetrievedDocument`, and the existing AgentsKit Chat `createAskAdapter`, session-aware adapter seam, deterministic escalation envelope, Web server handler, session CAS, Ask events, and SourceList projection. The implementation reuses those boundaries and adds only site-owned policy validation, trusted host composition, bounded cited projection, CAS coordination, and privacy-safe observations. It copies no controller, adapter lifecycle, retrieval, ranking, embeddings, storage engine, provider transport, memory engine, or renderer behavior. # ADR-0027: Fumadocs site is a first-class AgentsKit Chat dogfood host - Status: Proposed - Date: 2026-07-14 - Issue: [#71](https://github.com/AgentsKit-io/agentskit-chat/issues/71) ## Context AgentsKit Chat already has canonical Markdown documentation, a generated Doc Bridge corpus, deterministic answer artifacts, a trusted Ask backend contract, and native React rendering. A public documentation property must compose those surfaces without introducing a second documentation corpus, chat controller, retrieval engine, protocol, or browser memory implementation. The site must remain useful without provider credentials, distinguish shipped and unavailable capabilities honestly, and prove that hosted and self-hosted backends use the same public request and response contracts. ## Decision Create `apps/docs` as one Next.js App Router application using Fumadocs. It is a modular monolith and the only new deployable unit. The existing repository `docs/` tree remains the canonical human corpus. Fumadocs reads that tree directly and uses committed navigation metadata; the site does not maintain copied prose under a second content directory. Raw Markdown and `llms.txt` routes expose the same canonical or generated sources. The interactive documentation assistant is a normal React host of the public framework: 1. `@agentskit/chat-protocol` validates the generated local knowledge artifact. 2. `@agentskit/chat` composes deterministic resolution, the Ask adapter, and browser session memory. 3. `@agentskit/chat-react` renders the shared definition through the upstream `@agentskit/react` binding. 4. Exact known questions resolve from the verified local artifact before any network request. 5. Misses use the versioned Ask contract. The default endpoint is same-origin; operators may select the hosted endpoint without changing the definition. The self-hosted Next route is built with `createAskServiceHandler` from `@agentskit/chat-server`. Retrieval, generation, persistence, rate limiting, and metrics remain injected host adapters. Production adapters must compose published AgentsKit RAG, memory, and provider packages. If required adapters are absent, the route returns an explicit unavailable response; it never fabricates an answer or citation. Tests inject bounded public fixtures through the same factory rather than adding a test-only wire protocol. Fumadocs search is documentation navigation only. It is not used as a substitute for AgentsKit retrieval in the Ask backend. ## Containers ```mermaid flowchart LR U["Documentation user"] --> S["apps/docs · Next + Fumadocs"] S --> C["Canonical docs/ corpus"] S --> W["AgentsKit Chat React host"] W --> D{"Verified deterministic artifact"} D -->|"exact"| W D -->|"miss"| A["Versioned Ask endpoint"] A --> H["createAskServiceHandler"] H --> R["Injected AgentsKit Retriever / RAG"] H --> G["Injected AgentsKit provider adapter"] H --> P["Optional CAS persistence + metrics"] ``` ## Site modules ```text apps/docs ├── app/ Fumadocs pages, search/raw/llms routes, Ask route ├── components/ site shell and AgentsKit Chat presentation slots ├── lib/source.ts Fumadocs loader over canonical docs/ ├── lib/chat/ definition, verified artifact and backend factory └── tests/ contract, integration, accessibility and E2E evidence ``` ## Alternatives considered 1. Copy selected Markdown into `apps/docs/content/docs` — rejected because content and maturity statements would drift from the repository docs. 2. Embed a host-specific chat widget — rejected because it would not prove the released definition, protocol, memory, server, and renderer packages. 3. Use Fumadocs search as semantic retrieval — rejected because navigation search does not satisfy the AgentsKit RAG ownership boundary. 4. Require a hosted backend — rejected because the public server contract must remain self-hostable and testable without external credentials. 5. Add a separate documentation repository — rejected because it would weaken changeset, compatibility, Doc Bridge, and code-to-doc freshness gates. ## Consequences - Documentation and the site share one source of truth. - The site is deployable independently while remaining inside release and CI governance. - Known questions remain fast and available without provider credentials. - Reasoning answers require an explicitly configured backend and safe citations. - The site adds Next/Fumadocs build cost to the monorepo. - Provider and vector-store choices remain deployment concerns rather than new AgentsKit Chat abstractions. ## Guardrails - No chat controller, renderer, Ask decoder, browser memory, retrieval engine, provider transport, vector store, or Fumadocs search protocol is recreated. - All external/model/server boundaries use existing runtime schemas. - No provider secret enters client bundles or public diagnostics. - Maturity labels derive from released package and compatibility evidence. - Visual customization uses renderer slots and semantic tokens; portable behavior remains in the shared definition. - Any missing generic primitive is fixed and released in AgentsKit first. ## Acceptance This ADR remains Proposed until the HITL review for #71 approves the maturity copy, dogfood claim, and hosted/self-hosted deployment posture. # ADR-0028 — Public package consolidation - **Status:** Accepted - **Date:** 2026-07-14 ## Context AgentsKit Chat was implemented as twelve workspace packages and published every workspace boundary to npm. Protocol, server, and devtools are implementation modules of the Chat application layer, not independently versioned products. Publishing them separately adds release, provenance, documentation, and consumer dependency-graph overhead without providing an independent compatibility policy. Framework renderers remain separate because each has distinct runtime and peer dependencies. The CLI remains separate because it owns an executable binary. ## Decision `@agentskit/chat` is the single public package for shared Chat behavior. It publishes these subpaths from one versioned tarball: - `@agentskit/chat/protocol` - `@agentskit/chat/protocol/fixtures` - `@agentskit/chat/server` - `@agentskit/chat/devtools` The `protocol`, `server`, and `devtools` workspace packages remain internal modules with `private: true`. Renderer packages depend only on `@agentskit/chat`. Release automation must pack and verify the aggregated artifact and must not publish private workspace packages. Previously published package names receive deprecation releases directing users to the corresponding subpath. Existing versions are not unpublished. ## Consequences - The Chat npm surface drops from twelve packages to nine immediately. - Internal ownership and focused tests remain intact. - Shared Chat modules use one semver line and one provenance artifact. - Consumers migrate imports but do not need behavioral changes. - Renderer packages remain public until their framework-specific peer dependency and release requirements can be evaluated independently. # ADR-0029 — Renderers as Chat subpaths - **Status:** Accepted - **Date:** 2026-07-14 - **Supersedes:** The renderer-publication decision in ADR-0028 ## Context ADR-0028 kept seven framework renderers as standalone npm products because they have distinct peer dependencies and build formats. Those boundaries remain useful inside the monorepo, but npm optional peer dependencies and explicit subpath exports allow one public tarball without loading unrelated frameworks. Maintaining seven release identities creates substantially more operational work than the renderer compatibility boundaries require. ## Decision The React, React Native, Ink, Vue, Svelte, Solid, and Angular workspace packages become private implementation modules. Their native build artifacts are assembled into `@agentskit/chat` and exported respectively from `/react`, `/react-native`, `/ink`, `/vue`, `/svelte`, `/solid`, and `/angular`. Framework runtimes remain optional peers of `@agentskit/chat`. Importing the root or another renderer subpath does not load an unrelated framework. The CLI remains the only separate public Chat package because it owns an executable binary. Legacy renderer package names remain published and supported until a separate deprecation and removal phase is approved. ## Consequences - The future Chat npm surface drops from nine packages to two. - Renderer source ownership, native compilation, tests, and bundle budgets stay isolated in their existing workspace packages. - Consumers install one Chat package and select a renderer through a subpath. - Release verification must prove every exported artifact exists in the combined tarball and exercise a clean-room renderer build. # ADR-0030: Certify ecosystem product chats through one application layer - **Status:** Accepted - **Date:** 2026-07-14 - **Issue:** [#100](https://github.com/AgentsKit-io/agentskit-chat/issues/100) - **Parent PRD:** [#99](https://github.com/AgentsKit-io/agentskit-chat/issues/99) ## Context AgentsKit Chat 0.3.0 consolidates the public application framework into `@agentskit/chat` and `@agentskit/chat-cli`. AgentsKit Docs, the deployed Registry, and Playbook consume that graph, while other active consumers still use legacy package names, lack production deployment, or retain host-specific product chat surfaces. Low-level AgentsKit binding examples also look like chat applications even though their purpose is to teach the underlying binding. Issue closure alone cannot support an ecosystem-wide adoption claim. The claim needs one definition of a product chat, bounded treatment of private consumers, and evidence that fails closed when a version, migration, CI run, or production smoke is missing. ## Decision AgentsKit remains the runtime substrate. It owns controllers, lifecycle, framework bindings, adapters, tools, memory, RAG, replay, eval, and reusable agent primitives. AgentsKit Chat is the single application layer for product chats. It owns portable application definitions, deterministic routes, policy composition, component manifests, session envelopes, protocol, server seams, and native renderer shells. Hosts own configuration, content, branding, deployment, and business-specific actions or state machines. A **product chat** is an end-user conversational surface that composes application behavior, presentation, and a runtime or deterministic answer source. Every declared product chat must consume `@agentskit/chat` or a supported subpath at the exact certified version. Direct use of an AgentsKit binding is allowed only in an explicitly classified **low-level binding example** whose purpose is to teach that primitive. Such examples are excluded from product-chat adoption totals and may not be presented as framework hosts. The repository commits `ecosystem-adoption.json` as the public convergence ledger. A strict runtime schema verifies: - the fixed set of declared consumers and supported repositories; - product, infrastructure, and low-level-example classification; - exact framework version and supported import paths; - remaining legacy standalone packages; - public CI and production evidence; and - aggregate private attestations without URLs or implementation fields. `certified` is derived from evidence, not issue state. A public product chat requires the exact version, no legacy package, consolidated consumption, passing CI with an HTTPS evidence URL, and a passing production smoke URL. A private product chat requires the same contract plus a bounded private-audit attestation; its source, topology, identifiers, behavior, and business rules remain outside public artifacts. The ledger is an audited baseline, not a live network monitor. Cross-repository certification resolves evidence and updates the ledger through reviewed PRs. Repository CI validates schema and internal consistency without depending on network availability. ## System boundary ```mermaid flowchart TD AK["AgentsKit · reusable runtime primitives"] --> AC["AgentsKit Chat · application layer"] AC --> H["Product-chat hosts"] H --> B["Host configuration, branding, deployment"] H --> P["Private or product-specific behavior"] M["ecosystem-adoption.json"] --> G["Schema and certification gate"] G --> C{"Certified?"} C -->|"yes"| CL["Public adoption claim"] C -->|"no"| W["Migration or deployment work"] ``` ## Non-functional requirements - The gate is deterministic, offline, and adds no production dependency. - Exact-version rollback remains available per consumer until final certification. - Existing renderer bundle, accessibility, conformance, and performance budgets remain unchanged. - Public evidence contains no provider secret, user content, private source, internal topology, identifier, or business rule. - Missing evidence is incomplete, never implicitly successful. ## Alternatives considered 1. **Infer adoption from closed issues.** Rejected because issue state does not prove the current default branch, package graph, deployment, or production behavior. 2. **Require every educational chat example to use AgentsKit Chat.** Rejected because it would prevent AgentsKit from teaching its lower-level bindings and would blur the framework/runtime boundary. 3. **Publish the private AKOS inventory.** Rejected because convergence requires contract evidence, not disclosure of private behavior or topology. 4. **Run network checks in every repository test.** Rejected because ordinary CI must remain deterministic; network resolution belongs to the final cross-repository certification workflow. 5. **Allow semver ranges in the ledger.** Rejected because a range does not prove which artifact produced the evidence. ## Consequences ### Positive - “Powered by AgentsKit Chat” becomes measurable and reviewable. - Low-level examples remain valid without weakening product adoption claims. - Private consumers can prove conformance without exposing internal logic. - Missing migrations and deployments remain visible instead of being hidden by a closed milestone. ### Negative - Adding or removing a declared consumer requires a schema and ADR review. - Evidence updates add cross-repository maintenance work. - Broad promotion remains blocked until every required product chat is certified. ### Neutral - The ledger records external repositories but does not fetch or mutate them. - Existing host deployment and monitoring systems remain host-owned. ## Failure modes and mitigations | Failure | Mitigation | |---|---| | Stale successful URL | Final certification resolves URLs and records a new audited date. | | Legacy package reintroduced | Consumer repository gates plus this ledger keep certification false. | | Private detail enters public evidence | Private records accept only fixed aggregate attestation values. | | Workspace dogfood misrepresented as npm | Consumption mode is explicit and workspace is restricted to the framework-owned portal. | | Missing generic primitive discovered | Migration stops and the primitive is fixed and released in its upstream owner first. | ## Acceptance HITL approval was recorded on 2026-07-14 for the product-chat definition, the low-level-example exception, the private-attestation boundary, and the rule that broad promotion requires every declared product chat to be certified. ## References - [ADR-0002: upstream-first and no reimplementation](./0002-upstream-first-no-reimplementation.md) - [ADR-0027: Fumadocs framework dogfood](./0027-fumadocs-framework-dogfood.md) - [ADR-0028: public package consolidation](./0028-public-package-consolidation.md) - [ADR-0029: renderers as Chat subpaths](./0029-renderers-as-chat-subpaths.md) - [0.3 migration guide](../../releases/migration-to-0.3.md) - [Ecosystem adoption ledger](../../dogfood/ecosystem-adoption.md) # ADR-0031: Treat externally controlled chat as an application seam - **Status:** Accepted - **Date:** 2026-07-14 - **Issue:** [#106](https://github.com/AgentsKit-io/agentskit-chat/issues/106) - **Parent PRD:** [#99](https://github.com/AgentsKit-io/agentskit-chat/issues/99) ## Context Some product hosts already own a trusted session transport and receive complete chat snapshots from another process. Requiring those hosts to instantiate a second client controller creates two competing lifecycle owners. Copying the AgentsKit Chat presentation into each host would instead create renderer drift. The framework needs a controlled mode that accepts host state without becoming a second controller, transport, persistence layer, or authorization boundary. ## Decision `@agentskit/chat` exposes a framework-neutral controlled driver. Its input is a bounded serializable snapshot plus callbacks typed as a `Pick` over the existing AgentsKit lifecycle. The snapshot shell is strict and messages are validated by `validateMemoryRecord` from `@agentskit/core/memory-validation` before ISO timestamps are hydrated. The driver projects the validated state and callbacks into the existing `ChatReturn` presentation contract. It does not create a controller, consume an adapter stream, persist messages, or translate lifecycle semantics. The React shell accepts either its existing definition-owned mode or the controlled source. Separate acquisition components preserve React hook rules: the definition-owned component calls upstream `useChat`; the controlled component never calls it. Both feed one presentation component and reuse the same component registry, theming, lifecycle controls, and action confirmation coordinator. The host remains responsible for authentication, authorization, transport, persistence, snapshot freshness, and applying every callback result to its source of truth. Invalid snapshots fail closed before presentation. ## Guardrails - Do not add a local controller, reducer, adapter stream consumer, or message store to the controlled driver. - Do not mirror the canonical AgentsKit message schema; delegate it upstream. - Keep host credentials, topology, business rules, and private state out of the public snapshot. - Framework bindings may adapt reactivity but must consume this same driver and their existing presentation path. - A missing reusable primitive must be added and released in AgentsKit before downstream integration. ## Consequences Hosts with server-owned sessions can adopt the shared application shell without creating a competing runtime. Existing hosts retain the original API and behavior. Controlled hosts must rerender with a new snapshot after callbacks; the driver deliberately performs no optimistic state mutation. ## Alternatives considered 1. **Instantiate a hidden controller from the snapshot.** Rejected because the host and client would both own lifecycle and persistence. 2. **Create a separate controlled React renderer.** Rejected because component, accessibility, theming, and lifecycle presentation would drift. 3. **Accept canonical `Message[]` without runtime validation.** Rejected because snapshots cross an external boundary and dates are serialized. 4. **Copy the upstream message schema locally.** Rejected because it would fork the AgentsKit contract and create version drift. ## Acceptance HITL approval to implement the controlled-session slice and its upstream-first guardrails was recorded on 2026-07-14. ## References - [ADR-0002: upstream-first and no reimplementation](./0002-upstream-first-no-reimplementation.md) - [ADR-0030: ecosystem product-chat convergence](./0030-ecosystem-product-chat-convergence.md) - [Upstream adoption matrix](../upstream-adoption.md) # Architecture overview ## Context AgentsKit provides framework-neutral chat state and bindings for React, Vue, Svelte, Solid, Angular, React Native, and Ink. Building a product-grade interactive agent experience still requires teams to assemble deterministic routing, structured turns, action policy, component registries, sessions, streaming, persistence, replay, theming, and framework-specific shells. AgentsKit Chat is the opinionated application framework above those primitives. ## Architectural style The project begins as a TypeScript modular monorepo. Shared behavior is organized around deep, testable contracts; native renderers remain thin adapters over the corresponding AgentsKit binding. ## Product boundary AgentsKit owns adapters, model invocation, tools, memory, RAG, agent runtime, chat controller, and base framework bindings. AgentsKit Chat owns application definitions, the turn pipeline, deterministic interaction, action policy, session protocol, semantic component manifests, native application shells, scaffolding, and parity tooling. [ADR-0002](./adrs/0002-upstream-first-no-reimplementation.md) makes this boundary enforceable: AgentsKit Chat composes published AgentsKit APIs. Missing or defective framework-neutral primitives are changed at the source in `AgentsKit-io/agentskit` before this repository consumes them. The concrete source-to-responsibility mapping lives in the [AgentsKit upstream adoption matrix](./upstream-adoption.md). It is revalidated by each implementation issue rather than treated as permanently current. ## Containers ```text Application ├── shared chat definition ├── AgentsKit Chat server/runtime │ └── AgentsKit runtime, adapters, tools, memory, and RAG └── native client renderer └── corresponding AgentsKit UI binding ``` ## Planned modules ### Core Defines chats that compile to or derive AgentsKit `ChatConfig`, plus deterministic application routes, component manifests, conversation machines, and policy composition. It does not implement another controller, tool loop, confirmation engine, or model runtime. Conversation machines compile to the published `@agentskit/statechart` primitive. The Chat layer retains only application route matching, adapter composition, actions, traces, and session decision replay ([ADR-0023](./adrs/0023-conversation-transitions-use-agentskit-statechart.md)). ### Protocol Owns application-specific client-server events, runtime schemas, serialization, resumption, compatibility fixtures, and semantic fallbacks. Existing AgentsKit lifecycle and UI semantics are transported without being renamed or reimplemented. ### Server Mounts chat definitions onto Web-standard handlers and provides context, authentication seams, sessions, persistence ports, streaming, cancellation, and audit hooks. The first server slice is `@agentskit/chat-server`: a Web `Request`/`Response` handler that authenticates before parsing, drives the upstream controller, and streams versioned full snapshots as NDJSON. Framework HTTP adapters remain thin host bridges. The trusted Ask vertical composes deterministic escalation with authenticated site configuration, injected local/federated AgentsKit retrieval, cited Ask events, CAS persistence, cancellation, and privacy-safe baseline metrics. The same public contracts run hosted or self-hosted ([ADR-0026](./adrs/0026-trusted-ask-backend-vertical.md)). ### Native renderers React, Vue, Svelte, Solid, Angular, React Native, and Ink renderers consume the protocol and the matching AgentsKit package. Custom visual implementations are platform-specific; component identity, props schema, actions, and fallback behavior are shared. The first renderer set shares a runtime-validated semantic application theme and maps it through upstream CSS variables, React Native styles, and Ink theming. Composition slots remain native to each renderer, while fully headless state remains the corresponding AgentsKit `useChat` API ([ADR-0013](./adrs/0013-semantic-theme-native-slots.md)). The Vue renderer uses native named scoped slots and the controller-free `ChatRoot` released upstream in `@agentskit/vue@0.4.4`. Its application behavior remains identical to the shared renderer contract; no Vue reactive state is introduced downstream. The Svelte renderer uses the upstream `createChatStore` and Svelte 5 snippets. Store replacement is isolated to a keyed binding; an active store is cancelled through its upstream `stop` action before replacement, while application/session state stays mounted. The Solid renderer uses upstream `useChat` and native signals, control flow, and render props. Keyed owners isolate session and config identity; owner cleanup cancels active streams through `@agentskit/solid@0.4.4`. The Angular renderer uses the upstream `AgentskitChat` signal service and standalone components. A component-scoped provider isolates every shell; Angular content templates customize application surfaces, and upstream `destroy()` owns active-stream cancellation. The closed standard catalog adds 12 application components through one generic native presentation per renderer, while ChoiceList retains its action-aware specialization. Schemas, declared intent events, accessibility metadata, capability declarations, semantic fallbacks, and generated parity remain framework-neutral ([ADR-0015](./adrs/0015-standard-component-catalog.md)). ### CLI Detects the host framework and runtime, scaffolds a complete vertical slice, adds semantic components, validates configuration, and diagnoses renderer parity. The first slice is `@agentskit/chat-cli`: a safe, standard-library `init` for React, Expo/React Native, and Ink ([ADR-0014](./adrs/0014-safe-cli-scaffolding.md)). ### Devtools and eval Composes application traces, fixture capture, renderer conformance, accessibility evidence, and cross-platform parity reporting around AgentsKit replay and eval primitives. The concrete application trace/replay boundary is documented in [ADR-0017](./adrs/0017-application-traces-compose-upstream-replay.md) and the [devtools guide](../devtools.md). ## Upstream adoption Before implementation, each issue records the AgentsKit source and exports inspected, what is reused directly, what application-layer behavior is added, and any upstream gap. A generally useful primitive is implemented in AgentsKit first; AgentsKit Chat never vendors or temporarily forks it. Initial mandatory mappings include: - `defineChat` → AgentsKit `ChatConfig` and `createChatController`; - lifecycle → AgentsKit `ChatController` operations; - actions → AgentsKit `ToolDefinition` plus application policy; - confirmation → AgentsKit tool confirmation and HITL primitives; - portable UI → AgentsKit `UIMessage`/`UIElement` plus registered custom components; - message history → AgentsKit `ChatMemory`; - replay/eval → AgentsKit replay and eval primitives; - renderers → corresponding AgentsKit framework packages. ## Reference turn pipeline ```text input -> deterministic route or conversation transition -> AgentsKit agent when interpretation/generation is required -> runtime-validated turn envelope -> component/action registry validation -> authorization, confirmation, and action policy -> versioned events -> native renderer and persistence ``` ## External dogfood hosts The public alpha is consumed by Docs, Registry, and Playbook through immutable release assets. Host repositories keep corpus configuration and native styling; they do not own chat reducers, lifecycle, memory, cancellation, or a second component protocol. The Registry/Playbook ownership correction and parity evidence are recorded in [the dogfood migration record](../dogfood/registry-playbook.md). ## Public documentation property The Fumadocs application under `apps/docs` is a first-class dogfood host. It renders the canonical `docs/` corpus without copying it and composes the public protocol, chat, server, and React packages for deterministic-first documentation Ask. Hosted and self-hosted modes share one Ask contract; retrieval and provider execution remain injected AgentsKit adapters. See [ADR-0027](./adrs/0027-fumadocs-framework-dogfood.md). ## Initial architecture proof The first shared example must run from one unchanged chat definition in React, React Native, and Ink. These targets exercise DOM, native mobile, and terminal constraints. Vue, Svelte, and Solid now host the same shared references under `apps/example-{vue,svelte,solid}` with Playwright coverage for Vue. Angular remains covered by package-level conformance and agent-chat suites. ## Risks - Framework parity may become superficial. Mitigation: shared conformance fixtures and behavioral gates. - The framework may duplicate AgentsKit. Mitigation: dependency-direction checks and explicit ownership rules. - Generative UI can become unsafe. Mitigation: closed registries, runtime schemas, inert unknown output, semantic fallback, and action policy outside the model. - Package proliferation may create shallow modules. Mitigation: packages are introduced only by vertical implementation slices with a public outcome. # AgentsKit upstream adoption matrix **Status:** Proposed for human acceptance in [#31](https://github.com/AgentsKit-io/agentskit-chat/issues/31) **Inspected:** 2026-07-10 against the current `AgentsKit-io/agentskit` source checkout. Package versions are recorded to make later drift visible; supported package releases, not private workspace paths, are the integration boundary. ## Binding rule AgentsKit Chat is a composition layer. It may add application definitions, deterministic application behavior, policy, custom component manifests, transport, sessions, native shells, and scaffolding. It may not independently implement a reusable primitive already owned by AgentsKit. When this matrix identifies a missing generic primitive, work moves first to `AgentsKit-io/agentskit` and remains blocked here until a supported upstream release contains it. ## Typed application actions (#8) The tool/controller lifecycle and official confirmation renderers were inspected. AgentsKit Chat reuses `proposeToolCall`, `approve`, and `deny` from `@agentskit/core@1.11.0`, plus the released React, React Native, and Ink `ToolConfirmation` components. The missing generic seam was resolved in [upstream #1144](https://github.com/AgentsKit-io/agentskit/issues/1144) and [PR #1145](https://github.com/AgentsKit-io/agentskit/pull/1145) before integration. Local code owns only choice declarations and session-bound metadata; it contains no executor, tool validator, controller, or confirmation widget. ## Trusted action policy (#9) AgentsKit Chat consumes `ChatConfig.authorizeToolCall` from `@agentskit/core@1.12.0`, delivered by [upstream #1147](https://github.com/AgentsKit-io/agentskit/issues/1147) and [PR #1148](https://github.com/AgentsKit-io/agentskit/pull/1148). Upstream owns enforcement before proposal and execution. Local code adds only trusted capability resolution, composition, and replayable application traces. ## Persistent application sessions (#11) Inspected Core `ChatMemory`, controller memory hydration, canonical tool-call status and `approve`/`deny`, plus the published Memory adapters. AgentsKit Chat stores no messages: it adds only the versioned application-session metadata envelope, definition compatibility, deterministic decisions, cursor, and confirmation bindings. Hosts combine `SessionStorage` with their selected upstream `ChatMemory`. No upstream gap or reimplementation exists. ## Web-standard handler (#12) Inspected Core controller, adapter, stream, chat, and memory contracts plus published Memory adapters. `@agentskit/chat-server` reuses `createChatController`, canonical state/messages/tool calls, `ChatMemory`, and `StreamSource.abort`. It adds only the Web request boundary, trusted-context seam, outer deadline/cancellation, and projection into the existing snapshot protocol. Raw adapter chunks are not transported and no controller, reducer, provider client, message store, or authentication implementation is recreated. The missing generic cancellation seam for message IO was added upstream in [AgentsKit #1155](https://github.com/AgentsKit-io/agentskit/issues/1155) and [PR #1156](https://github.com/AgentsKit-io/agentskit/pull/1156), then released as `@agentskit/core@1.12.2` via [#1157](https://github.com/AgentsKit-io/agentskit/pull/1157). The handler consumes that public contract and forwards its request/cleanup signals to `ChatMemory`. ## Current upstream packages | Package | Inspected version | Role in AgentsKit Chat | |---|---:|---| | `@agentskit/core` | 1.12.2 | Controller, chat contracts, messages, tools, cancellable memory, serialization and validation, generative UI, HITL, events, errors. | | `@agentskit/runtime` | 0.9.1 | Autonomous agent execution and shared runtime context. | | `@agentskit/validation` | 0.2.1 | AJV-backed tool argument validation. | | `@agentskit/memory` | 0.11.0 | Persistent `ChatMemory` implementations, including validated injectable Web Storage. | | `@agentskit/eval` | 0.4.19 | Eval runner, recording/replay adapters, cassettes, snapshots, diffs, CI reporting, and universal replay boundaries. | | `@agentskit/react` | 0.7.1 | React hook, headless chat components, CSS variables, and data attributes. | | `@agentskit/react-native` | 0.4.4 | React Native hook, wrapper/content/input style pass-throughs, and testIDs. | | `@agentskit/ink` | 0.10.1 | Ink hook, terminal components, theme provider, progress observer, and Escape stream cancellation. | | `@agentskit/vue` | 0.4.4 | Vue composable, headless components, and controller-free slotted `ChatRoot`. | | `@agentskit/svelte` | 0.3.1 | Svelte store and components. | | `@agentskit/solid` | 0.4.4 | Solid hook, headless components, and owner-cleanup cancellation. | | `@agentskit/angular` | 0.4.6 | Angular signal service, standalone components, lifecycle-safe teardown, and typed partial-Ivy AOT packaging. | ## Replayable application traces (#21) The replay integration was initially inspected at AgentsKit revision `978ce3d77be7bbf76094b5919d240e50091bc824` and `@agentskit/eval@0.4.17`; the currently consumed release is `@agentskit/eval@0.4.19`. AgentsKit Chat consumes `createRecordingAdapter`, `createReplayAdapter`, `Cassette`, `serializeCassette`, and `parseCassette` from `@agentskit/eval/replay`. Upstream remains the sole owner of adapter recording, request fingerprints, cassettes, playback, time travel, filesystem boundaries, and eval reporting. `@agentskit/chat-devtools` adds only bounded/redacted application decisions and semantic cross-renderer comparison. No upstream gap or reimplementation exists. ## Support reference application (#22) Inspected AgentsKit revision `978ce3d77be7bbf76094b5919d240e50091bc824`: `@agentskit/core@1.12.2` supplies `defineTool`, typed schema inference, `requiresConfirmation`, controller proposal/approve/deny, and `ChatConfig.validateArgs`; `@agentskit/validation@0.2.1` supplies `createAjvValidator`. The support example consumes those contracts and the released framework bindings directly. Local code adds only an injected ticket-service seam, trusted host context, support routes, native presentation, and executable examples. No upstream behavior is copied and no gap blocks the example. ## Responsibility matrix | Planned concern | AgentsKit source inspected | Supported public API | Disposition | AgentsKit Chat responsibility | Upstream gap | |---|---|---|---|---|---| | Chat configuration | `core/src/types/chat.ts` | `ChatConfig` from `@agentskit/core` | Reuse directly | Store it under `ChatDefinition.chat`; do not mirror its fields. | None for #2. | | Chat state and lifecycle | `core/src/controller.ts`, `core/src/types/chat.ts` | `createChatController`, `ChatController`, `ChatState`, `ChatReturn` | Reuse directly | Compose deterministic application behavior around the upstream controller; never replace it. | None for #2. | | Send, stop, retry, edit, regenerate | `core/src/controller.ts` | `ChatController` methods and framework `useChat` equivalents | Reuse directly | Expose native framework ergonomics without changing lifecycle semantics. | None. | | Lifecycle controls | React/RN `Message` actions and Ink `InputBar.onSubmitInput` | Published renderer extension points | Reuse directly | Add platform-native retry/edit/regenerate affordances. | None for #10. | | Snapshot serialization | `core/src/memory.ts` | `serializeMessages` + memory validation | Reuse directly | Add the versioned turn envelope and lineage. | Undefined-field incompatibility fixed in [AgentsKit #1153](https://github.com/AgentsKit-io/agentskit/pull/1153) and released in `@agentskit/core` 1.12.1 via [#1154](https://github.com/AgentsKit-io/agentskit/pull/1154). | | Messages and tool calls | `core/src/types/message.ts`, `core/src/types/tool.ts` | `Message`, `ToolCall`, `ToolCallStatus` | Reuse directly | Add application metadata only through documented envelopes or metadata keys. | None. | | Tool definitions | `core/src/types/tool.ts` | `ToolDefinition`, `defineTool` | Reuse directly | Treat an application action as a tool plus trusted policy keyed by tool name. No second executor. | None. | | Tool execution | `core/src/agent-loop.ts`, `core/src/primitives.ts` | Controller/runtime execution and `executeToolCall` | Reuse controller/runtime | Supply tools and policy; never call a local duplicate tool loop. | None. | | Argument validation | `core/src/types/tool.ts`, `validation/src/ajv-validator.ts` | `ArgsValidator`, `createAjvValidator` | Reuse directly | Enable validation at model/tool boundaries and validate application-only envelopes separately. | None. | | Tool confirmation | `core/src/controller.ts`, framework `ToolConfirmation` components | `requiresConfirmation`, `approve`, `deny` | Reuse directly | Add identity, authorization, expiry, and audit policy before delegating approval. | None. | | Durable HITL | `core/src/hitl.ts` | `createApprovalGate`, `ApprovalStore` from `@agentskit/core/hitl` | Reuse directly | Bind a host-provided store and application reviewer identity. | None. | | Portable generative UI | `core/src/generative-ui.ts` | `UIMessage`, `UIElement`, `validateUIMessage`, `parseUIMessage` from `@agentskit/core/generative-ui` | Reuse and render | Render the upstream standard element set; do not redefine its discriminated union. | No blocker. Native rendering belongs to this application layer unless the primitive itself changes. | | Custom application UI | No equivalent closed component registry upstream | Upstream `UIElement` remains available for portable primitives | Extend at application layer | Own `componentKey`, runtime props schema, native implementations, interaction event, and semantic fallback. | None: application-specific by design. | | Message persistence | `core/src/types/memory.ts`, `core/src/memory.ts`, `memory` package | `ChatMemory`, serialization helpers, memory adapters | Reuse directly | Configure the selected message-memory implementation. | None. | | Application session | No upstream application-session envelope | `ChatMemory` covers messages only | Add application layer | Store session id, definition/version, conversation state, policy context reference, confirmation references, and event cursor; do not duplicate messages. | None: product metadata is outside `ChatMemory`. | | Retrieval | `core/src/types/retrieval.ts`, `core/src/rag.ts`, `rag` package | `Retriever`, `RetrievedDocument`, RAG implementations | Reuse directly | Configure retrieval and map retrieved-source metadata to UI. | None. | | Adapter/model execution | `core` adapter contract, `adapters` package | `AdapterFactory` and provider factories | Reuse directly | Accept supported adapters through `ChatConfig`. | None. | | Replay | `eval/src/replay/*` | `@agentskit/eval/replay` recording/replay adapters and cassettes | Reuse directly | Capture application-only decisions around upstream recorded model/tool behavior. | None. | | Eval and snapshots | `eval/src/*` | `runEval`, `@agentskit/eval/snapshot`, `diff`, `ci` | Reuse directly | Add renderer and application-policy conformance fixtures. | None. | | React rendering | `react/src/theme/{tokens,default}.css`, `react/src/components/*` | `useChat`, headless components, CSS variables, and `data-ak-*` hooks from `@agentskit/react` | Reuse directly | Supply an opinionated shell, native slots, application components, and semantic token mapping. | None. | | React Native rendering | `react-native/src/components.tsx` | `useChat`, native components, `style`, and `testID` seams | Reuse directly | Supply a native shell, native slots, application component styles, and capability-aware token mapping. | None. | | Ink rendering | `ink/src/useChat.ts`, `ink/src/components/{InputBar,ChatContainer,Message,ThinkingIndicator,theme}.tsx` | `useChat`, terminal components, `InkThemeProvider`, and `InkTheme` | Reuse directly | Supply the terminal application shell, native slots, semantic color mapping, and native presentation of the shared fallback. | Escape cancellation fixed upstream by [AgentsKit #1132](https://github.com/AgentsKit-io/agentskit/issues/1132) and released in `@agentskit/ink` 0.9.5 via [PR #1133](https://github.com/AgentsKit-io/agentskit/pull/1133). | | Vue/Svelte/Solid/Angular | Corresponding package sources | Corresponding supported composable/store/service and components | Reuse directly | Adapt only native composition conventions to the shared definition. | Verify again in each renderer issue because beta APIs may evolve. | | Deterministic application routes | No equivalent application router required in core | `ChatController.send` remains the agentic fallback | Add application layer | Match trusted application routes before delegating unresolved input to the upstream controller. | None: intentionally above core. | | Conversation state machine | No general public statechart contract in AgentsKit | Controller remains responsible for chat lifecycle | Add application layer initially | Coordinate application states without replacing controller lifecycle. Promote upstream only if a framework-neutral primitive proves independently useful. | Not currently proven. | | Client-server protocol | `core/src/types/{chat,message,tool,stream}.ts`, `core/src/memory.ts` | `ChatState`, `Message`, `ToolCall`, `TokenUsage`, `MemoryRecord`, `serializeMessages`, `deserializeMessages` | Reuse canonical shapes; add application envelope | Transport validated snapshots without redefining controller or adapter stream semantics. | None: versioned application transport belongs to AgentsKit Chat. | | Observability | Core observers and AgentsKit observability packages | `Observer` and supported integrations | Reuse directly | Add application identifiers and decisions without creating another telemetry substrate. | None. | ## Minimal `defineChat` contract for #2 The first slice uses the shallowest contract that preserves upstream ownership: ```ts import type { ChatConfig } from '@agentskit/core' export interface ChatDefinition { readonly id: string readonly chat: ChatConfig } export const defineChat = (definition: T): T => definition ``` The React binding passes `definition.chat` to `useChat` from `@agentskit/react`. It does not instantiate a custom controller, translate lifecycle states, wrap the adapter protocol, or copy upstream components. ### Ordered assistant content Inspected AgentsKit Core canonical `Message` and `StreamChunk` contracts plus the official framework bindings. AgentsKit Chat keeps the single canonical string message and upstream stream reducer, and adds only a bounded application-level record envelope for ordering text with its closed registered components. Text records are host-encoded and inert; component records reuse `ComponentRenderFrame` validation and manifest resolution. No upstream message, controller, stream, persistence, or generative-UI primitive is copied. See [ADR-0021](./adrs/0021-ordered-assistant-content-records.md). ### Shared Ask service integration Docs, Registry, and Playbook use one Ask application-protocol adapter from `@agentskit/chat`. The reusable browser-memory gap was resolved first in AgentsKit [#1191](https://github.com/AgentsKit-io/agentskit/issues/1191), [PR #1192](https://github.com/AgentsKit-io/agentskit/pull/1192), and released as `@agentskit/memory@0.11.0`. AgentsKit Chat Protocol owns the versioned bounded event decoder; AgentsKit Chat composes the public `createWebStorageMemory` seam and adds only wire projection, safe citations, transport behavior, and host presentation composition. See [ADR-0022](./adrs/0022-shared-ask-service-integration.md). Fields are added to `ChatDefinition` only when a vertical issue demonstrates application-layer behavior not represented by `ChatConfig`. This avoids mirroring upstream configuration and reduces compatibility work. ## Planned application-only extensions These are not part of #2 and require their owning vertical issues: ```ts interface ChatDefinition { readonly id: string readonly chat: ChatConfig readonly routes?: readonly DeterministicRoute[] readonly components?: ComponentManifest readonly policies?: Readonly> readonly conversation?: ConversationDefinition } ``` `routes`, `components`, `policies`, and `conversation` coordinate application behavior. They must delegate all chat lifecycle, model, tool, confirmation, memory, retrieval, and replay primitives to AgentsKit. ## Negative guardrail examples The following changes are rejected: - a new `ChatController` or lifecycle state equivalent; - a custom LLM stream consumer that bypasses the AgentsKit controller; - an action executor separate from AgentsKit tools/runtime; - a copied `UIElement` union or generative-UI parser; - message persistence outside `ChatMemory` without a documented upstream gap; - independent replay adapters or eval runners; - a renderer that bypasses its corresponding AgentsKit binding; - imports from another repository's source tree or unpublished workspace paths. ## ChoiceList adoption record (#7) Inspected `@agentskit/core/generative-ui` on 2026-07-11. The upstream standard union provides text, heading, list, button, image, card, stack, and artifact elements, but intentionally does not provide a closed custom application registry. AgentsKit Chat reuses the official controller and framework bindings unchanged and adds only the application registry, custom frame/event envelope, native `ChoiceList` implementations, and conformance fixtures. No upstream gap or copied implementation exists. ## Current gap verdict No upstream change blocks #7. `ChatConfig`, framework bindings, and the standard generative-UI union are reused unchanged; the closed registry and native custom component remain the application layer described in the responsibility matrix. Later issues must repeat the source audit because upstream beta packages and the application requirements may evolve. When a genuine generic gap appears, ADR-0002 requires an AgentsKit issue and supported upstream release before local integration. ## Theming and composition adoption record (#13) Inspected `@agentskit/react@0.7.1` theme CSS/component attributes, `@agentskit/react-native@0.4.4` component wrapper/content/input style and `testID` pass-throughs, and `@agentskit/ink@0.10.1` `components/theme.tsx` (`InkThemeProvider`, `InkTheme`) on 2026-07-11. AgentsKit Chat consumes those seams directly and adds only runtime-validated application tokens, capability-aware mappings, and renderer-native slots. Fully headless consumers use upstream `useChat`; no local state hook, design system, or renderer primitive is recreated. The missing native text-style seams were fixed upstream in AgentsKit #1158/#1159 and released in `@agentskit/react-native@0.4.4` via #1160 before downstream integration. ## CLI adoption record (#14) Inspected AgentsKit revision `main` on 2026-07-11: `@agentskit/cli@0.13.10` exports `writeStarterProject` from `packages/cli/src/index.ts`, implemented by `packages/cli/src/init.ts`, with the `init` command adapter in `packages/cli/src/commands/init.ts`; `@agentskit/templates@0.4.3` exports `scaffold` from `packages/templates/src/index.ts`, implemented by `packages/templates/src/scaffold.ts`. They own general AgentsKit starters and extension packages; neither represents a safe AgentsKit Chat application slice. The local CLI uses Node standard-library command/filesystem seams and generates imports of published AgentsKit contracts. It does not copy upstream templates, controller logic, adapters, or renderer primitives. No generic upstream gap blocks #14. ## Vue renderer adoption record (#15) Inspected AgentsKit revision `a86905c7a4cac3cb0642e8acf7e9e8bb540e8886` and `@agentskit/vue@0.4.3` on 2026-07-11: `packages/vue/src/index.ts` publicly exported `useChat`, `ChatContainer`, `Message`, `InputBar`, `ThinkingIndicator`, and `ToolConfirmation`; their implementations live in `useChat.ts`, `ChatContainer.ts`, and `components.ts`. The batteries-included `ChatContainer` always created a controller and could not safely host an application shell that already consumed `useChat`. The generic gap was fixed upstream in AgentsKit #1161/#1162 by adding the controller-free, slotted `ChatRoot`, then released as `@agentskit/vue@0.4.4` via #1163 before downstream integration. AgentsKit Chat consumes `useChat`, `ChatRoot`, and all standard components directly from `@agentskit/vue@0.4.4`. It adds only application session coordination, validated component-frame presentation, semantic theme mapping, and Vue-native scoped slots. No upstream source, controller, reactive store, renderer root, lifecycle, or confirmation behavior is copied or reimplemented. ## Svelte renderer adoption record (#16) Inspected AgentsKit revision `19dd8b68b76b03f577ea28d232a2e59cf78014d7` and `@agentskit/svelte@0.4.3` on 2026-07-11. `packages/svelte/src/index.ts` exports `createChatStore` from `useChat.ts` plus `ChatContainer`, `Message`, `InputBar`, `ThinkingIndicator`, and `ToolConfirmation` from `components/*.svelte`. `ChatContainer` is controller-free and renders its Svelte 5 child snippet, so no upstream gap blocks #16. AgentsKit Chat consumes those exports directly and adds only application session coordination, validated frames, semantic CSS mapping, typed customization snippets, and a keyed store-binding handoff that invokes upstream `stop` before replacing an active stream. It does not copy or recreate the upstream controller, store, components, lifecycle, streaming, or confirmation behavior. ## Solid renderer adoption record (#17) Inspected AgentsKit revision `f35bab77ba64731f2524f9132dd02f906c85a443` and `@agentskit/solid@0.4.3` on 2026-07-11. `packages/solid/src/index.ts` exports `useChat`, `ChatContainer`, `Message`, `InputBar`, `ThinkingIndicator`, and `ToolConfirmation`. The generic cleanup gap in `useChat` was fixed in AgentsKit [#1169](https://github.com/AgentsKit-io/agentskit/pull/1169) and released as `@agentskit/solid@0.4.4` before integration. AgentsKit Chat consumes those exports directly and adds only application session coordination, validated semantic-frame presentation, theme mapping, and Solid-native render props. No controller, store, stream consumer, lifecycle, confirmation, or headless component behavior is copied or reimplemented. ## Angular renderer adoption record (#18) Inspected AgentsKit revision `54a34f1256f7e309227c4fb9d6b563134b137fc4` and `@agentskit/angular@0.4.3` on 2026-07-11. `packages/angular/src/index.ts` exports the `AgentskitChat` signal service plus `ChatContainerComponent`, `MessageComponent`, `InputBarComponent`, `ThinkingIndicatorComponent`, and `ToolConfirmationComponent`. The generic teardown gap in `AgentskitChat.destroy()` was fixed in AgentsKit [#1171](https://github.com/AgentsKit-io/agentskit/issues/1171)/[#1172](https://github.com/AgentsKit-io/agentskit/pull/1172). The AOT distribution and declaration-export gaps were then fixed in [#1174](https://github.com/AgentsKit-io/agentskit/issues/1174)/[#1175](https://github.com/AgentsKit-io/agentskit/pull/1175) and [#1177](https://github.com/AgentsKit-io/agentskit/issues/1177)/[#1178](https://github.com/AgentsKit-io/agentskit/pull/1178). All are released in `@agentskit/angular@0.4.6` before integration. AgentsKit Chat consumes those exports directly and adds only application session coordination, validated semantic-frame presentation, CSS-variable theme mapping, and Angular-native content templates. No controller, signal store, stream consumer, lifecycle, confirmation, or upstream component behavior is copied or reimplemented. ## Standard component catalog adoption record (#19) Inspected AgentsKit revision `978ce3d77be7bbf76094b5919d240e50091bc824` and `@agentskit/core@1.12.2` on 2026-07-11, especially `packages/core/src/generative-ui.ts`. AgentsKit owns `UIElement` (`text`, `heading`, `list`, `button`, `image`, `card`, `stack`, and `artifact`) plus its validators. AgentsKit Chat does not extend or copy that union. It adds only the closed application schemas, intent events, accessibility/capability metadata, native application presentations, and parity evidence established by ADR-0007/ADR-0015. No generic upstream gap blocks #19. ## Deterministic onboarding adoption record (#23) Inspected AgentsKit revision `4d66eb192d636b53d0c7bec39894250dc71cde5f` and `@agentskit/core@1.12.2`: `ChatConfig`, `AdapterFactory`, typed tools, authorization, confirmation, and official React, React Native, and Ink bindings. AgentsKit owns controller lifecycle, messages, streams, memory, cancellation, tool execution, and confirmation. AgentsKit Chat adds only application routes, Form/ChoiceList intents, guards, and parity evidence. No upstream state-machine primitive exists, no upstream behavior is copied, and no generic gap blocks #23. ## Protected operations adoption record (#24) Inspected AgentsKit revision `4d66eb192d636b53d0c7bec39894250dc71cde5f`, specifically `packages/core/src/chat.ts`, `packages/core/src/tool-proposal-internal.ts`, `packages/core/src/tool-authorization-internal.ts`, and confirmation exports in `packages/react`, `packages/react-native`, and `packages/ink`, consumed through `@agentskit/core@1.12.2` and the published renderer packages. The reference adds only injected operations-domain behavior and a safe trace projection. No upstream primitive is copied and no generic gap blocks #24. ## Cited RAG adoption record (#25) Inspected AgentsKit revisions `4d66eb192d636b53d0c7bec39894250dc71cde5f` and `edb77757d2f2ca095733392657cafd8b7dd59a78`, `@agentskit/rag@0.4.12`, and `packages/rag/src/index.ts`, `rag.ts`, `types.ts`, `chunker.ts`, and `loaders.ts`. The example directly consumes `createRAG`, `RAG`, `RetrievedDocument`, and `VectorMemory`. Dogfooding found the optional S3 peer browser-bundle gap, fixed at the source in [AgentsKit #1180](https://github.com/AgentsKit-io/agentskit/issues/1180), [PR #1181](https://github.com/AgentsKit-io/agentskit/pull/1181), and release [PR #1182](https://github.com/AgentsKit-io/agentskit/pull/1182) before local integration. AgentsKit Chat adds only bounded SourceList projection, safe-link interaction resolution, and native/fallback evidence. No retrieval, chunking, embedding, storage-search, or reranking behavior is copied. ## Registry and Playbook dogfood adoption record (#27) Inspected `@agentskit/core@1.12.3`, `@agentskit/react@0.7.4`, and the memory package before host migration. Dogfooding found the reusable browser-storage gap and fixed it in AgentsKit [#1191](https://github.com/AgentsKit-io/agentskit/issues/1191), [PR #1192](https://github.com/AgentsKit-io/agentskit/pull/1192), and release [PR #1194](https://github.com/AgentsKit-io/agentskit/pull/1194) as `@agentskit/memory@0.11.0`. It also found three host copies of the Ask wire and transport boundary; those moved to the shared protocol and chat packages in [#66](https://github.com/AgentsKit-io/agentskit-chat/issues/66) and [PR #67](https://github.com/AgentsKit-io/agentskit-chat/pull/67), released as `v0.1.0-alpha.2`. Hosts now own only endpoint/corpus configuration, storage identities, branding, native presentation slots, and optional presentation-only tool projection. No controller, reducer, stream lifecycle, decoder, memory engine, renderer, or component protocol is copied or reimplemented. Full evidence is in the [Registry/Playbook dogfood record](../dogfood/registry-playbook.md). ## Conversation statechart adoption record (#28) Inspected the public `@agentskit/statechart@0.2.0` release and its exported `defineStatechart`, `createStatechartInstance`, `transitionStatechart`, immutable instance, event, definition, result, and diagnostic contracts. The generic gap recorded by the original conversation design was resolved in AgentsKit [#1199](https://github.com/AgentsKit-io/agentskit/issues/1199), [PR #1210](https://github.com/AgentsKit-io/agentskit/pull/1210), and the `0.2.0` release before downstream adoption. AgentsKit Chat compiles its existing application conversation declaration into the upstream definition and uses upstream transitions for initial validation, dispatch, resume validation, and history rebuild. It adds only route matching and precedence, adapter fallback, application actions/traces, retained decisions, and the existing session projection. No statechart validator, transition engine, upstream source, private source, or business behavior is copied or reimplemented. See [ADR-0023](./adrs/0023-conversation-transitions-use-agentskit-statechart.md). ## Deterministic answer plane adoption record (#69) Inspected the published `@agentskit/core@1.12.3` `AdapterFactory`, `AdapterRequest`, `AdapterContext.metadata`, `StreamSource`, `Message`, capabilities, and cancellation contracts. They already provide the correct composition seam: local application policy can select an answer before calling a backend without creating a controller or consuming its stream. AgentsKit Chat Protocol adds only versioned application schemas for site configuration, immutable local knowledge, answer provenance, citations, suggestions, confidence, escalation, and the canonical SHA-256 verification shared by producers and consumers. AgentsKit Chat adds only bounded normalized exact lookup, explicit choice resolution, projection into its existing ordered assistant-content and standard component protocols, and delegation to the injected upstream adapter. Artifact generation and caching remain host or Doc Bridge concerns. No controller, reducer, adapter lifecycle, retrieval engine, statechart, renderer primitive, upstream source, private source, or business data is copied or reimplemented. See accepted [ADR-0024](./adrs/0024-deterministic-answer-plane.md). The React Native release check also inspected the public `@agentskit/eval` replay exports used by devtools. A universal-bundle filesystem boundary was fixed upstream in AgentsKit [#1216](https://github.com/AgentsKit-io/agentskit/issues/1216), [PR #1217](https://github.com/AgentsKit-io/agentskit/pull/1217), and release `@agentskit/eval@0.4.19`. AgentsKit Chat consumes that release directly; it does not alias Node built-ins, copy replay behavior, or add a platform-specific fork. ## Trusted Ask backend adoption record (#70) Inspected the published AgentsKit Core adapter, controller, cancellation, message, token-usage, and observer contracts; AgentsKit RAG `createRAG`, `RAG`, `Retriever`, and `RetrievedDocument`; and the existing AgentsKit Chat Ask, deterministic escalation, session-aware adapter, Web handler, CAS session, and SourceList contracts. These already own provider execution, retrieval, streaming lifecycle, canonical messages, cancellation, usage, and rendering. The backend vertical consumes those seams through injected retriever and generator adapters. It adds only runtime-validated server-side site policy, browser-hint equality checks, bounded cited projection, CAS coordination, deadlines/rate-limit composition, and privacy-safe numeric observations. It copies no controller, adapter lifecycle, model transport, RAG, embeddings, ranking, vector storage, memory engine, authentication, rate limiter, telemetry backend, or renderer behavior. See accepted [ADR-0026](./adrs/0026-trusted-ask-backend-vertical.md). ## Stable v0 release adoption record (#30) The release audit inspected public AgentsKit `main@0573aff1f442ba39200ea38600f295d63f5715ad` and the published package boundaries consumed by the fixed v0 graph: `@agentskit/core@1.12.3`, `@agentskit/memory@0.11.0`, `@agentskit/statechart@0.2.0`, `@agentskit/eval@0.4.19`, `@agentskit/react@0.7.4`, `@agentskit/react-native@0.4.5`, `@agentskit/ink@0.10.4`, `@agentskit/vue@0.4.5`, `@agentskit/svelte@0.4.5`, `@agentskit/solid@0.4.5`, and `@agentskit/angular@0.4.7`. The inspected public source modules include Core controller/chat/tool/memory and generative-UI exports, Memory Web Storage, statechart definition/instance/ transition exports, Eval replay's universal and explicit IO entry points, and each framework binding's public hook/store/service plus native components. AgentsKit Chat continues to reuse those exports through declared package peers. The stable release adds only version alignment, package metadata, documentation, clean-install/provenance gates, and publishing automation. It introduces no controller, reducer, adapter lifecycle, statechart, memory, replay, tool, confirmation, RAG, or framework-binding implementation. No upstream gap was identified by release preparation. ## Fumadocs framework dogfood adoption record (#71) Inspected the published AgentsKit controller, React binding, RAG/Retriever, memory, provider-adapter, cancellation, and usage contracts together with the released AgentsKit Chat definition, deterministic artifact, Ask adapter, session memory, React renderer, protocol, and trusted Ask handler boundaries. The documentation property consumes those exports through package boundaries. It adds only Fumadocs navigation over the canonical repository corpus, site-owned presentation slots, verified local documentation entries, trusted site configuration, and deployment composition. Retrieval, generation, vector storage, canonical messages, controller lifecycle, cancellation, protocol decoding, and browser memory remain upstream or existing framework concerns. No private source, behavior, identifier, data, or business rule is copied or published. See proposed [ADR-0027](./adrs/0027-fumadocs-framework-dogfood.md). ## Externally controlled session adoption record (#106) Inspected `@agentskit/core@1.12.3` `ChatReturn`, `Message`, `StreamStatus`, `TokenUsage`, tool-confirmation lifecycle, and the public `@agentskit/core/memory-validation` entry point, plus `@agentskit/react@0.7.1` `useChat` and presentation components. Those contracts already define canonical state, messages, lifecycle callbacks, validation, and the definition-owned React controller path. AgentsKit Chat adds only a strict serializable application snapshot, delegates canonical message validation upstream, and projects host callbacks as a `Pick`. The React renderer uses separate acquisition components and one shared presentation path, so controlled mode never invokes `useChat` while definition-owned mode remains unchanged. Authentication, authorization, transport, persistence, and snapshot mutation remain host-owned. No controller, reducer, adapter consumer, message schema, memory engine, or React primitive is copied or reimplemented. No upstream gap blocks #106. See accepted [ADR-0031](./adrs/0031-controlled-chat-is-an-application-seam.md). ## Controlled Ink adoption record (#107) Inspected `@agentskit/ink@0.10.1` `useChat`, `InputBar`, `ToolConfirmation`, `InkThemeProvider`, semantic presentation components, and keyboard handling. AgentsKit already owns terminal editing, history, Escape cancellation, confirmation input, and lifecycle presentation. The Ink application shell consumes the same `ControlledChatSource` and `createControlledChatDriver` used by React, with separate definition-owned and controlled acquisition components feeding one existing terminal presentation. Controlled mode never invokes `useChat`; it continues to render the upstream input and confirmation components and keeps the existing last-unresolved interaction focus rule. The PTY tracer adds only a synthetic host-state fixture and injected callbacks. No controller, terminal input parser, lifecycle, confirmation widget, transport, persistence, or business behavior is copied or reimplemented. No upstream gap or new architecture decision blocks #107. # Hosted and self-hosted Ask backend `createAskServiceHandler` is the production boundary for semantic questions that the deterministic plane cannot answer. It accepts one public protocol in every deployment and derives all authority server-side. ```mermaid flowchart LR Q["Exact question"] --> D{"Deterministic artifact"} D -->|"match"| L["Local cited answer"] D -->|"miss"| H["Trusted Ask handler"] H --> A["Authenticate and resolve site"] A --> R["AgentsKit RAG / Retriever"] R --> G["AgentsKit provider / adapter"] G --> C["Cited Ask NDJSON"] C --> P["CAS persistence + private metrics"] ``` ## Shared production factory ```ts import { createAskServiceHandler } from '@agentskit/chat/server' export const ask = createAskServiceHandler({ authenticate: (request, signal) => auth.verify(request, signal), resolveSite: (identity, signal) => siteRegistry.resolve(identity.siteId, signal), resolveSubjectId: identity => identity.subjectId, retrievers: { local: { retrieve: input => localRag.retrieve(input) }, federated: { retrieve: input => federatedRag.retrieve(input) }, }, generator: providerGenerator, sessionStore: durableCasStore, rateLimit: input => limiter.consume(input.site.siteId, input.subjectId, input.signal), onMetric: metric => telemetry.record(metric), }) ``` `localRag` and `federatedRag` are adapters around the public AgentsKit RAG/Retriever APIs. `providerGenerator` delegates generation and usage to an AgentsKit provider. The handler never implements embeddings, vector search, ranking, model transport, authentication, or storage. ## Hosted route Web-standard platforms mount the shared factory directly: ```ts import { ask } from './ask-backend.js' export const POST = ask ``` ## Self-hosted Node route The Node bridge only translates HTTP to Web standards. It does not change the public request or response: ```ts import { createServer } from 'node:http' import { ask } from './ask-backend.js' createServer(async (incoming, outgoing) => { const disconnected = new AbortController() incoming.once('aborted', () => disconnected.abort()) outgoing.once('close', () => disconnected.abort()) const chunks: Buffer[] = [] for await (const chunk of incoming) chunks.push(Buffer.from(chunk)) const response = await ask(new Request(`http://localhost${incoming.url ?? '/'}`, { method: incoming.method, headers: incoming.headers as HeadersInit, signal: disconnected.signal, ...(chunks.length === 0 ? {} : { body: Buffer.concat(chunks) }), })) outgoing.writeHead(response.status, Object.fromEntries(response.headers)) if (response.body === null) return outgoing.end() for await (const chunk of response.body) outgoing.write(chunk) outgoing.end() }).listen(3000) ``` The package integration tests execute both local and federated configurations through the same handler contract and prove identical events through a direct hosted invocation and a real self-hosted Node streaming bridge. ## Site configuration Each trusted site record declares: - stable site and assistant identities; - suggestions shown by the host; - one authorized corpus and its `local` or `federated` mode; - registered components and actions; - request, retrieval, and generation deadlines; - maximum citation count; - required or disabled persistence. The browser cannot select this record. Legacy `corpus` and `persona` query parameters must equal the resolved record or the server returns `ASK_FORBIDDEN`. ## Persistence and replay Sites using `persistence.mode: required` must inject a CAS store. Keys contain trusted site and subject IDs plus the validated application session ID. `load` returns a bounded revisioned record; `save` receives the expected revision. A concurrent mutation produces `ASK_PERSISTENCE_CONFLICT` and increments the conflict metric. Provider details and user content never enter the diagnostic. ## Operational gates - Apply rate limits before parsing the request body. - Keep provider secrets and corpus credentials only in injected server adapters. - Propagate `AbortSignal` into authentication, rate limiting, retrieval, generation, and persistence. - Require at least one safe citation. Unsupported schemes, credentials in URLs, and malformed sources are dropped. - Export every backend metric, including zeros. `stream.snapshots` is zero for Ask because its public stream contains events rather than turn snapshots. - Alert on error, cancellation, conflict, and rate-limit ratios; establish latency/token/cost baselines before tuning. - Never log request bodies, prompts, generated answers, retrieved excerpts, credentials, subject IDs, or session IDs. See [ADR-0026](./architecture/adrs/0026-trusted-ask-backend-vertical.md) for the accepted boundary. # CLI ```bash agentskit-chat init my-chat --renderer react --yes agentskit-chat init my-mobile-chat --renderer react-native --yes agentskit-chat init my-terminal-chat --renderer ink --yes agentskit-chat init my-vue-chat --renderer vue --yes agentskit-chat init my-svelte-chat --renderer svelte --yes agentskit-chat init my-solid-chat --renderer solid --yes agentskit-chat init my-angular-chat --renderer angular --yes agentskit-chat add component status-card --renderer react,vue,ink --directory . --yes ``` Without `--renderer`, the command detects React, Vue, Svelte, Solid, Angular, React Native, or Ink dependencies from the current host project while writing to the requested empty child directory. Native-specific dependencies win over React when both are present. If detection is impossible, an interactive TTY asks once; non-interactive execution fails with an actionable flag. Every starter contains a shared `src/chat.ts`, Web-standard `src/server.ts`, native renderer entry, strict TypeScript configuration, test, and README. The target path must not exist: the CLI never merges, deletes, or overwrites project files. Generate native shell completion with `agentskit-chat completion bash`, `zsh`, or `fish`. `add component` creates `src/components/.ts` as the single strict schema, semantic identity, metadata, and fallback. It creates presentation files only for the comma-separated renderer targets. Register the exported definition in your component manifest and connect native files through the renderer's `standardComponent` slot. React, React Native, Ink, Vue, and Solid exports already match their slot callback props. Svelte and Angular files accept the slot's `frame` through a snippet or `#standardComponent` template wrapper. The command checks every destination first, rejects symbolic-link directories, and rolls back files created by a failed operation. ## Troubleshooting - `RENDERER_REQUIRED`: add one of the seven supported `--renderer` values. - `TARGET_EXISTS`: choose a new target path that does not exist. - `MANIFEST_INVALID`: repair `package.json` or use an explicit renderer and a new target path. - `COMPONENT_INVALID`: use a portable name beginning with a letter. - `COMPONENT_EXISTS`: rename the component or remove the collision yourself; the CLI never overwrites it. # Standard component catalog parity Generated by `pnpm catalog:report`. Do not edit manually. ## Contract reference | Component | Props | Events | Accessibility | Capabilities | Fallback | |---|---|---|---|---|---| | button-group | schema-defined | select (id) | group; keyboard=true; live=none | display, selection | yes | | choice-list | schema-defined | select (id) | group; keyboard=true; live=none | display, selection | yes | | form | schema-defined | submit (form) | form; keyboard=true; live=none | display, input | yes | | confirmation | schema-defined | confirm (none), cancel (none) | group; keyboard=true; live=none | display, action | yes | | progress | schema-defined | none | progressbar; keyboard=false; live=polite | display, progress | yes | | source-list | schema-defined | open (id) | list; keyboard=true; live=none | display, navigation | yes | | link-card | schema-defined | open (url) | link; keyboard=true; live=none | display, navigation | yes | | error-notice | schema-defined | retry (none) | alert; keyboard=true; live=none | display, action | yes | | tool-call | schema-defined | none | status; keyboard=false; live=polite | display | yes | | approval-request | schema-defined | approve (none), deny (none) | group; keyboard=true; live=none | display, action | yes | | table | schema-defined | none | table; keyboard=false; live=none | display | yes | | file-attachment | schema-defined | open (url) | link; keyboard=true; live=none | display, download | yes | ## Renderer parity | Component | react | react-native | ink | vue | svelte | solid | angular | |---|---|---|---|---|---|---|---| | button-group | yes | yes | yes | yes | yes | yes | yes | | choice-list | yes | yes | yes | yes | yes | yes | yes | | form | yes | yes | yes | yes | yes | yes | yes | | confirmation | yes | yes | yes | yes | yes | yes | yes | | progress | yes | yes | yes | yes | yes | yes | yes | | source-list | yes | yes | yes | yes | yes | yes | yes | | link-card | yes | yes | yes | yes | yes | yes | yes | | error-notice | yes | yes | yes | yes | yes | yes | yes | | tool-call | yes | yes | yes | yes | yes | yes | yes | | approval-request | yes | yes | yes | yes | yes | yes | yes | | table | yes | yes | yes | yes | yes | yes | yes | | file-attachment | yes | yes | yes | yes | yes | yes | yes | # Standard component catalog Import the complete closed manifest when an application accepts every standard component: ```ts import { StandardComponentCatalog, defineComponentManifest } from '@agentskit/chat' export const components = defineComponentManifest(StandardComponentCatalog) ``` The v0 catalog contains ButtonGroup, ChoiceList, Form, Confirmation, Progress, SourceList, LinkCard, ErrorNotice, ToolCall, ApprovalRequest, Table, and FileAttachment. Each exported `*Component` definition includes its strict Zod schema, declared events, accessibility contract, capabilities, and fallback builder. Individual `*PropsSchema` exports support authoring and tests. Interactive components emit a validated `ComponentInteractionEvent` with `type: 'interact'`, an event name, and its declared value. ChoiceList retains the compatible `ComponentSelectionEvent`. Events describe user intent only: hosts decide navigation and downloads, while tool effects continue through AgentsKit authorization and confirmation. Connect both host callbacks on any shell. `onComponentSelect` remains exclusive to ChoiceList for backward compatibility; `onComponentInteract` receives form, action, navigation, and download intents: ```tsx handleChoice(event.choiceId)} onComponentInteract={event => handleComponentIntent(event)} /> ``` The same callback names apply to React Native, Ink, Vue, Svelte, Solid, and Angular. Callback effects are host-owned; a synchronous callback failure leaves the component active so the user can retry. Unknown keys, invalid props, undeclared events, unsafe URLs, and non-JSON values are inert. URLs are limited to relative paths or HTTP(S). Collections and text are bounded at the schema boundary. See the [generated parity report](./catalog.generated.md) for renderer support. Regenerate it with `pnpm catalog:report`. ## Custom components Use `defineComponentManifest` with your own stable key and strict props schema. Optional metadata follows the same shape as the standard catalog. A custom native renderer belongs in each framework package or host slot; never serialize framework components into `ChatDefinition`. # ChoiceList `ChoiceList` is the first schema-backed application component. It presents one prompt and 1–20 choices, then emits the same `agentskit.chat.component` v1 selection event on every renderer. ## Typed actions A choice may declare `{ action: { name: 'send-email', input: { to: 'ada@example.com' } } }`. The name resolves only through `ChatConfig.tools`. The tool must be executable, opt into `requiresConfirmation`, and pass the configured AgentsKit argument validator. Selection proposes a canonical upstream tool call; it never executes the action. React, React Native, and Ink render the official AgentsKit confirmation component. For manual coordination, `createActionConfirmation` returns an expiring handle bound to session, action, validated input snapshot, and tool-call id. Approval and rejection are idempotent. The handle is correlation metadata, not authentication. ## Declare the manifest ```ts import { ChoiceListComponent, defineComponentManifest } from '@agentskit/chat' export const components = defineComponentManifest([ChoiceListComponent]) ``` Only declared keys can render. `resolveComponentFrame(frame, components)` validates the envelope and the registered props schema. A failure returns `{ ok: false, diagnostic }`; it does not render or invoke an interaction callback. ## Render frame ```json { "protocol": "agentskit.chat.component", "version": 1, "type": "render", "componentKey": "choice-list", "instanceId": "destination-choice", "props": { "prompt": "Where should we go?", "choices": [ { "id": "docs", "label": "Documentation", "description": "Read the component guide." }, { "id": "demo", "label": "Demo" } ] }, "fallback": { "kind": "choice-list", "summary": "Choose Documentation or Demo." } } ``` The frame may come from an agent response or a deterministic route. Treat both as untrusted input and resolve them before choosing a native renderer. Unsupported consumers display the fallback summary. A choice may declare `action` for a policy-controlled side effect. Deterministic answer choices remain v1-compatible: they display the exact alias in `description`, while the host-provided `choiceSubmission` callback delegates authorization to the deterministic adapter that projected the frame. Every first-party AgentChat renderer submits the returned visible alias through the existing AgentsKit chat controller when no action exists. Authorization is exact and single-use, and the same callback event is emitted for observability. Model-authored or replayed generic choices cannot gain an automatic submission path by imitating an instance ID. ## Native renderers - React: `` uses a labelled fieldset and native buttons. - Vue: `` uses the same labelled fieldset/native buttons and is replaceable through the typed `choiceList` scoped slot. - Svelte: `` uses a labelled fieldset/native buttons and is replaceable through the typed `choiceList` snippet. - Solid: `` uses a labelled fieldset/native buttons and is replaceable through the typed `choiceList` render prop. - Angular: `` uses a labelled fieldset/native buttons and is replaceable through the named `#choiceList` content template. - React Native: `` exposes native accessibility roles and labels. - Ink: `` supports Up/Down or a number followed by Enter. Each `onSelect` callback receives `{ protocol, version, type: 'select', componentKey, instanceId, choiceId }`. A choice id not declared in the validated frame is rejected. All application shells detect component frames in assistant messages (including deterministic route responses), resolve them against `definition.components`, and report the same selection event through `onComponentSelect`. A valid but unregistered component renders only its shared semantic fallback. ## Security Never select a renderer from an unchecked model string. Resolve against the closed manifest first. Component selection expresses user intent only; it does not execute an action. Authorization, confirmation, and side effects belong to the action-policy issues. # Renderer conformance matrix Generated by `pnpm conformance:report`. Do not edit manually. | Requirement | Scope | react | react-native | vue | svelte | solid | angular | ink | |---|---|---|---|---|---|---|---|---| | Shared turn protocol | universal | yes | yes | yes | yes | yes | yes | yes | | Standard component catalog | universal | yes | yes | yes | yes | yes | yes | yes | | Safe diagnostics and fallback | universal | yes | yes | yes | yes | yes | yes | yes | | Lifecycle controls | universal | yes | yes | yes | yes | yes | yes | yes | | DOM roles, names, and announcements | dom | yes | n/a | yes | yes | yes | yes | n/a | | DOM keyboard interaction | dom | yes | n/a | yes | yes | yes | yes | n/a | | Native roles, names, and state | native-mobile | n/a | yes | n/a | n/a | n/a | n/a | n/a | | Native touch interaction | native-mobile | n/a | yes | n/a | n/a | n/a | n/a | n/a | | Readable terminal output | terminal | n/a | n/a | n/a | n/a | n/a | n/a | yes | | Terminal keyboard flow | terminal | n/a | n/a | n/a | n/a | n/a | n/a | yes | | Escape cancellation | terminal | n/a | n/a | n/a | n/a | n/a | n/a | yes | | Graceful process exit | terminal | n/a | n/a | n/a | n/a | n/a | n/a | yes | # Release conformance AgentsKit Chat publishes one compatibility promise across React, React Native, Vue, Svelte, Solid, Angular, and Ink. The versioned source is [`conformance/manifest.json`](../../conformance/manifest.json); the readable matrix is generated from it. ## What the gate proves - Universal evidence covers the shared turn protocol, all standard components and events, safe diagnostics, and lifecycle controls. - DOM evidence covers roles, accessible names, announcements, and keyboard interaction. - React Native evidence covers native accessibility roles, labels, state, and touch interaction. - Ink evidence covers readable output, keyboard flows, Escape cancellation, and graceful exit in a PTY. Run `pnpm conformance:gate` to validate declarations and committed evidence. Run `pnpm test:conformance` to verify that deliberately broken fixtures report stable renderer, component, event, and requirement diagnostics. Run `pnpm conformance:report` after an intentional manifest change. ## Contributor workflow When adding or changing a renderer, component, event, or platform behavior: 1. Implement the behavior through the native AgentsKit binding; do not copy an upstream controller or protocol. 2. Add executable evidence in the owning renderer or example suite. 3. Update the renderer's `catalog-support.json` and `conformance/manifest.json`. 4. Regenerate the matrix and run the conformance, renderer, E2E, and PTY checks that apply. The gate fails closed if its manifest, support declaration, evidence path, generated report, or exception is invalid. ## Exception policy An exception is a temporary release decision, not an implicit unsupported state. It must name one renderer and requirement, explain the reason, identify an owner, and have an ISO date expiry. Expired exceptions fail the gate. Permanent differences belong in a platform-specific requirement or an ADR. See [ADR-0025](../architecture/adrs/0025-release-conformance-evidence-gate.md) for the architectural boundary and [the generated matrix](./matrix.generated.md) for current evidence. # Deterministic routes and conversation state Add a `conversation` to the same definition already consumed by every renderer: ```ts import { commandRoute, defineChat } from '@agentskit/chat' const supportChat = defineChat({ id: 'support', chat: { adapter }, conversation: { initial: 'idle', states: { idle: { on: { start: 'collecting' }, actions: ['start'] }, collecting: { on: { finish: 'complete' }, actions: ['cancel'] }, complete: { actions: ['restart'] }, }, routes: [ commandRoute({ id: 'start', command: '/start', event: 'start', response: () => 'What is your name?' }), commandRoute({ id: 'finish', command: '/name Ada', event: 'finish', states: ['collecting'], response: () => 'Welcome, Ada.' }), ], }, }) ``` Routes run in declaration order before the model adapter. A route can run only when its event is allowed by the current state. Unknown input and state-disallowed routes delegate to the original AgentsKit adapter unchanged. The session records deterministic decisions against upstream user-message identities. Retry and regenerate replay the same deterministic response; the next dispatch after an edit or truncation recomputes progress from retained history and removes stale decisions. Editing without regeneration does not itself transition the application machine. A failing matcher or response becomes a controller-owned error stream and does not advance state. Trace callbacks are observability-only and never block a turn. Conversation definitions compile to the published `@agentskit/statechart` contract. AgentsKit owns reusable definition validation, immutable state instances, and transition execution; AgentsKit Chat owns input routes, adapter fallback, trace projection, application actions, and session decision replay. Existing chat definitions and session snapshots do not expose a second statechart API. `createChatSession(definition)` exposes the derived `chat` configuration and `getConversationSnapshot()`. The snapshot contains only the current state and its allowed events/actions. Create one session per user or mounted renderer; the built-in renderers do this automatically. Use `onTrace` to observe decisions. Trace kinds distinguish `deterministic`, `agentic`, `repaired`, and `fallback` turns without copying prompt content into telemetry. ## Route identity context Route response callbacks receive `(input, context)`. Use `context.messageId` when a rendered application component needs a deterministic identity that is unique per turn; use `context.sessionId` only as a fallback when no user message exists. Never use a process-global counter for replayed route output. # Deployment modes The shared `ChatDefinition` is unchanged across deployment modes. Only the adapter, trusted host context, storage, and native renderer vary. ## Web-standard server — recommended Mount `createChatHandler` from `@agentskit/chat/server` in a Node, serverless, or edge route that supports Web `Request`, `Response`, `ReadableStream`, and `AbortSignal`. Authenticate before resolving a definition or parsing untrusted input. Keep provider keys, authorization, tenant context, durable storage, and audit sinks on the server. ### Host adapter recipes `createChatHandler` already speaks the Web Fetch API. Host frameworks only need to forward the request and return the response. #### Next.js App Router ```ts // app/api/chat/route.ts import { createChatHandler } from '@agentskit/chat/server' import { verifyBearer } from '@/lib/auth' import { chats } from '@/lib/chats' import { sessions } from '@/lib/session-storage' const handler = createChatHandler({ authenticate: async request => { const identity = await verifyBearer(request) return identity ? { ok: true, context: identity } : { ok: false, response: new Response('Unauthorized', { status: 401 }) } }, resolveDefinition: context => chats.forTenant(context?.tenantId), sessionStorage: context => sessions.forTenant(context?.tenantId), }) export const POST = (request: Request) => handler(request) ``` #### Hono ```ts import { Hono } from 'hono' import { createChatHandler } from '@agentskit/chat/server' const chat = createChatHandler({ /* authenticate, resolveDefinition, sessionStorage */ }) const app = new Hono() app.post('/api/chat', c => chat(c.req.raw)) export default app ``` #### Express (Node 18+) ```ts import express from 'express' import { createChatHandler } from '@agentskit/chat/server' const chat = createChatHandler({ /* … */ }) const app = express() app.post('/api/chat', async (req, res) => { const disconnected = new AbortController() const abort = () => { if (!res.writableEnded) disconnected.abort() } req.once('aborted', abort) res.once('close', abort) let reader: ReadableStreamDefaultReader | undefined try { const headers = new Headers() for (const [key, value] of Object.entries(req.headers)) { if (typeof value === 'string') headers.set(key, value) else if (Array.isArray(value)) value.forEach(item => headers.append(key, item)) } const request = new Request(`http://${req.headers.host}${req.url}`, { method: 'POST', headers, body: req, duplex: 'half', signal: disconnected.signal, } as RequestInit) const response = await chat(request) reader = response.body?.getReader() res.status(response.status) response.headers.forEach((value, key) => res.setHeader(key, value)) if (!reader) return res.end() while (true) { const result = await reader.read() if (result.done) break res.write(result.value) } res.end() } finally { if (disconnected.signal.aborted) await reader?.cancel().catch(() => undefined) req.removeListener('aborted', abort) res.removeListener('close', abort) } }) ``` #### Cloudflare Worker ```ts import { createChatHandler } from '@agentskit/chat/server' const chat = createChatHandler({ /* … */ }) export default { fetch(request: Request): Promise { const url = new URL(request.url) if (request.method === 'POST' && url.pathname === '/api/chat') return chat(request) return new Response('Not found', { status: 404 }) }, } ``` Mount `createAskServiceHandler` beside it when deterministic misses need cited semantic retrieval. Hosted routes and self-hosted Node bridges import the same factory; only the thin HTTP bridge varies. See the [Ask backend deployment guide](./backend.md). ## Direct trusted runtime A server process, desktop main process, or controlled terminal may inject an AgentsKit adapter directly into `defineChat`. Browser and mobile clients must not receive provider secrets. Use a server adapter or the shared Ask adapter for those clients. ## Browser and SSR hosts React, Vue, Svelte, Solid, and Angular use their native renderer package. Keep the definition in a framework-neutral module. SSR may render the shell, but a chat session starts only where the selected AgentsKit binding and transport are available. Persist messages through an upstream `ChatMemory`; AgentsKit Chat session storage contains application metadata, not a second message history. DOM parity demos for React, Vue, Svelte, and Solid live under `apps/example-*` and share `@agentskit/chat-example-shared`. ## React Native / Expo Use `@agentskit/chat/react-native` and call a trusted backend. Storage, navigation, safe-area layout, and platform permissions stay host-owned. The release gate exercises native-mobile accessibility contracts and Expo web/iOS production bundles. ## Ink / terminal Use `@agentskit/chat/ink` in an interactive TTY. Unsupported visual components render their validated semantic fallback. The PTY gate covers keyboard submit, choice, confirmation, lifecycle, stop, focus, and exit behavior. ## Deterministic and degraded operation Deterministic routes and verified local answer artifacts may resolve without a model. Unknown input delegates to the injected AgentsKit adapter when policy allows. Configure explicit offline/escalation behavior; never silently invent an answer when a required backend is unavailable. See [server details](./server.md), [Ask backend](./backend.md), [sessions](./sessions.md), and the [security policy](../SECURITY.md). # Replayable application traces Use the upstream recorder for model traffic and the Chat capture for application decisions: ```ts import { createRecordingAdapter, createReplayAdapter } from '@agentskit/eval/replay' import { captureActionPolicyTrace, captureActionTrace, captureTurnTrace, createReplayFixture, createTraceCapture, serializeReplayFixture } from '@agentskit/chat/devtools' const recording = createRecordingAdapter(adapter) const capture = createTraceCapture({ redactFields: ['token', 'password', 'authorization'] }) // Wire these into conversation.onTrace and capability-policy onTrace. const onTurnTrace = (trace) => captureTurnTrace(capture, trace) const onPolicyTrace = (trace) => captureActionPolicyTrace(capture, trace) const onActionChange = (confirmation) => captureActionTrace(capture, confirmation) const route = capture.append({ category: 'route', detail: { routeId: 'support', fromState: 'idle', toState: 'open' } }) capture.append({ category: 'policy', parentId: route.id, detail: { action: 'open-ticket', decision: 'allow' } }) const committedJson = serializeReplayFixture(createReplayFixture(recording.cassette, capture.snapshot())) const offlineAdapter = createReplayAdapter(recording.cassette) ``` Store `committedJson` as a normal JSON fixture. `parseReplayFixture` validates the application envelope, causal order, and upstream cassette version before replay. ## Privacy Configured redaction matches object field names case-insensitively at every nested depth and replaces values with `[REDACTED]`. It applies only to application trace details. Upstream cassettes contain adapter requests and streamed chunks, which may include prompts, tool arguments, or model output; sanitize or protect them according to your own retention policy before committing. ## Trace schema and limits | Field | Contract | |---|---| | `id` / `parentId` | Portable identifiers; a parent must precede its child | | `sequence` | Contiguous, zero-based causal order | | `at` | ISO-8601 timestamp | | `category` | `route`, `agent`, `repair`, `fallback`, `policy`, `action`, or `lifecycle` | | `detail` | JSON object, maximum 64 KiB UTF-8 | A fixture contains at most 10,000 traces and `parseReplayFixture` refuses JSON larger than 16 MiB before parsing. ## Renderer parity `compareRendererOutcomes` takes at least two renderer results. Each result identifies semantic outcomes by the composite `turnId` and `kind`, so one turn may contain text, component, and action outcomes. The first renderer is the baseline; missing or different outcomes become stable mismatch entries. Object-key order is ignored. Visual layout is intentionally outside this contract. # Ecosystem adoption ledger `ecosystem-adoption.json` is the machine-readable source of truth for the post-v0 convergence program in [PRD #99](https://github.com/AgentsKit-io/agentskit-chat/issues/99). It records what is certified today and what remains incomplete; it is not a promotional wishlist. `frameworkVersion` is the exact certification baseline proven by the consumer evidence, not an alias for the repository's next release version. It advances only after those consumers adopt and prove a newer published version. The workspace and release manifest versions must still match each other. Validate it locally with: ```bash pnpm ecosystem:adoption:check pnpm test:ecosystem-adoption ``` ## Classifications - **Product chat:** an end-user surface that composes application behavior, presentation, and a runtime or deterministic answer source. It must use the consolidated AgentsKit Chat package to become certified. - **Infrastructure consumer:** produces or validates a framework artifact but does not host a chat UI. It uses the relevant consolidated subpath. - **Low-level binding example:** intentionally teaches an AgentsKit binding directly. It is excluded from product-chat adoption claims and must be labelled as an example rather than a framework host. ## Certification states | Status | Meaning | |---|---| | `certified` | Exact framework version, no legacy packages, and required CI/production evidence pass. | | `migrating` | An active consumer still uses legacy packages or has an incomplete migration. | | `deployment-required` | Code and CI pass, but the required production property is absent. | | `inventory-required` | A private or complex consumer needs an approved boundary audit before migration. | | `excluded` | A low-level example is intentionally outside the product-chat total. | The current baseline certifies four of six declared product chats after the Doc Bridge production host adopted the consolidated package. The Chat portal still requires its canonical deployment and AKOS still requires bounded private certification; schema validity must never be confused with full convergence. ## Evidence rules Public `pass` evidence includes an HTTPS URL. Repository CI does not fetch those URLs, keeping ordinary builds deterministic. The final cross-repository certification resolves them, repeats production interactions, and updates the audited baseline through review. Private consumers use only the fixed `private-attestation` envelope. Public records may state aggregate pass, pending, and certification status; they may not contain private URLs, source excerpts, product behavior, identifiers, business rules, topology, or data. ## Updating a consumer 1. Complete the migration or deployment in the owning repository. 2. Run its frozen install, lint, typecheck, tests, build, import guard, and surface-appropriate smoke test. 3. Update the exact version, imports, legacy list, status, evidence, and `auditedAt` date in one reviewed change. 4. Run the adoption gate and the full AgentsKit Chat documentation/release gates. 5. Do not mark a consumer `certified` until every schema-derived requirement is satisfied. Adding a new declared consumer changes the certification scope and therefore requires an ADR review rather than an unreviewed JSON append. # Registry and Playbook dogfood migration Issue [#27](https://github.com/AgentsKit-io/agentskit-chat/issues/27) moves the existing Playbook Ask surface to AgentsKit Chat and adds the same shared surface to the production Registry host. The Docs host was migrated in the same round because it used another copy of the Ask protocol, transport, and memory logic. ## Ownership correction The issue originally named an Astro Registry host. Registry RFC 0002 and `AgentsKit-io/agentskit-registry@5d4fc56` superseded that premise: the Registry repository is a data-only corpus and `registry.agentskit.io` is the Next app at `AgentsKit-io/agentskit/apps/registry`. The migration therefore targets that live app and deletes the unbuilt `src/components/AskWidget.astro` orphan from the data repository. Astro is not reintroduced, so the Registry change is a new surface in the live host rather than a migration of a live Astro application. ## Shared boundary The live hosts first moved from immutable `v0.1.0-alpha.2` tarballs to the exact stable npm `0.1.0` graph for `@agentskit/chat`, `@agentskit/chat-protocol`, and `@agentskit/chat-react`. They pin one fixed stable graph per adoption round and compose supported `@agentskit/core` and `@agentskit/react` releases. Stable release assets include SHA-256 checksums and pass clean-room ESM, CJS, Vite, tarball, provenance, and renderer verification in the release workflow. AgentsKit owns controller lifecycle, messages, cancellation, retry, edit, regenerate, and the web-storage memory primitive. AgentsKit Chat Protocol owns the bounded Ask v1 event schemas and decoder. AgentsKit Chat owns `defineChat`, the Ask streaming adapter, safe citation projection, session-memory migration, ordered assistant content, the standard component manifest, `source-list`, and `AgentChat`. Each host owns only: - endpoint and `registry` or `playbook` corpus configuration; - an optional presentation projector for host-specific tools (Docs only); - canonical and legacy storage-key identities; - native brand shell, linked prose, composer, loading copy, citations, and CTA. Unknown events and tools are inert. Unsafe citation schemes are rejected, source counts/titles/fallbacks are bounded, malformed or oversized records are discarded safely, ordered content has cumulative limits, and connection setup has a deadline composed with AgentsKit cancellation without timing out a valid long-running response stream. The reusable storage seam was fixed first in AgentsKit [#1191](https://github.com/AgentsKit-io/agentskit/issues/1191) and [PR #1192](https://github.com/AgentsKit-io/agentskit/pull/1192), then released through [PR #1194](https://github.com/AgentsKit-io/agentskit/pull/1194) as `@agentskit/memory@0.11.0`. The shared Ask integration was tracked in AgentsKit Chat [#66](https://github.com/AgentsKit-io/agentskit-chat/issues/66), delivered by [PR #67](https://github.com/AgentsKit-io/agentskit-chat/pull/67), and released as [`v0.1.0-alpha.2`](https://github.com/AgentsKit-io/agentskit-chat/releases/tag/v0.1.0-alpha.2). ## Removed duplication The former Registry Astro widget and Playbook React widget independently owned message arrays, streaming flags, abort controllers, NDJSON loops, `sessionStorage`, rendering, scrolling, errors, and stop behavior. After parity, the orphan Astro file and the Playbook reducer/loop were removed. The live Registry, Docs, and Playbook shells now delegate those responsibilities to the framework/runtime. The data-only Registry repository also removed its orphan Astro widget. ## Parity evidence Protocol and integration conformance now live once in the shared packages: the release ran 92 protocol tests and 108 chat tests, plus all seven renderer builds. The consumer hosts retain strict typecheck and production-build evidence: Playbook generated 266 pages, Registry generated 389 routes, and Docs generated 1,179 routes. Frozen lockfile installs prove that the hosts consume the published npm graph rather than workspace substitutions. Browser evidence covers 375, 768, 1280, and 1440 px. Panels remain inside the layout viewport, all measured panel controls are at least 44 by 44 px, the composer is labelled, send enables from keyboard input, and open/close/clear semantics remain available. Opening focuses the composer, Escape closes the dialog, and closing restores focus to the launcher. Mobile uses physical `inset` edges instead of `100vw`/`100dvh`, avoiding scrollbar-dependent fixed-panel overflow. Host records: - Docs migration: [AgentsKit #1184](https://github.com/AgentsKit-io/agentskit/pull/1184) - Registry and Docs production adoption: [AgentsKit #1190](https://github.com/AgentsKit-io/agentskit/pull/1190) - Playbook adoption: [Agents Playbook #12](https://github.com/AgentsKit-io/agents-playbook/pull/12) - Stable Docs and Registry npm adoption: [AgentsKit #1222](https://github.com/AgentsKit-io/agentskit/pull/1222) - Stable Playbook npm adoption: [Agents Playbook #15](https://github.com/AgentsKit-io/agents-playbook/pull/15) - Data-only Registry cleanup: [AgentsKit Registry #75](https://github.com/AgentsKit-io/agentskit-registry/pull/75) - Host documentation: `AgentsKit-io/agentskit/apps/registry/README.md` and `AgentsKit-io/agents-playbook/content/docs/agentskit-chat.md` Private-consumer validation remains confidential. Its public release evidence is limited to synthetic framework contracts, the seven-renderer conformance matrix, reference applications, and privacy review; no private behavior or business rule is part of this record. ## No-reimplementation check Inspected AgentsKit `@agentskit/core@1.12.3`, `@agentskit/react@0.7.4`, and the memory package before editing the hosts. The missing reusable web-storage seam was implemented and released in AgentsKit itself. The duplicated Ask schemas, decoder, transport, citation policy, and migration logic were moved to AgentsKit Chat Protocol and AgentsKit Chat before host adoption. No controller, reducer, memory engine, lifecycle, renderer, stream decoder, or component protocol remains copied in a host. A host projector is presentation-only and must consume the shared validated event type. # DOM renderer parity examples React, Vue, Svelte, and Solid each host the **same** framework-neutral definitions from `@agentskit/chat-example-shared`. App packages own only native bootstrapping, theming chrome, and query-parameter routing. | App | Package | Dev | Proof | |-----|---------|-----|-------| | React | `@agentskit/chat-example-react` | `pnpm --filter @agentskit/chat-example-react dev` | Playwright project `react` | | Vue | `@agentskit/chat-example-vue` | `pnpm --filter @agentskit/chat-example-vue dev` | Playwright project `vue` | | Svelte | `@agentskit/chat-example-svelte` | `pnpm --filter @agentskit/chat-example-svelte dev` | package + app build | | Solid | `@agentskit/chat-example-solid` | `pnpm --filter @agentskit/chat-example-solid dev` | package + app build | Query parameters (all DOM examples): | Query | Reference | |-------|-----------| | _(default)_ | [Support](./support-reference.md) | | `?reference=onboarding` | [Onboarding](./onboarding-reference.md) | | `?reference=operations` | [Operations](./operations-reference.md) | | `?reference=operations-unauthorized` | Unauthorized operations denial | | `?reference=rag` | [Cited RAG](./rag-reference.md) | Angular remains covered by the package conformance and agent-chat suite under `packages/angular`. A full Angular example app is deferred to keep the monorepo build free of an application-level Angular CLI graph; the shell package is still part of the release matrix. Ink and React Native keep their dedicated proof apps (`@agentskit/chat-example-ink`, `@agentskit/chat-example-react-native`). # Deterministic onboarding reference This reference combines deterministic collection, injected recommendations, revision, and policy-protected completion without allowing model output or component payloads to mutate conversation state. Open React or React Native with `?reference=onboarding`; run Ink with `AK_EXAMPLE=onboarding`. ## State diagram ```text welcome --/onboarding--> collecting --valid form + /recommend--> review review --Revise answers + /revise--> collecting review --Use this setup + /accept--> confirming confirming --confirmed tool + /done--> complete ``` Unknown input stays agentic in the current state. Component interactions only record validated intent; the next command must satisfy the active state and guard. The recommender receives no session mutator. ## Component and accessibility map | Step | Component | Native evidence | |---|---|---| | Collection/revision | `Form` | labelled HTML controls, React Native radio/text controls, Ink keyboard fields | | Recommendation | `ChoiceList` | semantic buttons/pressables and arrow/number terminal selection | | Completion | actionable `ChoiceList` | upstream AgentsKit confirmation on every renderer | Frames use upstream message identity for deterministic per-turn IDs. Form values pass the closed catalog validator before accepting a supported role and non-empty goal. React uses native form semantics and focus styles; React Native exposes labelled radio/text controls; the Ink PTY suite completes without a pointer. ## Transcript ```text ❯ /onboarding Tell us how you work Primary role *: Engineering First goal *: Automate handoffs ❯ /recommend engineering starter 1. Use this setup — A guided workspace for Automate handoffs. 2. Revise answers ❯ /accept Ready to create your workspace? 1. Complete onboarding Allow complete-onboarding? 1. Yes 2. Yes, for session 3. No Onboarding confirmed. ❯ /done Onboarding complete. Your guided workspace is ready. ``` ## Ownership and upstream adoption `apps/example-shared` owns the coordinator, guarded routes, and recommendation domain. AgentsKit Chat owns sessions, Form/ChoiceList intents, policy composition, and per-turn identity. AgentsKit owns controller lifecycle, adapters, messages, tools, validation, confirmation, and official bindings. Create exactly one application factory result per authenticated session and inject that session's trusted context. The returned definition, session, and intent handlers form one unit; do not share the definition across sessions. Durable hosts must persist domain answers alongside the AgentsKit Chat session snapshot. Inspected AgentsKit revision `4d66eb192d636b53d0c7bec39894250dc71cde5f` and `@agentskit/core@1.12.2`. No state-machine primitive exists upstream; no source or behavior was copied, and no generic upstream gap blocks this reference. # Policy-protected operations reference Open React or React Native with `?reference=operations`, or run Ink with `AK_EXAMPLE=operations`. Type `/operations` to read `checkout-api` status or propose a restart. Both actions use separate trusted capabilities and explicit operator confirmation; restart additionally uses a trusted-session + call-ID idempotency key because it mutates state. ## Threat model | Threat | Guard | |---|---| | Prompt/component forges capability | capabilities come only from the injected host context | | Read permission implies mutation | `operations.read` and `operations.restart` are independent policy requirements | | Malformed/extra arguments | strict JSON Schema plus upstream AJV validation | | Stale, duplicate, rejected, or cross-session approval | session-bound, expiring, idempotent `createActionConfirmation` record | | Secret leaks through audit | trace capture records IDs/decisions/result metadata and redacts configured field names | Each factory result binds one trusted context, service, policy, session, and trace capture. Do not share it across authenticated sessions. ## Audit/replay example ```json [ { "category": "policy", "detail": { "action": "restart-operation", "phase": "propose", "decision": "allow" } }, { "category": "action", "detail": { "action": "restart-operation", "toolCallId": "app-1", "status": "pending" } }, { "category": "action", "detail": { "action": "restart-operation", "toolCallId": "app-1", "status": "approving" } }, { "category": "policy", "detail": { "action": "restart-operation", "phase": "execute", "decision": "allow" } }, { "category": "action", "detail": { "action": "restart-operation", "phase": "confirmed-execution", "toolCallId": "app-1", "operationId": "checkout-api" } }, { "category": "lifecycle", "detail": { "action": "restart-operation", "phase": "result", "toolCallId": "app-1", "status": "healthy", "revision": 2 } }, { "category": "action", "detail": { "action": "restart-operation", "toolCallId": "app-1", "status": "approved" } } ] ``` The example uses `@agentskit/chat/devtools` trace capture; it does not implement another ledger. Production hosts should append snapshots to a durable, access-controlled audit sink, preserve canonical tool-call correlation IDs, apply retention policy, and never store credentials or raw service errors. ## Upstream adoption Inspected AgentsKit revision `4d66eb192d636b53d0c7bec39894250dc71cde5f`: `packages/core/src/chat.ts`, `packages/core/src/tool-proposal-internal.ts`, `packages/core/src/tool-authorization-internal.ts`, and the confirmation components exported by `packages/react`, `packages/react-native`, and `packages/ink`. Published contracts from `@agentskit/core@1.12.2` are consumed directly. AgentsKit Chat adds only capability composition, application choices, and safe application traces. No upstream source or behavior is copied. # Cited RAG reference Open React or React Native with `?reference=rag`, or run Ink with `AK_EXAMPLE=rag`. Ask a question to retrieve a grounded answer rendered through the portable `SourceList` component. ## Architecture ```text question → AgentsKit RAG.retrieve → retrieved documents → provider-neutral answer formatter → validated SourceList frame → native renderer / text fallback ``` The application does not implement chunking, embeddings, vector search, thresholds, or reranking. Those remain in `@agentskit/rag`. The bundled deterministic embed/store are fixture adapters only; production hosts inject provider embeddings and a `VectorMemory` backend. The answer seam accepts a string, promise, or async iterable; the validated component frame is emitted through bounded stream chunks. ## Indexing and provider configuration ```ts import { createRAG } from '@agentskit/rag' import { createRagApplication } from '@agentskit/chat-example-shared' const rag = createRAG({ embed: providerEmbed, store: vectorMemory, topK: 5, threshold: 0.7 }) await rag.ingest([{ id: 'guide', content: markdown, source: 'https://docs.example.dev/guide', metadata: { title: 'Guide', url: 'https://docs.example.dev/guide' } }]) export const definition = createRagApplication({ rag }) ``` Keep embedding/vector credentials server-side. Apply deadlines and cancellation in provider/store adapters. Ingest canonical HTTPS or relative source URLs; documents with unsafe source schemes are dropped before protocol encoding. Bound titles, snippets, source counts, and answer text at the SourceList schema boundary. Source clicks cross the protocol as validated source ids. Each trusted host resolves the id against the latest validated frame before using its native navigation API; unrecognized, stale, or URL-less sources are ignored. ## Empty and failure behavior No retrieval result emits `No grounded sources were found` and never fabricates a citation. Invalid source metadata is dropped; if every source is unsafe, the response states that no safe grounded sources were found. Retrieval failures become an opaque upstream stream error without exposing provider internals. ## Upstream adoption Inspected AgentsKit revisions `4d66eb192d636b53d0c7bec39894250dc71cde5f` and `edb77757d2f2ca095733392657cafd8b7dd59a78`, `@agentskit/rag@0.4.12`, and `packages/rag/src/index.ts`, `rag.ts`, `types.ts`, `chunker.ts`, and `loaders.ts`. The reference consumes `createRAG`, `RAG`, `RetrievedDocument`, and `VectorMemory` directly. Dogfooding found and fixed the optional S3 peer browser-bundle gap at the source through [AgentsKit #1180](https://github.com/AgentsKit-io/agentskit/issues/1180), [PR #1181](https://github.com/AgentsKit-io/agentskit/pull/1181), and release [PR #1182](https://github.com/AgentsKit-io/agentskit/pull/1182). AgentsKit Chat adds only validated SourceList presentation, safe interaction resolution, and native/fallback evidence; no retrieval primitive is copied. # Support reference application The support reference demonstrates a production-shaped path through one unchanged definition on React, React Native, and Ink: ```text question → deterministic/provider answer /support → semantic ChoiceList Open support ticket → typed tool proposal trusted host policy → confirmation Approve → injected TicketService ``` The model cannot create tickets autonomously. It can propose `create-support-ticket`, but the action is registered with a strict JSON Schema, validated by `createAjvValidator`, authorized from host-owned context, and marked `requiresConfirmation`. Execution delegates to the injected service only after the upstream AgentsKit confirmation lifecycle approves it. ## Run locally ```bash pnpm install pnpm --filter @agentskit/chat-example-react dev pnpm --filter @agentskit/chat-example-react-native dev pnpm --filter @agentskit/chat-example-ink build pnpm --filter @agentskit/chat-example-ink start ``` Ask any question to exercise deterministic test mode. Type `/support`, select **Open support ticket**, then approve. The bundled in-memory service returns `SUP-1`. ## Deterministic tests ```bash pnpm --filter @agentskit/chat-example-shared test pnpm test:e2e pnpm test:pty ``` The shared unit suite injects a spy `TicketService` and proves it remains untouched before confirmation and after rejection. Browser and PTY suites complete the same flow through native controls. ## Real-provider mode `createSupportApplication` accepts any published AgentsKit `AdapterFactory`: ```ts import { createSupportApplication } from '@agentskit/chat-example-shared' export const support = createSupportApplication({ context: authenticatedRequestContext, adapter: providerAdapter, ticketService: productionTicketService, }) ``` Construct `providerAdapter` with the supported AgentsKit provider package used by your host. Keep credentials outside the definition. Create one definition and `ChatSession` per authenticated session so stable host context and policy session identity stay bound; never derive identity or capabilities from a prompt, component frame, or tool arguments. ## Persistence and deployment For a deployed application, combine the definition with `createChatHandler` from `@agentskit/chat/server`, a durable `SessionStorage`, and an upstream `ChatMemory` adapter. The application session stores only AgentsKit Chat metadata; canonical messages remain in upstream memory. Replace the demo ticket service with a durable, transactional implementation. If retry-safe ticket creation is required, the host integration must supply and persist an application-specific idempotency key; this simplified service does not expose AgentsKit's internal tool-call identity. Deploy the Web-standard handler behind host authentication. Forward request cancellation and deadlines, keep provider/ticket credentials server-side, and retain policy/action traces according to your audit requirements. ## Native evidence - React: responsive support workspace with semantic buttons, alerts, focus rings, and lifecycle controls. - React Native: safe-area layout, native accessibility roles, touch controls, and Expo web conformance. - Ink: keyboard choice navigation, numbered confirmation, Escape cancellation, and Ctrl+C shutdown. Terminal transcript: ```text ❯ /support Would you like a human support specialist to follow up? 1. Open support ticket 2. Keep chatting Allow create-support-ticket? 1. Yes 2. Yes, for session 3. No create-support-ticket · complete Ticket SUP-1 created for follow-up. ``` ## Ownership walkthrough `apps/example-shared` owns support-domain composition and injection seams. Renderer apps own presentation only. `@agentskit/chat` owns routes, policy composition, confirmation metadata, components, and sessions. AgentsKit owns controller lifecycle, typed tools, argument validation seam, confirmation execution, framework bindings, and memory. # Fumadocs framework dogfood handoff ## Ownership `apps/docs` is the public Fumadocs portal and the complete React dogfood host for AgentsKit Chat. It compiles the repository's root `docs/` directory in place; do not create a second prose corpus inside the app. The host may compose public framework contracts, configure deployment-specific adapters, and style renderer slots. It must not implement model execution, semantic retrieval, memory, controller lifecycle, or protocol validation. Generic gaps belong upstream in AgentsKit. ## Boundaries - `lib/knowledge.ts` is a small, bounded public artifact with integrity verification. Exact matching stays in `@agentskit/chat`. - `lib/chat-definition.ts` is the single definition rendered by the published React binding. Other clients continue to consume the same framework-neutral definition contract. - `lib/ask-handler.ts` is the host composition seam around `createAskServiceHandler`; retrieval and generation remain injected AgentsKit adapters. - `/api/ask` fails closed unless the deployment explicitly configures a hosted endpoint or injects self-hosted adapters. - `/api/search` is Fumadocs navigation search only. It is never a semantic Ask retriever. - `/raw`, `/llms.txt`, and `/llms-full.txt` expose only the canonical public corpus. The maturity claim is governed by Proposed ADR-0027 and needs HITL approval before it becomes Accepted. ## Checks ```bash pnpm --filter @agentskit/chat-docs lint pnpm --filter @agentskit/chat-docs test pnpm --filter @agentskit/chat-docs build pnpm --filter @agentskit/chat-docs test:e2e pnpm docs:bridge:index pnpm docs:bridge:gate ``` # Ink example handoff `apps/example-ink` is the executable terminal proof for unchanged support, onboarding, protected operations, and cited RAG definitions. It owns only Ink process startup and PTY verification. The PTY suite must prove the confirmed ticket flow, deterministic response output, Escape cancellation without process termination, and graceful Ctrl+C exit. ```bash pnpm --filter @agentskit/chat-example-ink lint pnpm --filter @agentskit/chat-example-ink test:pty ``` # React Native example Expo dogfood app for unchanged support, onboarding, protected operations, and cited RAG definitions. It proves native lifecycle, forms, policy denial, and source presentation. App-specific native dependencies live here. # React example handoff ## Ownership `apps/example-react` hosts shared support, onboarding, protected operations, and cited RAG references selected by query parameter. Domain behavior stays in `example-shared`. ## Checks ```bash pnpm --filter @agentskit/chat-example-react build pnpm test:e2e ``` # Shared example definition Owns support, onboarding, protected operations, and cited RAG definitions used unchanged by React, React Native, and Ink, plus injectable domain, retrieval, and trusted-host seams. Keep this package free of DOM, React, Expo, and React Native imports. It is example infrastructure, not a new framework runtime abstraction. # Solid example handoff ## Ownership `apps/example-solid` hosts shared support, onboarding, protected operations, and cited RAG references selected by query parameter. Domain behavior stays in `example-shared`. This package owns only Solid bootstrapping and query routing. ## Checks ```bash pnpm --filter @agentskit/chat-example-solid lint pnpm --filter @agentskit/chat-example-solid build ``` # Svelte example handoff ## Ownership `apps/example-svelte` hosts shared support, onboarding, protected operations, and cited RAG references selected by query parameter. Domain behavior stays in `example-shared`. This package owns only Svelte 5 bootstrapping and query routing. ## Checks ```bash pnpm --filter @agentskit/chat-example-svelte lint pnpm --filter @agentskit/chat-example-svelte build ``` # Vue example handoff ## Ownership `apps/example-vue` hosts shared support, onboarding, protected operations, and cited RAG references selected by query parameter. Domain behavior stays in `example-shared`. This package owns only Vue bootstrapping and query routing. ## Read first - [`../../examples/dom-renderer-parity.md`](../../examples/dom-renderer-parity.md) - [`../../examples/support-reference.md`](../../examples/support-reference.md) - [`../packages/vue.md`](../packages/vue.md) ## Checks ```bash pnpm --filter @agentskit/chat-example-vue lint pnpm --filter @agentskit/chat-example-vue build pnpm test:e2e --project=vue ``` # Architecture handoff ## Read first - Human source: [`../architecture/overview.md`](../architecture/overview.md) - Accepted decisions: [`../architecture/adrs/`](../architecture/adrs/) - Upstream-first guardrail: [`../architecture/adrs/0002-upstream-first-no-reimplementation.md`](../architecture/adrs/0002-upstream-first-no-reimplementation.md) - Current adoption matrix: [`../architecture/upstream-adoption.md`](../architecture/upstream-adoption.md) - Product requirements: [`../product/PRD.md`](../product/PRD.md) - Ecosystem convergence: [`../architecture/adrs/0030-ecosystem-product-chat-convergence.md`](../architecture/adrs/0030-ecosystem-product-chat-convergence.md) - Controlled host sessions: [`../architecture/adrs/0031-controlled-chat-is-an-application-seam.md`](../architecture/adrs/0031-controlled-chat-is-an-application-seam.md) - Adoption ledger: [`../dogfood/ecosystem-adoption.md`](../dogfood/ecosystem-adoption.md) ## Change route Use this ownership area for the universal chat definition, turn protocol, deterministic engine, action policy, session boundary, native renderer contract, theming contract, CLI architecture, and compatibility strategy. Do not place JSX, DOM types, React hooks, Vue reactivity, Svelte stores, Angular signals, React Native primitives, or Ink components in shared contracts. Before adding a primitive, inspect AgentsKit source and public exports. Reuse or compose the upstream API. If a generally useful primitive is missing, link and complete the upstream AgentsKit change before implementing the application-layer integration here. ## Required evidence - Runtime schemas for external and model-produced data. - Contract fixtures shared by every renderer. - Compatibility policy for versioned events. - ADR for new public architectural decisions. - Upstream-adoption record proving no AgentsKit primitive was copied or reimplemented. # Governance handoff ## Read first - Human source: [`../governance/issues.md`](../governance/issues.md) - Repository rules: [`../../AGENTS.md`](../../AGENTS.md) ## Issue requirements Every implementation issue is a thin, demoable vertical slice and includes: - parent PRD; - user stories; - end-to-end outcome; - acceptance criteria; - dependencies and blocked-by links; - test plan; - documentation impact; - explicit Definition of Done; - HITL or AFK classification. - upstream-adoption evidence and linked AgentsKit source changes where required. Horizontal foundation work is allowed only when it produces a verifiable contract, fixture, gate, or developer-facing outcome. # AgentsKit Chat agent index Use this index with `ak-docs query` to locate the owning documentation before changing the repository. ## Ownership - [Product](./product.md): requirements, user stories, scope, and release outcomes. - [Architecture](./architecture.md): system boundaries, contracts, modules, protocols, and ADRs. - [Governance](./governance.md): issue structure, dependencies, Definition of Done, and delivery rules. - [Stable release](./release.md): fixed package graph, provenance, compatibility, and HITL publication. - [Registry/Playbook dogfood](../dogfood/registry-playbook.md): live-host ownership correction, migration boundary, and parity evidence. - [Fumadocs framework dogfood](./apps/docs.md): canonical docs portal, deterministic public knowledge, and hosted/self-hosted Ask seam. - [Chat package](./packages/chat.md): minimal framework-neutral `defineChat` contract. - [Protocol package](./packages/protocol.md): versioned turn events, compatibility, and conformance fixtures. - [Trusted Ask backend](../backend.md): hosted/self-hosted site policy, cited retrieval, persistence, and metrics. - [React package](./packages/react.md): native React shell over `@agentskit/react`. - [Vue package](./packages/vue.md): native Vue shell over `@agentskit/vue`. - [Angular package](./packages/angular.md): standalone Angular shell over `@agentskit/angular`. - [Svelte package](./packages/svelte.md): native Svelte 5 shell over `@agentskit/svelte`. - [Solid package](./packages/solid.md): native Solid shell over `@agentskit/solid`. - [React Native package](./packages/react-native.md): native mobile shell over `@agentskit/react-native`. - [Ink package](./packages/ink.md): native terminal shell and semantic text fallback. - [CLI package](./packages/cli.md): safe project detection and application scaffolding. - [React example](./apps/example-react.md): deterministic executable proof. - [Vue example](./apps/example-vue.md): DOM parity host over the shared definition. - [Svelte example](./apps/example-svelte.md): Svelte 5 DOM parity host over the shared definition. - [Solid example](./apps/example-solid.md): Solid DOM parity host over the shared definition. - [React Native example](./apps/example-react-native.md): Expo proof using the shared definition. - [Ink example](./apps/example-ink.md): PTY proof using the shared definition. - [Shared example](./apps/example-shared.md): framework-neutral deterministic definition and fixture. ## Invariants - AgentsKit owns primitives; AgentsKit Chat owns the opinionated application layer. - Shared definitions and protocol are framework-neutral. - Renderers are native and consume the corresponding AgentsKit binding. - React, React Native, and Ink remain the original architecture-proof targets; Vue, Svelte, and Solid now host the same shared references in `apps/`. - Every render frame and action input is runtime validated. - Missing platform components degrade to declared semantic fallbacks. # Angular package handoff `packages/angular` owns only the standalone Angular application shell, content-template composition, semantic CSS mapping, specialized `ChoiceList`, and generic native `StandardComponentComponent`. It consumes `AgentskitChat`, `ChatContainerComponent`, `MessageComponent`, `InputBarComponent`, `ThinkingIndicatorComponent`, and `ToolConfirmationComponent` from the typed partial-Ivy `@agentskit/angular@0.4.6` package. Never add another controller, signal store, stream consumer, lifecycle implementation, confirmation engine, or container primitive. Each shell scopes the upstream service through its component provider. Config replacement and component destruction delegate cancellation to the upstream cleanup fixed in AgentsKit #1171/#1172. Checks: `pnpm --filter @agentskit/chat-angular lint && pnpm --filter @agentskit/chat-angular test && pnpm --filter @agentskit/chat-angular build`. # Chat package handoff ## Ownership `packages/chat` owns framework-neutral application definitions, the closed component manifest, component props validation, and the semantic fallback API from ADR-0003/ADR-0007. `ChatDefinition.chat` is the upstream `ChatConfig`; do not mirror its fields or add a controller. ## Read first - [`../../architecture/upstream-adoption.md`](../../architecture/upstream-adoption.md) - [`../../architecture/adrs/0002-upstream-first-no-reimplementation.md`](../../architecture/adrs/0002-upstream-first-no-reimplementation.md) - [`../../architecture/adrs/0003-semantic-fallback-envelope.md`](../../architecture/adrs/0003-semantic-fallback-envelope.md) - [`../../architecture/adrs/0006-session-scoped-deterministic-conversation.md`](../../architecture/adrs/0006-session-scoped-deterministic-conversation.md) - [`../../architecture/adrs/0007-closed-application-component-manifest.md`](../../architecture/adrs/0007-closed-application-component-manifest.md) - [`../../components/choice-list.md`](../../components/choice-list.md) - [`../../conversation/routes-and-state.md`](../../conversation/routes-and-state.md) - [`../../protocol/ask-service.md`](../../protocol/ask-service.md) - [`../../architecture/adrs/0022-shared-ask-service-integration.md`](../../architecture/adrs/0022-shared-ask-service-integration.md) - [`../../architecture/adrs/0023-conversation-transitions-use-agentskit-statechart.md`](../../architecture/adrs/0023-conversation-transitions-use-agentskit-statechart.md) - [`../../protocol/deterministic-answers.md`](../../protocol/deterministic-answers.md) - [`../../architecture/adrs/0024-deterministic-answer-plane.md`](../../architecture/adrs/0024-deterministic-answer-plane.md) ## Conversation boundary `createChatSession` may wrap only the upstream adapter boundary. It must not consume streams, mutate controller state, execute tools, or persist messages. Route precedence is declaration order; transition targets come from the active state definition. Every renderer creates a fresh session per definition mount. Conversation declarations compile to `@agentskit/statechart`. Use its published definition, immutable instance, and transition contracts; do not recreate generic state validation or transition assignment locally. This package retains only application route matching, adapter fallback, actions, traces, decision replay, and the existing session projection. `resumeChatSession` persists only the application envelope described by ADR-0011. Canonical messages must remain in upstream `ChatMemory`. Validate stored snapshots before hydration, bind them to session + definition revision, and keep terminal confirmations terminal. Deterministic route callbacks receive stable session/message identity per ADR-0018. Component-producing routes derive instance IDs from the message identity; never use definition-global counters or constant IDs for repeatable interactive routes. ## Component boundary Model or route frames are untrusted. Resolve them against `definition.components`; unknown or invalid frames are inert. Selection events express intent only and never execute an action. Standard AgentsKit `UIElement` types remain upstream. The standard application catalog is defined in `src/catalog.ts`. Every entry must ship schema, declared events, accessibility, capabilities, fallback, shared fixture, seven native presentations, and a regenerated parity report. Generic interactions remain intent-only; do not execute navigation, downloads, approvals, or tools in the catalog contract. Actionable choices use `createActionConfirmation` and the released upstream `ChatReturn.proposeToolCall`. Never validate or execute tools locally. Confirmation handles bind application-session metadata; authentication and durable audit remain outside this package. Authorization uses `createCapabilityPolicy` + `withActionPolicy`. Trusted context must come from a host closure, never a protocol frame or message. AgentsKit owns enforcement through `authorizeToolCall`; this package owns only capability composition and trace projection. ## Ask service boundary Docs, Registry, and Playbook must consume `createAskAdapter` and `createAskSessionMemory`. Do not recreate the Ask schema, NDJSON decoder, fetch loop, citation projection, or browser message store in a host. Host-specific visual tools may use `projectTool`, but the returned component frame remains runtime validated. Use a new canonical storage key and pass prior formats through `legacyKeys`. ## Deterministic answer boundary `createDeterministicAnswerResolver` performs only normalized whole-query equality over a bounded artifact and resolves an explicit ambiguous choice by entry ID. Do not add fuzzy, prefix, semantic, embedding, or model matching. `createDeterministicAnswerAdapter` may decide locally or delegate to an injected upstream `AdapterFactory`; it preserves streaming latency and cancellation while observing bounded output only to attach the unified backend envelope. Corrupt artifacts escalate without crashing startup, stale artifacts escalate, `fallback.mode` is enforced, and missing fallback becomes an offline response. The public contract is accepted by ADR-0024 with HITL approval recorded on 2026-07-13. ## Checks ```bash pnpm --filter @agentskit/chat lint pnpm --filter @agentskit/chat test ``` # CLI package handoff `packages/cli` owns AgentsKit Chat application detection, safe file generation, and command UX. - Reuse published AgentsKit/AgentsKit Chat packages in generated source. - Never copy private upstream templates or overwrite a non-empty target. - Keep CI non-interactive and diagnostics on stderr. - The closed renderer matrix is React, React Native, Ink, Vue, Svelte, Solid, and Angular. - Add each renderer to detection, templates, help/completion, golden, install, typecheck, and generated-test coverage together. - `add component` owns contract/native file generation but never source-code registration or overwrite merging. ```bash pnpm --filter @agentskit/chat-cli lint pnpm --filter @agentskit/chat-cli test pnpm --filter @agentskit/chat-cli build ``` # Devtools package handoff `packages/devtools` owns application-only trace capture, composition with upstream replay cassettes, and framework-neutral semantic parity reports. - Never record adapter requests/chunks or implement replay; use `@agentskit/eval/replay`. - Never execute actions or tools from a fixture. - Trace details are bounded JSON and configured sensitive keys are redacted recursively. - Parents must precede children and sequence values remain contiguous. - Renderer parity compares semantic outcomes, never markup or pixels. ```bash pnpm --filter @agentskit/chat-devtools lint pnpm --filter @agentskit/chat-devtools test pnpm --filter @agentskit/chat-devtools build ``` # Ink package handoff ## Owner `packages/ink` owns only the application-level Ink composition, native `ChoiceList` input/presentation, generic `StandardComponent`, and rendering of the shared semantic text fallback from ADR-0003/ADR-0007/ADR-0015. Only the latest unresolved component in a transcript owns terminal input. ## Upstream boundary Use `useChat`, `ChatContainer`, `Message`, `InputBar`, `ThinkingIndicator`, and `ToolConfirmation` from published `@agentskit/ink`. Keyboard editing, history, lifecycle, streaming, cancellation, and confirmation UI must remain upstream. Typed action proposal requires `@agentskit/ink` 0.10.0 or newer. Map supported semantic colors through the upstream `InkThemeProvider`; do not imitate unsupported spatial tokens in terminal output. Slots remain Ink components and must preserve single-owner keyboard input. Fully headless state uses upstream `useChat` directly. Lifecycle slash commands are consumed through upstream `InputBar.onSubmitInput` and call `ChatReturn` directly. Escape cancellation remains owned by `@agentskit/ink`. ## Checks ```bash pnpm --filter @agentskit/chat-ink lint pnpm --filter @agentskit/chat-ink test pnpm --filter @agentskit/chat-ink build ``` # Protocol package handoff ## Ownership `packages/protocol` owns the versioned turn and component wire envelopes, Zod schemas, encode/decode functions, safe diagnostics, bounded JSON validation, compatibility policy, and shared fixture corpus. It does not own the AgentsKit controller, `StreamChunk`, `AgentEvent`, a lifecycle reducer, transport, persistence, or renderer state. It owns the application-session envelope schema and compatibility decoder, but not its storage implementation. The session envelope must never contain canonical messages; those remain in upstream `ChatMemory`. It also owns the external Ask service v1 event schema, NDJSON bounds, and decoder. The fetch adapter, citation projection, and browser-memory composition remain in `packages/chat`. It owns the additive Ask backend request, trusted site configuration, grounded source, session record, usage, typed diagnostic, and privacy-safe metric v1 schemas. Authentication, site lookup, retrieval, generation, storage, rate limiting, and metric export remain server-host capabilities. It owns the accepted deterministic site-config, local-knowledge artifact, and unified-answer v1 envelopes, canonical SHA-256 serialization/verification, safe decoders, normalization rule, bounds, diagnostics, and conformance fixtures. Artifact generation, fetching, and caching remain host/Doc Bridge concerns; lookup and fallback composition remain in `packages/chat`. ## Read first - [`../../architecture/adrs/0004-snapshot-first-turn-protocol.md`](../../architecture/adrs/0004-snapshot-first-turn-protocol.md) - [`../../architecture/adrs/0005-upstream-memory-record-validation.md`](../../architecture/adrs/0005-upstream-memory-record-validation.md) - [`../../architecture/adrs/0007-closed-application-component-manifest.md`](../../architecture/adrs/0007-closed-application-component-manifest.md) - [`../../protocol/v1.md`](../../protocol/v1.md) - [`../../architecture/adrs/0002-upstream-first-no-reimplementation.md`](../../architecture/adrs/0002-upstream-first-no-reimplementation.md) - [`../../protocol/deterministic-answers.md`](../../protocol/deterministic-answers.md) - [`../../architecture/adrs/0024-deterministic-answer-plane.md`](../../architecture/adrs/0024-deterministic-answer-plane.md) - [`../../backend.md`](../../backend.md) - [`../../architecture/adrs/0026-trusted-ask-backend-vertical.md`](../../architecture/adrs/0026-trusted-ask-backend-vertical.md) ## Compatibility guardrail Optional additive v1 fields are compatible. Required-field or semantic changes need v2, migration fixtures, and a new ADR. Never silently coerce versions or expose raw validator diagnostics. Delegate message-record validation to `@agentskit/core/memory-validation`. Do not recreate canonical message, content-part, tool-call, token, or memory schemas here; missing capability must be fixed and released in AgentsKit first. ## Checks ```bash pnpm --filter @agentskit/chat-protocol lint pnpm --filter @agentskit/chat-protocol test pnpm --filter @agentskit/chat-protocol build ``` # React Native renderer - Owns only the native application shell, specialized `ChoiceListNative`, and generic `StandardComponentNative` presentation in `packages/react-native`. - Resolve shared frames against the framework-neutral manifest before rendering; emit only shared selection events. - Delegate controller state and UI primitives to `@agentskit/react-native`. - Keep React Native as a peer dependency; native modules belong to the Expo app. - Never add DOM assumptions or reproduce AgentsKit lifecycle behavior. - Native retry, edit, regenerate, and stop controls call `ChatReturn` directly; AgentsKit owns mutation and abort semantics. - Render typed actions with the published upstream `ToolConfirmation`; do not add a native confirmation duplicate. - Map semantic themes through upstream `style` pass-throughs and native application styles. Slots remain React Native components; fully headless state uses upstream `useChat` directly. - Validate with lint, unit tests, Expo example build, E2E, and doc-bridge gates. # React package handoff ## Ownership `packages/react` supplies an accessible application shell, specialized `ChoiceList`, and generic native `StandardComponent` presentation over `useChat` and headless components from `@agentskit/react`. Shared frames, manifests, props, and events remain framework-neutral. It must not implement chat state, streaming, cancellation, message persistence, or adapter behavior. Retry, edit, regenerate, and stop controls must call the corresponding `ChatReturn` methods directly. Do not reproduce truncation, lineage, abort, or late-chunk behavior in this package. Typed actions compose the published upstream `ToolConfirmation`; approval and denial go through the shared session coordinator and then the upstream hook. Semantic themes map only to published upstream CSS variables. Native slots stay in this package; fully headless state uses `useChat` from `@agentskit/react` directly. Preserve the default shell's log, alert, labeling, and keyboard semantics when replacing slots. ## Read first - [`../../getting-started/react.md`](../../getting-started/react.md) - [`../../architecture/upstream-adoption.md`](../../architecture/upstream-adoption.md) ## Checks ```bash pnpm --filter @agentskit/chat-react lint pnpm --filter @agentskit/chat-react test ``` # Server package handoff ## Ownership `packages/server` owns the Web-standard application boundary, trusted host-context seam, request validation, snapshot/Ask streaming, outer deadline/cancellation, trusted site-policy composition, and composition of existing session/message storage. It does not own adapters, provider fetch, controller state, stream reduction, tools, confirmation execution, message persistence, authentication implementations, or framework-specific HTTP adapters. ## Read first - [`../../architecture/adrs/0012-web-standard-snapshot-handler.md`](../../architecture/adrs/0012-web-standard-snapshot-handler.md) - [`../../server.md`](../../server.md) - [`../../backend.md`](../../backend.md) - [`../../protocol/v1.md`](../../protocol/v1.md) - [`../../sessions.md`](../../sessions.md) - [`../../architecture/adrs/0002-upstream-first-no-reimplementation.md`](../../architecture/adrs/0002-upstream-first-no-reimplementation.md) ## Guardrails Authenticate before body parsing. Never derive trusted context, site, corpus, assistant, components, actions, subject, or tenant from a protocol payload. Validate with `decodeTurnEvent` or the Ask backend schemas; emit only protocol snapshots/events/diagnostics. Use upstream `createChatController`, `ChatMemory`, RAG/Retriever, and provider adapters. Forward cancellation through every host seam and preserve the outer deadline. Ask telemetry must never contain prompts, answers, sources, credentials, subject IDs, or session IDs. ## Checks ```bash pnpm --filter @agentskit/chat-server lint pnpm --filter @agentskit/chat-server test pnpm --filter @agentskit/chat-server build ``` # Solid package handoff `packages/solid` owns only the Solid application shell, typed render props, semantic CSS mapping, specialized `ChoiceList`, and generic native `StandardComponent`. It consumes `useChat`, `ChatContainer`, `Message`, `InputBar`, `ThinkingIndicator`, and `ToolConfirmation` from `@agentskit/solid@0.4.4`. Never add another controller, reactive store, stream consumer, lifecycle implementation, confirmation engine, or container primitive. Config and session identity are isolated with keyed Solid owners. Owner cleanup delegates cancellation to the upstream binding fixed in AgentsKit #1169. Checks: `pnpm --filter @agentskit/chat-solid lint && pnpm --filter @agentskit/chat-solid test && pnpm --filter @agentskit/chat-solid build`. # Svelte package handoff `packages/svelte` owns only the Svelte 5 application shell, typed snippets, semantic CSS mapping, specialized `ChoiceList`, and generic native `StandardComponent`. It consumes `createChatStore`, `ChatContainer`, `Message`, `InputBar`, `ThinkingIndicator`, and `ToolConfirmation` from `@agentskit/svelte@0.4.3`. Never add another controller, store, stream consumer, lifecycle implementation, confirmation engine, or container primitive. The internal keyed binding calls the upstream `stop` action before replacing an actively streaming store and preserves application/session state. Checks: `pnpm --filter @agentskit/chat-svelte lint && pnpm --filter @agentskit/chat-svelte test && pnpm --filter @agentskit/chat-svelte build`. # Vue package handoff ## Ownership `packages/vue` owns only the Vue application shell, native scoped slots, semantic theme mapping, specialized `ChoiceList`, and generic `StandardComponent` presentation. It consumes `useChat`, `ChatRoot`, `Message`, `InputBar`, `ThinkingIndicator`, and `ToolConfirmation` from `@agentskit/vue@0.4.4`. Do not add another controller, reactive chat store, stream consumer, lifecycle implementation, confirmation engine, or renderer root. Generic Vue binding gaps belong upstream in `AgentsKit-io/agentskit` first. ## Read first - [`../../getting-started/vue.md`](../../getting-started/vue.md) - [`../../architecture/upstream-adoption.md`](../../architecture/upstream-adoption.md) ## Checks ```bash pnpm --filter @agentskit/chat-vue lint pnpm --filter @agentskit/chat-vue test pnpm --filter @agentskit/chat-vue build ``` # Product handoff ## Read first - Human source: [`../product/PRD.md`](../product/PRD.md) - Delivery graph: [`../product/roadmap.md`](../product/roadmap.md) - Architecture: [`../architecture/overview.md`](../architecture/overview.md) ## Change route Use this ownership area for user stories, scope, release outcomes, positioning, and success criteria. Product changes that alter a public boundary require a corresponding architecture review and, after acceptance, a new ADR. ## Checks ```bash pnpm docs:bridge:index pnpm docs:bridge:gate ``` # Stable release handoff ## Read first - [Release process](../releases/release-process.md) - [Compatibility matrix](../releases/compatibility.md) - [Stability policy](../releases/stability.md) - [Security policy](../../SECURITY.md) - [Repository rules](../../AGENTS.md) ## Ownership `release/manifest.json` is the source of truth for the fixed public package graph and renderer set. Package manifests own exact peer ranges and exports. The conformance manifest owns component/event/platform evidence. Changesets own future version intent; accepted ADRs/RFCs own contract change decisions. Do not publish from a developer machine. A stable tag contained in `main` starts the protected `npm` environment workflow. The stable workflow is OIDC-only. The protected `npm` environment must not contain `NPM_TOKEN` or `NODE_AUTH_TOKEN`; both public packages trust the GitHub Actions publisher for `.github/workflows/release.yml` and environment `npm`. ## Checks ```bash pnpm lint pnpm test:conformance pnpm test pnpm build pnpm conformance:gate pnpm docs:bridge:gate pnpm test:e2e pnpm test:pty pnpm release:gate pnpm release:pack ``` Do not close a release issue until npm provenance, clean npm installation, GitHub checksums, host smoke tests, and human launch review are linked publicly. # Angular quick start Install the application shell and its native peers: ```bash pnpm add @agentskit/chat @agentskit/chat/angular @agentskit/angular @angular/core @angular/common rxjs ``` Keep the definition in a framework-neutral module, then import the standalone component: ```ts import { Component } from '@angular/core' import { AgentChatComponent } from '@agentskit/chat/angular' import { supportChat } from './support-chat' @Component({ selector: 'app-support', standalone: true, imports: [AgentChatComponent], template: `
{{ message.content }}
`, }) export class SupportComponent { readonly supportChat = supportChat } ``` `AgentChatComponent` delegates state, streaming, tools, confirmation, retry, editing, regeneration, cancellation, and teardown to `AgentskitChat` from `@agentskit/angular`. Each mounted shell receives an isolated service provider. Content templates customize `container`, `message`, `input`, `thinking`, `confirmation`, and `choiceList` without entering the shared definition. A custom container receives the default `content` template and must render it with `ngTemplateOutlet`. The default shell uses a polite log, semantic alerts, labeled controls, and native buttons/fieldset elements. A replacement template owns equivalent accessibility semantics. # Ink quick start Install the application shell and its published upstream peers: ```bash pnpm add @agentskit/chat @agentskit/chat/ink @agentskit/ink ink react ``` Define the chat in a framework-neutral module and render it from an Ink entry point: ```tsx import { defineChat } from '@agentskit/chat' import { AgentChat } from '@agentskit/chat/ink' import { render } from 'ink' import { adapter } from './adapter.js' const chat = defineChat({ id: 'support', chat: { adapter } }) render() ``` When a trusted terminal host already owns the session, pass the same framework-neutral controlled source used by React: ```tsx import type { ControlledChatSource } from '@agentskit/chat' import { AgentChat } from '@agentskit/chat/ink' export const mountChat = (source: ControlledChatSource) => render() ``` Controlled mode validates the serialized snapshot and delegates input, cancellation, lifecycle commands, confirmation, and component interactions to the host callbacks. It does not invoke the upstream `useChat` hook or create a second controller. The official Ink input, Escape cancellation, confirmation, theme, semantic fallback, and single-owner keyboard behavior remain unchanged. The host must rerender with its next snapshot after a callback and remains responsible for authentication, authorization, transport, persistence, and business behavior. The shell delegates lifecycle, streaming, input history, Escape cancellation, and terminal components to `@agentskit/ink`. Validate unsupported visual output with `parseSemanticFallback` from `@agentskit/chat`, then render it with Ink's `SemanticFallback`; the shared formatter keeps its kind and readable summary stable across platforms. # React Native Install `@agentskit/chat`, `@agentskit/chat/react-native`, `@agentskit/react-native`, React, and React Native. Native dependencies belong to the Expo application, not the renderer package. ```tsx import { AgentChatNative } from '@agentskit/chat/react-native' import { chat } from './chat' export default function App() { return } ``` ```ts // chat.ts import { defineChat } from '@agentskit/chat' import { adapter } from './adapter' export const chat = defineChat({ id: 'hello-world', chat: { adapter } }) ``` The Expo example in this repository keeps its deterministic shared fixture private because it is test infrastructure, not a published API. The renderer consumes `@agentskit/react-native` directly. The shared definition contains no DOM, React web, Expo, or React Native imports. Version `0.4.4` or newer is required; the release is tested against the published `0.4.x` line for cancellation, native accessibility, and bundle safety. # React quick start AgentsKit Chat keeps the upstream AgentsKit configuration intact and adds a small application definition. ```ts import { openai } from '@agentskit/adapters' import { defineChat } from '@agentskit/chat' export const supportChat = defineChat({ id: 'support', chat: { adapter: openai({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4.1-mini', }), systemPrompt: 'Help users understand the product.', }, }) ``` Render it with the native React binding: ```tsx import { AgentChat } from '@agentskit/chat/react' import { supportChat } from './support-chat' export const Support = () => ( ) ``` `AgentChat` passes `supportChat.chat` directly to `useChat` from `@agentskit/react`. Streaming, tools, memory, retry, editing, regeneration, confirmation, and cancellation remain AgentsKit behavior. Run the deterministic repository example: ```bash pnpm install pnpm dev ``` Send any message to receive a deterministic streamed response. Send `/fail` to verify the accessible error path without a provider key. ## Host-owned sessions When another trusted process already owns the chat session, pass its validated projection through the controlled source instead of creating a second client controller: ```tsx import type { ControlledChatSource } from '@agentskit/chat' import { AgentChat } from '@agentskit/chat/react' import { supportChat } from './support-chat' export const Support = ({ source }: { source: ControlledChatSource }) => ( ) ``` `source.snapshot` contains `sessionId`, serialized canonical `messages`, `status`, `input`, a bounded error envelope, and token `usage`. `source.actions` implements the existing AgentsKit send, stop, retry, edit, regenerate, input, clear, proposal, approval, and denial callbacks. The host must rerender with its next snapshot after a callback. Invalid snapshots fail closed, and controlled mode does not call `useChat` or instantiate another controller. Authentication, authorization, transport, persistence, optimistic updates, and business behavior remain outside the public driver. # Get started AgentsKit Chat `0.4.0` lets one framework-neutral definition run through a native renderer. Start with the CLI or select a framework guide. ```bash pnpm dlx @agentskit/chat-cli@0.4.0 init my-chat --renderer react --yes cd my-chat pnpm install pnpm test ``` The CLI supports `react`, `react-native`, `ink`, `vue`, `svelte`, `solid`, and `angular`. It creates a shared definition, Web-standard handler, native shell, test, and architecture note without copying an AgentsKit runtime. ## Renderer quick starts - [React](./react.md) - [React Native / Expo](./react-native.md) - [Ink](./ink.md) - [Vue](./vue.md) - [Svelte](./svelte.md) - [Solid](./solid.md) - [Angular](./angular.md) Continue with the [API reference](../api-reference.md), [deployment modes](../deployment.md), and [compatibility matrix](../releases/compatibility.md). # Solid quick start ```bash pnpm add @agentskit/chat @agentskit/chat/solid @agentskit/solid solid-js ``` ```tsx import { AgentChat } from '@agentskit/chat/solid' import { supportChat } from './support-chat' export function SupportChat() { return
{message.content}
} /> } ``` The shell delegates streaming, tools, confirmation, retry, editing, regeneration, and cancellation to `useChat` from `@agentskit/solid`. Typed render props customize native Solid surfaces without entering the shared definition. # Svelte quick start ```bash pnpm add @agentskit/chat @agentskit/chat/svelte @agentskit/svelte svelte ``` ```svelte {#snippet message(item)}
{item.content}
{/snippet}
``` The shell delegates streaming, tools, confirmation, retry, editing, regeneration, and cancellation to `createChatStore` from `@agentskit/svelte`. Typed Svelte 5 snippets customize native surfaces without entering the shared definition. # Vue quick start Install the application shell and its native peers: ```bash pnpm add @agentskit/chat @agentskit/chat/vue @agentskit/vue vue ``` Keep the chat definition in a framework-neutral module, then render it from Vue: ```vue ``` `AgentChat` delegates state, streaming, tools, confirmation, retry, editing, regeneration, and cancellation to `useChat` from `@agentskit/vue`. Named scoped slots customize the `container`, `message`, `input`, `thinking`, `confirmation`, and `choiceList` surfaces without entering the shared definition. The default shell uses a polite log, semantic alerts, labeled controls, and native buttons/fieldset elements. A replacement slot owns equivalent accessibility semantics. # Ecosystem documentation tracer ## Outcome Make `/docs` the canonical public entry point, remove the separate sparse landing experience, and connect AgentsKit Chat to the six sibling products without changing runtime contracts. ## Acceptance criteria - `/` redirects to `/docs`, and `/docs` renders a concise, useful Fumadocs index. - the index contains a verified quick start, one useful Mermaid diagram, key journeys, six contextual sibling links, and machine-readable documentation entry points. - the hosted seven-product ecosystem bar identifies AgentsKit Chat as the current product. - `llms.txt` and raw Markdown expose the new canonical index without a second prose corpus. ## Dependencies and test plan - Reuse the canonical `docs/` corpus, Fumadocs source loader, existing quick start, raw routes, machine surfaces, and hosted ecosystem bar. - Run docs lint, unit tests, build, E2E accessibility/navigation checks, Doc Bridge index/gate, and exact doctor coverage. ## Documentation impact and Definition of Done This page and `docs/index.md` are the only new prose. Completion requires the root redirect, canonical metadata, responsive docs rendering, valid links, green checks, and Doc Bridge 100/100. ## Upstream-adoption record - **Inspected source:** `apps/docs`, ADR-0027, the canonical quick starts, API reference, and ecosystem adoption docs. - **Reused exports:** Fumadocs `source`, `DocsLayout`, existing machine routes, and the public AgentsKit Chat documentation contracts. - **Local application behavior:** routing, navigation, and documentation presentation only. - **Linked upstream work:** none; no framework-neutral runtime gap was found. # Issue governance ## Structure Issues use tracer-bullet slices. A completed implementation issue must produce a narrow but complete outcome through the relevant contract, runtime, renderer, test, and documentation layers. Each issue contains: 1. Parent PRD and user stories. 2. Outcome and non-goals. 3. Acceptance criteria. 4. Dependencies. 5. Test plan. 6. Documentation impact. 7. Definition of Done. 8. Delivery mode: AFK or HITL. 9. Upstream adoption: inspected AgentsKit source, reused exports, local responsibility, and linked upstream gaps. ## Definition of Done baseline An issue is done only when: - acceptance criteria are demonstrated by public behavior; - runtime boundaries validate untrusted data; - unit and integration tests pass; - cross-framework fixtures pass for every affected renderer; - accessibility and platform conventions are covered where UI changes; - public behavior is documented; - agent handoffs and ownership metadata remain accurate; - `pnpm docs:bridge:gate` passes; - no untracked TODO, disabled behavior, or undocumented breaking change remains. - the issue records upstream reuse and introduces no duplicate AgentsKit primitive; - any generic upstream gap is fixed and released from `AgentsKit-io/agentskit` before local integration. Issue-specific requirements extend this baseline rather than replace it. ## Dependencies Issues are created in dependency order. `Blocked by` must reference real issue numbers. Parallel slices must avoid overlapping ownership or explicitly state their shared contract fixture. --- title: AgentsKit Chat description: Build one versioned chat application and render it natively across every supported AgentsKit client. --- Define routes, actions, components, sessions, and backend seams once. AgentsKit Chat composes those application concerns over AgentsKit, then each supported framework renders them natively. AgentsKit Chat architecture: one definition, deterministic answers, optional grounded Ask, and seven native renderers ## Start with a working shell ```bash pnpm dlx @agentskit/chat-cli@0.4.0 init my-chat --renderer react --yes cd my-chat pnpm install pnpm test ``` The CLI creates a shared definition, Web-standard handler, native shell, test, and architecture note. Choose another supported renderer in the [framework quick starts](./getting-started/README.md). ## Choose the shortest path Missing framework-neutral primitives are fixed in AgentsKit first. Chat owns application composition and versioned boundaries; it does not copy the controller, lifecycle, tools, memory, RAG, or framework bindings. ## Continue through the ecosystem Every product is optional. Follow the next problem, not a prescribed suite. For agent consumption, use [llms.txt](/llms.txt), the [full corpus](/llms-full.txt), canonical Markdown from each page, or the [for-agents handoffs](./for-agents/index.md). # Streaming lifecycle AgentsKit owns the lifecycle. AgentsKit Chat renderers expose the same `ChatReturn` operations without wrapping their semantics: | Operation | AgentsKit method | Default renderer interaction | |---|---|---| | Cancel | `stop()` | Stop button; Escape in Ink | | Retry | `retry()` | Retry response; `/retry` in Ink | | Edit | `edit(messageId, content)` | Edit last message; `/edit ` in Ink | | Regenerate | `regenerate(messageId?)` | Regenerate response; `/regenerate` in Ink | Cancellation aborts the upstream source. AgentsKit prevents chunks arriving after the abort from mutating canonical state. The framework does not implement another abort flag or stream reducer. ## Lineage and reconnect A v1 `server.turn.snapshot` may include: ```json { "lineage": { "operation": "regenerate", "parentTurnId": "turn-previous", "sourceMessageId": "message-assistant" } } ``` Use `createSnapshotEvent(...)` to project canonical AgentsKit messages plus submit/retry/edit/regenerate lineage. Use `createTurnSnapshotCursor(sessionId)` when applying snapshots received from a transport. The expected session is fixed before the first delivery; its first valid snapshot initializes reconnect state. Later snapshots must have a greater sequence. Duplicate, stale, malformed, and foreign-session events do nothing. Snapshots remain complete projections. The cursor chooses the current projection; it never merges messages or interprets adapter chunks. # Positioning ## One-line **AgentsKit Chat** is the cross-framework application layer for interactive agent experiences — one definition, native shells on every AgentsKit UI binding. ## What it is not | Adjacent tool | Difference | |---------------|------------| | **Vercel AI SDK UI** | React-first hooks and transport for streaming UIs. AgentsKit Chat is framework-neutral application contracts (routes, policy, components, sessions) with seven native renderers. | | **assistant-ui** | Excellent React assistant primitives and composition. AgentsKit Chat reuses AgentsKit runtime/bindings and targets web, native mobile, and terminal with a shared protocol. | | **CopilotKit** | Productized copilots and backend integrations, often React-centric. AgentsKit Chat is an open MIT application framework above the AgentsKit substrate, not a hosted copilot SaaS. | | **AgentsKit core** | Runtime, adapters, tools, memory, RAG, and base UI bindings. AgentsKit Chat composes those primitives; it does not replace them ([ADR-0002](../architecture/adrs/0002-upstream-first-no-reimplementation.md)). | ## When to choose it - You already use or want AgentsKit adapters/runtime. - You need the same agent application on React, Vue, Svelte, Solid, Angular, React Native, and/or Ink. - You want deterministic routes, closed component registries, and capability policy outside the model. ## When not to choose it - You only need a React chat UI and are happy assembling AI SDK pieces. - You need a multi-tenant hosted SaaS control plane (out of v0 scope). - You need automatic conversion of arbitrary custom components across frameworks. # PRD: AgentsKit Chat v0 foundation ## Problem statement AgentsKit developers can build chat interfaces across several frameworks, but product-grade interactive agent applications still require substantial repeated architecture. Teams must independently design structured turns, deterministic interaction, safe actions, component registries, sessions, streaming, persistence, theming, scaffolding, and cross-platform behavior. These implementations drift and obscure the fact that AgentsKit provides the underlying primitives. ## Solution Create an open-source, MIT-licensed, cross-framework application framework on top of AgentsKit. Developers define agent behavior, actions, components, policies, sessions, and deterministic flows once, then render native experiences in React, Vue, Svelte, Solid, Angular, React Native, and Ink. The initial architecture proof delivers one unchanged shared chat definition through React, React Native, and Ink. The project then expands renderer parity, scaffolding, server integrations, persistence, documentation, devtools, and dogfood migrations. ## User stories 1. As an application developer, I want to define a chat once so that its behavior is shared by web, mobile, and terminal clients. 2. As an AgentsKit user, I want the framework to consume AgentsKit packages directly so that I keep the same adapters, tools, memory, RAG, and runtime ecosystem. 3. As a developer, I want typed actions so that a model cannot execute arbitrary application operations. 4. As a product owner, I want sensitive actions to require confirmation so that the user remains in control. 5. As a security engineer, I want authorization and policy outside the model so that natural-language output cannot grant capabilities. 6. As a UI developer, I want schema-backed component manifests so that agents can render interactive interfaces safely. 7. As a terminal user, I want semantic fallbacks so that every supported interaction remains usable without a graphical renderer. 8. As a designer, I want semantic theme tokens and framework-native customization so that the chat fits the host product. 9. As a framework user, I want headless access and composable slots so that I can replace the default presentation. 10. As a user, I want streaming, cancellation, retry, editing, and regeneration so that the experience feels responsive and controllable. 11. As a returning user, I want persistent sessions that can resume on another client so that conversations are not tied to one interface. 12. As an operator, I want deterministic routes and conversation state machines so that known workflows do not depend on model interpretation. 13. As an operator, I want replayable turn traces so that I can explain how a response or action was produced. 14. As a tester, I want shared contract fixtures so that every renderer demonstrates equivalent behavior. 15. As an accessibility reviewer, I want platform-appropriate semantics and keyboard or assistive interaction so that the default experience is usable. 16. As a new user, I want an `init` command that detects my framework so that I can launch a working chat without assembling infrastructure. 17. As a developer, I want to add a semantic component through the CLI so that its contract and platform implementations stay aligned. 18. As a maintainer, I want versioned protocol schemas and compatibility tests so that clients and servers can evolve independently. 19. As a framework maintainer, I want ownership and documentation discoverable through doc-bridge so that agents edit the correct module. 20. As an AgentsKit maintainer, I want real applications to exercise every UI binding so that interoperability gaps are discovered upstream. 21. As an ecosystem user, I want complete support, onboarding, operations, and RAG examples so that the framework is not perceived as documentation-only. 22. As a framework maintainer, I want black-box consumer requirements validated through synthetic public examples so hosts retain only product-specific behavior. 23. As an AgentsKit site maintainer, I want to migrate the existing Ask experience so that public dogfood uses the released framework. 24. As a Playbook or Registry maintainer, I want the shared framework widget and runtime so that duplicated chat implementations disappear. ## Implementation decisions - Use a TypeScript modular monorepo with pnpm, strict typing, named exports, Vitest, Playwright where applicable, and Changesets for public packages. - Keep the shared core independent of DOM and framework-specific reactive primitives. - Use versioned runtime schemas for model output and client-server events. - Build native renderer packages over the matching AgentsKit binding. - Prove the contract first with React, React Native, and Ink. - Treat UI components as semantic identities with shared props schemas and platform implementations. - Require text or semantic fallback for every portable component. - Keep authorization, confirmation, action execution, secret handling, and audit outside model control. - Use Web-standard handlers and events so Node, serverless, and edge-compatible adapters can be layered without changing chat definitions. - Use `@agentskit/doc-bridge` from repository creation for ownership, retrieval, gates, MCP, and ecosystem federation. ## Testing decisions - Test public behavior rather than internal implementation details. - Maintain shared fixtures for turn parsing, action policy, component validation, streaming, resumption, and fallback behavior. - Run renderer conformance tests against the same fixtures. - Require accessibility evidence for graphical renderers and keyboard-flow evidence for Ink. - Add end-to-end examples that reuse one shared definition across React, React Native, and Ink. - Use dogfood migrations as integration evidence only after the standalone examples pass. ## Out of scope - Replacing AgentsKit adapters, runtime, memory, RAG, tools, or existing framework bindings. - Automatic source-level conversion of arbitrary custom UI components between frameworks. - A hosted multi-tenant SaaS in the first release. - Visual workflow authoring in the first release. - Guaranteeing identical visual layout across DOM, native mobile, and terminal environments. ## Further notes The product position is: **One agent experience. Every interface.** The framework is an opinionated application layer for interactive agent experiences, not a competing low-level agent framework. # AgentsKit Chat v0 roadmap The parent product requirement is [GitHub issue #1](https://github.com/AgentsKit-io/agentskit-chat/issues/1). Every child issue includes user stories, acceptance criteria, real dependency links, a test plan, documentation impact, delivery mode, and an explicit Definition of Done. ## Milestone [v0 — Cross-framework foundation](https://github.com/AgentsKit-io/agentskit-chat/milestone/1) ## Delivery graph ```mermaid flowchart TD I1["#1 PRD"] --> I31["#31 Upstream convergence gate"] I31 --> I2["#2 React vertical slice"] I2 --> I3["#3 React Native parity"] I2 --> I4["#4 Ink parity"] I2 --> I5["#5 Protocol + conformance"] I3 --> I5 I4 --> I5 I5 --> I6["#6 Deterministic routes + state"] I5 --> I7["#7 Schema-backed ChoiceList"] I5 --> I10["#10 Streaming lifecycle"] I7 --> I8["#8 Typed action + confirmation"] I8 --> I9["#9 Authorization + policy"] I10 --> I11["#11 Cross-client sessions"] I5 --> I12["#12 Web-standard server"] I11 --> I12 I7 --> I13["#13 Theming + headless composition"] I5 --> I15["#15 Vue"] I5 --> I16["#16 Svelte"] I5 --> I17["#17 Solid"] I5 --> I18["#18 Angular"] I7 --> I19["#19 Component catalog"] I15 --> I19 I16 --> I19 I17 --> I19 I18 --> I19 I3 --> I14["#14 Initial init CLI"] I4 --> I14 I12 --> I14 I14 --> I20["#20 Cross-framework CLI"] I19 --> I20 I5 --> I21["#21 Replay + diagnostics"] I11 --> I21 I8 --> I22["#22 Support reference"] I12 --> I22 I19 --> I22 I6 --> I23["#23 Onboarding reference"] I19 --> I23 I9 --> I24["#24 Operations reference"] I21 --> I24 I12 --> I25["#25 RAG reference"] I19 --> I25 I25 --> I26["#26 AgentsKit Docs dogfood"] I26 --> I27["#27 Registry + Playbook dogfood"] I6 --> I28["#28 Private-reference extraction"] I9 --> I28 I13 --> I28 I21 --> I28 I13 --> I29["#29 Accessibility + conformance gates"] I19 --> I29 I20 --> I29 I20 --> I30["#30 v0 release"] I22 --> I30 I23 --> I30 I24 --> I30 I25 --> I30 I26 --> I30 I27 --> I30 I28 --> I30 I29 --> I30 ``` The diagram highlights the critical path. Individual issue bodies are authoritative for the complete dependency set. ## Architecture proof - [#31 Upstream convergence and ownership gate](https://github.com/AgentsKit-io/agentskit-chat/issues/31) — HITL, blocks implementation - [#2 React hello-world vertical slice](https://github.com/AgentsKit-io/agentskit-chat/issues/2) — AFK - [#3 React Native parity](https://github.com/AgentsKit-io/agentskit-chat/issues/3) — AFK - [#4 Ink parity](https://github.com/AgentsKit-io/agentskit-chat/issues/4) — AFK - [#5 Versioned protocol and conformance fixtures](https://github.com/AgentsKit-io/agentskit-chat/issues/5) — HITL ## Core interactive behavior - [#6 Deterministic routes and conversational state](https://github.com/AgentsKit-io/agentskit-chat/issues/6) - [#7 Schema-backed ChoiceList](https://github.com/AgentsKit-io/agentskit-chat/issues/7) - [#8 Typed action with confirmation](https://github.com/AgentsKit-io/agentskit-chat/issues/8) - [#9 Authorization and action policy](https://github.com/AgentsKit-io/agentskit-chat/issues/9) - [#10 Streaming lifecycle parity](https://github.com/AgentsKit-io/agentskit-chat/issues/10) - [#11 Persistent cross-client sessions](https://github.com/AgentsKit-io/agentskit-chat/issues/11) - [#12 Web-standard server handler](https://github.com/AgentsKit-io/agentskit-chat/issues/12) - [#13 Semantic theming and headless composition](https://github.com/AgentsKit-io/agentskit-chat/issues/13) — HITL ## Renderer and CLI expansion - [#14 Initial `init` for React, React Native, and Ink](https://github.com/AgentsKit-io/agentskit-chat/issues/14) - [#15 Vue renderer](https://github.com/AgentsKit-io/agentskit-chat/issues/15) - [#16 Svelte renderer](https://github.com/AgentsKit-io/agentskit-chat/issues/16) - [#17 Solid renderer](https://github.com/AgentsKit-io/agentskit-chat/issues/17) - [#18 Angular renderer](https://github.com/AgentsKit-io/agentskit-chat/issues/18) - [#19 Cross-framework component catalog](https://github.com/AgentsKit-io/agentskit-chat/issues/19) - [#20 Cross-framework CLI and component generator](https://github.com/AgentsKit-io/agentskit-chat/issues/20) - [#21 Replay and parity diagnostics](https://github.com/AgentsKit-io/agentskit-chat/issues/21) ## Reference applications - [#22 Support application](https://github.com/AgentsKit-io/agentskit-chat/issues/22) - [#23 Deterministic onboarding](https://github.com/AgentsKit-io/agentskit-chat/issues/23) - [#24 Policy-protected operations](https://github.com/AgentsKit-io/agentskit-chat/issues/24) - [#25 Cited RAG application](https://github.com/AgentsKit-io/agentskit-chat/issues/25) ## Dogfood and release - [#26 AgentsKit Docs migration](https://github.com/AgentsKit-io/agentskit-chat/issues/26) - [#27 Registry and Playbook migration](https://github.com/AgentsKit-io/agentskit-chat/issues/27) - [#28 Private-reference capability extraction](https://github.com/AgentsKit-io/agentskit-chat/issues/28) — HITL - [#29 Accessibility and platform-conformance gates](https://github.com/AgentsKit-io/agentskit-chat/issues/29) - [#30 v0 documentation and release](https://github.com/AgentsKit-io/agentskit-chat/issues/30) — HITL ## Parallel work After #5 is accepted, #6, #7, #10, #15, #16, #17, and #18 have independent ownership and may proceed concurrently. Renderer work shares protocol fixtures but must not alter the accepted protocol without a new ADR and coordinated compatibility review. # Ask service integration `@agentskit/chat` exposes the shared client boundary used by AgentsKit Docs, Registry, and Playbook: ```ts import { createAskAdapter, createAskSessionMemory, defineChat } from '@agentskit/chat' const definition = defineChat({ id: 'registry-ask', chat: { adapter: createAskAdapter({ endpoint: 'https://ask.agentskit.io/v1/ask', corpus: 'registry', persona: 'registry-guide', }), memory: createAskSessionMemory({ key: 'agentskit:registry:ask:v2', legacyKeys: ['agentskit:registry:ask'], }), }, }) ``` `@agentskit/chat-protocol` owns the v1 Ask event schema, bounds, and NDJSON decoder. The adapter consumes that runtime boundary, converts text and citations into one ordered canonical assistant-message string, and delegates message/controller behavior to AgentsKit. `answer` becomes text and `cite` becomes the standard `source-list` component. Unknown and malformed events are inert. The adapter recognizes valid Ask records even when an intermediary serves NDJSON as `text/plain`; a body that is not valid Ask NDJSON remains safely encoded as text records. The additive backend request is `agentskit.chat.ask` v1. It carries bounded projected messages, the application session ID when available, and only a validated low-confidence deterministic escalation. It never carries trusted site, corpus, assistant, component, action, tenant, or subject authority. The server resolves those fields from authenticated context using the `agentskit.chat.backend-site` v1 schema. Transport chunk boundaries do not affect decoding: limits apply to individual Ask records, the retained partial line, and a fixed per-decode record budget. An oversized partial line is discarded through its next newline so its suffix cannot become a separate event. The resulting assistant message also enforces the ordered-content byte and record limits before emitting a chunk, so a long service response cannot create canonical content that its renderers or memory reject. `endpoint`, `corpus`, and `persona` are the transport configuration seam. Relative endpoints remain relative for same-origin proxies. The 30-second deadline covers connection establishment only; after headers arrive, the stream continues until completion or the host calls the upstream stop/abort lifecycle. Host-only application tools may be projected into a validated `ComponentRenderFrame`: ```ts createAskAdapter({ corpus: 'docs', projectTool(event) { if (event.name !== 'codeBlock') return undefined return docsCodeBlockFrame(event) }, }) ``` The callback cannot bypass component-frame runtime validation. It is presentation policy, not another protocol decoder. `createAskSessionMemory` composes the released `@agentskit/memory/web-storage` implementation. Use a new canonical key and list old host keys under `legacyKeys`; canonical records are runtime validated and bounded, and valid legacy data remains readable when Web Storage is full or read-only. The built-in migration accepts the historical `{ role, text }`, `{ role, content }`, and Docs `{ role: 'assistant', parts }` formats. See [ADR-0022](../architecture/adrs/0022-shared-ask-service-integration.md). The trusted server vertical, diagnostics, persistence, and privacy-safe metric contracts are specified by [ADR-0026](../architecture/adrs/0026-trusted-ask-backend-vertical.md) and the [backend guide](../backend.md). # Deterministic answer protocol The deterministic answer plane resolves declared exact facts before the backend. It is intentionally small: commands, package identities, navigation, contribution links, ecosystem relationships, restricted FAQs, and document titles may be local. Reasoning, comparisons, recommendations, unknown input, and multi-cause diagnosis escalate. > This v1 contract is accepted in [ADR-0024](../architecture/adrs/0024-deterministic-answer-plane.md), with HITL approval recorded on 2026-07-13. ## Host integration Decode the site-owned configuration and immutable artifact at the trust boundary, verify its expected hash, then compose it with the existing backend adapter: ```ts import { ChoiceListComponent, SourceListComponent, createAskAdapter, createDeterministicAnswerAdapter, defineChat, defineComponentManifest, } from '@agentskit/chat' import { decodeDeterministicSiteConfig, verifyLocalKnowledgeArtifact, } from '@agentskit/chat/protocol' const site = decodeDeterministicSiteConfig(siteConfigJson) if (!site.ok) throw new Error(site.diagnostic.message) const artifact = await verifyLocalKnowledgeArtifact(artifactJson, { expectedContentHash: site.value.artifact.contentHash, expectedSiteId: site.value.siteId, }) // A corrupt local artifact must not prevent the backend chat from starting. const localArtifact = artifact.ok ? artifact.value : null const fallback = site.value.fallback.mode === 'backend' ? createAskAdapter({ corpus: 'docs' }) : undefined const adapter = createDeterministicAnswerAdapter({ artifact: localArtifact, expectedContentHash: site.value.artifact.contentHash, expectedSiteId: site.value.siteId, fallbackMode: site.value.fallback.mode, fallback, backend: { provider: 'ask' }, }) const definition = defineChat({ id: site.value.siteId, components: defineComponentManifest([ChoiceListComponent, SourceListComponent]), choiceSubmission: adapter.resolveChoiceSubmission, chat: { adapter }, }) ``` The host owns loading and cache headers. `verifyLocalKnowledgeArtifact` requires the trusted hash and site ID from site config, validates the bounded schema, computes portable SHA-256 over the canonical accepted v1 fields (excluding the self-referential `contentHash` field), and compares all three values. Producers can call `computeLocalKnowledgeArtifactContentHash` before a `contentHash` field exists, so generation and verification share exactly one byte contract without a placeholder. Hashing does not require Web Crypto in React Native or terminal runtimes. An invalid artifact reaches the adapter as a safe `corrupt` escalation; it does not prevent a configured backend from starting. ## Exact means exact Both artifact producers and clients use `normalizeKnowledgeKey`: Unicode NFKC, trim, whitespace collapse, and stable case folding. After that normalization, the whole query must equal a declared value. The runtime performs no token, prefix, fuzzy, embedding, semantic, or model match. ```mermaid flowchart LR Q["User query"] --> N["Bounded normalization"] N --> E{"Exact entries"} E -->|"one"| A["Local answer · high"] E -->|"many"| C["Choices · medium"] E -->|"none"| B["Backend escalation · low"] B -->|"unavailable"| O["Safe offline response"] ``` An expired artifact always escalates, even when its index contains an exact match. Resolver and adapter construction synchronously recheck the trusted site/hash anchors and canonical digest, so mutation after initial loading becomes a safe corrupt escalation. If no backend is configured, an unknown question produces a safe offline escalation instead of an invented answer. The artifact schema requires every ambiguous entry to expose a globally unique exact alias in addition to its shared alias. Deterministic ChoiceLists display that alias in the existing `description` field. The host wires `adapter.resolveChoiceSubmission` into `definition.choiceSubmission`; the AgentsKit Chat session wrapper supplies its identity through the adapter's optional session-aware extension without mutating the upstream request, and the adapter authorizes only an exact frame it projected for that session. The returned reservation exposes the same visible alias, commits only after `chat.send` succeeds, and releases on failure so a retry remains functional. All seven first-party renderers follow this transaction, so clicking and typing use the same upstream controller without an incompatible v1 component prop. A generic frame cannot obtain a submission merely by imitating an instance ID, and one session cannot consume another session's authorization. The adapter bounds both per-session and cross-session state with claimed-reservation-safe LRU eviction; long-lived headless hosts may also call `releaseChoiceSession(sessionId)` during teardown. Headless consumers may instead call `resolveChoice(choiceId, originalQuery)`; the resolver accepts only an ID actually offered for that ambiguous query. ## Unified response envelope Every response uses `agentskit.chat.answer` v1 and one outcome: | Outcome | Confidence | Provenance | Use | |---|---|---|---| | `answer` | `high/exact` or `high/backend` | local artifact entry or backend | One authoritative result | | `choices` | `medium/ambiguous` | local artifact entries | More than one exact result | | `escalation` | `low` with matching reason | none | Miss, stale, corrupt, or offline | Local adapter chunks expose the validated envelope at `chunk.metadata.answer`. Backend fallback receives the low-confidence envelope at `request.context.metadata['agentskit.chat.escalation']`. Its chunks continue streaming immediately and cancellation is forwarded unchanged; the composition observes bounded text incrementally and attaches the validated `high/backend` envelope to the final chunk. If backend output exceeds the answer-envelope limit, the visible stream is preserved and final metadata reports a low `corrupt` protocol escalation instead of claiming a truncated high-confidence answer. ## Artifact limits and compatibility Artifacts are capped at 512 KiB, 1,024 entries, and 16 aliases per entry. Queries are capped at 512 characters. Links accept safe root-relative paths or credential-free HTTP(S). Diagnostics are stable and never echo the rejected payload or raw Zod issues. Optional additive v1 fields are compatible. A new required field, normalization rule, outcome, or confidence meaning requires v2, migration fixtures, and a new ADR. Conformance fixtures cover exact match, ambiguity, miss, stale, corrupt, hash mismatch, backend provenance, and offline behavior through `@agentskit/chat/protocol/fixtures`. # Turn protocol v1 ## Ordered assistant content Use ordered assistant content when one canonical assistant message must combine streamed prose with portable application components. Create one encoder per assistant turn and yield each encoded value as a normal AgentsKit text chunk: ```ts import { createAssistantContentEncoder } from '@agentskit/chat/protocol' const content = createAssistantContentEncoder() yield { type: 'text', content: content.encode({ kind: 'text', text: answerChunk }) } yield { type: 'text', content: content.encode({ kind: 'component', frame: sourceListFrame }) } ``` Never concatenate raw model output into the envelope. `encode` validates and JSON-escapes every record. The first call writes the `agentskit.chat.content/1` prefix; later calls continue the same envelope. `decodeAssistantContent` returns completed ordered parts and marks an incomplete trailing record with `complete: false`, allowing renderers to hide partial transport bytes. Total content is limited to 256 KiB, 512 records, and 16 KiB per input text record; component frames retain their existing props and semantic-fallback limits. Existing plain-text messages and single `ComponentRenderFrame` messages remain valid. See [ADR-0021](../architecture/adrs/0021-ordered-assistant-content-records.md). The v1 protocol identifier is `agentskit.chat.turn`; every event carries version `1`, stable event/session/turn ids, a non-negative sequence, and an ISO timestamp. ## Events | Event | Direction | Payload | |---|---|---| | `client.turn.submit` | client → application | non-blank `input` | | `server.turn.snapshot` | application → client | canonical message wire records, AgentsKit status, token usage, optional safe diagnostic and lifecycle lineage | | `server.turn.diagnostic` | application → client | versioned code, safe message, retryability | Snapshots are complete projections, not deltas. Streaming sends successive snapshots with stable message ids and increasing sequence numbers. Optional lineage records `submit`, `retry`, `edit`, or `regenerate` plus parent turn/source message identities. `createSnapshotEvent` creates the canonical projection from AgentsKit messages. `createTurnSnapshotCursor(sessionId)` applies only newer snapshots from the expected session, making reconnect, duplicate delivery, and stale delivery inert without implementing another lifecycle reducer. Message records are validated by `validateMemoryRecord` from the published `@agentskit/core/memory-validation` subpath. The protocol package owns only the application envelope and must not reproduce AgentsKit message, content-part, tool-call, or memory schemas. ## Decode safely ```ts import { decodeTurnEvent } from '@agentskit/chat/protocol' const result = decodeTurnEvent(untrustedInput) if (!result.ok) { // Do not mutate state. report(result.diagnostic) return } apply(result.event) ``` The decoder returns `PROTOCOL_UNSUPPORTED_VERSION`, `PROTOCOL_UNKNOWN_EVENT`, or `PROTOCOL_INVALID_PAYLOAD`. It never returns raw Zod issues or copies the rejected payload into the diagnostic. ## Component frames and interactions The sibling `agentskit.chat.component` v1 envelope carries custom application render frames and semantic interaction events. A render frame contains `componentKey`, `instanceId`, unknown `props` for validation by the closed application manifest, and a required semantic fallback. A selection event preserves the component and instance identities plus the selected `choiceId`. Envelope decoding validates structure only. `@agentskit/chat` then verifies registration and the component-specific props schema. Unknown types, versions, components, invalid props, and invalid JSON are inert typed diagnostics. ## Compatibility - Optional additive fields are allowed within v1 and ignored by older decoders. - Unknown event kinds and versions are inert. - Removing or renaming required fields, changing meaning, or changing lifecycle semantics requires v2, migration fixtures, and a new ADR. - Versions are never silently coerced. `StreamChunk` and `AgentEvent` remain upstream AgentsKit adapter/observer contracts and are not wire events. ## Application sessions Persistent application metadata uses the separate `agentskit.chat.session` v1 envelope. `decodeSessionSnapshot` validates current snapshots and explicitly migrates the supported v0 shape. It contains session/definition identity, definition revision, cursor, deterministic decisions, and confirmation bindings; canonical messages are excluded and remain in AgentsKit `ChatMemory`. # Alpha dogfood channel `v0.1.0-alpha.2` is the current public artifact channel used by the external Docs, Registry, and Playbook migrations before stable v0. It adds the shared Ask-service integration on top of the ordered assistant-content protocol introduced in `alpha.1`. The GitHub prerelease contains npm-compatible tarballs and `SHA256SUMS` for `@agentskit/chat-protocol`, `@agentskit/chat`, `@agentskit/chat-react`, and `@agentskit/chat-server`. Consumers pin all required release asset URLs in their package manifest and lockfile. Because pnpm correctly rewrites `workspace:*` edges to the alpha semver while that semver is intentionally absent from npm, consumers must also redirect the two internal graph roots with `pnpm.overrides`: ```json { "dependencies": { "@agentskit/chat-protocol": "https://github.com/AgentsKit-io/agentskit-chat/releases/download/v0.1.0-alpha.2/agentskit-chat-protocol-0.1.0-alpha.2.tgz", "@agentskit/chat": "https://github.com/AgentsKit-io/agentskit-chat/releases/download/v0.1.0-alpha.2/agentskit-chat-0.1.0-alpha.2.tgz", "@agentskit/chat-react": "https://github.com/AgentsKit-io/agentskit-chat/releases/download/v0.1.0-alpha.2/agentskit-chat-react-0.1.0-alpha.2.tgz", "@agentskit/chat-server": "https://github.com/AgentsKit-io/agentskit-chat/releases/download/v0.1.0-alpha.2/agentskit-chat-server-0.1.0-alpha.2.tgz" }, "pnpm": { "overrides": { "@agentskit/chat-protocol": "https://github.com/AgentsKit-io/agentskit-chat/releases/download/v0.1.0-alpha.2/agentskit-chat-protocol-0.1.0-alpha.2.tgz", "@agentskit/chat": "https://github.com/AgentsKit-io/agentskit-chat/releases/download/v0.1.0-alpha.2/agentskit-chat-0.1.0-alpha.2.tgz" } } } ``` The clean-room verifier exercises this exact graph resolution. Consumers must not omit the overrides, import this repository's source, or use a local workspace path. The tag workflow verifies that the tagged commit belongs to `main`, installs from the committed lockfile, runs lint, test, build, and doc-bridge gates, packs the four-package graph, verifies its manifests and checksums, and exercises ESM, CJS, and a production Vite build in a clean-room consumer. It uploads a draft, verifies the complete asset set, and only then publishes with the repository-scoped GitHub token. GitHub Immutable Releases is enabled for this repository. No npm credential is used. This is intentionally narrower than the stable v0 release. See [ADR-0020](../architecture/adrs/0020-public-alpha-dogfood-channel.md) and [issue #30](https://github.com/AgentsKit-io/agentskit-chat/issues/30) for the remaining all-renderer, provenance, compatibility, documentation, and npm publication gates. # v0.4 compatibility matrix The two public AgentsKit Chat packages ship as the fixed `0.4.0` group. The peer ranges in the published manifests are authoritative; this table summarizes the supported minimums and release evidence. | Renderer | AgentsKit binding | Host framework | Evidence | |---|---|---|---| | React | `@agentskit/react ^0.7.1` | React 18+ | component tests, Chromium E2E, conformance | | React Native | `@agentskit/react-native ^0.4.4` | React 18+, React Native | component/accessibility tests, Expo web/iOS bundle, conformance | | Ink | `@agentskit/ink ^0.10.1` | Ink 7.1+, React 18+ | component tests, real PTY interactions, conformance | | Vue | `@agentskit/vue ^0.4.4` | Vue 3.4+ | component tests, build, conformance | | Svelte | `@agentskit/svelte ^0.4.4` | Svelte 5+ | component tests, SSR test, package build, conformance | | Solid | `@agentskit/solid ^0.4.4` | Solid 1.9+ | component tests, build, conformance | | Angular | `@agentskit/angular ^0.4.6` | Angular 18.1–21, RxJS 7 | component tests, partial-Ivy AOT package test, conformance | Core packages require the published AgentsKit ranges in their manifests: `@agentskit/core` 1.12.x, `@agentskit/memory` 0.11.x, `@agentskit/statechart` 0.2.x, and `@agentskit/eval` 0.4.19+ where used. The release workflow builds on Node 24 with npm 11 and also runs the normal CI matrix on Node 22. The [generated conformance matrix](../conformance/matrix.generated.md) is the release-blocking component/event/platform record. A range being installable is not enough: a new upstream version is promoted only after the repository gates pass against it. # Stable public launch checklist Executable close-out checklist for [issue #104](https://github.com/AgentsKit-io/agentskit-chat/issues/104). AgentsKit Chat `0.4.0` adds controlled React and Ink sessions to the consolidated two-package graph. ## Documentation - [x] Quick starts for React, React Native, Ink, Vue, Svelte, Solid, and Angular - [x] API reference, deployment modes, stability, security, and changelog - [x] Compatibility matrix, `0.4.0` release notes, and `0.2.x` migration map - [x] Agent handoffs and doc-bridge ownership routing - [x] Host adapter recipes for Next.js, Hono, Express, and Cloudflare Workers - [x] README install path targets the two-package npm graph ## Engineering gates Run from a clean checkout of `main`: ```bash pnpm install --frozen-lockfile pnpm lint pnpm test pnpm build pnpm test:conformance pnpm conformance:gate pnpm bundle:budget pnpm docs:bridge:index pnpm docs:bridge:doctor pnpm docs:bridge:gate pnpm release:gate pnpm release:pack pnpm test:e2e pnpm test:pty ``` - [ ] Release and post-merge workflows pass on the `0.4.0` line - [ ] Both public tarballs match `SHA256SUMS` - [ ] All seven renderer exports pass clean-install verification - [ ] Expo web and iOS production exports pass in the release workflow ## Distribution - [ ] Immutable `v0.4.0` tag and GitHub release are public - [ ] Both packages resolve from npm at exact `0.4.0` - [ ] npm provenance attestations and registry signatures verify - [ ] Clean external installation, ESM/CJS imports, and CLI scaffolding pass - [ ] npm Trusted Publishing succeeds for both packages through OIDC - [x] Temporary bootstrap `NPM_TOKEN` is absent from the protected `npm` environment The stable workflow must remain token-free. Its protected publish job uses `id-token: write`, npm 11, repository `AgentsKit-io/agentskit-chat`, workflow `release.yml`, and environment `npm`. ## Dogfood - [ ] AgentsKit Docs and Registry use the consolidated public package - [ ] Registry, Playbook, Doc Bridge, and approved private tracers pass their declared pins - [ ] Frozen installs, strict typechecks/tests, and production builds pass - [ ] Public browser smoke passes for Docs, Registry, and Playbook - [ ] No private behavior, identifier, data, or topology enters public evidence ## Product close-out - [ ] Record release, provenance, checksums, clean install, and host smoke links on #104 - [ ] Complete the convergence ledger only after every public and private tracer passes # Migrate from `0.1.0-alpha.2` Stable `0.1.0` keeps the alpha.2 application and protocol contracts and adds the complete npm package matrix. No source-level API rename is required. 1. Replace GitHub release-asset URLs with npm ranges. 2. Remove `pnpm.overrides` entries that redirected internal alpha packages. 3. Install the renderer, core, protocol/server packages, and their documented peers at `0.1.0`. 4. Regenerate and commit the lockfile. 5. Run the host typecheck, production build, interaction/E2E suite, and any stored-session migration fixtures. Example: ```json { "dependencies": { "@agentskit/chat": "^0.1.0", "@agentskit/chat-protocol": "^0.1.0", "@agentskit/chat-react": "^0.1.0", "@agentskit/chat-server": "^0.1.0" } } ``` The immutable alpha release and checksums remain available for reproducible old lockfiles, but receive no new features. Do not mix alpha asset overrides with stable npm packages in one dependency graph. # Upgrade from 0.1.x to 0.2.0 AgentsKit Chat packages are a fixed version group. Upgrade every installed `@agentskit/chat*` package to `0.2.0` in the same change and commit the updated lockfile. ```json { "dependencies": { "@agentskit/chat": "0.2.0", "@agentskit/chat-protocol": "0.2.0", "@agentskit/chat-react": "0.2.0", "@agentskit/chat-server": "0.2.0" } } ``` Existing definitions and renderer shells require no source migration. The new backend APIs are additive. Applications adopting them should configure the documented server boundary, keep corpus and persona authority in trusted server configuration, and limit client hosts to documented identity, endpoint, and presentation seams. After updating, run the host typecheck, tests, production build, and a real streaming smoke test. A service that emits Ask NDJSON may use the canonical NDJSON content type; `0.2.0` also recognizes valid records when an intermediary incorrectly labels the response as `text/plain`. See the [backend guide](../backend.md), [Ask protocol](../protocol/ask-service.md), and [deployment guide](../deployment.md). # Upgrade from 0.2.x to 0.3.0 AgentsKit Chat `0.3.0` reduces the public npm graph to `@agentskit/chat` and `@agentskit/chat-cli`. Remove the former standalone Chat packages after moving their imports to the corresponding subpaths. | Former package | `0.3.0` import | |---|---| | `@agentskit/chat-protocol` | `@agentskit/chat/protocol` | | `@agentskit/chat-server` | `@agentskit/chat/server` | | `@agentskit/chat-devtools` | `@agentskit/chat/devtools` | | `@agentskit/chat-react` | `@agentskit/chat/react` | | `@agentskit/chat-react-native` | `@agentskit/chat/react-native` | | `@agentskit/chat-ink` | `@agentskit/chat/ink` | | `@agentskit/chat-vue` | `@agentskit/chat/vue` | | `@agentskit/chat-svelte` | `@agentskit/chat/svelte` | | `@agentskit/chat-solid` | `@agentskit/chat/solid` | | `@agentskit/chat-angular` | `@agentskit/chat/angular` | Install only the consolidated package, the AgentsKit binding, and framework peers used by the host: ```bash npm install @agentskit/chat@0.3.0 @agentskit/react react npm install --save-dev @agentskit/chat-cli@0.3.0 ``` Protocol versions, deterministic answers, Ask requests, session persistence, component identities, and renderer behavior are unchanged. The migration is a package/import rewrite rather than a runtime or business-logic migration. After updating, remove obsolete package entries from the manifest and lockfile, perform a frozen install, and run typecheck, tests, production build, and a real streaming interaction smoke test. Do not keep aliases or compatibility overrides that hide a remaining legacy import. ## Optional migration for host-owned sessions Hosts that already receive a complete chat snapshot from a trusted session service may replace local controller wiring with the additive `controlled` prop on the React or Ink `AgentChat`. The source supplies a serializable snapshot and the canonical AgentsKit lifecycle callbacks. Do not run both modes for one mounted chat, and do not move authentication, authorization, persistence, transport, or business rules into the public driver. Hosts that use `definition.chat` locally need no change. # Release process Stable publishing is intentionally HITL and runs only from `.github/workflows/release.yml` on a stable `v*` tag contained in `main`. ## Prepare 1. Add a Changeset for every public behavior or contract change. 2. Run `pnpm version:packages`, review the fixed-group versions/changelogs, and update `release/manifest.json` plus the release notes. 3. Run `pnpm release:gate`, `pnpm release:pack`, and the full CI matrix. 4. Obtain architecture, security/privacy, compatibility, and launch review. 5. Merge the release PR. Create the stable tag only after all required checks and the protected `npm` environment are ready. ## Publish The workflow verifies tag ancestry and exact fixed-group versions, installs from the lockfile without a dependency cache, runs every quality/conformance, browser, native, PTY, documentation, package, and clean-room gate, then packs both public packages with SHA-256 checksums. The protected publish job uses a GitHub-hosted runner with `id-token: write` and npm provenance. The `0.1.0` bootstrap used a short-lived token because the package names did not yet exist. That credential has been removed. Both public packages now trust `AgentsKit-io/agentskit-chat`, workflow `release.yml`, environment `npm`; npm 11 uses GitHub OIDC and every publish requests public access and provenance. A GitHub release is made public only after both npm publishes succeed. See npm's official guidance for [trusted publishing](https://docs.npmjs.com/trusted-publishers/), [provenance](https://docs.npmjs.com/generating-provenance-statements/), and [scoped public packages](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/). ## Verify Confirm both npm pages show the version in `release/manifest.json` and provenance, install the package graph from npm in a clean directory, run `npm audit signatures`, verify GitHub assets against `SHA256SUMS`, and smoke the Docs, Registry, and Playbook hosts. Record links and results on issue #30 before closing the milestone. # v0 stability and upgrades AgentsKit Chat follows SemVer as a fixed package group. - Patch releases preserve public TypeScript signatures, protocol versions, component/event meaning, and persisted session compatibility. - During `0.x`, a minor release may change a public contract, but only with an RFC or ADR as required, a changelog entry, a migration guide, and conformance evidence for all affected renderers. - Protocol v1 payloads remain runtime validated. A breaking wire change uses a new protocol version and an explicit decoder/migration path. - Deprecated APIs remain for at least one subsequent minor release unless a security issue makes that unsafe. - AgentsKit peer minimums change only after upstream-first inspection and the full release matrix. No upstream implementation is copied downstream. Applications should pin a minor line (`^0.4.0` for this release), commit their lockfile, run renderer conformance and host E2E before upgrades, and review both the changelog and migration guide. Stable `0.1.x` consumers should follow the [0.2 migration](./migration-to-0.2.md). Alpha GitHub tarball consumers should follow [the alpha migration](./migration-from-alpha.md). Stable `0.2.x` consumers should follow the [0.3 package consolidation migration](./migration-to-0.3.md). Applications already on `0.3.x` can adopt `0.4.0` without changing their definition-owned sessions; controlled React and Ink modes are additive. # AgentsKit Chat 0.1.0 AgentsKit Chat v0 is the first stable cross-framework application layer over AgentsKit: one typed chat definition, native experiences in seven renderers. ## Included - Framework-neutral application definitions, deterministic conversation routes, statechart composition, typed actions, policy, confirmation, session metadata, semantic themes, and registered components. - Runtime-validated turn, session, ordered content, Ask, component, and local deterministic-answer protocols. - React, React Native, Ink, Vue, Svelte, Solid, and Angular native shells. - Web-standard server handler, seven-renderer CLI/component generator, and replay/conformance devtools. - Support, onboarding, protected operations, and cited RAG references. - Public Docs, Registry, and Playbook dogfood evidence plus synthetic private- consumer conformance with no private behavior in public artifacts. Public adoption evidence is linked from the [dogfood migration record](../dogfood/registry-playbook.md). Confidential validation contributes no source, fixture, schema, prompt, policy, data shape, or business behavior to this release. ## Quality promise Release is blocked on all package tests/builds, clean installs, ESM/CJS export checks, protocol meta-tests, renderer component/event conformance, accessibility, browser E2E, Expo web/iOS bundles, Ink PTY interactions, doc-bridge, package metadata, checksums, and npm provenance. See [get started](../getting-started/README.md), [compatibility](./compatibility.md), [stability](./stability.md), [security](../../SECURITY.md), and [the changelog](../../CHANGELOG.md). # AgentsKit Chat 0.2.0 `0.2.0` is the trusted-backend release for ecosystem dogfooding. It keeps the same framework-neutral chat definition and seven native renderers from `0.1.0` while adding a production boundary for requests that cannot be answered by a host's deterministic artifact. ## What changed - The browser sends only a runtime-validated Ask request and resumable session cursor. Authentication determines the trusted site, corpus, and persona. - Exact facts stay local through the deterministic answer plane. Only a validated escalation may reach the backend. - Hosts inject AgentsKit RAG retrieval and model generation. Local and federated sources use the same grounded citation contract. - CAS persistence prevents concurrent turns and cursor rollback. Deadlines, cancellation, rate limits, safe diagnostics, and request IDs are mandatory. - Metrics include latency, event counts, retrieval, persistence, token usage, and cost without prompts, answers, source content, subjects, or session IDs. - The shared Ask adapter recognizes valid NDJSON even when an intermediary incorrectly labels it as `text/plain`; ordinary text keeps its safe fallback. ## Upgrade Keep the fixed package group on one minor line: ```json { "dependencies": { "@agentskit/chat": "^0.2.0", "@agentskit/chat-protocol": "^0.2.0", "@agentskit/chat-react": "^0.2.0", "@agentskit/chat-server": "^0.2.0" } } ``` Existing `0.1.x` chat definitions remain source-compatible. Upgrade the fixed package graph together and follow the [0.2 migration guide](./migration-to-0.2.md). See the [backend guide](../backend.md), [Ask protocol](../protocol/ask-service.md), and [ADR-0026](../architecture/adrs/0026-trusted-ask-backend-vertical.md) for integration and security details. ## Evidence The release gate builds and clean-installs all 12 packages, exercises all seven renderers, runs browser and terminal E2E, validates Doc Bridge output, and verifies the immutable tarball checksums before publication. # AgentsKit Chat 0.3.0 `0.3.0` is the public-package consolidation release. The framework still owns the same protocol, server, devtools, and seven native renderer contracts, but npm consumers install only `@agentskit/chat` and `@agentskit/chat-cli`. ## What changed - `@agentskit/chat/protocol`, `/protocol/fixtures`, `/server`, and `/devtools` replace the former standalone infrastructure packages. - `/react`, `/react-native`, `/ink`, `/vue`, `/svelte`, `/solid`, and `/angular` expose the native renderers from the same public package. - Framework runtimes and AgentsKit bindings remain optional peers. Importing one subpath does not load an unrelated renderer. - Renderer workspaces remain private implementation modules with independent builds, tests, accessibility evidence, and bundle budgets. - The CLI generates projects against the consolidated public surface. ## Upgrade Follow the [0.3 migration guide](./migration-to-0.3.md). Replace former package dependencies and imports with `@agentskit/chat` subpaths, update the CLI to `0.3.0`, install the selected renderer's peer dependencies, and run the host's typecheck, tests, production build, and interaction smoke test. ## Evidence The stable workflow builds and clean-installs both public tarballs, verifies all seven renderer exports, runs browser, Expo, and PTY gates, checks Doc Bridge and README claims, publishes through npm Trusted Publishing with provenance, and attaches immutable SHA-256 checksums to the GitHub release. # AgentsKit Chat 0.4.0 `0.4.0` adds an externally controlled session seam for hosts that already own their transport and lifecycle state. The host supplies a validated serializable snapshot and lifecycle callbacks; AgentsKit Chat supplies the shared application presentation without creating another controller or session store. ## What changed - `createControlledChatDriver` and `parseControlledChatSnapshot` expose the framework-neutral controlled contract from `@agentskit/chat`. - React `AgentChat` and Ink `AgentChat` accept a controlled source while keeping their existing definition-owned mode compatible. - The controlled lifecycle covers input, send, cancel, retry, edit, regenerate, approval, denial, and semantic component interaction. - Runtime validation rejects unknown or malformed external state before it can reach a renderer. - Synthetic fixtures exercise idle, streaming, error, cancellation-ready, confirmation, and semantic-component states across the shared driver. ## Ownership boundary Controlled mode does not move transport, authentication, authorization, persistence, business actions, or host session state into AgentsKit Chat. It is an application projection over host-owned state. Definition-owned applications may continue using `defineChat`, `createChatSession`, and the existing renderer props without changes. ## Upgrade Update both fixed-group packages together: ```bash npm install @agentskit/chat@0.4.0 npm install --save-dev @agentskit/chat-cli@0.4.0 ``` No migration is required for existing definition-owned sessions. Adopt the controlled source only when the host must retain ownership of its controller and persisted session contract. ## Evidence The stable workflow builds and clean-installs both public tarballs, verifies all seven renderer exports, runs browser, Expo, and real-PTY gates, checks the controlled synthetic conformance suite, and publishes through npm Trusted Publishing with provenance and immutable SHA-256 checksums. # Web-standard server handler `createChatHandler` mounts one shared chat definition behind the platform `Request`, `Response`, `ReadableStream`, and `AbortSignal` APIs. For deterministic misses that need cited semantic retrieval, use the sibling [`createAskServiceHandler`](./backend.md). Both handlers use Web standards; the Ask vertical additionally resolves a complete trusted site policy and shares one public request/configuration contract across hosted and self-hosted deployments. ```ts const handleChat = createChatHandler({ authenticate: async request => { const identity = await verifyBearer(request) return identity ? { ok: true, context: identity } : { ok: false, response: new Response('Unauthorized', { status: 401 }) } }, resolveDefinition: context => chats.forTenant(context?.tenantId), sessionStorage: context => sessions.forTenant(context?.tenantId), timeoutMs: 30_000, }) ``` Send an encoded `client.turn.submit` event as `application/json`. A successful response is `application/x-ndjson`; every line is an encoded turn event and can be passed to `decodeTurnEvent` and a session-bound snapshot cursor. Authentication completes before parsing untrusted JSON. Trusted context exists only in the host closure and is never read from the submit payload. Return a host-owned response for authentication failures. Canonical messages remain in `definition.chat.memory`. Application metadata uses the CAS `SessionStorage` contract. The handler waits for the final canonical message save before closing a successful response. `sessionStorage` is required because the persisted cursor, active-turn lease, and latest 64 terminal turns prevent sequence rollback, concurrent execution, and recent sequential replay of model/tool effects. Terminal outcomes distinguish `completed` from `indeterminate` when canonical memory could not be saved. The handler claims the turn through CAS before creating the controller and releases it only after bounded cleanup. State updates are coalesced to the latest full snapshot under backpressure. The default body limit is 64 KiB and deadline is 30 seconds. Method, media type, body, event, timeout, cancellation, and internal failures use safe versioned diagnostics. Request abort, response cancellation, and timeout stop the upstream AgentsKit controller/source. ## Deployment - Next/Remix/SvelteKit/edge: export the returned handler directly where Web handlers are supported. - Node HTTP: translate the incoming request to `Request` and pipe the returned `Response.body`; the package test contains a reference bridge. - Express/Hono adapters should remain thin and must forward disconnect cancellation. ## Semantic Ask backend `createAskServiceHandler` authenticates and resolves site identity before body parsing, treats client corpus/persona parameters only as equality-checked compatibility hints, and injects the trusted site policy into local or federated AgentsKit retrieval. A successful answer always includes at least one safe citation. Rate limiting, provider generation, CAS storage, and telemetry are host adapters rather than bundled infrastructure. See the [complete backend guide](./backend.md) and [ADR-0026](./architecture/adrs/0026-trusted-ask-backend-vertical.md). # Persistent cross-client sessions AgentsKit remains the message authority. Configure one of its `ChatMemory` implementations on `definition.chat.memory`, then store only AgentsKit Chat application metadata through `SessionStorage`. ```ts const session = await resumeChatSession(definition, { sessionId: 'customer-42', storage: applicationSessionStorage, }) ``` The same preparation works before mounting React Native or Ink. Each client loads the same session id from the host storage and points its `ChatConfig.memory` at the same canonical conversation. `SessionStorage.save(snapshot, expectedCursor)` must be atomic: create only when `expectedCursor` is `undefined`, update only when the stored cursor equals it, and return `false` on conflict. This CAS claim prevents two resumed clients from resolving the same pending action. A plain last-write-wins key/value write is unsafe and does not implement the port. Snapshots use `agentskit.chat.session` version 1. They contain definition identity/revision, deterministic application state, monotonic cursor, and pending or terminal confirmation bindings. They never contain messages. Increment `definition.revision` when a state-machine change makes old application metadata incompatible. `resumeChatSession` starts clean when `load` returns `null` or `undefined`. Invalid JSON, unknown versions, session mismatch, definition mismatch, and revision mismatch reject before hydration. Version 0 is the only implicit migration currently supported. Call `session.persist()` at an explicit durability boundary. Deterministic transitions also schedule saves. Confirmation changes await durable storage; a failure rejects the operation. Resolution moves through a durable processing status before delegation and reaches terminal status only after upstream success. A crash leaves processing state for host reconciliation, while restored processing/terminal confirmations remain inert. # Theming and native composition Pass the same semantic theme to any application shell: ```ts const theme = { colors: { accent: '#7c3aed', danger: '#dc2626' }, spacing: { medium: 16 }, radius: { large: 18 }, } ``` React, Vue, Svelte, Solid, Angular, React Native, and Ink application shells accept `theme`. Input is runtime validated, unknown tokens fail early, and missing tokens use accessible defaults. ## Capability map | Intent | React | Vue | Svelte | Solid | Angular | React Native | Ink | |---|---|---|---|---|---|---|---| | colors | AgentsKit CSS variables | same CSS variables | same CSS variables | same CSS variables | same CSS variables | upstream wrapper/text styles | complete `InkTheme` | | spacing | CSS variables | CSS variables | CSS variables | CSS variables | CSS variables | numeric native styles | unsupported | | radius | CSS variables | CSS variables | CSS variables | CSS variables | CSS variables | native border radius | unsupported | | font family | CSS stack/variable | CSS stack/variable | CSS stack/variable | CSS stack/variable | CSS stack/variable | native default/custom loaded family | unsupported | The mapping helpers are public for host integration: React, Vue, Svelte, Solid, and Angular publish CSS-variable helpers; native and terminal publish `toChatNativeStyles` and `toChatInkTheme`. ## Native slots React, React Native, and Ink accept a component slot map. Vue uses typed named scoped slots. Svelte uses typed Svelte 5 snippets. Solid uses typed render props named `container`, `message`, `input`, `thinking`, `confirmation`, and `choiceList`. Angular uses named content templates for `container`, `message`, `input`, `thinking`, `confirmation`, and `choiceList`. Composition is never serialized into `ChatDefinition`. ```tsx ``` Defaults provide live-region announcements, labeled controls, alerts, native button behavior, and one active Ink keyboard owner. A replacement slot must preserve equivalent accessibility and keyboard semantics. ## Fully headless state Use the upstream binding directly when no default shell is wanted: ```ts import { useChat } from '@agentskit/react' // or vue / react-native / ink const state = useChat(definition.chat) ``` Vue headless consumers import the same `useChat` contract from `@agentskit/vue`. AgentsKit remains the owner of streaming, messages, tools, memory, retry/edit/regenerate, and cancellation. AgentsKit Chat does not wrap or reproduce that hook.