@threadplane/chat 0.0.47 → 0.0.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,9 @@
4
4
 
5
5
  ### Changed
6
6
 
7
+ - **`@angular/forms` peer dependency removed:** `chat-input` now binds its textarea with a direct `[value]`/`(input)` pair (fixes the composer keeping sent text under zoneless + OnPush). `@threadplane/chat` no longer requires `@angular/forms` — consumers may drop it unless they use it themselves.
8
+ - **json-render store isolation:** `<chat>`'s json-render message surfaces no longer fall back to the conversation-wide internal store — each surface self-seeds from its spec's `state` unless you pass an explicit `[store]`. Pass `[store]` (e.g. `signalStateStore({})`) when dashboards should receive backend agent state (STATE_SNAPSHOT) or share live values across surfaces; same-key dashboards in different messages are now isolated by default. Tool views (`chat-tool-views`) keep the previous shared-store behavior.
9
+ - **Public API trim:** `@threadplane/chat` no longer re-exports `provideViews` / `VIEW_REGISTRY` from `@threadplane/render`. Consumers using `<render-spec>` / `<render-element>` directly should import from `@threadplane/render`. For chat's markdown view overrides, provide `MARKDOWN_VIEW_REGISTRY` directly using `overrideViews(cacheplaneMarkdownViews, { … })` from `@threadplane/render` — the previously-documented `provideViews(withViews(…))` pattern never actually drove rendering.
7
10
  - **License:** `@threadplane/chat` is dual-licensed under PolyForm Noncommercial 1.0.0 (free noncommercial use) or a Threadplane Commercial license (production use inside a for-profit context).
8
11
 
9
12
  ### Migration
package/README.md CHANGED
@@ -1,28 +1,54 @@
1
1
  # @threadplane/chat
2
2
 
3
- Drop-in agent chat UI for Angular 20+. Headless primitives that read from a runtime-neutral `Agent` contract, plus opinionated compositions (`<chat>`, `<chat-debug>`, GenUI surfaces) you can ship in days.
3
+ Drop-in agent chat UI for Angular 20+. Headless UI primitives plus opinionated compositions that read a runtime-neutral `Agent` contract ship a production chat surface in days without coupling to a specific backend.
4
4
 
5
5
  Part of [Threadplane](https://github.com/cacheplane/angular-agent-framework).
6
6
 
7
- `@threadplane/chat` is source-available and free for noncommercial use under PolyForm Noncommercial License 1.0.0. Commercial production use requires a Threadplane Commercial license.
7
+ <p>
8
+ <a href="https://www.npmjs.com/package/@threadplane/chat">
9
+ <img alt="npm version" src="https://img.shields.io/npm/v/@threadplane%2Fchat?color=6C8EFF&labelColor=080B14&style=flat-square" />
10
+ </a>
11
+ <img alt="Angular 20+" src="https://img.shields.io/badge/Angular-20%2B%20%7C%2021-6C8EFF?labelColor=080B14&style=flat-square" />
12
+ <img alt="License" src="https://img.shields.io/badge/License-PolyForm%20NC%20%7C%20Commercial-6C8EFF?labelColor=080B14&style=flat-square" />
13
+ </p>
8
14
 
9
- This package is not licensed as OSI open source because commercial use requires a license. Threadplane uses a source-available model for `@threadplane/chat` while keeping protocol and ecosystem packages permissively licensed where appropriate.
15
+ **Source-available.** Free for noncommercial use under PolyForm Noncommercial License 1.0.0. Commercial production use SaaS, internal tools, agency work, paid client projects requires a [Threadplane Commercial license](https://threadplane.ai/pricing).
10
16
 
11
- ## Commercial use
17
+ ---
12
18
 
13
- Building a commercial product, SaaS application, internal business tool, agency deliverable, or paid client project with `@threadplane/chat` requires a Threadplane Commercial license.
19
+ ## What it does
14
20
 
15
- Free under PolyForm Noncommercial:
21
+ - **Full chat surface in one tag.** `<chat [agent]="agent" />` wires up message history, streaming output, typing indicator, input, interrupts, tool calls, subagents, citations, and generative UI — all from a single binding.
22
+ - **Layered architecture.** Use the opinionated compositions for fast shipping, drop down to individual primitives (30+) to build custom layouts, or mix both.
23
+ - **Runtime-neutral.** Compositions consume an `Agent` contract. The library has no hard dependency on LangGraph, AG-UI, or any other backend — swap or combine adapters without touching your UI.
24
+ - **A2UI generative UI.** Agents emit structured surface specs; `<a2ui-surface>` renders them as interactive Angular components with a themeable `--a2ui-*` token system.
16
25
 
17
- - Personal, hobby, student, academic, nonprofit, public-demo use
18
- - Open-source applications released under an OSI-approved license
19
- - 30 calendar days of commercial evaluation from your first commercial use (good-faith — no tracking, no email required)
26
+ ---
20
27
 
21
- See [COMMERCIAL-USE.md](./COMMERCIAL-USE.md) for the definition of commercial use, [LICENSE-COMMERCIAL.md](./LICENSE-COMMERCIAL.md) for the commercial license summary, and the [Threadplane pricing page](https://threadplane.ai/pricing) for plans.
28
+ ## Install
22
29
 
23
- ## Using a commercial license
30
+ ```bash
31
+ npm install @threadplane/chat
32
+ ```
33
+
34
+ **Peer dependencies:**
24
35
 
25
- After purchase, Threadplane emails a signed license token to the address on your receipt. The license is valid for 12 months and the same email contains the token to paste into your app's `provideChat()` configuration:
36
+ ```
37
+ @angular/core ^20.0.0 || ^21.0.0
38
+ @angular/common ^20.0.0 || ^21.0.0
39
+ @angular/platform-browser ^20.0.0 || ^21.0.0
40
+ @threadplane/licensing *
41
+ @threadplane/render *
42
+ @threadplane/a2ui *
43
+ @json-render/core ^0.16.0
44
+ @langchain/core ^1.1.33
45
+ rxjs ~7.8.0
46
+ marked ^15.0.0 || ^16.0.0
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Quick start
26
52
 
27
53
  ```typescript
28
54
  // app.config.ts
@@ -30,134 +56,251 @@ import { ApplicationConfig } from '@angular/core';
30
56
  import { provideChat } from '@threadplane/chat';
31
57
 
32
58
  export const appConfig: ApplicationConfig = {
33
- providers: [
34
- provideChat({
35
- license: 'eyJ…', // The token from your purchase email.
36
- }),
37
- ],
59
+ providers: [provideChat({ license: 'eyJ…' })],
38
60
  };
39
61
  ```
40
62
 
41
- The library verifies the token's signature on boot. A missing, expired, or tampered token logs a `console.warn` advisory but does not block rendering — chat continues to work either way. Tokens are validated offline; no calls to Threadplane are made at runtime.
63
+ ```typescript
64
+ // my.component.ts
65
+ import { Component } from '@angular/core';
66
+ import { ChatComponent } from '@threadplane/chat';
67
+ import { agent } from '@threadplane/langgraph';
68
+
69
+ @Component({
70
+ selector: 'app-root',
71
+ imports: [ChatComponent],
72
+ template: `<chat [agent]="myAgent" />`,
73
+ })
74
+ export class AppComponent {
75
+ myAgent = agent({ apiUrl: '/api/langgraph', graphId: 'agent' });
76
+ }
77
+ ```
42
78
 
43
- The license string is safe to commit to source control if your repository is private, or to read from a build-time env var for public repositories:
79
+ Get the `agent` signal from `@threadplane/langgraph` (for LangGraph Platform backends) or `@threadplane/ag-ui` (for AG-UI-compatible backends). See those packages for setup details.
44
80
 
45
- ```typescript
46
- declare const THREADPLANE_LICENSE: string | undefined;
81
+ ---
47
82
 
48
- providers: [
49
- provideChat({
50
- license: typeof THREADPLANE_LICENSE === 'string' ? THREADPLANE_LICENSE : undefined,
51
- }),
52
- ],
53
- ```
83
+ ## Capabilities
54
84
 
55
- (See `examples/chat/angular/` in the framework repo for a working example.)
85
+ ### Compositions
56
86
 
57
- ## Runtime adapters
87
+ Ready-to-use full-feature layouts:
58
88
 
59
- Chat primitives consume a runtime-neutral `Agent` contract. Two adapters ship today:
89
+ | Component | Selector | Description |
90
+ |---|---|---|
91
+ | `ChatComponent` | `<chat>` | Full-page chat layout; primary entry point |
92
+ | `ChatPopupComponent` | `<chat-popup>` | Floating popup with a launcher button |
93
+ | `ChatSidebarComponent` | `<chat-sidebar>` | Sidebar-docked layout |
94
+ | `ChatSidenavComponent` | `<chat-sidenav>` | Sidenav host with project/thread list panel |
95
+ | `ChatTimelineSliderComponent` | `<chat-timeline-slider>` | Time-travel slider for agent checkpoint history |
96
+ | `ChatInterruptPanelComponent` | `<chat-interrupt-panel>` | Full interrupt-handling composition |
97
+ | `ChatApprovalCardComponent` | `<chat-approval-card>` | Approval/rejection dialog for HITL flows |
98
+ | `ChatToolCallCardComponent` | `<chat-tool-call-card>` | Rich card for a single tool call |
99
+ | `ChatSubagentCardComponent` | `<chat-subagent-card>` | Rich card for a subagent delegation |
60
100
 
61
- - **`@threadplane/langgraph`** — for LangGraph / LangGraph Platform backends.
62
- - **`@threadplane/ag-ui`** — for any AG-UI-compatible backend (LangGraph, CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit runtime).
101
+ ### Primitives
63
102
 
64
- Custom backends can implement `Agent` directly with no library dependency.
103
+ 30+ standalone components for custom layouts:
65
104
 
66
- See the capability matrix in the docs site for which primitives require which runtime capabilities.
105
+ `<chat-message-list>`, `<chat-message>`, `<chat-message-actions>`, `<chat-window>`, `<chat-input>`, `<chat-typing-indicator>`, `<chat-tool-calls>`, `<chat-subagents>`, `<chat-citations>`, `<chat-streaming-md>`, `<chat-trace>`, `<chat-reasoning>`, `<chat-interrupt>`, `<chat-error>`, `<chat-scroll-bubble>`, `<chat-launcher-button>`, `<chat-suggestions>`, `<chat-welcome>`, `<chat-select>`, `<chat-thread-list>`, `<chat-project-list>`, `<chat-timeline>`, `<chat-generative-ui>`, `<chat-genui-skeleton>`, `<chat-overflow-menu>`, `<chat-confirm-dialog>`, `<chat-history-search-palette>`, `<chat-sidenav-scrim>`.
67
106
 
68
- ## Citations
107
+ Custom content templates for message bubbles, tool call rows, and citation cards use structural directives: `MessageTemplateDirective`, `ChatToolCallTemplateDirective`, and `ChatCitationCardTemplateDirective`.
69
108
 
70
- Chat messages can include citations to sources. The `Citation` interface provides structured metadata for each source:
109
+ ### Human-in-the-loop (interrupts)
71
110
 
72
- ```ts
73
- interface Citation {
74
- id: string; // Unique identifier for the citation
75
- index?: number; // Display index (1-based) for inline markers
76
- title?: string; // Source title
77
- url?: string; // Source URL
78
- snippet?: string; // Quoted excerpt
79
- extra?: unknown; // Custom fields per adapter
80
- }
111
+ `<chat-interrupt-panel>` surfaces the current `AgentInterrupt` from an agent and renders approve/reject controls. `<chat-approval-card>` composes as a dialog for explicit approval workflows. Both emit typed action results (`InterruptAction`, `ChatApprovalAction`) that the caller submits back to the agent.
112
+
113
+ ```html
114
+ <chat-interrupt-panel
115
+ [agent]="agent"
116
+ (interruptAction)="onAction($event)"
117
+ />
118
+ ```
119
+
120
+ ### Tool calls and subagents
121
+
122
+ `<chat-tool-calls>` renders in-progress and completed tool calls. Customize per-call layout with `ChatToolCallTemplateDirective` — the `chatToolCallTemplate` input takes a tool name to match, or `"*"` for all; the template context exposes the `ToolCall` (`$implicit`) and its `status`:
123
+
124
+ ```html
125
+ <chat-tool-calls [agent]="agent">
126
+ <ng-template chatToolCallTemplate="*" let-call let-status="status">
127
+ <my-tool-card [call]="call" [status]="status" />
128
+ </ng-template>
129
+ </chat-tool-calls>
81
130
  ```
82
131
 
83
- ### Message citations
132
+ `<chat-subagents>` and `<chat-subagent-card>` track delegated subagent activity with live status.
84
133
 
85
- Adapters populate `Message.citations?: Citation[]` from their respective backends. Messages are rendered with the `<chat-citations [message]="message" />` primitive, which displays a collapsible sources panel under assistant messages.
134
+ ### Citations
86
135
 
87
- ### Rendering sources
136
+ The `Citation` interface provides structured source metadata for assistant messages:
88
137
 
89
- Use the `<chat-citations>` component to render a sources panel. Customize the card layout with the optional `chatCitationCard` ng-template:
138
+ ```ts
139
+ interface Citation {
140
+ id: string;
141
+ index?: number; // 1-based display index for inline superscript markers
142
+ title?: string;
143
+ url?: string;
144
+ snippet?: string;
145
+ extra?: unknown; // adapter-specific fields
146
+ }
147
+ ```
148
+
149
+ Use `<chat-citations>` to render a collapsible sources panel under assistant messages. Customize the card layout with the `chatCitationCard` template directive:
90
150
 
91
151
  ```html
92
- <chat [agent]="agent" />
93
- <!-- or explicit: -->
94
152
  <chat-citations [message]="message">
95
153
  <ng-template chatCitationCard let-citation>
96
- <div class="custom-card">
97
- <a [href]="citation.url">{{ citation.title }}</a>
98
- <p class="text-sm text-gray-600">{{ citation.snippet }}</p>
99
- </div>
154
+ <a [href]="citation.url">{{ citation.title }}</a>
155
+ <p>{{ citation.snippet }}</p>
100
156
  </ng-template>
101
157
  </chat-citations>
102
158
  ```
103
159
 
104
- ### Inline markers
160
+ Inline citation markers are rendered automatically by `MarkdownCitationReferenceComponent` inside streaming markdown output — superscript indices link to the corresponding card in the sources panel.
161
+
162
+ `CitationsResolverService` resolves raw `Citation` references into `ResolvedCitation` objects with full source metadata.
163
+
164
+ **Adapter integration:**
165
+ - **LangGraph** — reads from `message.additional_kwargs.citations` (preferred) or `.sources` (fallback).
166
+ - **AG-UI** — `bridgeCitationsState` reads `state.citations[messageId]` from the agent state on `STATE_SNAPSHOT` and `STATE_DELTA` events.
105
167
 
106
- Markdown rendering registers `chat-md-citation-reference` in the markdown view registry. Citation indices are rendered as superscript markers inline with the message text. The markers link to the corresponding citation in the sources panel.
168
+ ### GenUI / A2UI surfaces
107
169
 
108
- ### Adapter integration
170
+ `<chat-generative-ui>` renders A2UI surface specs emitted by agents. `<a2ui-surface>` is the underlying host that maps the spec to Angular catalog components (`A2uiButtonComponent`, `A2uiTextFieldComponent`, `A2uiCheckBoxComponent`, etc.).
109
171
 
110
- Each runtime adapter extracts citations into the `Message.citations` array:
172
+ Agents can emit surface specs via `buildA2uiActionMessage(...)`. Actions from catalog components flow back to the agent as structured messages.
111
173
 
112
- - **LangGraph** reads from `message.additional_kwargs.citations` (preferred) or `message.additional_kwargs.sources` (fallback)
113
- - **AG-UI** — reads from STATE_DELTA at JSON Pointer `/citations/{messageId}`
174
+ The built-in catalog ships via `a2uiBasicCatalog`. Compose a custom catalog with `withViews()` and pass it to the surface.
114
175
 
115
- The `CitationsResolverService` is provided to query citations in message-first or markdown-fallback order.
176
+ ### Streaming markdown
116
177
 
117
- ## A2UI surface theming
178
+ `<chat-streaming-md>` renders markdown token-by-token as the agent streams. The `cacheplaneMarkdownViews` registry maps each CommonMark node type to an Angular component.
118
179
 
119
- `<a2ui-surface>` declares ~50 internal `--a2ui-*` tokens at `:host` with dark-theme defaults (spacing, typography, shape radius, focus ring, motion, elevation, color). Catalog components consume them via `var(--a2ui-*)`. Override at `:root` to retheme.
180
+ Override individual node renderers:
120
181
 
121
- ### Agent-driven theming (v1 wire format)
182
+ ```typescript
183
+ import { MARKDOWN_VIEW_REGISTRY, cacheplaneMarkdownViews } from '@threadplane/chat';
184
+ import { overrideViews } from '@threadplane/render';
185
+ import { MyCodeBlockComponent } from './my-code-block.component';
186
+
187
+ providers: [
188
+ {
189
+ provide: MARKDOWN_VIEW_REGISTRY,
190
+ useValue: overrideViews(cacheplaneMarkdownViews, { 'code-block': MyCodeBlockComponent }),
191
+ },
192
+ ];
193
+ ```
122
194
 
123
- Agents control exactly two knobs per the canonical A2UI v1-compatible spec, set via `beginRendering.styles`:
195
+ Per-instance, bind the registry on `<chat-streaming-md [viewRegistry]="…" />` instead. Styling uses the existing `--ngaf-chat-*` / `--a2ui-*` tokens — see the [Theming](#theming) section.
124
196
 
125
- - `font` primary font family
126
- - `primaryColor` — hex `#RRGGBB`
197
+ The `renderMarkdown(md, options?)` function produces a parse tree for use outside streaming contexts.
127
198
 
128
- Both flow through to the rendered surface as inline styles on `<a2ui-surface>` (highest specificity), overriding any consumer `:root` defaults for the lifetime of that surface.
199
+ ### Theming
129
200
 
130
- ### Built-in theme presets
201
+ `<a2ui-surface>` declares ~50 `--a2ui-*` CSS custom properties at `:host` with dark-theme defaults covering color, spacing, typography, shape radius, focus ring, motion, and elevation. Catalog components consume them via `var(--a2ui-*)`.
131
202
 
132
- Four CSS files ship in the package, each declaring `:root` overrides for the relevant tokens:
203
+ **Built-in presets** import one in your global stylesheet:
133
204
 
134
205
  ```css
135
- /* In your global stylesheet */
136
- @import '@threadplane/chat/themes/default-dark.css'; /* lib defaults, explicit */
137
- @import '@threadplane/chat/themes/default-light.css'; /* neutral light, blue accent */
138
- @import '@threadplane/chat/themes/material-dark.css'; /* Material Design 3 dark */
139
- @import '@threadplane/chat/themes/material-light.css'; /* Material Design 3 light */
206
+ @import '@threadplane/chat/themes/default-dark.css'; /* lib defaults, explicit */
207
+ @import '@threadplane/chat/themes/default-light.css'; /* neutral light, blue accent */
208
+ @import '@threadplane/chat/themes/material-dark.css'; /* Material Design 3 dark */
209
+ @import '@threadplane/chat/themes/material-light.css'; /* Material Design 3 light */
140
210
  ```
141
211
 
142
- Material presets map [Material Design 3 color tokens](https://m3.material.io/styles/color/the-color-system/tokens) to the `--a2ui-*` vocabulary no `@angular/material` runtime dep, just CSS custom-property declarations.
212
+ Material presets map M3 color tokens to the `--a2ui-*` vocabulary with no `@angular/material` runtime dependency.
143
213
 
144
- ### Custom themes
214
+ **Agent-driven theming.** Agents control two knobs per the A2UI v1 wire format, set via `beginRendering.styles`: `font` (font family string) and `primaryColor` (hex `#RRGGBB`). These flow to `<a2ui-surface>` as inline styles and take precedence over `:root` defaults for that surface.
145
215
 
146
- Override any subset of the ~50 tokens at `:root`:
216
+ **Custom themes.** Override any token at `:root`:
147
217
 
148
218
  ```css
149
219
  :root {
150
- --a2ui-primary: #FF6B35; /* brand orange */
151
- --a2ui-shape-medium: 4px; /* sharper corners */
152
- --a2ui-spacing-3: 16px; /* roomier layouts */
220
+ --a2ui-primary: #FF6B35;
221
+ --a2ui-shape-medium: 4px;
222
+ --a2ui-spacing-3: 16px;
153
223
  }
154
224
  ```
155
225
 
156
- The token surface:
157
- - **Color** — `--a2ui-primary`, `--a2ui-on-primary`, `--a2ui-secondary`, `--a2ui-surface`, `--a2ui-on-surface`, `--a2ui-surface-variant`, `--a2ui-on-surface-variant`, `--a2ui-outline`, `--a2ui-outline-variant`, `--a2ui-error`, `--a2ui-on-error`, `--a2ui-scrim`
158
- - **Spacing** — `--a2ui-spacing-1` (4px) through `--a2ui-spacing-7` (40px)
159
- - **Typography** — `--a2ui-typography-{h1..h5,body,caption,label}-{size,weight,line-height}`
160
- - **Shape** — `--a2ui-shape-{extra-small,small,medium,large,extra-large}`
161
- - **Focus ring** — `--a2ui-focus-ring-color`, `--a2ui-focus-ring-width`
162
- - **Motion** `--a2ui-motion-duration-{short,medium,long}`, `--a2ui-motion-easing-{standard,emphasized}`
163
- - **Elevation** — `--a2ui-elevation-{0,1,2,3,4,5}` (box-shadow tokens)
226
+ The full token vocabulary (`--a2ui-primary`, `--a2ui-spacing-1..7`, `--a2ui-typography-*`, `--a2ui-shape-*`, `--a2ui-elevation-*`, etc.) is documented at [threadplane.ai/docs/chat](https://threadplane.ai/docs/chat).
227
+
228
+ ---
229
+
230
+ ## Runtime adapters
231
+
232
+ Chat compositions consume the runtime-neutral `Agent` contract. Two adapters ship today:
233
+
234
+ - **`@threadplane/langgraph`** — for LangGraph / LangGraph Platform backends.
235
+ - **`@threadplane/ag-ui`** — for AG-UI-compatible backends (LangGraph, CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit runtime).
236
+
237
+ Custom backends implement the `Agent` (or `AgentWithHistory`) interface directly with no library dependency.
238
+
239
+ ---
240
+
241
+ ## Commercial use
242
+
243
+ Building a commercial product, SaaS application, internal business tool, agency deliverable, or paid client project with `@threadplane/chat` requires a Threadplane Commercial license.
244
+
245
+ Free under PolyForm Noncommercial:
246
+
247
+ - Personal, hobby, student, academic, nonprofit, and public-demo use
248
+ - Open-source applications released under an OSI-approved license
249
+ - 30 calendar days of commercial evaluation from your first commercial use (good-faith — no tracking, no email required)
250
+
251
+ See [COMMERCIAL-USE.md](./COMMERCIAL-USE.md) for the definition of commercial use, [LICENSE-COMMERCIAL.md](./LICENSE-COMMERCIAL.md) for the commercial license summary, and the [Threadplane pricing page](https://threadplane.ai/pricing) for plans.
252
+
253
+ ## Using a commercial license
254
+
255
+ After purchase, Threadplane emails a signed license token to the address on your receipt. The license is valid for 12 months. Pass the token to `provideChat()`:
256
+
257
+ ```typescript
258
+ // app.config.ts
259
+ import { ApplicationConfig } from '@angular/core';
260
+ import { provideChat } from '@threadplane/chat';
261
+
262
+ export const appConfig: ApplicationConfig = {
263
+ providers: [
264
+ provideChat({
265
+ license: 'eyJ…', // Token from your purchase email.
266
+ }),
267
+ ],
268
+ };
269
+ ```
270
+
271
+ The library verifies the token's signature on boot. A missing, expired, or tampered token logs a `console.warn` advisory but does not block rendering — chat continues to work either way. Tokens are validated offline; no calls to Threadplane are made at runtime.
272
+
273
+ The license string is safe to commit to source control for private repositories, or read from a build-time env var for public ones:
274
+
275
+ ```typescript
276
+ declare const THREADPLANE_LICENSE: string | undefined;
277
+
278
+ providers: [
279
+ provideChat({
280
+ license: typeof THREADPLANE_LICENSE === 'string' ? THREADPLANE_LICENSE : undefined,
281
+ }),
282
+ ],
283
+ ```
284
+
285
+ ---
286
+
287
+ ## Reliability
288
+
289
+ `@threadplane/chat` follows a patch-only release cadence (`0.0.x`). The runtime-neutral `Agent` contract is a stability boundary: adapter updates do not break chat UI code and vice versa. The package is covered by the monorepo's CI lint, test, and build pipeline on every commit.
290
+
291
+ ---
292
+
293
+ ## Documentation
294
+
295
+ Full API reference, capability matrix, and examples: [threadplane.ai/docs/chat](https://threadplane.ai/docs/chat).
296
+
297
+ ---
298
+
299
+ ## License
300
+
301
+ `@threadplane/chat` is dual-licensed:
302
+
303
+ - **Noncommercial use:** [PolyForm Noncommercial License 1.0.0](./LICENSE.md)
304
+ - **Commercial use:** [Threadplane Commercial License](./LICENSE-COMMERCIAL.md)
305
+
306
+ See [COMMERCIAL-USE.md](./COMMERCIAL-USE.md) for the definition of commercial use.
@@ -140,8 +140,6 @@ function assertReasoningFixtureMessages(messages) {
140
140
  }
141
141
  }
142
142
 
143
- // SPDX-License-Identifier: MIT
144
-
145
143
  /**
146
144
  * Generated bundle index. Do not edit.
147
145
  */
@@ -1 +1 @@
1
- {"version":3,"file":"threadplane-chat-testing.mjs","sources":["../../../../libs/chat/testing/agent-conformance.ts","../../../../libs/chat/testing/agent-with-history-conformance.ts","../../../../libs/chat/testing/reasoning-fixture.ts","../../../../libs/chat/testing/public-api.ts","../../../../libs/chat/testing/threadplane-chat-testing.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\nimport { describe, it, expect } from 'vitest';\nimport type { Agent } from '@threadplane/chat';\n\n/**\n * Runs a suite of contract conformance assertions against a factory that\n * produces a fresh Agent. Adapter packages should call this in their\n * own test suites to verify the contract is satisfied.\n */\nexport function runAgentConformance(\n label: string,\n factory: () => Agent,\n): void {\n describe(`${label} — Agent conformance`, () => {\n it('exposes required core signals', () => {\n const a = factory();\n expect(typeof a.messages).toBe('function');\n expect(typeof a.status).toBe('function');\n expect(typeof a.isLoading).toBe('function');\n expect(typeof a.error).toBe('function');\n expect(typeof a.toolCalls).toBe('function');\n expect(typeof a.state).toBe('function');\n });\n\n it('messages() returns an array', () => {\n expect(Array.isArray(factory().messages())).toBe(true);\n });\n\n it('toolCalls() returns an array', () => {\n expect(Array.isArray(factory().toolCalls())).toBe(true);\n });\n\n it('state() returns a plain object', () => {\n const v = factory().state();\n expect(typeof v).toBe('object');\n expect(v).not.toBeNull();\n });\n\n it('status() returns one of the allowed values', () => {\n expect(['idle', 'running', 'error']).toContain(factory().status());\n });\n\n it('isLoading() is true only when status === \"running\"', () => {\n const a = factory();\n if (a.isLoading()) {\n expect(a.status()).toBe('running');\n }\n });\n\n it('submit() returns a Promise', () => {\n const result = factory().submit({ message: 'test' });\n expect(result).toBeInstanceOf(Promise);\n });\n\n it('stop() returns a Promise', () => {\n const result = factory().stop();\n expect(result).toBeInstanceOf(Promise);\n });\n\n it('events$ is an Observable-like with .subscribe', () => {\n const agent = factory();\n expect(typeof agent.events$.subscribe).toBe('function');\n });\n });\n}\n","// SPDX-License-Identifier: MIT\nimport { describe, it, expect } from 'vitest';\nimport type { AgentWithHistory, AgentCheckpoint } from '@threadplane/chat';\nimport { runAgentConformance } from './agent-conformance';\n\n/**\n * Conformance suite for AgentWithHistory implementations.\n *\n * Runs the base Agent conformance suite, then verifies the history\n * signal is present and returns an array of AgentCheckpoint-shaped entries.\n */\nexport function runAgentWithHistoryConformance(\n label: string,\n factory: (seed?: { history?: AgentCheckpoint[] }) => AgentWithHistory,\n): void {\n runAgentConformance(label, () => factory());\n\n describe(`${label} — history`, () => {\n it('exposes a history signal', () => {\n const agent = factory();\n expect(typeof agent.history).toBe('function');\n expect(Array.isArray(agent.history())).toBe(true);\n });\n\n it('reflects seeded checkpoints', () => {\n const seed: AgentCheckpoint[] = [\n { id: 'c1', label: 'Step 1', values: { foo: 1 } },\n { id: 'c2', label: 'Step 2', values: { foo: 2 } },\n ];\n const agent = factory({ history: seed });\n const entries = agent.history();\n expect(entries).toHaveLength(2);\n expect(entries[0].id).toBe('c1');\n expect(entries[1].values).toEqual({ foo: 2 });\n });\n });\n}\n","// libs/chat/testing/reasoning-fixture.ts\n// SPDX-License-Identifier: MIT\n//\n// Provider-neutral fixture for the reasoning conformance test. Both\n// adapters (langgraph + ag-ui) translate this abstract sequence into\n// their own wire format and assert that the resulting Agent.messages()\n// produces a single assistant Message with the expected reasoning\n// string, response content, and a numeric (>= 0) reasoningDurationMs.\n//\n// \"Abstract events\" mirror the AG-UI shape — REASONING_*/TEXT_*. Any\n// adapter that streams reasoning before text should be able to satisfy\n// this fixture. The shared assertions live in\n// `assertReasoningFixtureMessages(messages)` so each adapter's spec\n// just constructs the events and calls the assertion.\n\nimport type { Message } from '@threadplane/chat';\n\nexport const REASONING_FIXTURE_MESSAGE_ID = 'fixture-msg-1';\nexport const REASONING_FIXTURE_REASONING = 'I read the prompt and decided to greet the user.';\nexport const REASONING_FIXTURE_RESPONSE = 'Hello!';\n\nexport interface AbstractEvent {\n kind:\n | 'reasoning-start'\n | 'reasoning-chunk'\n | 'reasoning-end'\n | 'text-start'\n | 'text-chunk'\n | 'text-end';\n delta?: string;\n}\n\n/**\n * Canonical sequence: reasoning starts, three reasoning chunks, reasoning\n * ends, text starts, three text chunks, text ends.\n */\nexport const REASONING_FIXTURE_EVENTS: AbstractEvent[] = [\n { kind: 'reasoning-start' },\n { kind: 'reasoning-chunk', delta: 'I read the prompt ' },\n { kind: 'reasoning-chunk', delta: 'and decided ' },\n { kind: 'reasoning-chunk', delta: 'to greet the user.' },\n { kind: 'reasoning-end' },\n { kind: 'text-start' },\n { kind: 'text-chunk', delta: 'Hel' },\n { kind: 'text-chunk', delta: 'lo' },\n { kind: 'text-chunk', delta: '!' },\n { kind: 'text-end' },\n];\n\n/**\n * Assertion — common to both adapters. Throws if the produced messages\n * don't match the shared expectation.\n */\nexport function assertReasoningFixtureMessages(messages: readonly Message[]): void {\n if (messages.length !== 1) {\n throw new Error(`Expected exactly 1 message, got ${messages.length}: ${JSON.stringify(messages)}`);\n }\n const m = messages[0];\n if (m.role !== 'assistant') {\n throw new Error(`Expected assistant role, got ${m.role}`);\n }\n if (m.content !== REASONING_FIXTURE_RESPONSE) {\n throw new Error(`Expected content ${JSON.stringify(REASONING_FIXTURE_RESPONSE)}, got ${JSON.stringify(m.content)}`);\n }\n if (m.reasoning !== REASONING_FIXTURE_REASONING) {\n throw new Error(`Expected reasoning ${JSON.stringify(REASONING_FIXTURE_REASONING)}, got ${JSON.stringify(m.reasoning)}`);\n }\n if (typeof m.reasoningDurationMs !== 'number') {\n throw new Error(`Expected reasoningDurationMs to be a number, got ${typeof m.reasoningDurationMs}`);\n }\n if (m.reasoningDurationMs < 0) {\n throw new Error(`Expected reasoningDurationMs >= 0, got ${m.reasoningDurationMs}`);\n }\n}\n","// SPDX-License-Identifier: MIT\nexport { runAgentConformance } from './agent-conformance';\nexport { runAgentWithHistoryConformance } from './agent-with-history-conformance';\nexport {\n REASONING_FIXTURE_MESSAGE_ID,\n REASONING_FIXTURE_REASONING,\n REASONING_FIXTURE_RESPONSE,\n REASONING_FIXTURE_EVENTS,\n assertReasoningFixtureMessages,\n type AbstractEvent,\n} from './reasoning-fixture';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"threadplane-chat-testing.mjs","sources":["../../../../libs/chat/testing/agent-conformance.ts","../../../../libs/chat/testing/agent-with-history-conformance.ts","../../../../libs/chat/testing/reasoning-fixture.ts","../../../../libs/chat/testing/threadplane-chat-testing.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\nimport { describe, it, expect } from 'vitest';\nimport type { Agent } from '@threadplane/chat';\n\n/**\n * Runs a suite of contract conformance assertions against a factory that\n * produces a fresh Agent. Adapter packages should call this in their\n * own test suites to verify the contract is satisfied.\n */\nexport function runAgentConformance(\n label: string,\n factory: () => Agent,\n): void {\n describe(`${label} — Agent conformance`, () => {\n it('exposes required core signals', () => {\n const a = factory();\n expect(typeof a.messages).toBe('function');\n expect(typeof a.status).toBe('function');\n expect(typeof a.isLoading).toBe('function');\n expect(typeof a.error).toBe('function');\n expect(typeof a.toolCalls).toBe('function');\n expect(typeof a.state).toBe('function');\n });\n\n it('messages() returns an array', () => {\n expect(Array.isArray(factory().messages())).toBe(true);\n });\n\n it('toolCalls() returns an array', () => {\n expect(Array.isArray(factory().toolCalls())).toBe(true);\n });\n\n it('state() returns a plain object', () => {\n const v = factory().state();\n expect(typeof v).toBe('object');\n expect(v).not.toBeNull();\n });\n\n it('status() returns one of the allowed values', () => {\n expect(['idle', 'running', 'error']).toContain(factory().status());\n });\n\n it('isLoading() is true only when status === \"running\"', () => {\n const a = factory();\n if (a.isLoading()) {\n expect(a.status()).toBe('running');\n }\n });\n\n it('submit() returns a Promise', () => {\n const result = factory().submit({ message: 'test' });\n expect(result).toBeInstanceOf(Promise);\n });\n\n it('stop() returns a Promise', () => {\n const result = factory().stop();\n expect(result).toBeInstanceOf(Promise);\n });\n\n it('events$ is an Observable-like with .subscribe', () => {\n const agent = factory();\n expect(typeof agent.events$.subscribe).toBe('function');\n });\n });\n}\n","// SPDX-License-Identifier: MIT\nimport { describe, it, expect } from 'vitest';\nimport type { AgentWithHistory, AgentCheckpoint } from '@threadplane/chat';\nimport { runAgentConformance } from './agent-conformance';\n\n/**\n * Conformance suite for AgentWithHistory implementations.\n *\n * Runs the base Agent conformance suite, then verifies the history\n * signal is present and returns an array of AgentCheckpoint-shaped entries.\n */\nexport function runAgentWithHistoryConformance(\n label: string,\n factory: (seed?: { history?: AgentCheckpoint[] }) => AgentWithHistory,\n): void {\n runAgentConformance(label, () => factory());\n\n describe(`${label} — history`, () => {\n it('exposes a history signal', () => {\n const agent = factory();\n expect(typeof agent.history).toBe('function');\n expect(Array.isArray(agent.history())).toBe(true);\n });\n\n it('reflects seeded checkpoints', () => {\n const seed: AgentCheckpoint[] = [\n { id: 'c1', label: 'Step 1', values: { foo: 1 } },\n { id: 'c2', label: 'Step 2', values: { foo: 2 } },\n ];\n const agent = factory({ history: seed });\n const entries = agent.history();\n expect(entries).toHaveLength(2);\n expect(entries[0].id).toBe('c1');\n expect(entries[1].values).toEqual({ foo: 2 });\n });\n });\n}\n","// libs/chat/testing/reasoning-fixture.ts\n// SPDX-License-Identifier: MIT\n//\n// Provider-neutral fixture for the reasoning conformance test. Both\n// adapters (langgraph + ag-ui) translate this abstract sequence into\n// their own wire format and assert that the resulting Agent.messages()\n// produces a single assistant Message with the expected reasoning\n// string, response content, and a numeric (>= 0) reasoningDurationMs.\n//\n// \"Abstract events\" mirror the AG-UI shape — REASONING_*/TEXT_*. Any\n// adapter that streams reasoning before text should be able to satisfy\n// this fixture. The shared assertions live in\n// `assertReasoningFixtureMessages(messages)` so each adapter's spec\n// just constructs the events and calls the assertion.\n\nimport type { Message } from '@threadplane/chat';\n\nexport const REASONING_FIXTURE_MESSAGE_ID = 'fixture-msg-1';\nexport const REASONING_FIXTURE_REASONING = 'I read the prompt and decided to greet the user.';\nexport const REASONING_FIXTURE_RESPONSE = 'Hello!';\n\nexport interface AbstractEvent {\n kind:\n | 'reasoning-start'\n | 'reasoning-chunk'\n | 'reasoning-end'\n | 'text-start'\n | 'text-chunk'\n | 'text-end';\n delta?: string;\n}\n\n/**\n * Canonical sequence: reasoning starts, three reasoning chunks, reasoning\n * ends, text starts, three text chunks, text ends.\n */\nexport const REASONING_FIXTURE_EVENTS: AbstractEvent[] = [\n { kind: 'reasoning-start' },\n { kind: 'reasoning-chunk', delta: 'I read the prompt ' },\n { kind: 'reasoning-chunk', delta: 'and decided ' },\n { kind: 'reasoning-chunk', delta: 'to greet the user.' },\n { kind: 'reasoning-end' },\n { kind: 'text-start' },\n { kind: 'text-chunk', delta: 'Hel' },\n { kind: 'text-chunk', delta: 'lo' },\n { kind: 'text-chunk', delta: '!' },\n { kind: 'text-end' },\n];\n\n/**\n * Assertion — common to both adapters. Throws if the produced messages\n * don't match the shared expectation.\n */\nexport function assertReasoningFixtureMessages(messages: readonly Message[]): void {\n if (messages.length !== 1) {\n throw new Error(`Expected exactly 1 message, got ${messages.length}: ${JSON.stringify(messages)}`);\n }\n const m = messages[0];\n if (m.role !== 'assistant') {\n throw new Error(`Expected assistant role, got ${m.role}`);\n }\n if (m.content !== REASONING_FIXTURE_RESPONSE) {\n throw new Error(`Expected content ${JSON.stringify(REASONING_FIXTURE_RESPONSE)}, got ${JSON.stringify(m.content)}`);\n }\n if (m.reasoning !== REASONING_FIXTURE_REASONING) {\n throw new Error(`Expected reasoning ${JSON.stringify(REASONING_FIXTURE_REASONING)}, got ${JSON.stringify(m.reasoning)}`);\n }\n if (typeof m.reasoningDurationMs !== 'number') {\n throw new Error(`Expected reasoningDurationMs to be a number, got ${typeof m.reasoningDurationMs}`);\n }\n if (m.reasoningDurationMs < 0) {\n throw new Error(`Expected reasoningDurationMs >= 0, got ${m.reasoningDurationMs}`);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}