@m6d/cortex-angular 0.11.1 → 0.13.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/README.md +85 -79
- package/fesm2022/m6d-cortex-angular.mjs +892 -368
- package/package.json +6 -3
- package/theme.css +1 -1
- package/types/m6d-cortex-angular.d.ts +84 -33
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m6d/cortex-angular",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.13.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
|
|
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
|
-
*
|
|
47
|
-
*
|
|
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
|
|
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
|
|
72
|
-
*
|
|
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
|
|
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
|
|
155
|
-
* and whoever administers that is not necessarily whoever owns this
|
|
156
|
-
* Enable only when both are the same party and the embeds are this
|
|
157
|
-
* own pages
|
|
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
|
|
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;
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
358
|
-
* Returns the delivery
|
|
359
|
-
*
|
|
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
|
|
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.
|
|
367
|
-
*
|
|
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
|
|
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
|
|
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.
|