@threadplane/chat 0.0.47 → 0.0.49

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.
@@ -3,7 +3,7 @@ import { input, inject, TemplateRef, Directive, contentChildren, computed, Chang
3
3
  import { NgTemplateOutlet, NgComponentOutlet, KeyValuePipe } from '@angular/common';
4
4
  import { createPartialMarkdownParser, materialize } from '@cacheplane/partial-markdown';
5
5
  import { views, RenderSpecComponent, toRenderRegistry, signalStateStore, RenderElementComponent } from '@threadplane/render';
6
- export { VIEW_REGISTRY, provideViews, toRenderRegistry, views, withViews, withoutViews } from '@threadplane/render';
6
+ export { toRenderRegistry, views, withViews, withoutViews } from '@threadplane/render';
7
7
  import * as i1 from '@angular/forms';
8
8
  import { FormsModule } from '@angular/forms';
9
9
  import { runLicenseCheck, inferNoncommercial, LICENSE_PUBLIC_KEY } from '@threadplane/licensing';
@@ -4152,6 +4152,39 @@ function pluralize(word, count) {
4152
4152
  return count === 1 ? word : `${word}s`;
4153
4153
  }
4154
4154
 
4155
+ /**
4156
+ * Resolves the tool calls that belong to a specific message, mirroring the
4157
+ * per-message scoping the chat lib uses to avoid every assistant bubble
4158
+ * re-rendering the whole thread's tool-call list.
4159
+ *
4160
+ * - No message context → the agent's global tool-call list.
4161
+ * - Assistant message with `toolCallIds` (LangGraph) → those ids, in order.
4162
+ * - Assistant message with `tool_use` content blocks (Anthropic) → those ids.
4163
+ * - Assistant message with no linkage → [] (this message emitted no calls).
4164
+ * - Any non-assistant message → the global list (callers filter as needed).
4165
+ *
4166
+ * MUST be called inside a reactive context (computed/effect) so the
4167
+ * `agent.toolCalls()` signal read registers a dependency.
4168
+ */
4169
+ function resolveMessageToolCalls(agent, message) {
4170
+ const all = agent.toolCalls();
4171
+ if (message && message.role === 'assistant') {
4172
+ if (message.toolCallIds && message.toolCallIds.length > 0) {
4173
+ return message.toolCallIds
4174
+ .map((id) => all.find((tc) => tc.id === id))
4175
+ .filter((x) => !!x);
4176
+ }
4177
+ if (Array.isArray(message.content)) {
4178
+ const blocks = message.content.filter((b) => b.type === 'tool_use');
4179
+ return blocks
4180
+ .map((b) => all.find((tc) => tc.id === b.id))
4181
+ .filter((x) => !!x);
4182
+ }
4183
+ return [];
4184
+ }
4185
+ return all;
4186
+ }
4187
+
4155
4188
  // libs/chat/src/lib/primitives/chat-tool-calls/chat-tool-calls.component.ts
4156
4189
  // SPDX-License-Identifier: MIT
4157
4190
  class ChatToolCallsComponent {
@@ -4175,30 +4208,7 @@ class ChatToolCallsComponent {
4175
4208
  }
4176
4209
  return map;
4177
4210
  }, ...(ngDevMode ? [{ debugName: "templateRegistry" }] : []));
4178
- toolCalls = computed(() => {
4179
- const msg = this.message();
4180
- if (msg && msg.role === 'assistant') {
4181
- const all = this.agent().toolCalls();
4182
- // Prefer adapter-provided `toolCallIds` (LangGraph populates it from
4183
- // raw `tool_calls`). This is the per-message scope that prevents
4184
- // every AI bubble from re-rendering the entire thread's tool-call
4185
- // list when content is a flat string (the LangGraph adapter's
4186
- // default — extractTextContent reduces complex content to string).
4187
- if (msg.toolCallIds && msg.toolCallIds.length > 0) {
4188
- return msg.toolCallIds.map(id => all.find(tc => tc.id === id)).filter((x) => !!x);
4189
- }
4190
- // Anthropic-style content-block path: extract tool_use IDs inline.
4191
- if (Array.isArray(msg.content)) {
4192
- const blocks = msg.content.filter((b) => b.type === 'tool_use');
4193
- return blocks.map(b => all.find(tc => tc.id === b.id)).filter((x) => !!x);
4194
- }
4195
- // Assistant message provided but no tool-call linkage available:
4196
- // this message didn't emit any tool calls. Return empty.
4197
- return [];
4198
- }
4199
- // No message context at all (e.g. agent-level pinning) → global list.
4200
- return this.agent().toolCalls();
4201
- }, ...(ngDevMode ? [{ debugName: "toolCalls" }] : []));
4211
+ toolCalls = computed(() => resolveMessageToolCalls(this.agent(), this.message()), ...(ngDevMode ? [{ debugName: "toolCalls" }] : []));
4202
4212
  groups = computed(() => {
4203
4213
  const excludeSet = new Set(this.excludeToolNames());
4204
4214
  const calls = this.toolCalls().filter(tc => !excludeSet.has(tc.name));
@@ -4314,6 +4324,148 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
4314
4324
  `, styles: [":host{display:block;margin-bottom:20px}.ctc__group{border:1px solid var(--ngaf-chat-separator);border-radius:var(--ngaf-chat-radius-card);margin:0 0 4px}.ctc__group-header{display:flex;align-items:center;gap:.5rem;width:100%;padding:8px 12px;background:transparent;border:0;font:inherit;color:var(--ngaf-chat-text);cursor:pointer;text-align:left}.ctc__group-chevron{width:10px;height:10px;transition:transform .12s ease}.ctc__group[data-expanded=true] .ctc__group-chevron{transform:rotate(90deg)}.ctc__group-body{padding:0 12px 8px;border-top:1px solid var(--ngaf-chat-separator)}\n"] }]
4315
4325
  }], propDecorators: { agent: [{ type: i0.Input, args: [{ isSignal: true, alias: "agent", required: true }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], grouping: [{ type: i0.Input, args: [{ isSignal: true, alias: "grouping", required: false }] }], groupSummary: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupSummary", required: false }] }], excludeToolNames: [{ type: i0.Input, args: [{ isSignal: true, alias: "excludeToolNames", required: false }] }], templates: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ChatToolCallTemplateDirective), { isSignal: true }] }] } });
4316
4326
 
4327
+ // SPDX-License-Identifier: MIT
4328
+ const CHAT_GENERATIVE_UI_STYLES = `
4329
+ :host {
4330
+ display: block;
4331
+ color: var(--ngaf-chat-text);
4332
+ font-size: var(--ngaf-chat-font-size);
4333
+ line-height: var(--ngaf-chat-line-height);
4334
+ }
4335
+ .chat-generative-ui__error {
4336
+ color: var(--ngaf-chat-error-text);
4337
+ background: var(--ngaf-chat-error-bg);
4338
+ border: 1px solid var(--ngaf-chat-error-border);
4339
+ border-radius: var(--ngaf-chat-radius-card);
4340
+ padding: 8px 12px;
4341
+ font-size: var(--ngaf-chat-font-size-sm);
4342
+ }
4343
+ `;
4344
+
4345
+ // libs/chat/src/lib/primitives/chat-generative-ui/chat-generative-ui.component.ts
4346
+ // SPDX-License-Identifier: MIT
4347
+ class ChatGenerativeUiComponent {
4348
+ spec = input(null, ...(ngDevMode ? [{ debugName: "spec" }] : []));
4349
+ registry = input(undefined, ...(ngDevMode ? [{ debugName: "registry" }] : []));
4350
+ store = input(undefined, ...(ngDevMode ? [{ debugName: "store" }] : []));
4351
+ handlers = input(undefined, ...(ngDevMode ? [{ debugName: "handlers" }] : []));
4352
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
4353
+ events = output();
4354
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatGenerativeUiComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4355
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: ChatGenerativeUiComponent, isStandalone: true, selector: "chat-generative-ui", inputs: { spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null }, registry: { classPropertyName: "registry", publicName: "registry", isSignal: true, isRequired: false, transformFunction: null }, store: { classPropertyName: "store", publicName: "store", isSignal: true, isRequired: false, transformFunction: null }, handlers: { classPropertyName: "handlers", publicName: "handlers", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { events: "events" }, ngImport: i0, template: `
4356
+ @if (spec()) {
4357
+ <render-spec
4358
+ [spec]="spec()"
4359
+ [registry]="registry()"
4360
+ [store]="store()"
4361
+ [handlers]="handlers()"
4362
+ [loading]="loading()"
4363
+ (events)="events.emit($event)"
4364
+ />
4365
+ }
4366
+ `, isInline: true, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ":host{display:block;color:var(--ngaf-chat-text);font-size:var(--ngaf-chat-font-size);line-height:var(--ngaf-chat-line-height)}.chat-generative-ui__error{color:var(--ngaf-chat-error-text);background:var(--ngaf-chat-error-bg);border:1px solid var(--ngaf-chat-error-border);border-radius:var(--ngaf-chat-radius-card);padding:8px 12px;font-size:var(--ngaf-chat-font-size-sm)}\n"], dependencies: [{ kind: "component", type: RenderSpecComponent, selector: "render-spec", inputs: ["spec", "registry", "store", "functions", "handlers", "loading"], outputs: ["events"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4367
+ }
4368
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatGenerativeUiComponent, decorators: [{
4369
+ type: Component,
4370
+ args: [{ selector: 'chat-generative-ui', standalone: true, imports: [RenderSpecComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
4371
+ @if (spec()) {
4372
+ <render-spec
4373
+ [spec]="spec()"
4374
+ [registry]="registry()"
4375
+ [store]="store()"
4376
+ [handlers]="handlers()"
4377
+ [loading]="loading()"
4378
+ (events)="events.emit($event)"
4379
+ />
4380
+ }
4381
+ `, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ":host{display:block;color:var(--ngaf-chat-text);font-size:var(--ngaf-chat-font-size);line-height:var(--ngaf-chat-line-height)}.chat-generative-ui__error{color:var(--ngaf-chat-error-text);background:var(--ngaf-chat-error-bg);border:1px solid var(--ngaf-chat-error-border);border-radius:var(--ngaf-chat-radius-card);padding:8px 12px;font-size:var(--ngaf-chat-font-size-sm)}\n"] }]
4382
+ }], propDecorators: { spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }], registry: [{ type: i0.Input, args: [{ isSignal: true, alias: "registry", required: false }] }], store: [{ type: i0.Input, args: [{ isSignal: true, alias: "store", required: false }] }], handlers: [{ type: i0.Input, args: [{ isSignal: true, alias: "handlers", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], events: [{ type: i0.Output, args: ["events"] }] } });
4383
+
4384
+ // libs/chat/src/lib/primitives/chat-tool-views/chat-tool-views.component.ts
4385
+ // SPDX-License-Identifier: MIT
4386
+ /**
4387
+ * Renders a frontend component for a tool call by reusing the chat
4388
+ * composition's `views` registry. A tool call whose `name` matches a
4389
+ * registry key is bridged into a synthetic one-element render spec
4390
+ * (`{ root: name, elements: { [name]: { type: name, props } } }`) and
4391
+ * rendered through the existing render-spec pipeline.
4392
+ *
4393
+ * Props merge the live `args` (present while the call streams) with the
4394
+ * `result` (on completion) and always include `status`, so a view
4395
+ * component can show its own loading/empty/error states. `RenderElement`
4396
+ * filters props down to the component's declared inputs, so extra keys
4397
+ * (and a `status` a component chooses not to declare) are harmless.
4398
+ */
4399
+ class ChatToolViewsComponent {
4400
+ agent = input.required(...(ngDevMode ? [{ debugName: "agent" }] : []));
4401
+ message = input(undefined, ...(ngDevMode ? [{ debugName: "message" }] : []));
4402
+ views = input(undefined, ...(ngDevMode ? [{ debugName: "views" }] : []));
4403
+ store = input(undefined, ...(ngDevMode ? [{ debugName: "store" }] : []));
4404
+ handlers = input({}, ...(ngDevMode ? [{ debugName: "handlers" }] : []));
4405
+ registry = computed(() => {
4406
+ const v = this.views();
4407
+ return v ? toRenderRegistry(v) : undefined;
4408
+ }, ...(ngDevMode ? [{ debugName: "registry" }] : []));
4409
+ toolViews = computed(() => {
4410
+ const v = this.views();
4411
+ if (!v)
4412
+ return [];
4413
+ const names = new Set(Object.keys(v));
4414
+ return resolveMessageToolCalls(this.agent(), this.message())
4415
+ .filter((tc) => names.has(tc.name))
4416
+ .map((tc) => ({ id: tc.id, loading: tc.status === 'running', spec: toToolViewSpec(tc) }));
4417
+ }, ...(ngDevMode ? [{ debugName: "toolViews" }] : []));
4418
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatToolViewsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4419
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: ChatToolViewsComponent, isStandalone: true, selector: "chat-tool-views", inputs: { agent: { classPropertyName: "agent", publicName: "agent", isSignal: true, isRequired: true, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, views: { classPropertyName: "views", publicName: "views", isSignal: true, isRequired: false, transformFunction: null }, store: { classPropertyName: "store", publicName: "store", isSignal: true, isRequired: false, transformFunction: null }, handlers: { classPropertyName: "handlers", publicName: "handlers", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
4420
+ @for (view of toolViews(); track view.id) {
4421
+ <chat-generative-ui
4422
+ [spec]="view.spec"
4423
+ [registry]="registry()"
4424
+ [store]="store()"
4425
+ [handlers]="handlers()"
4426
+ [loading]="view.loading"
4427
+ />
4428
+ }
4429
+ `, isInline: true, dependencies: [{ kind: "component", type: ChatGenerativeUiComponent, selector: "chat-generative-ui", inputs: ["spec", "registry", "store", "handlers", "loading"], outputs: ["events"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4430
+ }
4431
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatToolViewsComponent, decorators: [{
4432
+ type: Component,
4433
+ args: [{
4434
+ selector: 'chat-tool-views',
4435
+ standalone: true,
4436
+ imports: [ChatGenerativeUiComponent],
4437
+ changeDetection: ChangeDetectionStrategy.OnPush,
4438
+ template: `
4439
+ @for (view of toolViews(); track view.id) {
4440
+ <chat-generative-ui
4441
+ [spec]="view.spec"
4442
+ [registry]="registry()"
4443
+ [store]="store()"
4444
+ [handlers]="handlers()"
4445
+ [loading]="view.loading"
4446
+ />
4447
+ }
4448
+ `,
4449
+ }]
4450
+ }], propDecorators: { agent: [{ type: i0.Input, args: [{ isSignal: true, alias: "agent", required: true }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], views: [{ type: i0.Input, args: [{ isSignal: true, alias: "views", required: false }] }], store: [{ type: i0.Input, args: [{ isSignal: true, alias: "store", required: false }] }], handlers: [{ type: i0.Input, args: [{ isSignal: true, alias: "handlers", required: false }] }] } });
4451
+ /** Wraps a tool call into a synthetic single-element render spec. */
4452
+ function toToolViewSpec(tc) {
4453
+ const args = isRecord$2(tc.args) ? tc.args : {};
4454
+ const result = isRecord$2(tc.result) ? tc.result : {};
4455
+ return {
4456
+ root: tc.name,
4457
+ elements: {
4458
+ [tc.name]: {
4459
+ type: tc.name,
4460
+ props: { ...args, ...result, status: tc.status },
4461
+ },
4462
+ },
4463
+ };
4464
+ }
4465
+ function isRecord$2(v) {
4466
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
4467
+ }
4468
+
4317
4469
  // libs/chat/src/lib/compositions/chat-subagent-card/chat-subagent-card.component.ts
4318
4470
  // SPDX-License-Identifier: MIT
4319
4471
  /**
@@ -5783,63 +5935,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
5783
5935
  }]
5784
5936
  }], propDecorators: { agent: [{ type: i0.Input, args: [{ isSignal: true, alias: "agent", required: true }] }], checkpointSelected: [{ type: i0.Output, args: ["checkpointSelected"] }], templateRef: [{ type: i0.ContentChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
5785
5937
 
5786
- // SPDX-License-Identifier: MIT
5787
- const CHAT_GENERATIVE_UI_STYLES = `
5788
- :host {
5789
- display: block;
5790
- color: var(--ngaf-chat-text);
5791
- font-size: var(--ngaf-chat-font-size);
5792
- line-height: var(--ngaf-chat-line-height);
5793
- }
5794
- .chat-generative-ui__error {
5795
- color: var(--ngaf-chat-error-text);
5796
- background: var(--ngaf-chat-error-bg);
5797
- border: 1px solid var(--ngaf-chat-error-border);
5798
- border-radius: var(--ngaf-chat-radius-card);
5799
- padding: 8px 12px;
5800
- font-size: var(--ngaf-chat-font-size-sm);
5801
- }
5802
- `;
5803
-
5804
- // libs/chat/src/lib/primitives/chat-generative-ui/chat-generative-ui.component.ts
5805
- // SPDX-License-Identifier: MIT
5806
- class ChatGenerativeUiComponent {
5807
- spec = input(null, ...(ngDevMode ? [{ debugName: "spec" }] : []));
5808
- registry = input(undefined, ...(ngDevMode ? [{ debugName: "registry" }] : []));
5809
- store = input(undefined, ...(ngDevMode ? [{ debugName: "store" }] : []));
5810
- handlers = input(undefined, ...(ngDevMode ? [{ debugName: "handlers" }] : []));
5811
- loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
5812
- events = output();
5813
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatGenerativeUiComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5814
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: ChatGenerativeUiComponent, isStandalone: true, selector: "chat-generative-ui", inputs: { spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null }, registry: { classPropertyName: "registry", publicName: "registry", isSignal: true, isRequired: false, transformFunction: null }, store: { classPropertyName: "store", publicName: "store", isSignal: true, isRequired: false, transformFunction: null }, handlers: { classPropertyName: "handlers", publicName: "handlers", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { events: "events" }, ngImport: i0, template: `
5815
- @if (spec()) {
5816
- <render-spec
5817
- [spec]="spec()"
5818
- [registry]="registry()"
5819
- [store]="store()"
5820
- [handlers]="handlers()"
5821
- [loading]="loading()"
5822
- (events)="events.emit($event)"
5823
- />
5824
- }
5825
- `, isInline: true, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ":host{display:block;color:var(--ngaf-chat-text);font-size:var(--ngaf-chat-font-size);line-height:var(--ngaf-chat-line-height)}.chat-generative-ui__error{color:var(--ngaf-chat-error-text);background:var(--ngaf-chat-error-bg);border:1px solid var(--ngaf-chat-error-border);border-radius:var(--ngaf-chat-radius-card);padding:8px 12px;font-size:var(--ngaf-chat-font-size-sm)}\n"], dependencies: [{ kind: "component", type: RenderSpecComponent, selector: "render-spec", inputs: ["spec", "registry", "store", "functions", "handlers", "loading"], outputs: ["events"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5826
- }
5827
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatGenerativeUiComponent, decorators: [{
5828
- type: Component,
5829
- args: [{ selector: 'chat-generative-ui', standalone: true, imports: [RenderSpecComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
5830
- @if (spec()) {
5831
- <render-spec
5832
- [spec]="spec()"
5833
- [registry]="registry()"
5834
- [store]="store()"
5835
- [handlers]="handlers()"
5836
- [loading]="loading()"
5837
- (events)="events.emit($event)"
5838
- />
5839
- }
5840
- `, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ":host{display:block;color:var(--ngaf-chat-text);font-size:var(--ngaf-chat-font-size);line-height:var(--ngaf-chat-line-height)}.chat-generative-ui__error{color:var(--ngaf-chat-error-text);background:var(--ngaf-chat-error-bg);border:1px solid var(--ngaf-chat-error-border);border-radius:var(--ngaf-chat-radius-card);padding:8px 12px;font-size:var(--ngaf-chat-font-size-sm)}\n"] }]
5841
- }], propDecorators: { spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }], registry: [{ type: i0.Input, args: [{ isSignal: true, alias: "registry", required: false }] }], store: [{ type: i0.Input, args: [{ isSignal: true, alias: "store", required: false }] }], handlers: [{ type: i0.Input, args: [{ isSignal: true, alias: "handlers", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], events: [{ type: i0.Output, args: ["events"] }] } });
5842
-
5843
5938
  // libs/chat/src/lib/styles/chat-welcome.styles.ts
5844
5939
  // SPDX-License-Identifier: MIT
5845
5940
  const CHAT_WELCOME_STYLES = `
@@ -7695,7 +7790,7 @@ function isStructurallyComplete(env) {
7695
7790
  * message, so the chat composition can render "Search flights" instead
7696
7791
  * of a raw `{"version":"v1","action":...}` JSON dump as a user bubble.
7697
7792
  *
7698
- * Per the A2UI v0.9 spec, action messages flow on the client → agent
7793
+ * Per the A2UI spec, action messages flow on the client → agent
7699
7794
  * return channel and are framed as typed events (closer to tool calls
7700
7795
  * than user utterances). The spec is silent on chat-bubble rendering;
7701
7796
  * Google's "A2UI in Practice" article and the Stream Chat reference
@@ -7895,6 +7990,15 @@ class ChatComponent {
7895
7990
  const v = this.views();
7896
7991
  return v ? toRenderRegistry(v) : undefined;
7897
7992
  }, ...(ngDevMode ? [{ debugName: "renderRegistry" }] : []));
7993
+ /** Tool names that have a registered view (keys of the `views` registry).
7994
+ * These render as inline tool-views and are excluded from the default
7995
+ * tool-call card so they don't render twice. */
7996
+ viewToolNames = computed(() => Object.keys(this.views() ?? {}), ...(ngDevMode ? [{ debugName: "viewToolNames" }] : []));
7997
+ /** Union of GenUI dispatcher tool names and registered view tool names. */
7998
+ excludedToolNames = computed(() => [
7999
+ ...this.genuiToolNames(),
8000
+ ...this.viewToolNames(),
8001
+ ], ...(ngDevMode ? [{ debugName: "excludedToolNames" }] : []));
7898
8002
  messageContent = messageContent;
7899
8003
  /**
7900
8004
  * Renderable content for a human-role message bubble. Most human
@@ -7903,7 +8007,7 @@ class ChatComponent {
7903
8007
  * on a rendered surface) flow through the same submit channel and
7904
8008
  * land in the message stream as a HumanMessage whose content is a
7905
8009
  * JSON-serialized `A2uiActionMessage`. Showing the raw JSON as if
7906
- * the user typed it leaks the protocol; per the A2UI v0.9 spec
8010
+ * the user typed it leaks the protocol; per the A2UI spec
7907
8011
  * those events resemble tool calls more than user utterances.
7908
8012
  *
7909
8013
  * `a2uiActionLabel` returns a short human-readable label for
@@ -8015,6 +8119,38 @@ class ChatComponent {
8015
8119
  store.update(event.data);
8016
8120
  });
8017
8121
  });
8122
+ // Sync the agent STATE signal into the render store. AG-UI delivers
8123
+ // backend data as agent shared state (STATE_SNAPSHOT/STATE_DELTA); other
8124
+ // adapters expose graph state via the same `state()` signal. The render
8125
+ // store keys are JSON pointers, so map each top-level state key `k` to
8126
+ // `/k`. Exclude `messages` (carried in the snapshot but irrelevant to the
8127
+ // render store and large). Fires for any adapter whose `state()` carries
8128
+ // non-message keys; no-op when no render store is active. Coexists with
8129
+ // the events$ `state_update` path above (both merge into the same store).
8130
+ effect(() => {
8131
+ let agentRef;
8132
+ try {
8133
+ agentRef = this.agent();
8134
+ }
8135
+ catch {
8136
+ return;
8137
+ }
8138
+ const stateFn = agentRef.state;
8139
+ if (typeof stateFn !== 'function')
8140
+ return;
8141
+ const state = stateFn.call(agentRef);
8142
+ const store = this.resolvedStore();
8143
+ if (!store || state == null || typeof state !== 'object' || Array.isArray(state))
8144
+ return;
8145
+ const updates = {};
8146
+ for (const [k, v] of Object.entries(state)) {
8147
+ if (k === 'messages')
8148
+ continue;
8149
+ updates['/' + k] = v;
8150
+ }
8151
+ if (Object.keys(updates).length > 0)
8152
+ store.update(updates);
8153
+ });
8018
8154
  // Spec 4: flip CHAT_LIFECYCLE.firstMessageSent when the agent's stream
8019
8155
  // starts, regardless of submit path (input-bound, programmatic, suggestion-
8020
8156
  // click). Sticky — guarded so we never re-set a flag that's already true.
@@ -8419,11 +8555,18 @@ class ChatComponent {
8419
8555
  [durationMs]="message.reasoningDurationMs"
8420
8556
  />
8421
8557
  }
8422
- <chat-tool-calls [agent]="agent()" [message]="message" [excludeToolNames]="genuiToolNames()">
8558
+ <chat-tool-calls [agent]="agent()" [message]="message" [excludeToolNames]="excludedToolNames()">
8423
8559
  <ng-container ngProjectAs="[chatToolCallTemplate]">
8424
8560
  <ng-content select="[chatToolCallTemplate]" />
8425
8561
  </ng-container>
8426
8562
  </chat-tool-calls>
8563
+ <chat-tool-views
8564
+ [agent]="agent()"
8565
+ [message]="message"
8566
+ [views]="views()"
8567
+ [store]="resolvedStore()"
8568
+ [handlers]="handlers()"
8569
+ />
8427
8570
  <chat-subagents [agent]="agent()" />
8428
8571
  @if (classified.markdown(); as md) {
8429
8572
  <chat-streaming-md [content]="md" [streaming]="agent().isLoading() && i === agent().messages().length - 1" />
@@ -8506,7 +8649,7 @@ class ChatComponent {
8506
8649
  </div>
8507
8650
  </div>
8508
8651
  }
8509
- `, isInline: true, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ":host{display:flex;flex-direction:column;flex:1 1 auto;height:100%;min-height:0;max-height:100%;overflow:hidden;background:var(--ngaf-chat-bg)}:host>chat-welcome{display:flex;flex:1 1 auto;width:100%}.chat-shell{display:flex;flex:1;min-height:0;overflow:hidden}.chat-shell__sidebar{width:240px;flex-shrink:0;border-right:1px solid var(--ngaf-chat-separator);background:var(--ngaf-chat-surface-alt);overflow-y:auto;display:none}@media(min-width:768px){.chat-shell__sidebar{display:block}}.chat-shell__main{flex:1;min-width:0;display:flex;flex-direction:column;min-height:0}.chat-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:60px 20px;color:var(--ngaf-chat-text-muted);text-align:center;flex:1;min-height:0}.chat-empty[hidden]{display:none}.chat-empty__title{font-size:1.125rem;font-weight:500;color:var(--ngaf-chat-text);margin:0}.chat-empty__sub{margin:0;font-size:var(--ngaf-chat-font-size-sm)}.chat-scroll{flex:1;min-height:0;overflow-y:auto;padding-top:var(--ngaf-chat-edge-pad)}.chat-scroll::-webkit-scrollbar{width:6px}.chat-scroll::-webkit-scrollbar-thumb{background:var(--ngaf-chat-separator);border-radius:10px}[chatFooter]{padding-bottom:var(--ngaf-chat-edge-pad)}.chat-footer-wrap{position:relative}\n"], dependencies: [{ kind: "component", type: ChatWindowComponent, selector: "chat-window" }, { kind: "component", type: ChatMessageListComponent, selector: "chat-message-list", inputs: ["agent"] }, { kind: "directive", type: MessageTemplateDirective, selector: "ng-template[chatMessageTemplate]", inputs: ["chatMessageTemplate"] }, { kind: "component", type: ChatMessageComponent, selector: "chat-message", inputs: ["role", "current", "streaming", "prevRole", "message"] }, { kind: "component", type: ChatInputComponent, selector: "chat-input", inputs: ["agent", "submitOnEnter", "placeholder", "showStopButton"], outputs: ["submitted", "stopped"] }, { kind: "component", type: ChatTypingIndicatorComponent, selector: "chat-typing-indicator", inputs: ["agent"] }, { kind: "component", type: ChatErrorComponent, selector: "chat-error", inputs: ["agent"] }, { kind: "component", type: ChatThreadListComponent, selector: "chat-thread-list", inputs: ["threads", "activeThreadId", "showNewThreadButton", "actions", "mode", "projects"], outputs: ["threadSelected", "newThreadRequested"] }, { kind: "component", type: ChatGenerativeUiComponent, selector: "chat-generative-ui", inputs: ["spec", "registry", "store", "handlers", "loading"], outputs: ["events"] }, { kind: "component", type: ChatStreamingMdComponent, selector: "chat-streaming-md", inputs: ["content", "streaming", "viewRegistry"] }, { kind: "component", type: ChatToolCallsComponent, selector: "chat-tool-calls", inputs: ["agent", "message", "grouping", "groupSummary", "excludeToolNames"] }, { kind: "component", type: ChatSubagentsComponent, selector: "chat-subagents", inputs: ["agent"] }, { kind: "component", type: A2uiSurfaceComponent, selector: "a2ui-surface", inputs: ["surface", "state", "catalog", "handlers", "surfaceFallback"], outputs: ["events", "action"] }, { kind: "component", type: ChatMessageActionsComponent, selector: "chat-message-actions", inputs: ["content", "disabled"], outputs: ["regenerate", "rate", "contentCopied"] }, { kind: "component", type: ChatWelcomeComponent, selector: "chat-welcome" }, { kind: "component", type: ChatSelectComponent, selector: "chat-select", inputs: ["options", "value", "placeholder", "disabled", "menuLabel"], outputs: ["valueChange"] }, { kind: "component", type: ChatReasoningComponent, selector: "chat-reasoning", inputs: ["content", "isStreaming", "durationMs", "label", "defaultExpanded"] }, { kind: "component", type: ChatScrollBubbleComponent, selector: "chat-scroll-bubble", inputs: ["mode"], outputs: ["clicked"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8652
+ `, isInline: true, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ":host{display:flex;flex-direction:column;flex:1 1 auto;height:100%;min-height:0;max-height:100%;overflow:hidden;background:var(--ngaf-chat-bg)}:host>chat-welcome{display:flex;flex:1 1 auto;width:100%}.chat-shell{display:flex;flex:1;min-height:0;overflow:hidden}.chat-shell__sidebar{width:240px;flex-shrink:0;border-right:1px solid var(--ngaf-chat-separator);background:var(--ngaf-chat-surface-alt);overflow-y:auto;display:none}@media(min-width:768px){.chat-shell__sidebar{display:block}}.chat-shell__main{flex:1;min-width:0;display:flex;flex-direction:column;min-height:0}.chat-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:60px 20px;color:var(--ngaf-chat-text-muted);text-align:center;flex:1;min-height:0}.chat-empty[hidden]{display:none}.chat-empty__title{font-size:1.125rem;font-weight:500;color:var(--ngaf-chat-text);margin:0}.chat-empty__sub{margin:0;font-size:var(--ngaf-chat-font-size-sm)}.chat-scroll{flex:1;min-height:0;overflow-y:auto;padding-top:var(--ngaf-chat-edge-pad)}.chat-scroll::-webkit-scrollbar{width:6px}.chat-scroll::-webkit-scrollbar-thumb{background:var(--ngaf-chat-separator);border-radius:10px}[chatFooter]{padding-bottom:var(--ngaf-chat-edge-pad)}.chat-footer-wrap{position:relative}\n"], dependencies: [{ kind: "component", type: ChatWindowComponent, selector: "chat-window" }, { kind: "component", type: ChatMessageListComponent, selector: "chat-message-list", inputs: ["agent"] }, { kind: "directive", type: MessageTemplateDirective, selector: "ng-template[chatMessageTemplate]", inputs: ["chatMessageTemplate"] }, { kind: "component", type: ChatMessageComponent, selector: "chat-message", inputs: ["role", "current", "streaming", "prevRole", "message"] }, { kind: "component", type: ChatInputComponent, selector: "chat-input", inputs: ["agent", "submitOnEnter", "placeholder", "showStopButton"], outputs: ["submitted", "stopped"] }, { kind: "component", type: ChatTypingIndicatorComponent, selector: "chat-typing-indicator", inputs: ["agent"] }, { kind: "component", type: ChatErrorComponent, selector: "chat-error", inputs: ["agent"] }, { kind: "component", type: ChatThreadListComponent, selector: "chat-thread-list", inputs: ["threads", "activeThreadId", "showNewThreadButton", "actions", "mode", "projects"], outputs: ["threadSelected", "newThreadRequested"] }, { kind: "component", type: ChatGenerativeUiComponent, selector: "chat-generative-ui", inputs: ["spec", "registry", "store", "handlers", "loading"], outputs: ["events"] }, { kind: "component", type: ChatStreamingMdComponent, selector: "chat-streaming-md", inputs: ["content", "streaming", "viewRegistry"] }, { kind: "component", type: ChatToolCallsComponent, selector: "chat-tool-calls", inputs: ["agent", "message", "grouping", "groupSummary", "excludeToolNames"] }, { kind: "component", type: ChatToolViewsComponent, selector: "chat-tool-views", inputs: ["agent", "message", "views", "store", "handlers"] }, { kind: "component", type: ChatSubagentsComponent, selector: "chat-subagents", inputs: ["agent"] }, { kind: "component", type: A2uiSurfaceComponent, selector: "a2ui-surface", inputs: ["surface", "state", "catalog", "handlers", "surfaceFallback"], outputs: ["events", "action"] }, { kind: "component", type: ChatMessageActionsComponent, selector: "chat-message-actions", inputs: ["content", "disabled"], outputs: ["regenerate", "rate", "contentCopied"] }, { kind: "component", type: ChatWelcomeComponent, selector: "chat-welcome" }, { kind: "component", type: ChatSelectComponent, selector: "chat-select", inputs: ["options", "value", "placeholder", "disabled", "menuLabel"], outputs: ["valueChange"] }, { kind: "component", type: ChatReasoningComponent, selector: "chat-reasoning", inputs: ["content", "isStreaming", "durationMs", "label", "defaultExpanded"] }, { kind: "component", type: ChatScrollBubbleComponent, selector: "chat-scroll-bubble", inputs: ["mode"], outputs: ["clicked"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
8510
8653
  }
8511
8654
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatComponent, decorators: [{
8512
8655
  type: Component,
@@ -8515,7 +8658,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
8515
8658
  ChatWindowComponent, ChatMessageListComponent, MessageTemplateDirective, ChatMessageComponent,
8516
8659
  ChatInputComponent, ChatTypingIndicatorComponent, ChatErrorComponent,
8517
8660
  ChatThreadListComponent, ChatGenerativeUiComponent,
8518
- ChatStreamingMdComponent, ChatToolCallsComponent, ChatSubagentsComponent, A2uiSurfaceComponent,
8661
+ ChatStreamingMdComponent, ChatToolCallsComponent, ChatToolViewsComponent, ChatSubagentsComponent, A2uiSurfaceComponent,
8519
8662
  ChatMessageActionsComponent, ChatWelcomeComponent, ChatSelectComponent, ChatReasoningComponent,
8520
8663
  ChatScrollBubbleComponent,
8521
8664
  ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
@@ -8575,11 +8718,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImpor
8575
8718
  [durationMs]="message.reasoningDurationMs"
8576
8719
  />
8577
8720
  }
8578
- <chat-tool-calls [agent]="agent()" [message]="message" [excludeToolNames]="genuiToolNames()">
8721
+ <chat-tool-calls [agent]="agent()" [message]="message" [excludeToolNames]="excludedToolNames()">
8579
8722
  <ng-container ngProjectAs="[chatToolCallTemplate]">
8580
8723
  <ng-content select="[chatToolCallTemplate]" />
8581
8724
  </ng-container>
8582
8725
  </chat-tool-calls>
8726
+ <chat-tool-views
8727
+ [agent]="agent()"
8728
+ [message]="message"
8729
+ [views]="views()"
8730
+ [store]="resolvedStore()"
8731
+ [handlers]="handlers()"
8732
+ />
8583
8733
  <chat-subagents [agent]="agent()" />
8584
8734
  @if (classified.markdown(); as md) {
8585
8735
  <chat-streaming-md [content]="md" [streaming]="agent().isLoading() && i === agent().messages().length - 1" />
@@ -10117,76 +10267,155 @@ class ChatInterruptPanelComponent {
10117
10267
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: ChatInterruptPanelComponent, isStandalone: true, selector: "chat-interrupt-panel", inputs: { agent: { classPropertyName: "agent", publicName: "agent", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { action: "action" }, ngImport: i0, template: `
10118
10268
  @if (interrupt()) {
10119
10269
  <div role="alert" class="chat-interrupt-panel">
10120
- <p class="chat-interrupt-panel__title">
10121
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
10122
- Agent paused
10270
+ <p class="chat-interrupt-panel__eyebrow">
10271
+ <span class="chat-interrupt-panel__dot" aria-hidden="true"></span>
10272
+ Agent paused — review needed
10123
10273
  </p>
10124
10274
  <p class="chat-interrupt-panel__body">{{ interruptReason() }}</p>
10125
-
10126
10275
  <div class="chat-interrupt-panel__actions">
10127
- <button
10128
- type="button"
10129
- class="chat-interrupt-panel__btn chat-interrupt-panel__btn--primary"
10130
- (click)="action.emit('accept')"
10131
- >Accept</button>
10132
- <button
10133
- type="button"
10134
- class="chat-interrupt-panel__btn chat-interrupt-panel__btn--secondary"
10135
- (click)="action.emit('edit')"
10136
- >Edit</button>
10137
- <button
10138
- type="button"
10139
- class="chat-interrupt-panel__btn chat-interrupt-panel__btn--secondary"
10140
- (click)="action.emit('respond')"
10141
- >Respond</button>
10142
- <button
10143
- type="button"
10144
- class="chat-interrupt-panel__btn chat-interrupt-panel__btn--tertiary"
10145
- (click)="action.emit('ignore')"
10146
- >Ignore</button>
10276
+ <button type="button" class="btn btn-primary" (click)="action.emit('accept')">Accept</button>
10277
+ <button type="button" class="btn btn-secondary" (click)="action.emit('edit')">Edit</button>
10278
+ <button type="button" class="btn btn-secondary" (click)="action.emit('respond')">Respond</button>
10279
+ <button type="button" class="btn btn-text" (click)="action.emit('ignore')">Ignore</button>
10147
10280
  </div>
10148
10281
  </div>
10149
10282
  }
10150
- `, isInline: true, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ".chat-interrupt-panel{background:var(--ngaf-chat-surface);color:var(--ngaf-chat-text);border:1px solid var(--ngaf-chat-separator);border-left:3px solid var(--ngaf-chat-warning-text);border-radius:var(--ngaf-chat-radius-card);padding:12px 16px;font-size:var(--ngaf-chat-font-size-sm)}.chat-interrupt-panel__title{font-weight:600;margin:0 0 4px;display:flex;align-items:center;gap:6px;color:var(--ngaf-chat-text)}.chat-interrupt-panel__title svg{color:var(--ngaf-chat-warning-text)}.chat-interrupt-panel__body{margin:0 0 12px;color:var(--ngaf-chat-text-muted);white-space:pre-wrap}.chat-interrupt-panel__actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.chat-interrupt-panel__btn{padding:6px 14px;font-size:var(--ngaf-chat-font-size-sm);border-radius:var(--ngaf-chat-radius-button);border:0;cursor:pointer;font-weight:500;transition:transform .2s ease,opacity .2s ease}.chat-interrupt-panel__btn:hover{transform:scale(1.03)}.chat-interrupt-panel__btn--primary{background:var(--ngaf-chat-primary);color:var(--ngaf-chat-on-primary)}.chat-interrupt-panel__btn--secondary{background:transparent;color:var(--ngaf-chat-text);border:1px solid var(--ngaf-chat-separator)}.chat-interrupt-panel__btn--tertiary{background:transparent;color:var(--ngaf-chat-text-muted);padding:6px 8px;font-size:var(--ngaf-chat-font-size-sm)}.chat-interrupt-panel__btn--tertiary:hover{color:var(--ngaf-chat-text)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10283
+ `, isInline: true, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ".chat-interrupt-panel{background:var(--ngaf-chat-surface);color:var(--ngaf-chat-text);border:1px solid var(--ngaf-chat-separator);border-radius:var(--ngaf-chat-radius-card);padding:14px 16px;font-size:var(--ngaf-chat-font-size-sm)}.chat-interrupt-panel__eyebrow{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.12em;color:var(--ngaf-chat-warning-text);margin:0 0 8px;display:flex;align-items:center;gap:6px}.chat-interrupt-panel__dot{width:6px;height:6px;border-radius:999px;background:var(--ngaf-chat-warning-text);flex:0 0 6px}.chat-interrupt-panel__body{margin:0 0 12px;color:var(--ngaf-chat-text);white-space:pre-wrap}.chat-interrupt-panel__actions{display:flex;gap:6px;flex-wrap:wrap;align-items:center}.btn{border:0;padding:6px 14px;border-radius:var(--ngaf-chat-radius-button);font-size:12px;font-weight:500;cursor:pointer;transition:transform .2s ease,opacity .2s ease}.btn:hover{transform:scale(1.03)}.btn-primary{background:var(--ngaf-chat-primary);color:var(--ngaf-chat-on-primary)}.btn-secondary{background:transparent;color:var(--ngaf-chat-text);border:1px solid var(--ngaf-chat-separator)}.btn-text{background:transparent;color:var(--ngaf-chat-text-muted);padding:6px 10px}.btn-text:hover{color:var(--ngaf-chat-text)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10151
10284
  }
10152
10285
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatInterruptPanelComponent, decorators: [{
10153
10286
  type: Component,
10154
10287
  args: [{ selector: 'chat-interrupt-panel', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
10155
10288
  @if (interrupt()) {
10156
10289
  <div role="alert" class="chat-interrupt-panel">
10157
- <p class="chat-interrupt-panel__title">
10158
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
10159
- Agent paused
10290
+ <p class="chat-interrupt-panel__eyebrow">
10291
+ <span class="chat-interrupt-panel__dot" aria-hidden="true"></span>
10292
+ Agent paused — review needed
10160
10293
  </p>
10161
10294
  <p class="chat-interrupt-panel__body">{{ interruptReason() }}</p>
10162
-
10163
10295
  <div class="chat-interrupt-panel__actions">
10164
- <button
10165
- type="button"
10166
- class="chat-interrupt-panel__btn chat-interrupt-panel__btn--primary"
10167
- (click)="action.emit('accept')"
10168
- >Accept</button>
10169
- <button
10170
- type="button"
10171
- class="chat-interrupt-panel__btn chat-interrupt-panel__btn--secondary"
10172
- (click)="action.emit('edit')"
10173
- >Edit</button>
10174
- <button
10175
- type="button"
10176
- class="chat-interrupt-panel__btn chat-interrupt-panel__btn--secondary"
10177
- (click)="action.emit('respond')"
10178
- >Respond</button>
10179
- <button
10180
- type="button"
10181
- class="chat-interrupt-panel__btn chat-interrupt-panel__btn--tertiary"
10182
- (click)="action.emit('ignore')"
10183
- >Ignore</button>
10296
+ <button type="button" class="btn btn-primary" (click)="action.emit('accept')">Accept</button>
10297
+ <button type="button" class="btn btn-secondary" (click)="action.emit('edit')">Edit</button>
10298
+ <button type="button" class="btn btn-secondary" (click)="action.emit('respond')">Respond</button>
10299
+ <button type="button" class="btn btn-text" (click)="action.emit('ignore')">Ignore</button>
10184
10300
  </div>
10185
10301
  </div>
10186
10302
  }
10187
- `, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ".chat-interrupt-panel{background:var(--ngaf-chat-surface);color:var(--ngaf-chat-text);border:1px solid var(--ngaf-chat-separator);border-left:3px solid var(--ngaf-chat-warning-text);border-radius:var(--ngaf-chat-radius-card);padding:12px 16px;font-size:var(--ngaf-chat-font-size-sm)}.chat-interrupt-panel__title{font-weight:600;margin:0 0 4px;display:flex;align-items:center;gap:6px;color:var(--ngaf-chat-text)}.chat-interrupt-panel__title svg{color:var(--ngaf-chat-warning-text)}.chat-interrupt-panel__body{margin:0 0 12px;color:var(--ngaf-chat-text-muted);white-space:pre-wrap}.chat-interrupt-panel__actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.chat-interrupt-panel__btn{padding:6px 14px;font-size:var(--ngaf-chat-font-size-sm);border-radius:var(--ngaf-chat-radius-button);border:0;cursor:pointer;font-weight:500;transition:transform .2s ease,opacity .2s ease}.chat-interrupt-panel__btn:hover{transform:scale(1.03)}.chat-interrupt-panel__btn--primary{background:var(--ngaf-chat-primary);color:var(--ngaf-chat-on-primary)}.chat-interrupt-panel__btn--secondary{background:transparent;color:var(--ngaf-chat-text);border:1px solid var(--ngaf-chat-separator)}.chat-interrupt-panel__btn--tertiary{background:transparent;color:var(--ngaf-chat-text-muted);padding:6px 8px;font-size:var(--ngaf-chat-font-size-sm)}.chat-interrupt-panel__btn--tertiary:hover{color:var(--ngaf-chat-text)}\n"] }]
10303
+ `, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ".chat-interrupt-panel{background:var(--ngaf-chat-surface);color:var(--ngaf-chat-text);border:1px solid var(--ngaf-chat-separator);border-radius:var(--ngaf-chat-radius-card);padding:14px 16px;font-size:var(--ngaf-chat-font-size-sm)}.chat-interrupt-panel__eyebrow{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.12em;color:var(--ngaf-chat-warning-text);margin:0 0 8px;display:flex;align-items:center;gap:6px}.chat-interrupt-panel__dot{width:6px;height:6px;border-radius:999px;background:var(--ngaf-chat-warning-text);flex:0 0 6px}.chat-interrupt-panel__body{margin:0 0 12px;color:var(--ngaf-chat-text);white-space:pre-wrap}.chat-interrupt-panel__actions{display:flex;gap:6px;flex-wrap:wrap;align-items:center}.btn{border:0;padding:6px 14px;border-radius:var(--ngaf-chat-radius-button);font-size:12px;font-weight:500;cursor:pointer;transition:transform .2s ease,opacity .2s ease}.btn:hover{transform:scale(1.03)}.btn-primary{background:var(--ngaf-chat-primary);color:var(--ngaf-chat-on-primary)}.btn-secondary{background:transparent;color:var(--ngaf-chat-text);border:1px solid var(--ngaf-chat-separator)}.btn-text{background:transparent;color:var(--ngaf-chat-text-muted);padding:6px 10px}.btn-text:hover{color:var(--ngaf-chat-text)}\n"] }]
10188
10304
  }], propDecorators: { agent: [{ type: i0.Input, args: [{ isSignal: true, alias: "agent", required: true }] }], action: [{ type: i0.Output, args: ["action"] }] } });
10189
10305
 
10306
+ // SPDX-License-Identifier: MIT
10307
+ class ChatApprovalCardComponent {
10308
+ agent = input.required(...(ngDevMode ? [{ debugName: "agent" }] : []));
10309
+ matchKind = input(undefined, ...(ngDevMode ? [{ debugName: "matchKind" }] : []));
10310
+ title = input('Approval required', ...(ngDevMode ? [{ debugName: "title" }] : []));
10311
+ showEdit = input(false, ...(ngDevMode ? [{ debugName: "showEdit" }] : []));
10312
+ action = output();
10313
+ bodyTemplate = contentChild('body', ...(ngDevMode ? [{ debugName: "bodyTemplate" }] : []));
10314
+ dialogRef = viewChild('dialogEl', ...(ngDevMode ? [{ debugName: "dialogRef" }] : []));
10315
+ interrupt = computed(() => this.agent().interrupt?.(), ...(ngDevMode ? [{ debugName: "interrupt" }] : []));
10316
+ payload = computed(() => {
10317
+ const i = this.interrupt();
10318
+ if (!i)
10319
+ return undefined;
10320
+ const v = i.value;
10321
+ const want = this.matchKind();
10322
+ if (want !== undefined) {
10323
+ if (!v || typeof v !== 'object' || v.kind !== want) {
10324
+ return undefined;
10325
+ }
10326
+ }
10327
+ return v;
10328
+ }, ...(ngDevMode ? [{ debugName: "payload" }] : []));
10329
+ constructor() {
10330
+ effect(() => {
10331
+ const p = this.payload();
10332
+ const dialog = this.dialogRef()?.nativeElement;
10333
+ if (!dialog)
10334
+ return;
10335
+ if (p && !dialog.open) {
10336
+ dialog.showModal();
10337
+ }
10338
+ else if (!p && dialog.open) {
10339
+ dialog.close();
10340
+ }
10341
+ });
10342
+ }
10343
+ emit(action) {
10344
+ this.action.emit(action);
10345
+ // 'approve' and 'cancel' are terminal — they resolve the interrupt, so the
10346
+ // dialog closes. 'edit' is non-terminal: the caller reveals an inline
10347
+ // editor in the body slot and submits the resume itself, so we leave the
10348
+ // dialog open.
10349
+ if (action !== 'edit') {
10350
+ this.closeDialog();
10351
+ }
10352
+ }
10353
+ onCancelEvent(ev) {
10354
+ // Native dialog's cancel event fires on Escape. Treat as cancel.
10355
+ ev.preventDefault();
10356
+ this.action.emit('cancel');
10357
+ this.closeDialog();
10358
+ }
10359
+ closeDialog() {
10360
+ const dialog = this.dialogRef()?.nativeElement;
10361
+ if (!dialog)
10362
+ return;
10363
+ if (dialog.open)
10364
+ dialog.close();
10365
+ }
10366
+ onDialogClose() {
10367
+ // Native close — no-op; emit happens in emit() / onCancelEvent.
10368
+ }
10369
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatApprovalCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10370
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: ChatApprovalCardComponent, isStandalone: true, selector: "chat-approval-card", inputs: { agent: { classPropertyName: "agent", publicName: "agent", isSignal: true, isRequired: true, transformFunction: null }, matchKind: { classPropertyName: "matchKind", publicName: "matchKind", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, showEdit: { classPropertyName: "showEdit", publicName: "showEdit", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action" }, queries: [{ propertyName: "bodyTemplate", first: true, predicate: ["body"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "dialogRef", first: true, predicate: ["dialogEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
10371
+ <dialog #dialogEl class="chat-approval-card" (close)="onDialogClose()" (cancel)="onCancelEvent($event)">
10372
+ <div class="chat-approval-card__header">
10373
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
10374
+ <h4>{{ title() }}</h4>
10375
+ </div>
10376
+ <div class="chat-approval-card__body">
10377
+ @if (bodyTemplate(); as tpl) {
10378
+ @if (payload(); as p) {
10379
+ <ng-container *ngTemplateOutlet="tpl; context: { $implicit: p }"></ng-container>
10380
+ }
10381
+ }
10382
+ </div>
10383
+ <div class="chat-approval-card__actions">
10384
+ <button type="button" class="btn btn-text" (click)="emit('cancel')">Cancel</button>
10385
+ @if (showEdit()) {
10386
+ <button type="button" class="btn btn-secondary" (click)="emit('edit')">Edit</button>
10387
+ }
10388
+ <button type="button" class="btn btn-primary" (click)="emit('approve')">Approve</button>
10389
+ </div>
10390
+ </dialog>
10391
+ `, isInline: true, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ":host{display:contents}dialog.chat-approval-card{width:440px;max-width:calc(100vw - 32px);margin:auto;padding:0;border:0;border-radius:12px;background:var(--ngaf-chat-surface);color:var(--ngaf-chat-text);box-shadow:0 20px 50px #0000002e}dialog.chat-approval-card::backdrop{background:#00000080;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.chat-approval-card__header{padding:14px 16px 12px;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--ngaf-chat-separator)}.chat-approval-card__header h4{margin:0;font-size:14px;font-weight:600;color:var(--ngaf-chat-text)}.chat-approval-card__header svg{color:var(--ngaf-chat-warning-text);width:16px;height:16px;flex:0 0 16px}.chat-approval-card__body{padding:14px 16px;font-size:var(--ngaf-chat-font-size-sm, 13px);color:var(--ngaf-chat-text)}.chat-approval-card__actions{padding:8px 16px 14px;display:flex;gap:6px;justify-content:flex-end;align-items:center}.btn{border:0;padding:6px 14px;border-radius:8px;font-size:12px;font-weight:500;cursor:pointer;transition:transform .2s ease,opacity .2s ease}.btn:hover{transform:scale(1.03)}.btn-primary{background:var(--ngaf-chat-primary);color:var(--ngaf-chat-on-primary)}.btn-secondary{background:transparent;color:var(--ngaf-chat-text);border:1px solid var(--ngaf-chat-separator)}.btn-text{background:transparent;color:var(--ngaf-chat-text-muted);padding:6px 10px}.btn-text:hover{color:var(--ngaf-chat-text)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10392
+ }
10393
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: ChatApprovalCardComponent, decorators: [{
10394
+ type: Component,
10395
+ args: [{ selector: 'chat-approval-card', standalone: true, imports: [NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: `
10396
+ <dialog #dialogEl class="chat-approval-card" (close)="onDialogClose()" (cancel)="onCancelEvent($event)">
10397
+ <div class="chat-approval-card__header">
10398
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
10399
+ <h4>{{ title() }}</h4>
10400
+ </div>
10401
+ <div class="chat-approval-card__body">
10402
+ @if (bodyTemplate(); as tpl) {
10403
+ @if (payload(); as p) {
10404
+ <ng-container *ngTemplateOutlet="tpl; context: { $implicit: p }"></ng-container>
10405
+ }
10406
+ }
10407
+ </div>
10408
+ <div class="chat-approval-card__actions">
10409
+ <button type="button" class="btn btn-text" (click)="emit('cancel')">Cancel</button>
10410
+ @if (showEdit()) {
10411
+ <button type="button" class="btn btn-secondary" (click)="emit('edit')">Edit</button>
10412
+ }
10413
+ <button type="button" class="btn btn-primary" (click)="emit('approve')">Approve</button>
10414
+ </div>
10415
+ </dialog>
10416
+ `, styles: [":host{font-family:var(--ngaf-chat-font-family);color:var(--ngaf-chat-text)}\n", ":host{display:contents}dialog.chat-approval-card{width:440px;max-width:calc(100vw - 32px);margin:auto;padding:0;border:0;border-radius:12px;background:var(--ngaf-chat-surface);color:var(--ngaf-chat-text);box-shadow:0 20px 50px #0000002e}dialog.chat-approval-card::backdrop{background:#00000080;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}.chat-approval-card__header{padding:14px 16px 12px;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--ngaf-chat-separator)}.chat-approval-card__header h4{margin:0;font-size:14px;font-weight:600;color:var(--ngaf-chat-text)}.chat-approval-card__header svg{color:var(--ngaf-chat-warning-text);width:16px;height:16px;flex:0 0 16px}.chat-approval-card__body{padding:14px 16px;font-size:var(--ngaf-chat-font-size-sm, 13px);color:var(--ngaf-chat-text)}.chat-approval-card__actions{padding:8px 16px 14px;display:flex;gap:6px;justify-content:flex-end;align-items:center}.btn{border:0;padding:6px 14px;border-radius:8px;font-size:12px;font-weight:500;cursor:pointer;transition:transform .2s ease,opacity .2s ease}.btn:hover{transform:scale(1.03)}.btn-primary{background:var(--ngaf-chat-primary);color:var(--ngaf-chat-on-primary)}.btn-secondary{background:transparent;color:var(--ngaf-chat-text);border:1px solid var(--ngaf-chat-separator)}.btn-text{background:transparent;color:var(--ngaf-chat-text-muted);padding:6px 10px}.btn-text:hover{color:var(--ngaf-chat-text)}\n"] }]
10417
+ }], ctorParameters: () => [], propDecorators: { agent: [{ type: i0.Input, args: [{ isSignal: true, alias: "agent", required: true }] }], matchKind: [{ type: i0.Input, args: [{ isSignal: true, alias: "matchKind", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], showEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEdit", required: false }] }], action: [{ type: i0.Output, args: ["action"] }], bodyTemplate: [{ type: i0.ContentChild, args: ['body', { isSignal: true }] }], dialogRef: [{ type: i0.ViewChild, args: ['dialogEl', { isSignal: true }] }] } });
10418
+
10190
10419
  // SPDX-License-Identifier: MIT
10191
10420
  let markedParse = null;
10192
10421
  let markedLoadAttempted = false;
@@ -11362,5 +11591,5 @@ function mockAgent(opts = {}) {
11362
11591
  * Generated bundle index. Do not edit.
11363
11592
  */
11364
11593
 
11365
- export { A2uiAudioPlayerComponent, A2uiButtonComponent, A2uiCardComponent, A2uiCheckBoxComponent, A2uiColumnComponent, A2uiDateTimeInputComponent, A2uiDividerComponent, A2uiIconComponent, A2uiImageComponent, A2uiListComponent, A2uiModalComponent, A2uiMultipleChoiceComponent, A2uiRowComponent, A2uiSliderComponent, A2uiSurfaceComponent, A2uiTabsComponent, A2uiTextComponent, A2uiTextFieldComponent, A2uiVideoComponent, CHAT_CONFIG, CHAT_LIFECYCLE, CHAT_MARKDOWN_STYLES, ChatCitationCardTemplateDirective, ChatCitationsCardComponent, ChatCitationsComponent, ChatComponent, ChatConfirmDialogComponent, ChatErrorComponent, ChatGenerativeUiComponent, ChatGenuiSkeletonComponent, ChatHistorySearchPaletteComponent, ChatInputComponent, ChatInterruptComponent, ChatInterruptPanelComponent, ChatLauncherButtonComponent, ChatMessageActionsComponent, ChatMessageComponent, ChatMessageListComponent, ChatOverflowMenuComponent, ChatPopupComponent, ChatProjectListComponent, ChatReasoningComponent, ChatScrollBubbleComponent, ChatSelectComponent, ChatSidebarComponent, ChatSidenavComponent, ChatSidenavScrimComponent, ChatStreamingMdComponent, ChatSubagentCardComponent, ChatSubagentsComponent, ChatSuggestionsComponent, ChatThreadListComponent, ChatTimelineComponent, ChatTimelineSliderComponent, ChatToolCallCardComponent, ChatToolCallTemplateDirective, ChatToolCallsComponent, ChatTraceComponent, ChatTypingIndicatorComponent, ChatWelcomeComponent, ChatWelcomeSuggestionComponent, ChatWindowComponent, CitationsResolverService, ICON_AGENT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CHEVRON_UP, ICON_SEND, ICON_TOOL, ICON_WARNING, IS_HEADER_ROW, MARKDOWN_VIEW_REGISTRY, MarkdownAutolinkComponent, MarkdownBlockquoteComponent, MarkdownChildrenComponent, MarkdownCitationReferenceComponent, MarkdownCodeBlockComponent, MarkdownDocumentComponent, MarkdownEmphasisComponent, MarkdownHardBreakComponent, MarkdownHeadingComponent, MarkdownImageComponent, MarkdownInlineCodeComponent, MarkdownLinkComponent, MarkdownListComponent, MarkdownListItemComponent, MarkdownParagraphComponent, MarkdownSoftBreakComponent, MarkdownStrikethroughComponent, MarkdownStrongComponent, MarkdownTableCellComponent, MarkdownTableComponent, MarkdownTableRowComponent, MarkdownTextComponent, MarkdownThematicBreakComponent, MessageTemplateDirective, a2uiBasicCatalog, buildA2uiActionMessage, cacheplaneMarkdownViews, createA2uiSurfaceStore, createContentClassifier, createParseTreeStore, createPartialArgsBridge, emitBinding, extractErrorMessage, formatDuration, getInterrupt, getMessageType, isAssistantMessage, isSystemMessage, isToolMessage, isTyping, isUserMessage, messageContent, mockAgent, normalizeEnvelopeArgs, normalizeViewEntry, provideChat, renderMarkdown, statusColor, submitMessage, surfaceToSpec };
11594
+ export { A2uiAudioPlayerComponent, A2uiButtonComponent, A2uiCardComponent, A2uiCheckBoxComponent, A2uiColumnComponent, A2uiDateTimeInputComponent, A2uiDividerComponent, A2uiIconComponent, A2uiImageComponent, A2uiListComponent, A2uiModalComponent, A2uiMultipleChoiceComponent, A2uiRowComponent, A2uiSliderComponent, A2uiSurfaceComponent, A2uiTabsComponent, A2uiTextComponent, A2uiTextFieldComponent, A2uiVideoComponent, CHAT_CONFIG, CHAT_LIFECYCLE, CHAT_MARKDOWN_STYLES, ChatApprovalCardComponent, ChatCitationCardTemplateDirective, ChatCitationsCardComponent, ChatCitationsComponent, ChatComponent, ChatConfirmDialogComponent, ChatErrorComponent, ChatGenerativeUiComponent, ChatGenuiSkeletonComponent, ChatHistorySearchPaletteComponent, ChatInputComponent, ChatInterruptComponent, ChatInterruptPanelComponent, ChatLauncherButtonComponent, ChatMessageActionsComponent, ChatMessageComponent, ChatMessageListComponent, ChatOverflowMenuComponent, ChatPopupComponent, ChatProjectListComponent, ChatReasoningComponent, ChatScrollBubbleComponent, ChatSelectComponent, ChatSidebarComponent, ChatSidenavComponent, ChatSidenavScrimComponent, ChatStreamingMdComponent, ChatSubagentCardComponent, ChatSubagentsComponent, ChatSuggestionsComponent, ChatThreadListComponent, ChatTimelineComponent, ChatTimelineSliderComponent, ChatToolCallCardComponent, ChatToolCallTemplateDirective, ChatToolCallsComponent, ChatToolViewsComponent, ChatTraceComponent, ChatTypingIndicatorComponent, ChatWelcomeComponent, ChatWelcomeSuggestionComponent, ChatWindowComponent, CitationsResolverService, ICON_AGENT, ICON_CHECK, ICON_CHEVRON_DOWN, ICON_CHEVRON_UP, ICON_SEND, ICON_TOOL, ICON_WARNING, IS_HEADER_ROW, MARKDOWN_VIEW_REGISTRY, MarkdownAutolinkComponent, MarkdownBlockquoteComponent, MarkdownChildrenComponent, MarkdownCitationReferenceComponent, MarkdownCodeBlockComponent, MarkdownDocumentComponent, MarkdownEmphasisComponent, MarkdownHardBreakComponent, MarkdownHeadingComponent, MarkdownImageComponent, MarkdownInlineCodeComponent, MarkdownLinkComponent, MarkdownListComponent, MarkdownListItemComponent, MarkdownParagraphComponent, MarkdownSoftBreakComponent, MarkdownStrikethroughComponent, MarkdownStrongComponent, MarkdownTableCellComponent, MarkdownTableComponent, MarkdownTableRowComponent, MarkdownTextComponent, MarkdownThematicBreakComponent, MessageTemplateDirective, a2uiBasicCatalog, buildA2uiActionMessage, cacheplaneMarkdownViews, createA2uiSurfaceStore, createContentClassifier, createParseTreeStore, createPartialArgsBridge, emitBinding, extractErrorMessage, formatDuration, getInterrupt, getMessageType, isAssistantMessage, isSystemMessage, isToolMessage, isTyping, isUserMessage, messageContent, mockAgent, normalizeEnvelopeArgs, normalizeViewEntry, provideChat, renderMarkdown, statusColor, submitMessage, surfaceToSpec };
11366
11595
  //# sourceMappingURL=threadplane-chat.mjs.map