@m6d/cortex-cli 0.22.0 → 0.24.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.
@@ -1,14 +1,22 @@
1
1
  import { z } from "zod";
2
2
  import { CONNECT_ACCOUNT_TOOL, REQUEST_LOCATION_TOOL } from "../client-tools/index";
3
3
  import { backendErrorSchema } from "../errors/index";
4
- import { pendingApprovalSchema, TRIGGER_ANSWER_TOOL_NAME } from "../triggers/index";
4
+ import { runRecordSchema, runWaitSchema } from "../runs/index";
5
+ import { TRIGGER_ANSWER_TOOL_NAME } from "../triggers/index";
5
6
  import { isRecord, readJsonPointer } from "../json/index";
6
- import { APPROVAL_TITLE_FIELD, LOCALES } from "../wire/index";
7
+ import {
8
+ APPROVAL_TITLE_FIELD,
9
+ CANCEL_TASK_TOOL,
10
+ DELEGATE_TASK_TOOL,
11
+ LOCALES,
12
+ SCHEDULE_WAKE_TOOL,
13
+ } from "../wire/index";
7
14
 
8
15
  /*
9
16
  * The server protocol: what Cortex calls on a customer's backend. `GET /`
10
17
  * answers the manifest (the variables it fills, the native tools it runs,
11
- * whether it verifies end-user tokens, the events it hears); the other
18
+ * whether it verifies end-user tokens, whether Cortex may act for a user
19
+ * whose token is gone, the events it hears); the other
12
20
  * routes are the calls behind them. Every request carries the server key as
13
21
  * its bearer, and a run-scoped one also the run's token in
14
22
  * `END_USER_TOKEN_HEADER` when the run has one. A refusal on any route
@@ -121,6 +129,14 @@ export const BUILT_IN_VARIABLES = [
121
129
  type: "string",
122
130
  computed: true,
123
131
  },
132
+ {
133
+ name: "threadId",
134
+ description:
135
+ "The thread's id: a backend that keeps it fires a trigger back into the thread",
136
+ example: "3d206d66-4b28-4608-904c-3e8599e63a7b",
137
+ type: "string",
138
+ computed: true,
139
+ },
124
140
  ] as const satisfies ReadonlyArray<VariableDeclaration>;
125
141
 
126
142
  /**
@@ -129,6 +145,8 @@ export const BUILT_IN_VARIABLES = [
129
145
  * carries them to the .NET SDK.
130
146
  */
131
147
  export const BUILT_IN_TOOL_NAMES = [
148
+ CANCEL_TASK_TOOL,
149
+ DELEGATE_TASK_TOOL,
132
150
  "describeDataset",
133
151
  "executeCode",
134
152
  "fetchImage",
@@ -138,6 +156,7 @@ export const BUILT_IN_TOOL_NAMES = [
138
156
  "readReport",
139
157
  "searchRegistries",
140
158
  "searchThreadHistory",
159
+ SCHEDULE_WAKE_TOOL,
141
160
  REQUEST_LOCATION_TOOL,
142
161
  CONNECT_ACCOUNT_TOOL,
143
162
  TRIGGER_ANSWER_TOOL_NAME,
@@ -188,33 +207,58 @@ export const serverToolSchema = z.object({
188
207
  });
189
208
  export type ServerTool = z.infer<typeof serverToolSchema>;
190
209
 
191
- /** The events a server hears at `POST /events/:event`, and a webhook delivers. */
192
- export const SERVER_EVENTS = ["approval_requested"] as const;
210
+ /**
211
+ * The events a server hears at `POST /events/:event`, and a webhook
212
+ * delivers: a run that waits on a decision, and a run a trigger started
213
+ * that ended. A chat's runs end in their widget, which follows them.
214
+ */
215
+ export const SERVER_EVENTS = ["run_waiting", "run_finished"] as const;
216
+ export type ServerEventName = (typeof SERVER_EVENTS)[number];
193
217
 
194
218
  /** The header a webhook delivery names its event in, so a receiver tells events apart without parsing the body. */
195
219
  export const EVENT_HEADER = "X-Cortex-Event";
196
220
 
197
- /**
198
- * A run parked on server tools that need the end user's approval, as every
199
- * receiver hears of it: a webhook's body and the agent's server's event are
200
- * this one object. `firingId` and `triggerName` are set when a trigger
201
- * started the run, which is what a receiver answering through the firing
202
- * route needs. Every parked call is named; one whose input would carry the
203
- * event past `SERVER_REQUEST_MAX_BYTES` comes without its input.
204
- */
205
- export const approvalRequestedEventSchema = z.object({
206
- event: z.literal("approval_requested"),
221
+ /** What every event names: the agent it happened on, the run, the trigger that started that run if one did, and when. */
222
+ const runEventFields = {
207
223
  agentName: nameSchema,
208
224
  agentId: z.uuid(),
209
- threadId: z.string(),
210
- runId: z.string(),
211
- userId: z.string(),
212
- firingId: z.uuid().optional(),
225
+ runId: z.uuid(),
213
226
  triggerName: z.string().optional(),
214
- approvals: z.array(pendingApprovalSchema.partial({ input: true })),
215
227
  occurredAt: z.iso.datetime(),
228
+ };
229
+
230
+ /**
231
+ * A run parked on calls that wait for a decision, as every receiver hears
232
+ * of it: a webhook's body and the agent's server's event are this one
233
+ * object. `runId` names the run a receiver decides through
234
+ * `POST /agents/:agentName/runs/:id/answer`. Every call it waits on is
235
+ * named, a client tool's among them; one whose input would carry the event
236
+ * past `SERVER_REQUEST_MAX_BYTES` comes without its input. A run waiting
237
+ * only on its widget's calls is not announced.
238
+ */
239
+ export const runWaitingEventSchema = z.object({
240
+ event: z.literal("run_waiting"),
241
+ ...runEventFields,
242
+ threadId: z.string(),
243
+ userId: z.string(),
244
+ waits: z.array(runWaitSchema.partial({ input: true })),
245
+ });
246
+ export type RunWaitingEvent = z.infer<typeof runWaitingEventSchema>;
247
+
248
+ /** A run a trigger started that ended, with its record: how it ended and what it answered with. */
249
+ export const runFinishedEventSchema = z.object({
250
+ event: z.literal("run_finished"),
251
+ ...runEventFields,
252
+ run: runRecordSchema,
216
253
  });
217
- export type ApprovalRequestedEvent = z.infer<typeof approvalRequestedEventSchema>;
254
+ export type RunFinishedEvent = z.infer<typeof runFinishedEventSchema>;
255
+
256
+ /** Any event a receiver hears, told apart by `event`: `POST /events/:event` names it too. */
257
+ export const serverEventSchema = z.discriminatedUnion("event", [
258
+ runWaitingEventSchema,
259
+ runFinishedEventSchema,
260
+ ]);
261
+ export type ServerEvent = z.infer<typeof serverEventSchema>;
218
262
 
219
263
  /** What a local `$ref` (`#/$defs/order`) points at in `root`, read as the input compiler reads it; undefined for any other. */
220
264
  function pointedAt(root: unknown, ref: string) {
@@ -305,6 +349,13 @@ export const serverManifestSchema = z.object({
305
349
  tools.forEach((tool, index) => refuseBlockedApprovalTitle(tool, context, [index]));
306
350
  }),
307
351
  verify: z.boolean(),
352
+ /**
353
+ * Whether Cortex may act for a user whose token is gone, naming them in
354
+ * the body with `user.delegated`. Without it, such a run names no user
355
+ * to the server (`user: null`, as on a trigger's run), so it never acts
356
+ * for someone who is not there.
357
+ */
358
+ delegation: z.boolean(),
308
359
  events: z.array(z.enum(SERVER_EVENTS)),
309
360
  });
310
361
  export type ServerManifest = z.infer<typeof serverManifestSchema>;
@@ -312,9 +363,34 @@ export type ServerManifest = z.infer<typeof serverManifestSchema>;
312
363
  /** The agent a call is for. */
313
364
  const agentSchema = z.object({ id: z.uuid(), name: nameSchema });
314
365
 
315
- /** The end user a run acts for, as their token was verified; null for a trigger's run. */
366
+ /**
367
+ * Why Cortex acts for a user with no valid token of theirs: their message
368
+ * waited past its token (`queued`), or a run (`run`), a schedule (`schedule`)
369
+ * or a trigger (`trigger`) started the work.
370
+ */
371
+ export const DELEGATION_REASONS = ["queued", "run", "schedule", "trigger"] as const;
372
+
373
+ /** Cortex acting for a user who is not there: why, and the run that acts. */
374
+ export const delegationSchema = z.object({
375
+ reason: z.enum(DELEGATION_REASONS),
376
+ runId: z.uuid(),
377
+ });
378
+ export type Delegation = z.infer<typeof delegationSchema>;
379
+
380
+ /**
381
+ * The end user a run acts for, as their token was verified; null for a
382
+ * trigger's run, and for a run acting for a user who is not there toward a
383
+ * server whose manifest does not set `delegation`. `delegated` when the run acts for them without their
384
+ * token, told only to a server whose manifest sets `delegation`, which
385
+ * then trusts the body's user as it trusts the key: a tool reads it to
386
+ * refuse what a user who is not there should not do.
387
+ */
316
388
  const serverUserSchema = z
317
- .object({ id: userIdSchema, claims: z.record(z.string(), z.unknown()) })
389
+ .object({
390
+ id: userIdSchema,
391
+ claims: z.record(z.string(), z.unknown()),
392
+ delegated: delegationSchema.optional(),
393
+ })
318
394
  .nullable();
319
395
 
320
396
  /** `POST /variables`: the values one thread runs with, asked once per thread. */
@@ -356,8 +432,3 @@ export const verifyResponseSchema = z.union([
356
432
  z.object({ sub: z.null() }),
357
433
  ]);
358
434
  export type VerifyResponse = z.infer<typeof verifyResponseSchema>;
359
-
360
- /** `POST /events/:event`: the body of each event a manifest subscribes to. */
361
- export const serverEventSchemas = {
362
- approval_requested: approvalRequestedEventSchema,
363
- } as const satisfies Record<(typeof SERVER_EVENTS)[number], z.ZodType>;
@@ -1,116 +1,59 @@
1
1
  import { z } from "zod";
2
2
 
3
- import { TRIGGER_DROP_REASONS, TRIGGER_REASONS } from "../wire/index";
3
+ import { inputReceiptSchema, runRecordSchema } from "../runs/index";
4
4
 
5
5
  /*
6
- * Triggers: named entry points on an agent. A firing is one invocation with a
7
- * payload and starts at most one run. The end user's message is the built-in
8
- * `message` trigger; every other trigger is fired by a caller over
6
+ * Triggers: named entry points on an agent. A caller fires one over
9
7
  * `POST /agents/:agentName/triggers/:name` with the trigger's secret as its
10
- * bearer. These are the shapes Cortex answers that caller with.
8
+ * bearer; the fire is an input on the thread the trigger's policy names,
9
+ * and the run that takes it is read and decided under `/runs/:id`. These
10
+ * are the shapes Cortex answers that caller with.
11
11
  */
12
12
 
13
- export const MESSAGE_TRIGGER_NAME = "message";
14
-
15
13
  /** The tool a trigger run with an output schema answers through; no other tool may take its name. */
16
14
  export const TRIGGER_ANSWER_TOOL_NAME = "submitAnswer";
17
15
 
18
- export const TRIGGER_OUTCOMES = [
19
- "started",
20
- /** The run parked on a tool that needs approval; `POST .../firings/:id/answer` resumes it. */
21
- "awaiting_approval",
22
- "completed",
23
- "failed",
24
- "rejected",
25
- "dropped",
26
- ] as const;
27
- export type TriggerOutcome = (typeof TRIGGER_OUTCOMES)[number];
28
-
29
- /** The engine route of agent `agentName`, which the widget chats with and its triggers sit under. */
16
+ /** The engine route of agent `agentName`, which the widget chats with and its triggers and runs sit under. */
30
17
  export function agentRoute(agentName: string) {
31
18
  return `/agents/${encodeURIComponent(agentName)}`;
32
19
  }
33
20
 
34
- /** The engine route a trigger is fired at on agent `agentName`; its firings are read and answered under it. */
21
+ /** The engine route a trigger is fired at on agent `agentName`; its runs are listed under it. */
35
22
  export function triggerRoute(agentName: string, name: string) {
36
23
  return `${agentRoute(agentName)}/triggers/${encodeURIComponent(name)}`;
37
24
  }
38
25
 
39
- /** One server tool call a parked run waits on; `POST .../firings/:id/answer` decides it. */
40
- export const pendingApprovalSchema = z.object({
41
- toolCallId: z.string(),
42
- toolName: z.string(),
43
- input: z.unknown(),
26
+ /** The engine route a run of agent `agentName` is read at, and answered and cancelled under. */
27
+ export function runRoute(agentName: string, runId: string) {
28
+ return `${agentRoute(agentName)}/runs/${encodeURIComponent(runId)}`;
29
+ }
30
+
31
+ /** What a fire answers once its run started: its input, run and thread; `GET .../runs/:id` reads the run. */
32
+ export const fireStartedSchema = z.object({
33
+ inputId: z.uuid(),
34
+ state: z.literal("started"),
35
+ runId: z.uuid(),
36
+ threadId: z.uuid(),
44
37
  });
45
38
 
46
- export const triggerFiringResponseSchema = z.discriminatedUnion("outcome", [
47
- z.object({
48
- firingId: z.uuid(),
49
- outcome: z.literal("started"),
50
- threadId: z.uuid(),
51
- runId: z.string(),
52
- }),
53
- /* The shapes a caller that waited (`?wait=`) gets instead of `started`: the run ended, or it parked for a decision. */
54
- z.object({
55
- firingId: z.uuid(),
56
- outcome: z.literal("awaiting_approval"),
57
- threadId: z.uuid(),
58
- runId: z.string(),
59
- approvals: z.array(pendingApprovalSchema),
60
- }),
61
- z.object({
62
- firingId: z.uuid(),
63
- outcome: z.literal("completed"),
64
- threadId: z.uuid(),
65
- runId: z.string(),
66
- /** The validated answer when the trigger declares an output schema, else the run's last text (null when it wrote none), cut past 1 MiB with a trailing `…[truncated]`. */
67
- output: z.unknown(),
68
- }),
69
- z.object({
70
- firingId: z.uuid(),
71
- outcome: z.literal("failed"),
72
- threadId: z.uuid(),
73
- runId: z.string(),
74
- reason: z.enum(TRIGGER_REASONS).nullable(),
75
- }),
76
- z.object({
77
- firingId: z.uuid(),
78
- outcome: z.literal("dropped"),
79
- reason: z.enum(TRIGGER_DROP_REASONS),
80
- threadId: z.uuid().nullable(),
81
- }),
82
- z.object({
83
- firingId: z.uuid(),
84
- outcome: z.literal("rejected"),
85
- issues: z.array(z.object({ path: z.string(), message: z.string() })),
86
- }),
87
- ]);
88
- export type TriggerFiringResponse = z.infer<typeof triggerFiringResponseSchema>;
39
+ /** A fire whose payload the trigger refused, each issue at its path. */
40
+ export const fireRejectedSchema = z.object({
41
+ state: z.literal("rejected"),
42
+ issues: z.array(z.object({ path: z.string(), message: z.string() })),
43
+ });
89
44
 
90
45
  /**
91
- * `POST /agents/:agentName/triggers/:name/firings/:id/answer`: the decision on one parked call.
92
- * A call id is as long as the model provider made it (some carry a signature), so only the
93
- * request's body limit bounds it.
46
+ * What `POST /agents/:agentName/triggers/:name` answers as JSON: the run it
47
+ * started (`started`, 202); with `?wait=`, that run once it ended (a
48
+ * `RunRecord`, 200) or parked on a decision (202); the fire's input while it
49
+ * waits on the thread, once refused, or once cancelled before any run took
50
+ * it (an `InputReceipt`: 202, or 409, and 429 for a spent budget or
51
+ * bucket); or a payload the trigger refused (422).
94
52
  */
95
- export const triggerFiringAnswerSchema = z.object({
96
- toolCallId: z.string().min(1),
97
- approved: z.boolean(),
98
- });
99
- export type TriggerFiringAnswer = z.infer<typeof triggerFiringAnswerSchema>;
100
-
101
- /** One row of `GET /agents/:agentName/triggers/:name/firings`, and the answer of `.../firings/:id`. */
102
- export const triggerFiringRecordSchema = z.object({
103
- id: z.uuid(),
104
- triggerName: z.string(),
105
- threadId: z.uuid().nullable(),
106
- runId: z.string().nullable(),
107
- outcome: z.enum(TRIGGER_OUTCOMES),
108
- reason: z.enum(TRIGGER_REASONS).nullable(),
109
- /** What a completed run answered with, as a fire's `completed` answer carries it; null otherwise. */
110
- output: z.unknown(),
111
- /** The calls an `awaiting_approval` firing waits on; filled by the single-firing read, empty in the list and for every other outcome. */
112
- approvals: z.array(pendingApprovalSchema),
113
- createdAt: z.iso.datetime(),
114
- finishedAt: z.iso.datetime().nullable(),
115
- });
116
- export type TriggerFiringRecord = z.infer<typeof triggerFiringRecordSchema>;
53
+ export const fireAnswerSchema = z.union([
54
+ fireStartedSchema,
55
+ fireRejectedSchema,
56
+ inputReceiptSchema,
57
+ runRecordSchema,
58
+ ]);
59
+ export type FireAnswer = z.infer<typeof fireAnswerSchema>;
@@ -8,6 +8,8 @@
8
8
  * alike.
9
9
  */
10
10
 
11
+ import type { RunReason, RunStatus } from "../runs/index";
12
+
11
13
  /** The UI locales every localized value is keyed by; the console's message catalogs follow it. */
12
14
  export const LOCALES = ["en", "ar"] as const;
13
15
  export type Locale = (typeof LOCALES)[number];
@@ -31,8 +33,8 @@ export function byLocale<T>(valueOf: (locale: Locale) => T) {
31
33
  export const TOOL_SURFACES = ["inline", "modal"] as const;
32
34
  export type ToolSurface = (typeof TOOL_SURFACES)[number];
33
35
 
34
- /** `trigger` rows hold a firing's rendered instructions; the model sees them as `user` turns. */
35
- export const MESSAGE_ROLES = ["system", "user", "assistant", "trigger"] as const;
36
+ /** `event` rows hold something that happened, a trigger's rendered instructions among them; the model sees them as `user` turns. */
37
+ export const MESSAGE_ROLES = ["system", "user", "assistant", "event"] as const;
36
38
  export type MessageRole = (typeof MESSAGE_ROLES)[number];
37
39
 
38
40
  /** The fenced blocks an agent may draw in its replies instead of text. */
@@ -46,6 +48,29 @@ export const DEFAULT_MEDIA_FORMS = [
46
48
  "map",
47
49
  ] as const satisfies readonly MediaForm[];
48
50
 
51
+ /**
52
+ * What an agent does with a message someone sends while a run holds its
53
+ * thread: refuses it, queues it as the next turn, or steers the running turn
54
+ * with it before its next step (one that arrives too late is queued).
55
+ */
56
+ export const WHILE_BUSY = ["refuse", "queue", "steer"] as const;
57
+ export type WhileBusy = (typeof WHILE_BUSY)[number];
58
+
59
+ /**
60
+ * Where one message sent while a run holds its thread goes, named by its
61
+ * sender under `forwardedProps[MESSAGE_BUSY_KEY]`, or set on a waiting one
62
+ * by `PATCH /threads/:threadId/inputs/:inputId` (`{ busy }`): `steer` joins
63
+ * the running turn, `queue` waits for a turn of its own.
64
+ */
65
+ export const MESSAGE_BUSY = ["queue", "steer"] as const;
66
+ export type MessageBusy = (typeof MESSAGE_BUSY)[number];
67
+ export const MESSAGE_BUSY_KEY = "busy";
68
+
69
+ /** What a message sent while a run holds the thread does: its sender's pick, never past an agent that refuses. */
70
+ export function busyFor(whileBusy: WhileBusy, picked: MessageBusy | undefined) {
71
+ return whileBusy === "refuse" ? whileBusy : (picked ?? whileBusy);
72
+ }
73
+
49
74
  export const ATTACHMENT_MIME_TYPES = [
50
75
  "image/png",
51
76
  "image/jpeg",
@@ -221,6 +246,8 @@ export type AgentCapabilities = {
221
246
  messages: boolean;
222
247
  /** True when the caller only reads, as an operator inspecting a user's thread does. */
223
248
  readOnly: boolean;
249
+ /** What a message sent while a run holds the thread does; under `refuse`, nothing sends until the run ends. */
250
+ whileBusy: WhileBusy;
224
251
  /** Prompt chips on the blank-chat landing per locale; clicking one sends its `prompt` as the first message. */
225
252
  suggestions: Suggestions;
226
253
  /** Headline of the blank-chat landing per locale; "" falls back to the widget's built-in copy. */
@@ -240,8 +267,8 @@ export type MessageMetadata = {
240
267
  isAborted?: boolean;
241
268
  tokenUsage?: TokenUsage;
242
269
  attachments?: AttachmentSummary[];
243
- /** Set on `trigger` messages: the firing that wrote them. */
244
- trigger?: TriggerFiringSummary;
270
+ /** Set on `event` messages: what happened, and the input that brought it. */
271
+ event?: EventSummary;
245
272
  };
246
273
 
247
274
  /**
@@ -280,6 +307,22 @@ export const STATEMENT_TOOLS = ["executeCode", "queryDataset", "describeDataset"
280
307
  */
281
308
  export const APPROVAL_TITLE_FIELD = "approvalTitle";
282
309
 
310
+ /**
311
+ * The chat's own tool by which an agent hands a task to a copy of itself.
312
+ * Its input's `brief` is the task, which the copy's card shows.
313
+ */
314
+ export const DELEGATE_TASK_TOOL = "delegateTask";
315
+
316
+ /** The chat's tool by which an agent stops a copy it handed a task, by the subagent id its start answered. */
317
+ export const CANCEL_TASK_TOOL = "cancelTask";
318
+
319
+ /**
320
+ * The chat's tool by which an agent schedules a check-back: it comes back
321
+ * to the thread on its own at a time it picks, with a note to itself. Its
322
+ * output's `checkBackId` is the input the user may cancel.
323
+ */
324
+ export const SCHEDULE_WAKE_TOOL = "scheduleWake";
325
+
283
326
  /** A line the model wrote into a call input's `field`, trimmed; undefined while it streams in or is blank. */
284
327
  export function modelLineOf(input: unknown, field: string) {
285
328
  if (!input || typeof input !== "object") return undefined;
@@ -306,6 +349,23 @@ export type ToolCallMetadata = {
306
349
  embed?: ClientToolBinding;
307
350
  /** Set beside `answeredBy` on a `connectAccount` call: the widget starts the sign-in with it. */
308
351
  connect?: ConnectAccountBinding;
352
+ /**
353
+ * Set on a call a run paused on for the user's decision: the interrupt the run raised for
354
+ * it and the run it paused, so a client that reloads re-prompts the decision exactly as the
355
+ * live run asked it.
356
+ */
357
+ paused?: { runId: string; interrupt: RunInterrupt };
358
+ };
359
+
360
+ /** An interrupt as a paused run raises it on its end, in AG-UI's shape. */
361
+ export type RunInterrupt = {
362
+ id: string;
363
+ reason: string;
364
+ message?: string;
365
+ toolCallId?: string;
366
+ responseSchema?: Record<string, unknown>;
367
+ expiresAt?: string;
368
+ metadata?: Record<string, unknown>;
309
369
  };
310
370
 
311
371
  /** The `metadata.cortex` a tool call carries, when Cortex stamped one. */
@@ -314,11 +374,16 @@ export function toolCallMetadataOf(part: { metadata?: unknown }) {
314
374
  return metadata?.cortex ?? undefined;
315
375
  }
316
376
 
317
- export type TriggerFiringSummary = {
377
+ /** An event that entered a conversation: a trigger fired, a schedule came due, a run finished. */
378
+ export type EventSummary = {
379
+ source: "trigger" | "schedule" | "run";
380
+ /** The trigger's name, or what the schedule or run was for. */
318
381
  name: string;
319
- firingId: string;
320
- firedAt: string;
382
+ inputId: string;
383
+ at: string;
321
384
  payload: unknown;
385
+ /** What a trigger that leaves a note tells the thread, for its user to read. */
386
+ note?: string;
322
387
  };
323
388
 
324
389
  /**
@@ -332,19 +397,19 @@ export type TriggerFiringSummary = {
332
397
  */
333
398
  export type CortexMessage<TPart = unknown> = {
334
399
  id: string;
335
- /** `trigger` rows carry a firing's instructions; the UIs render them as an event, not a bubble. */
400
+ /** `event` rows carry what happened; the UIs render them as an event, not a bubble. */
336
401
  role: MessageRole;
337
402
  parts: TPart[];
338
403
  metadata?: MessageMetadata;
339
404
  };
340
405
 
341
406
  /**
342
- * A stored message as the SDK's chat client takes it. The SDK has no `trigger`
343
- * role, so a firing's message travels as a user turn; its true role stays on
344
- * the envelope (`metadata.trigger`), which is what the UIs render the event from.
407
+ * A stored message as the SDK's chat client takes it. The SDK has no `event`
408
+ * role, so an event message travels as a user turn; its true role stays on
409
+ * the envelope (`metadata.event`), which is what the UIs render the event from.
345
410
  */
346
411
  export function toUiMessage<TPart>(message: CortexMessage<TPart>) {
347
- return { ...message, role: message.role === "trigger" ? ("user" as const) : message.role };
412
+ return { ...message, role: message.role === "event" ? ("user" as const) : message.role };
348
413
  }
349
414
 
350
415
  /**
@@ -427,14 +492,20 @@ export type ThreadRunStartedEvent = {
427
492
 
428
493
  /**
429
494
  * Run `runId` ended, committed or not, and left the thread free, so it takes
430
- * a new message now; whatever turn it committed is in the transcript. Sent
431
- * only for a run announced as started, and never by one whose thread another
432
- * run had already taken, so a finish naming a run other than the one a
433
- * client saw start last is stale.
495
+ * a new message now; whatever turn it committed is in the transcript.
496
+ * `status` and `reason` say how: `waiting` for a run parked on calls that
497
+ * wait. Sent only for a run announced as started, and never by one whose
498
+ * thread another run had already taken, so a finish naming a run other than
499
+ * the one a client saw start last is stale.
434
500
  */
435
501
  export type ThreadRunFinishedEvent = {
436
502
  type: "thread:run-finished";
437
- payload: { thread: ThreadSummary; runId: string };
503
+ payload: {
504
+ thread: ThreadSummary;
505
+ runId: string;
506
+ status: RunStatus;
507
+ reason: RunReason | null;
508
+ };
438
509
  };
439
510
 
440
511
  export type ThreadMessagesUpdatedEvent = {
@@ -442,13 +513,26 @@ export type ThreadMessagesUpdatedEvent = {
442
513
  payload: { thread: ThreadSummary };
443
514
  };
444
515
 
516
+ /**
517
+ * The inputs waiting on the thread changed: one was offered, taken up as a
518
+ * turn of its own, or cancelled. `inputIds` are the ones still waiting, in
519
+ * the order dispatch takes them; `GET /threads/:threadId/inputs` reads
520
+ * them. A steer the running turn took in waits on until that turn stores
521
+ * it, which announces nothing: the run's stream carries it.
522
+ */
523
+ export type ThreadInputsChangedEvent = {
524
+ type: "thread:inputs-changed";
525
+ payload: { threadId: string; inputIds: string[] };
526
+ };
527
+
445
528
  export type WsEvent =
446
529
  | ThreadCreatedEvent
447
530
  | ThreadDeletedEvent
448
531
  | ThreadTitleUpdatedEvent
449
532
  | ThreadRunStartedEvent
450
533
  | ThreadRunFinishedEvent
451
- | ThreadMessagesUpdatedEvent;
534
+ | ThreadMessagesUpdatedEvent
535
+ | ThreadInputsChangedEvent;
452
536
 
453
537
  /** The header the widget sends the browser's IANA time zone in; the turn reasons in that clock. */
454
538
  export const TIMEZONE_HEADER = "X-Cortex-Timezone";
@@ -473,53 +557,33 @@ export const OPERATOR_HEADER = "X-Cortex-Operator";
473
557
  export const INSPECT_HEADER = "X-Cortex-Inspect";
474
558
 
475
559
  /**
476
- * Why a firing was dropped: the thread was busy with another run, a call on
477
- * it waits for a decision (answered 409: decide that firing first), the
478
- * daily budget was spent, or the agent's request bucket was.
479
- */
480
- export const TRIGGER_DROP_REASONS = ["busy", "approval_pending", "budget", "rate_limited"] as const;
481
- export type TriggerDropReason = (typeof TRIGGER_DROP_REASONS)[number];
482
-
483
- /**
484
- * Every reason a firing records beside its outcome: why it was dropped, why
485
- * its payload was rejected (`invalid_payload`, `missing_thread_key`), that
486
- * no turn ran for it (`no_turn`), or why a run with an output schema failed
487
- * without an answer the schema accepted (`no_output`, `invalid_output`).
488
- */
489
- export const TRIGGER_REASONS = [
490
- ...TRIGGER_DROP_REASONS,
491
- "invalid_payload",
492
- "missing_thread_key",
493
- "no_turn",
494
- "no_output",
495
- "invalid_output",
496
- ] as const;
497
- export type TriggerReason = (typeof TRIGGER_REASONS)[number];
498
-
499
- /**
500
- * Body of a `POST /chat` the thread did not take: 409 while a run holds the
501
- * thread or a call on it waits for a decision, or when the answers it
502
- * carried fit no open call, were already applied, or answer a call whose
503
- * origin is out of reach (`no_turn`); 429 once the agent's request bucket is
504
- * spent, with `Retry-After`.
560
+ * One input waiting on a thread, in the order dispatch takes them, in
561
+ * `GET /threads/:threadId/inputs`: the user message as it will be stored
562
+ * once a run takes it up, and how it waits: to steer the running turn, or
563
+ * for a turn of its own. `DELETE /threads/:threadId/inputs/:inputId`
564
+ * cancels it and `PATCH` (`{ busy }`) moves it to steer or queue, unless a
565
+ * run already took it in (404).
505
566
  */
506
- export type ChatDropped = {
507
- outcome: "dropped";
508
- reason: Exclude<TriggerDropReason, "budget"> | "no_turn";
567
+ export type WaitingInput<TPart = unknown> = {
568
+ inputId: string;
569
+ delivery: "prompt" | "steer" | "followUp" | "answer" | "note";
570
+ message: CortexMessage<TPart>;
571
+ /** Whether it is text alone, which a running turn may take in as a steer; one with files waits for a turn of its own. */
572
+ textOnly: boolean;
509
573
  };
510
574
 
511
575
  /**
512
- * The drop a widget tells its user about by name: a call on the thread waits
513
- * for approval, so the message goes back to the composer. Any other drop
514
- * shows as a failed turn.
576
+ * The refusal a widget tells its user about by name: a call on the thread
577
+ * waits for approval, so the message goes back to the composer. Any other
578
+ * refusal shows as a failed turn.
515
579
  */
516
- export type SendRefusal = Extract<ChatDropped["reason"], "approval_pending">;
580
+ export type SendRefusal = "approval_pending";
517
581
 
518
582
  /**
519
583
  * What became of the client tool answers a `POST /chat` carried, by the
520
584
  * status Cortex answered it with, apart from how the run it started goes.
521
585
  * `persisted` (2xx): Cortex took them up, and sending them again changes
522
- * nothing. `refused` (4xx, a `ChatDropped` among them): it did not take
586
+ * nothing. `refused` (4xx, an `InputReceipt` among them): it did not take
523
587
  * them up as given, and the stored transcript says what stands. `lost`
524
588
  * (5xx, or no answer at all): its word never came, and only the stored
525
589
  * transcript can tell.