@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.
- package/package.json +1 -1
- package/src/commands/management.ts +8 -5
- package/src/commands/setup.ts +15 -8
- package/src/contracts/client-tools/index.ts +21 -0
- package/src/contracts/egress-policy/index.ts +1 -0
- package/src/contracts/egress-policy/url.ts +1 -1
- package/src/contracts/management/procedures.generated.ts +352 -184
- package/src/contracts/manifest/index.ts +186 -64
- package/src/contracts/runs/index.ts +187 -0
- package/src/contracts/schedule/index.ts +62 -0
- package/src/contracts/server/index.ts +101 -29
- package/src/contracts/triggers/index.ts +36 -93
- package/src/contracts/wire/index.ts +162 -55
- package/src/management/flags.ts +14 -4
- package/src/scaffold/files.ts +8 -3
- package/src/ui/prompts.ts +18 -24
|
@@ -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 {
|
|
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 {
|
|
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,
|
|
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
|
-
/**
|
|
191
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
/**
|
|
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({
|
|
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 {
|
|
3
|
+
import { inputReceiptSchema, runRecordSchema } from "../runs/index";
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
|
-
* Triggers: named entry points on an agent. 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
|
|
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
|
-
|
|
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
|
|
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
|
-
/**
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
92
|
-
*
|
|
93
|
-
*
|
|
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
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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>;
|