@m6d/cortex-angular 0.11.1 → 0.12.0

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@m6d/cortex-angular",
3
- "version": "0.11.1",
4
- "description": "Reusable AI agent chat UI library for Angular",
3
+ "version": "0.12.0",
4
+ "description": "Angular chat widget for Cortex agents",
5
5
  "license": "MIT",
6
6
  "sideEffects": false,
7
7
  "exports": {
@@ -40,7 +40,10 @@
40
40
  },
41
41
  "releaseWatchPaths": [
42
42
  "internal/client",
43
- "internal/contracts"
43
+ "internal/contracts/src/charts",
44
+ "internal/contracts/src/client-tools",
45
+ "internal/contracts/src/markdown",
46
+ "internal/contracts/src/wire"
44
47
  ],
45
48
  "module": "fesm2022/m6d-cortex-angular.mjs",
46
49
  "typings": "types/m6d-cortex-angular.d.ts",
package/theme.css CHANGED
@@ -64,7 +64,7 @@
64
64
  /* These read --cortex-font-size (or the color named) when unset, so they ship
65
65
  * unset: declaring one here gives that part its own value. Code blocks default
66
66
  * to a tint of the heading color, so they stand a step off whatever they sit
67
- * on: the transcript, the user's bubble, or a host's own surface.
67
+ * on: the transcript, the user's bubble, or a consumer's own surface.
68
68
  *
69
69
  * --cortex-font-size-message: 0.875rem; message text and the composer
70
70
  * --cortex-font-size-header: 0.875rem; the chat and history titles
@@ -22,6 +22,11 @@ declare const ATTACHMENT_ORIGINS: readonly [
22
22
  "tool"
23
23
  ];
24
24
  export type AttachmentOrigin = (typeof ATTACHMENT_ORIGINS)[number];
25
+ declare function checkAttachmentPolicy(file: {
26
+ type: string;
27
+ size: number;
28
+ name: string;
29
+ }): "type" | "size" | "name" | "ok";
25
30
  export type AttachmentSummary = {
26
31
  id: string;
27
32
  filename: string;
@@ -43,8 +48,16 @@ export type ThreadSummary = {
43
48
  };
44
49
  };
45
50
  /**
46
- * What the server offers this agent's clients, fetched once at widget startup.
47
- * The widget hides UI for anything absent; enforcement stays server-side.
51
+ * Body of `POST /threads`. Only a console operator's request is read: its
52
+ * test thread keeps `variables` as the session the agent's server would
53
+ * otherwise fill per end user.
54
+ */
55
+ export type CreateThreadRequest = {
56
+ variables?: Record<string, string | number | boolean>;
57
+ };
58
+ /**
59
+ * What Cortex offers this agent's clients, fetched once at widget startup.
60
+ * The widget hides UI for anything absent; Cortex enforces each.
48
61
  */
49
62
  export type AgentCapabilities = {
50
63
  attachments: boolean;
@@ -63,18 +76,24 @@ export type Suggestion = {
63
76
  title: string;
64
77
  prompt: string;
65
78
  };
66
- /** The runtime contract's `suggestionsSchema` and `greetingSchema` are built from these. */
79
+ /** The manifest's suggestion and greeting schemas are built from these. */
67
80
  export type Suggestions = Record<Locale, Suggestion[]>;
68
81
  export type Greeting = Record<Locale, string>;
69
82
  export type CortexHeaders = Record<string, string>;
70
83
  /**
71
- * How the widget reaches its cortex server. `baseUrl` may be a thunk so a host
72
- * with a reactive origin (an Angular Signal is itself a `() => string`) is read
84
+ * How the widget reaches Cortex. `baseUrl` may be a thunk so a consumer with
85
+ * a reactive origin (an Angular Signal is itself a `() => string`) is read
73
86
  * at the moment of each request rather than captured once.
74
87
  */
75
88
  export type CortexTransport = {
76
89
  baseUrl: string | (() => string);
77
90
  getHeaders: () => CortexHeaders | Promise<CortexHeaders>;
91
+ /**
92
+ * Sent with every thread the widget creates. The console's playground puts
93
+ * a test thread's variables here; Cortex honours it only from a verified
94
+ * console operator.
95
+ */
96
+ threadBody?: CreateThreadRequest;
78
97
  };
79
98
  export type ColorScheme = "light" | "dark";
80
99
  export type MediaRenderContext = {
@@ -98,7 +117,7 @@ export type CortexTheme = "light" | "dark" | "system";
98
117
  export type CortexToolCallContext = {
99
118
  threadId: string;
100
119
  };
101
- /** A tool the server left for the client to answer. */
120
+ /** A tool call Cortex left for the client to answer. */
102
121
  export type CortexToolCall = {
103
122
  toolCallId: string;
104
123
  toolName: string;
@@ -151,16 +170,16 @@ export type CortexConfig = {
151
170
  * Render embedded tool pages served from this page's own origin with their
152
171
  * full identity — which, same-origin, means no effective sandbox at all: such
153
172
  * an embed can reach into the host page. By default they are refused
154
- * (rendered inert), because embed URLs come from Control Center tool config,
155
- * and whoever administers that is not necessarily whoever owns this site.
156
- * Enable only when both are the same party and the embeds are this site's
157
- * own pages — then a second origin just for embeds is no longer needed.
173
+ * (rendered inert), because embed URLs come from the tool's definition in
174
+ * Cortex, and whoever administers that is not necessarily whoever owns this
175
+ * site. Enable only when both are the same party and the embeds are this
176
+ * site's own pages, so the embeds need no origin of their own.
158
177
  */
159
178
  trustSameOriginEmbeds?: boolean;
160
179
  hooks?: CortexHooks;
161
180
  };
162
181
  /**
163
- * What the header receives, the host's own or the widget's: where the widget
182
+ * What the header receives, the consumer's own or the widget's: where the widget
164
183
  * is, what to show, and the actions the default header offers. One header
165
184
  * serves both screens; `screen` says which is showing.
166
185
  */
@@ -171,10 +190,10 @@ export type CortexHeaderContext = {
171
190
  title: string | null;
172
191
  /** `agentName` in the widget's locale. */
173
192
  agentName: string | undefined;
174
- /** How many conversations the history lists; 0 while it loads. */
175
- threadCount: number;
193
+ /** How many conversations the history lists; undefined while the first load is out. */
194
+ threadCount: number | undefined;
176
195
  isWorking: boolean;
177
- /** Whether the host enabled the debug switch, and whether it is on. */
196
+ /** Whether the consumer enabled the debug switch, and whether it is on. */
178
197
  debug: {
179
198
  shown: boolean;
180
199
  on: boolean;
@@ -187,13 +206,13 @@ export type CortexHeaderContext = {
187
206
  toggleDebug: () => void;
188
207
  };
189
208
  /**
190
- * What the blank-chat landing receives, the host's own or the widget's. The
209
+ * What the blank-chat landing receives, the consumer's own or the widget's. The
191
210
  * composer arrives ready to place as the SDK's own view type (`View`), so a
192
211
  * custom landing keeps the widget's attachments and send rules. Each SDK
193
212
  * exports it as `CortexLandingContext` with its view type filled in.
194
213
  */
195
214
  export type LandingContext<View> = {
196
- /** The agent's greeting in the widget's locale, or the built-in one. */
215
+ /** The agent's greeting in the widget's locale, or the built-in one; empty until the handshake answers. */
197
216
  greeting: string;
198
217
  /** Empty until the handshake answers, and for an agent that takes no messages. */
199
218
  suggestions: Suggestion[];
@@ -210,7 +229,7 @@ export type LandingContext<View> = {
210
229
  /** Shows the whole history: its screen, or the sidebar in full mode, as the header's `openHistory`. */
211
230
  openHistory: () => void;
212
231
  };
213
- /** What the history's list receives, the host's own or the widget's. Its header is the header slot. */
232
+ /** What the history's list receives, the consumer's own or the widget's. Its header is the header slot. */
214
233
  export type CortexThreadListContext = {
215
234
  /** Newest first; undefined while the first load is out. */
216
235
  threads: ThreadSummary[] | undefined;
@@ -220,20 +239,41 @@ export type CortexThreadListContext = {
220
239
  deleteThread: (threadId: string) => void;
221
240
  newChat: () => void;
222
241
  };
223
- /** Each part of the widget a host can draw itself, with the context it receives. */
242
+ /** Each part of the widget a consumer can draw itself, with the context it receives. */
224
243
  export type CortexSlotContexts<View> = {
225
244
  header: CortexHeaderContext;
226
245
  landing: LandingContext<View>;
227
246
  threadList: CortexThreadListContext;
228
247
  };
248
+ /** Why Cortex dropped a message instead of running it: a call on the thread waits for approval. */
249
+ export type SendRefusal = "approval_pending";
250
+ /** Why the composer refused a file before uploading it: the attachment policy's reason, or one file too many. */
251
+ export type AttachmentRefusal = Exclude<ReturnType<typeof checkAttachmentPolicy>, "ok"> | "count";
229
252
  export type QueuedAttachment = {
230
253
  localId: string;
231
254
  filename: string;
232
255
  status: "uploading" | "ready" | "deleting" | "error";
256
+ /** Set on an `error` chip the composer never uploaded. */
257
+ refusal?: AttachmentRefusal;
233
258
  threadId?: string;
234
259
  attachmentId?: string;
235
260
  summary?: AttachmentSummary;
236
261
  };
262
+ /**
263
+ * Why Cortex left a read unanswered: it could not be reached (or failed),
264
+ * it refused the caller's sign-in (401), or it serves no such agent (404).
265
+ */
266
+ export type Unanswered = "unreachable" | "refused" | "missing";
267
+ /**
268
+ * A turn shown as failed, with Cortex's reason when it gave one (a message
269
+ * that never got out has none). The reason may name the model provider's
270
+ * answer, so a widget shows it only where its debug view is offered. A
271
+ * message Cortex refused names why instead.
272
+ */
273
+ export type TurnFailure = {
274
+ reason: string | undefined;
275
+ refusal?: SendRefusal;
276
+ };
237
277
  declare const ATTACHMENT_STATUSES$1: readonly [
238
278
  "pending",
239
279
  "described",
@@ -261,7 +301,7 @@ export type CortexAngularConfig = CortexConfig & {
261
301
  };
262
302
  /** The landing's context, its composer a template to place with `ngTemplateOutlet`. */
263
303
  export type CortexLandingContext = LandingContext<TemplateRef<unknown>>;
264
- /** Each part of the widget a host can draw itself, with the context it receives. */
304
+ /** Each part of the widget a consumer can draw itself, with the context it receives. */
265
305
  export type CortexAngularSlotContexts = CortexSlotContexts<TemplateRef<unknown>>;
266
306
  declare class AttachmentQueueService {
267
307
  readonly items: import("@angular/core").WritableSignal<QueuedAttachment[]>;
@@ -281,6 +321,7 @@ declare class AttachmentQueueService {
281
321
  summary: AttachmentSummary$1;
282
322
  localId: string;
283
323
  filename: string;
324
+ refusal?: "type" | "size" | "name" | "count";
284
325
  threadId?: string;
285
326
  attachmentId?: string;
286
327
  }[];
@@ -294,7 +335,7 @@ declare class AttachmentQueueService {
294
335
  declare class CortexChatService {
295
336
  /**
296
337
  * Selection is the Thread handed to `selectThread`, resolved against the live
297
- * list: the row there receives the title and isRunning updates the server
338
+ * list: the row there receives the title and isRunning updates Cortex
298
339
  * pushes, so the selection follows them without hand-syncing. The snapshot
299
340
  * only answers for a thread the list has not caught up with yet.
300
341
  */
@@ -306,14 +347,22 @@ declare class CortexChatService {
306
347
  readonly isAgentWorking: import("@angular/core").Signal<boolean>;
307
348
  /**
308
349
  * Derived, never hand-synced: a call only counts as owed while the stream is
309
- * idle — mid-turn an unsettled call is just a server tool executing. Reading
350
+ * idle — mid-turn an unsettled call is just a tool Cortex is executing. Reading
310
351
  * straight from the SDK's signals is what brings the composer back the
311
352
  * instant `addToolResult` settles the call part — the SDK's delivery promise
312
353
  * only resolves once the continuation turn ends, far too late to gate UI on.
313
354
  */
314
355
  readonly hasPendingToolCalls: import("@angular/core").Signal<boolean>;
315
- /** What the server offers this agent's clients; undefined until the handshake answers. */
356
+ /** A call waits for the user to approve or deny it: no message may go out until it is decided. */
357
+ readonly awaitsApproval: import("@angular/core").Signal<boolean>;
358
+ /** The last turn failed: Cortex said its run failed, or the message never got out. */
359
+ readonly turnFailure: import("@angular/core").WritableSignal<TurnFailure | undefined>;
360
+ /** The text of a message Cortex refused, handed back to the composer until a message goes out. */
361
+ readonly returnedDraft: import("@angular/core").WritableSignal<string | undefined>;
362
+ /** What Cortex offers this agent's clients; undefined until the handshake answers. */
316
363
  readonly capabilities: import("@angular/core").WritableSignal<AgentCapabilities | undefined>;
364
+ /** Why the handshake has not answered yet, once a try has failed. */
365
+ readonly unanswered: import("@angular/core").WritableSignal<Unanswered | undefined>;
317
366
  private readonly events;
318
367
  private readonly selectedThreadSnapshot;
319
368
  private readonly config;
@@ -340,7 +389,7 @@ declare class CortexChatService {
340
389
  * Capability-gated controls stay hidden until the handshake answers, so it
341
390
  * is asked until it does, and afresh after every reconnect: the old answer
342
391
  * may be stale (a trigger toggled while the socket was down), so it is
343
- * forgotten first.
392
+ * forgotten first. Why it has not answered is shown until an answer arrives.
344
393
  */
345
394
  private loadCapabilities;
346
395
  get events$(): Observable<"onError" | "onFinish" | "onThreadSelected" | "onSend">;
@@ -354,17 +403,17 @@ declare class CortexChatService {
354
403
  */
355
404
  ensureThread(): Observable<string>;
356
405
  send(prompt: string, attachments?: AttachmentSummary[]): Observable<void>;
357
- /** Answers a tool call on behalf of a consumer-supplied tool component.
358
- * Returns the delivery promise so callers (the client tool embed) can
359
- * re-arm on a transient failure instead of stranding the pending call. */
406
+ /** Answers a tool call on behalf of a tool component or the client tool
407
+ * embed. Returns the delivery, which fails when the answer did not reach
408
+ * Cortex, so the caller can ask for it again instead of stranding the call. */
360
409
  addToolResult(toolCallId: string, toolName: string, output: unknown): Promise<void>;
361
- /** The user's decision on a server tool the run parked on; the run resumes once delivered. */
410
+ /** The user's decision on a call the run parked on; the run resumes once delivered. */
362
411
  respondToApproval(approvalId: string, approved: boolean): Promise<void>;
363
412
  abort(): Promise<void>;
364
413
  deleteThread(threadId: string): Observable<void>;
365
414
  /**
366
- * Points the widget at a thread with a chat client of its own. The client is
367
- * server-authoritative: on mount it asks cortex for the transcript and for
415
+ * Points the widget at a thread with a chat client of its own. Cortex owns
416
+ * the transcript: on mount the client asks it for the transcript and for
368
417
  * whatever run is still generating, so selecting a thread, reloading the page
369
418
  * and a second tab all re-attach through one path.
370
419
  */
@@ -398,17 +447,19 @@ export declare class CortexChatWidgetComponent implements OnInit {
398
447
  protected readonly viewMode: import("@angular/core").Signal<CortexViewMode>;
399
448
  private readonly composer;
400
449
  private readonly landingComposer;
401
- /** The host's own parts by slot; each replaces the widget's default and gets the same context. */
450
+ /** The consumer's own parts by slot; each replaces the widget's default and gets the same context. */
402
451
  private readonly slotDirectives;
403
452
  protected readonly slots: import("@angular/core").Signal<Map<keyof CortexAngularSlotContexts, TemplateRef<{
404
453
  $implicit: CortexHeaderContext | CortexThreadListContext | LandingContext<TemplateRef<unknown>>;
405
454
  }>>>;
455
+ /** Why the handshake has not answered, worded for the composer's place. */
456
+ protected readonly unansweredNotice: import("@angular/core").Signal<"translate_access_refused" | "translate_agent_unreachable" | "translate_agent_missing" | undefined>;
406
457
  protected readonly chatHeader: import("@angular/core").Signal<{
407
458
  screen: "chat" | "threads";
408
459
  viewMode: CortexViewMode;
409
460
  title: string | null;
410
461
  agentName: string | undefined;
411
- threadCount: number;
462
+ threadCount: number | undefined;
412
463
  isWorking: boolean;
413
464
  debug: {
414
465
  shown: boolean;
@@ -424,7 +475,7 @@ export declare class CortexChatWidgetComponent implements OnInit {
424
475
  viewMode: CortexViewMode;
425
476
  title: string | null;
426
477
  agentName: string | undefined;
427
- threadCount: number;
478
+ threadCount: number | undefined;
428
479
  isWorking: boolean;
429
480
  debug: {
430
481
  shown: boolean;
@@ -482,7 +533,7 @@ export type SlotTemplateContext<Slot extends SlotName> = {
482
533
  $implicit: CortexAngularSlotContexts[Slot];
483
534
  };
484
535
  /**
485
- * Marks the host's own part of `<cortex-chat-widget>`, named by `cortexSlot`:
536
+ * Marks the consumer's own part of `<cortex-chat-widget>`, named by `cortexSlot`:
486
537
  * `header`, `landing` or `threadList`. It replaces the widget's default and
487
538
  * receives the context the default is drawn from; `header` serves both the
488
539
  * chat and the history screen.