@m6d/cortex-cli 0.21.0 → 0.23.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
- import { REQUEST_LOCATION_TOOL } from "../client-tools/index";
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,7 +156,9 @@ export const BUILT_IN_TOOL_NAMES = [
138
156
  "readReport",
139
157
  "searchRegistries",
140
158
  "searchThreadHistory",
159
+ SCHEDULE_WAKE_TOOL,
141
160
  REQUEST_LOCATION_TOOL,
161
+ CONNECT_ACCOUNT_TOOL,
142
162
  TRIGGER_ANSWER_TOOL_NAME,
143
163
  ] as const;
144
164
 
@@ -187,33 +207,58 @@ export const serverToolSchema = z.object({
187
207
  });
188
208
  export type ServerTool = z.infer<typeof serverToolSchema>;
189
209
 
190
- /** The events a server hears at `POST /events/:event`, and a webhook delivers. */
191
- 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];
192
217
 
193
218
  /** The header a webhook delivery names its event in, so a receiver tells events apart without parsing the body. */
194
219
  export const EVENT_HEADER = "X-Cortex-Event";
195
220
 
196
- /**
197
- * A run parked on server tools that need the end user's approval, as every
198
- * receiver hears of it: a webhook's body and the agent's server's event are
199
- * this one object. `firingId` and `triggerName` are set when a trigger
200
- * started the run, which is what a receiver answering through the firing
201
- * route needs. Every parked call is named; one whose input would carry the
202
- * event past `SERVER_REQUEST_MAX_BYTES` comes without its input.
203
- */
204
- export const approvalRequestedEventSchema = z.object({
205
- 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 = {
206
223
  agentName: nameSchema,
207
224
  agentId: z.uuid(),
208
- threadId: z.string(),
209
- runId: z.string(),
210
- userId: z.string(),
211
- firingId: z.uuid().optional(),
225
+ runId: z.uuid(),
212
226
  triggerName: z.string().optional(),
213
- approvals: z.array(pendingApprovalSchema.partial({ input: true })),
214
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,
215
253
  });
216
- 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>;
217
262
 
218
263
  /** What a local `$ref` (`#/$defs/order`) points at in `root`, read as the input compiler reads it; undefined for any other. */
219
264
  function pointedAt(root: unknown, ref: string) {
@@ -304,6 +349,13 @@ export const serverManifestSchema = z.object({
304
349
  tools.forEach((tool, index) => refuseBlockedApprovalTitle(tool, context, [index]));
305
350
  }),
306
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(),
307
359
  events: z.array(z.enum(SERVER_EVENTS)),
308
360
  });
309
361
  export type ServerManifest = z.infer<typeof serverManifestSchema>;
@@ -311,9 +363,34 @@ export type ServerManifest = z.infer<typeof serverManifestSchema>;
311
363
  /** The agent a call is for. */
312
364
  const agentSchema = z.object({ id: z.uuid(), name: nameSchema });
313
365
 
314
- /** 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
+ */
315
388
  const serverUserSchema = z
316
- .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
+ })
317
394
  .nullable();
318
395
 
319
396
  /** `POST /variables`: the values one thread runs with, asked once per thread. */
@@ -355,8 +432,3 @@ export const verifyResponseSchema = z.union([
355
432
  z.object({ sub: z.null() }),
356
433
  ]);
357
434
  export type VerifyResponse = z.infer<typeof verifyResponseSchema>;
358
-
359
- /** `POST /events/:event`: the body of each event a manifest subscribes to. */
360
- export const serverEventSchemas = {
361
- approval_requested: approvalRequestedEventSchema,
362
- } 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>;