@m6d/cortex-cli 0.15.0 → 0.17.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 +45 -28
- package/package.json +3 -13
- package/src/commands/apply.ts +61 -38
- package/src/commands/management.ts +64 -19
- package/src/commands/new.ts +8 -10
- package/src/commands/setup.ts +83 -38
- package/src/contracts/datasets/dataset-sql.ts +36 -2
- package/src/contracts/datasets/datasets.ts +84 -182
- package/src/contracts/egress-policy/scrub.ts +11 -2
- package/src/contracts/errors/index.ts +7 -0
- package/src/contracts/json/index.ts +40 -17
- package/src/contracts/manifest/index.ts +601 -168
- package/src/contracts/server/index.ts +44 -53
- package/src/contracts/triggers/index.ts +13 -9
- package/src/contracts/wire/index.ts +149 -27
- package/src/management/client.ts +79 -117
- package/src/management/profiles.ts +11 -22
- package/src/scaffold/files.ts +13 -11
- package/src/ui/prompts.ts +30 -9
- package/src/contracts/charts/chart-spec.ts +0 -217
- package/src/contracts/charts/index.ts +0 -8
- package/src/contracts/datasets/datasets.fixtures.json +0 -2849
- package/src/contracts/markdown/index.ts +0 -47
- package/src/contracts/pattern/index.ts +0 -276
- package/src/contracts/server/reserved-names.fixtures.json +0 -16
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { REQUEST_LOCATION_TOOL } from "../client-tools/index";
|
|
3
3
|
import { backendErrorSchema } from "../errors/index";
|
|
4
4
|
import { pendingApprovalSchema, TRIGGER_ANSWER_TOOL_NAME } from "../triggers/index";
|
|
5
|
-
import { isRecord,
|
|
5
|
+
import { isRecord, readJsonPointer } from "../json/index";
|
|
6
6
|
import { APPROVAL_TITLE_FIELD, LOCALES } from "../wire/index";
|
|
7
7
|
|
|
8
8
|
/*
|
|
@@ -22,8 +22,11 @@ export const END_USER_TOKEN_HEADER = "X-End-User-Token";
|
|
|
22
22
|
export const AGENT_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
23
23
|
export const agentSlugSchema = z.string().regex(AGENT_SLUG_PATTERN);
|
|
24
24
|
|
|
25
|
+
/** The longest end-user id Cortex stores. */
|
|
26
|
+
export const USER_ID_MAX_LENGTH = 255;
|
|
27
|
+
|
|
25
28
|
/** An end user's id, as wide as Cortex stores one. */
|
|
26
|
-
export const userIdSchema = z.string().max(
|
|
29
|
+
export const userIdSchema = z.string().max(USER_ID_MAX_LENGTH);
|
|
27
30
|
|
|
28
31
|
/**
|
|
29
32
|
* An ASCII JavaScript identifier: what a tool name (a `tools.<name>()`
|
|
@@ -32,8 +35,22 @@ export const userIdSchema = z.string().max(255);
|
|
|
32
35
|
*/
|
|
33
36
|
export const IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
34
37
|
|
|
38
|
+
/** The longest identifier Cortex stores, for tool, variable and MCP server names alike. */
|
|
39
|
+
export const IDENTIFIER_MAX_LENGTH = 64;
|
|
40
|
+
|
|
41
|
+
export const identifierSchema = z.string().regex(IDENTIFIER_PATTERN).max(IDENTIFIER_MAX_LENGTH);
|
|
42
|
+
|
|
43
|
+
/** A localized value's key. */
|
|
35
44
|
export const localeSchema = z.enum(LOCALES);
|
|
36
45
|
|
|
46
|
+
/** How many variables and tools a server's manifest declares at most, and how long each of their texts runs. */
|
|
47
|
+
export const SERVER_MANIFEST_LIMITS = {
|
|
48
|
+
variables: 100,
|
|
49
|
+
tools: 500,
|
|
50
|
+
description: 500,
|
|
51
|
+
example: 200,
|
|
52
|
+
} as const;
|
|
53
|
+
|
|
37
54
|
/** The value one thread's variable holds: what its server fills, or an operator sets on a test thread. */
|
|
38
55
|
const variableValueSchema = z.union([z.string(), z.number(), z.boolean()]);
|
|
39
56
|
|
|
@@ -46,9 +63,9 @@ export const variableValuesSchema = z.record(z.string(), variableValueSchema);
|
|
|
46
63
|
* prompts and as tool input sources alike.
|
|
47
64
|
*/
|
|
48
65
|
export const variableDeclarationSchema = z.object({
|
|
49
|
-
name:
|
|
50
|
-
description: z.string().max(
|
|
51
|
-
example: z.string().max(
|
|
66
|
+
name: identifierSchema,
|
|
67
|
+
description: z.string().max(SERVER_MANIFEST_LIMITS.description),
|
|
68
|
+
example: z.string().max(SERVER_MANIFEST_LIMITS.example),
|
|
52
69
|
/** The type its value takes. */
|
|
53
70
|
type: z.enum(["string", "number", "boolean"]),
|
|
54
71
|
/** Computed by Cortex for every thread; no server can set it. */
|
|
@@ -86,8 +103,8 @@ export const BUILT_IN_VARIABLES = [
|
|
|
86
103
|
|
|
87
104
|
/**
|
|
88
105
|
* The names of Cortex's built-in tools. No other tool may take one: the
|
|
89
|
-
* model would see two tools under one name.
|
|
90
|
-
*
|
|
106
|
+
* model would see two tools under one name. `constants.fixtures.json`
|
|
107
|
+
* carries them to the .NET SDK.
|
|
91
108
|
*/
|
|
92
109
|
export const BUILT_IN_TOOL_NAMES = [
|
|
93
110
|
"describeDataset",
|
|
@@ -107,30 +124,22 @@ export function isBuiltInToolName(name: string) {
|
|
|
107
124
|
return BUILT_IN_TOOL_NAMES.some((builtIn) => builtIn === name);
|
|
108
125
|
}
|
|
109
126
|
|
|
110
|
-
/** The longest name a tool takes, a server's or one defined in Cortex. */
|
|
111
|
-
export const TOOL_NAME_MAX_LENGTH = 64;
|
|
112
|
-
|
|
113
127
|
/** A tool name, a server's or one defined in Cortex: an identifier no built-in tool holds. */
|
|
114
|
-
const declaredToolNameSchema =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
.refine((name) => !isBuiltInToolName(name), "the name of a built-in tool");
|
|
128
|
+
const declaredToolNameSchema = identifierSchema.refine(
|
|
129
|
+
(name) => !isBuiltInToolName(name),
|
|
130
|
+
"the name of a built-in tool",
|
|
131
|
+
);
|
|
119
132
|
|
|
120
133
|
const jsonSchemaSchema = z.record(z.string(), z.unknown());
|
|
121
134
|
|
|
122
135
|
/** A variable the server fills for its agents' threads; the built-in ones are Cortex's. */
|
|
123
136
|
const serverVariableSchema = z.object({
|
|
124
|
-
name:
|
|
125
|
-
.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
"the name of a built-in variable",
|
|
131
|
-
),
|
|
132
|
-
description: z.string().max(500),
|
|
133
|
-
example: z.string().max(200),
|
|
137
|
+
name: identifierSchema.refine(
|
|
138
|
+
(name) => !BUILT_IN_VARIABLES.some((builtIn) => builtIn.name === name),
|
|
139
|
+
"the name of a built-in variable",
|
|
140
|
+
),
|
|
141
|
+
description: z.string().max(SERVER_MANIFEST_LIMITS.description),
|
|
142
|
+
example: z.string().max(SERVER_MANIFEST_LIMITS.example),
|
|
134
143
|
type: variableDeclarationSchema.shape.type,
|
|
135
144
|
});
|
|
136
145
|
export type ServerVariable = z.infer<typeof serverVariableSchema>;
|
|
@@ -143,7 +152,7 @@ export type ServerVariable = z.infer<typeof serverVariableSchema>;
|
|
|
143
152
|
*/
|
|
144
153
|
export const serverToolSchema = z.object({
|
|
145
154
|
name: declaredToolNameSchema,
|
|
146
|
-
description: z.string().trim().min(1).max(
|
|
155
|
+
description: z.string().trim().min(1).max(SERVER_MANIFEST_LIMITS.description),
|
|
147
156
|
inputSchema: jsonSchemaSchema.refine(
|
|
148
157
|
(schema) => schema.type === "object",
|
|
149
158
|
"a tool's input is an object",
|
|
@@ -182,27 +191,9 @@ export const approvalRequestedEventSchema = z.object({
|
|
|
182
191
|
});
|
|
183
192
|
export type ApprovalRequestedEvent = z.infer<typeof approvalRequestedEventSchema>;
|
|
184
193
|
|
|
185
|
-
/** A JSON Pointer token as a key: percent-decoded, then unescaped; undefined when its encoding is broken. */
|
|
186
|
-
function pointerKey(token: string) {
|
|
187
|
-
try {
|
|
188
|
-
return unescapePointerToken(decodeURIComponent(token));
|
|
189
|
-
} catch {
|
|
190
|
-
return undefined;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
194
|
/** What a local `$ref` (`#/$defs/order`) points at in `root`, read as the input compiler reads it; undefined for any other. */
|
|
195
195
|
function pointedAt(root: unknown, ref: string) {
|
|
196
|
-
|
|
197
|
-
let node = root;
|
|
198
|
-
for (const token of ref.slice(1).split("/").slice(1)) {
|
|
199
|
-
const key = pointerKey(token);
|
|
200
|
-
node =
|
|
201
|
-
key !== undefined && (isRecord(node) || Array.isArray(node))
|
|
202
|
-
? new Map<string, unknown>(Object.entries(node)).get(key)
|
|
203
|
-
: undefined;
|
|
204
|
-
}
|
|
205
|
-
return node;
|
|
196
|
+
return ref.startsWith("#") ? readJsonPointer(root, ref) : undefined;
|
|
206
197
|
}
|
|
207
198
|
|
|
208
199
|
/** Whether one schema node names `APPROVAL_TITLE_FIELD` as a property or a required field. */
|
|
@@ -259,18 +250,15 @@ export function blocksApprovalTitle(inputSchema: unknown) {
|
|
|
259
250
|
);
|
|
260
251
|
}
|
|
261
252
|
|
|
262
|
-
/** Refuses, under `path`, a tool that needs approval whose input `blocksApprovalTitle`. */
|
|
253
|
+
/** Refuses, under `path` and with `message`, a tool that needs approval whose input `blocksApprovalTitle`. */
|
|
263
254
|
export function refuseBlockedApprovalTitle(
|
|
264
255
|
tool: { requiresApproval?: boolean; inputSchema: unknown },
|
|
265
256
|
context: z.RefinementCtx,
|
|
266
257
|
path: Array<string | number>,
|
|
258
|
+
message = `a tool that needs approval cannot take an input named ${APPROVAL_TITLE_FIELD}, nor one a closed sub-schema describes: Cortex adds the approval card's title to it`,
|
|
267
259
|
) {
|
|
268
260
|
if (!tool.requiresApproval || !blocksApprovalTitle(tool.inputSchema)) return;
|
|
269
|
-
context.addIssue({
|
|
270
|
-
code: "custom",
|
|
271
|
-
path: [...path, "inputSchema"],
|
|
272
|
-
message: `a tool that needs approval cannot take an input named ${APPROVAL_TITLE_FIELD}, nor one a closed sub-schema describes: Cortex adds the approval card's title to it`,
|
|
273
|
-
});
|
|
261
|
+
context.addIssue({ code: "custom", path: [...path, "inputSchema"], message });
|
|
274
262
|
}
|
|
275
263
|
|
|
276
264
|
function uniqueNames(entries: ReadonlyArray<{ name: string }>) {
|
|
@@ -280,10 +268,13 @@ function uniqueNames(entries: ReadonlyArray<{ name: string }>) {
|
|
|
280
268
|
/** `GET /`: everything the server offers, keyed by `version`, which changes whenever anything else does. */
|
|
281
269
|
export const serverManifestSchema = z.object({
|
|
282
270
|
version: z.string().min(1).max(128),
|
|
283
|
-
variables: z
|
|
271
|
+
variables: z
|
|
272
|
+
.array(serverVariableSchema)
|
|
273
|
+
.max(SERVER_MANIFEST_LIMITS.variables)
|
|
274
|
+
.refine(uniqueNames, "variable names repeat"),
|
|
284
275
|
tools: z
|
|
285
276
|
.array(serverToolSchema)
|
|
286
|
-
.max(
|
|
277
|
+
.max(SERVER_MANIFEST_LIMITS.tools)
|
|
287
278
|
.refine(uniqueNames, "tool names repeat")
|
|
288
279
|
.superRefine((tools, context) => {
|
|
289
280
|
tools.forEach((tool, index) => refuseBlockedApprovalTitle(tool, context, [index]));
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
+
import { TRIGGER_DROP_REASONS, TRIGGER_REASONS } from "../wire/index";
|
|
4
|
+
|
|
3
5
|
/*
|
|
4
6
|
* Triggers: named entry points on an agent. A firing is one invocation with a
|
|
5
7
|
* payload and starts at most one run. The end user's message is the built-in
|
|
@@ -24,13 +26,15 @@ export const TRIGGER_OUTCOMES = [
|
|
|
24
26
|
] as const;
|
|
25
27
|
export type TriggerOutcome = (typeof TRIGGER_OUTCOMES)[number];
|
|
26
28
|
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export
|
|
29
|
+
/** The engine route of agent `agentSlug`, which the widget chats with and its triggers sit under. */
|
|
30
|
+
export function agentRoute(agentSlug: string) {
|
|
31
|
+
return `/agents/${encodeURIComponent(agentSlug)}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The engine route a trigger is fired at on agent `agentSlug`; its firings are read and answered under it. */
|
|
35
|
+
export function triggerRoute(agentSlug: string, name: string) {
|
|
36
|
+
return `${agentRoute(agentSlug)}/triggers/${encodeURIComponent(name)}`;
|
|
37
|
+
}
|
|
34
38
|
|
|
35
39
|
/** One server tool call a parked run waits on; `POST .../firings/:id/answer` decides it. */
|
|
36
40
|
export const pendingApprovalSchema = z.object({
|
|
@@ -67,7 +71,7 @@ export const triggerFiringResponseSchema = z.discriminatedUnion("outcome", [
|
|
|
67
71
|
outcome: z.literal("failed"),
|
|
68
72
|
threadId: z.uuid(),
|
|
69
73
|
runId: z.string(),
|
|
70
|
-
reason: z.
|
|
74
|
+
reason: z.enum(TRIGGER_REASONS).nullable(),
|
|
71
75
|
}),
|
|
72
76
|
z.object({
|
|
73
77
|
firingId: z.uuid(),
|
|
@@ -101,7 +105,7 @@ export const triggerFiringRecordSchema = z.object({
|
|
|
101
105
|
threadId: z.uuid().nullable(),
|
|
102
106
|
runId: z.string().nullable(),
|
|
103
107
|
outcome: z.enum(TRIGGER_OUTCOMES),
|
|
104
|
-
reason: z.
|
|
108
|
+
reason: z.enum(TRIGGER_REASONS).nullable(),
|
|
105
109
|
/** What a completed run answered with, as a fire's `completed` answer carries it; null otherwise. */
|
|
106
110
|
output: z.unknown(),
|
|
107
111
|
/** The calls an `awaiting_approval` firing waits on; filled by the single-firing read, empty in the list and for every other outcome. */
|
|
@@ -8,10 +8,25 @@
|
|
|
8
8
|
* alike.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
/** The UI locales every localized value is keyed by. */
|
|
11
|
+
/** The UI locales every localized value is keyed by; the console's message catalogs follow it. */
|
|
12
12
|
export const LOCALES = ["en", "ar"] as const;
|
|
13
13
|
export type Locale = (typeof LOCALES)[number];
|
|
14
14
|
|
|
15
|
+
/** The locale a value falls back to, and the one a service is known by. */
|
|
16
|
+
export const BASE_LOCALE = LOCALES[0];
|
|
17
|
+
|
|
18
|
+
/** The way each locale's script runs, which the console lays its text out by. */
|
|
19
|
+
export const LOCALE_DIRECTIONS = { en: "ltr", ar: "rtl" } as const satisfies Record<
|
|
20
|
+
Locale,
|
|
21
|
+
"ltr" | "rtl"
|
|
22
|
+
>;
|
|
23
|
+
|
|
24
|
+
/** A localized value with one entry per locale, each `valueOf` that locale. */
|
|
25
|
+
export function byLocale<T>(valueOf: (locale: Locale) => T) {
|
|
26
|
+
const entries = LOCALES.map((locale) => [locale, valueOf(locale)] as const);
|
|
27
|
+
return Object.fromEntries(entries) as Record<Locale, T>;
|
|
28
|
+
}
|
|
29
|
+
|
|
15
30
|
/** Where an embedded client tool renders: in the message flow or in a modal. */
|
|
16
31
|
export const TOOL_SURFACES = ["inline", "modal"] as const;
|
|
17
32
|
export type ToolSurface = (typeof TOOL_SURFACES)[number];
|
|
@@ -70,6 +85,32 @@ export type AttachmentSummary = {
|
|
|
70
85
|
description: string | null;
|
|
71
86
|
};
|
|
72
87
|
|
|
88
|
+
/**
|
|
89
|
+
* How an anonymous agent's visitors' user ids begin: Cortex issues each
|
|
90
|
+
* visitor an id of its own, which their browser keeps. No issuer ever mints
|
|
91
|
+
* a subject shaped like this.
|
|
92
|
+
*/
|
|
93
|
+
export const VISITOR_USER_PREFIX = "visitor:";
|
|
94
|
+
|
|
95
|
+
/** The reCAPTCHA action a visitor's sign-in runs, which Cortex checks the token was made for. */
|
|
96
|
+
export const VISITOR_RECAPTCHA_ACTION = "cortex_visitor";
|
|
97
|
+
|
|
98
|
+
/** `POST /visitors`: a visitor's sign-in to an anonymous agent, with a reCAPTCHA token when the agent checks visitors. */
|
|
99
|
+
export type VisitorRequest = { recaptcha?: string };
|
|
100
|
+
|
|
101
|
+
/** A visitor's token, the bearer of every request after the sign-in. */
|
|
102
|
+
export type VisitorToken = { token: string };
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Why a sign-in got no token: the agent checks visitors with reCAPTCHA and
|
|
106
|
+
* the request carried no token, or one that failed. `siteKey` is the key the
|
|
107
|
+
* widget runs reCAPTCHA with.
|
|
108
|
+
*/
|
|
109
|
+
export type VisitorRefusal = {
|
|
110
|
+
error: "recaptcha_required" | "recaptcha_failed";
|
|
111
|
+
siteKey: string;
|
|
112
|
+
};
|
|
113
|
+
|
|
73
114
|
export type ThreadSummary = {
|
|
74
115
|
id: string;
|
|
75
116
|
title: string | null;
|
|
@@ -121,8 +162,6 @@ export type ClientToolBinding = {
|
|
|
121
162
|
toolId: string;
|
|
122
163
|
surface: ToolSurface;
|
|
123
164
|
embedOrigin: string;
|
|
124
|
-
/** Variables the initiate call's input reads. */
|
|
125
|
-
variables: string[];
|
|
126
165
|
};
|
|
127
166
|
|
|
128
167
|
/**
|
|
@@ -189,11 +228,16 @@ export const STATEMENT_TOOLS = ["executeCode", "queryDataset", "describeDataset"
|
|
|
189
228
|
*/
|
|
190
229
|
export const APPROVAL_TITLE_FIELD = "approvalTitle";
|
|
191
230
|
|
|
231
|
+
/** A line the model wrote into a call input's `field`, trimmed; undefined while it streams in or is blank. */
|
|
232
|
+
export function modelLineOf(input: unknown, field: string) {
|
|
233
|
+
if (!input || typeof input !== "object") return undefined;
|
|
234
|
+
const line: unknown = Reflect.get(input, field);
|
|
235
|
+
return typeof line === "string" && line.trim() ? line.trim() : undefined;
|
|
236
|
+
}
|
|
237
|
+
|
|
192
238
|
/** The title the model wrote into an approval call's input, or undefined while it has none. */
|
|
193
239
|
export function approvalTitleOf(input: unknown) {
|
|
194
|
-
|
|
195
|
-
const title: unknown = input[APPROVAL_TITLE_FIELD];
|
|
196
|
-
return typeof title === "string" && title.trim() ? title.trim() : undefined;
|
|
240
|
+
return modelLineOf(input, APPROVAL_TITLE_FIELD);
|
|
197
241
|
}
|
|
198
242
|
|
|
199
243
|
/**
|
|
@@ -260,16 +304,27 @@ export function toUiMessage<TPart>(message: CortexMessage<TPart>) {
|
|
|
260
304
|
export const CLIENT_TOOL_ANSWERS_KEY = "clientToolAnswers";
|
|
261
305
|
|
|
262
306
|
/**
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
307
|
+
* How a call was answered: a client tool's output (`complete` / `error`), or
|
|
308
|
+
* the user's decision on a server tool that asked for approval
|
|
309
|
+
* (`approved` / `denied`).
|
|
310
|
+
*/
|
|
311
|
+
export const CLIENT_TOOL_ANSWER_STATES = ["complete", "error", "approved", "denied"] as const;
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* One answered call, which Cortex applies to the stored transcript. A
|
|
315
|
+
* decision carries a null output.
|
|
266
316
|
*/
|
|
267
317
|
export type ClientToolAnswer = {
|
|
268
318
|
toolCallId: string;
|
|
269
319
|
output: unknown;
|
|
270
|
-
state:
|
|
320
|
+
state: (typeof CLIENT_TOOL_ANSWER_STATES)[number];
|
|
271
321
|
};
|
|
272
322
|
|
|
323
|
+
/** Whether the answer is the user's decision on a call that asked for approval, rather than a tool's output. */
|
|
324
|
+
export function isDecision(answer: Pick<ClientToolAnswer, "state">) {
|
|
325
|
+
return answer.state === "approved" || answer.state === "denied";
|
|
326
|
+
}
|
|
327
|
+
|
|
273
328
|
/**
|
|
274
329
|
* The id a parked tool call's decision is filed under: the one on its
|
|
275
330
|
* approval record, or `approval_<toolCallId>`, which is how the SDK names
|
|
@@ -279,6 +334,11 @@ export function approvalIdOf(part: { id: string; approval?: { id: string } }) {
|
|
|
279
334
|
return part.approval?.id ?? `approval_${part.id}`;
|
|
280
335
|
}
|
|
281
336
|
|
|
337
|
+
/** Whether a tool call asked the user for approval: it carries an approval record, or waits for one. */
|
|
338
|
+
export function askedApproval(part: { approval?: unknown; state?: string }) {
|
|
339
|
+
return part.approval !== undefined || part.state === "approval-requested";
|
|
340
|
+
}
|
|
341
|
+
|
|
282
342
|
/**
|
|
283
343
|
* Response of `POST /chat/:threadId/tools/:toolCallId/initiate` for a pending
|
|
284
344
|
* call stamped with an embed. The payload is for the widget only; it never
|
|
@@ -328,40 +388,102 @@ export type ThreadMessagesUpdatedEvent = {
|
|
|
328
388
|
payload: { thread: ThreadSummary };
|
|
329
389
|
};
|
|
330
390
|
|
|
331
|
-
export type ToolProgressEvent = {
|
|
332
|
-
type: "thread:tool-progress";
|
|
333
|
-
payload: {
|
|
334
|
-
threadId: string;
|
|
335
|
-
toolCallId: string;
|
|
336
|
-
toolName: string;
|
|
337
|
-
status: "started" | "finished";
|
|
338
|
-
};
|
|
339
|
-
};
|
|
340
|
-
|
|
341
391
|
export type WsEvent =
|
|
342
392
|
| ThreadCreatedEvent
|
|
343
393
|
| ThreadDeletedEvent
|
|
344
394
|
| ThreadTitleUpdatedEvent
|
|
345
395
|
| ThreadRunStartedEvent
|
|
346
396
|
| ThreadRunFinishedEvent
|
|
347
|
-
| ThreadMessagesUpdatedEvent
|
|
348
|
-
| ToolProgressEvent;
|
|
397
|
+
| ThreadMessagesUpdatedEvent;
|
|
349
398
|
|
|
350
399
|
/** The header the widget sends the browser's IANA time zone in; the turn reasons in that clock. */
|
|
351
400
|
export const TIMEZONE_HEADER = "X-Cortex-Timezone";
|
|
352
401
|
|
|
353
402
|
/**
|
|
354
|
-
* The console operator's
|
|
355
|
-
* thread inspector on every request to an agent and by the fire and
|
|
356
|
-
* calls Cortex makes to a trigger's route. A verified one makes the
|
|
357
|
-
* request also carries an end-user token to
|
|
358
|
-
* opens as test threads. A WebSocket
|
|
403
|
+
* The header carrying a console operator's token, sent by the playground and
|
|
404
|
+
* the thread inspector on every request to an agent and by the fire and
|
|
405
|
+
* answer calls Cortex makes to a trigger's route. A verified one makes the
|
|
406
|
+
* operator the caller unless the request also carries an end-user token to
|
|
407
|
+
* test as, and marks the threads it opens as test threads. A WebSocket
|
|
408
|
+
* upgrade carries it as `?operator=`.
|
|
359
409
|
*/
|
|
360
410
|
export const OPERATOR_HEADER = "X-Cortex-Operator";
|
|
361
411
|
|
|
362
412
|
/**
|
|
363
413
|
* Beside the operator token, the id of a thread the operator inspects: the
|
|
364
414
|
* request then reads as that thread's owner, read-only, with the debug view.
|
|
415
|
+
* The engine refuses any request carrying it but a read (GET, HEAD, OPTIONS),
|
|
416
|
+
* on every agent route.
|
|
365
417
|
* A WebSocket upgrade carries it as `?inspect=`.
|
|
366
418
|
*/
|
|
367
419
|
export const INSPECT_HEADER = "X-Cortex-Inspect";
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Why a firing was dropped: the thread was busy with another run, a call on
|
|
423
|
+
* it waits for a decision (answered 409: decide that firing first), the
|
|
424
|
+
* daily budget was spent, or the agent's request bucket was.
|
|
425
|
+
*/
|
|
426
|
+
export const TRIGGER_DROP_REASONS = ["busy", "approval_pending", "budget", "rate_limited"] as const;
|
|
427
|
+
export type TriggerDropReason = (typeof TRIGGER_DROP_REASONS)[number];
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Every reason a firing records beside its outcome: why it was dropped, why
|
|
431
|
+
* its payload was rejected (`invalid_payload`, `missing_thread_key`), that
|
|
432
|
+
* no turn ran for it (`no_turn`), or why a run with an output schema failed
|
|
433
|
+
* without an answer the schema accepted (`no_output`, `invalid_output`).
|
|
434
|
+
*/
|
|
435
|
+
export const TRIGGER_REASONS = [
|
|
436
|
+
...TRIGGER_DROP_REASONS,
|
|
437
|
+
"invalid_payload",
|
|
438
|
+
"missing_thread_key",
|
|
439
|
+
"no_turn",
|
|
440
|
+
"no_output",
|
|
441
|
+
"invalid_output",
|
|
442
|
+
] as const;
|
|
443
|
+
export type TriggerReason = (typeof TRIGGER_REASONS)[number];
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Body of a `POST /chat` the thread did not take: 409 while a run holds the
|
|
447
|
+
* thread or a call on it waits for a decision, or when the answers it
|
|
448
|
+
* carried fit no open call, were already applied, or answer a call whose
|
|
449
|
+
* origin is out of reach (`no_turn`); 429 once the agent's request bucket is
|
|
450
|
+
* spent, with `Retry-After`.
|
|
451
|
+
*/
|
|
452
|
+
export type ChatDropped = {
|
|
453
|
+
outcome: "dropped";
|
|
454
|
+
reason: Exclude<TriggerDropReason, "budget"> | "no_turn";
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* The drop a widget tells its user about by name: a call on the thread waits
|
|
459
|
+
* for approval, so the message goes back to the composer. Any other drop
|
|
460
|
+
* shows as a failed turn.
|
|
461
|
+
*/
|
|
462
|
+
export type SendRefusal = Extract<ChatDropped["reason"], "approval_pending">;
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* What became of the client tool answers a `POST /chat` carried, by the
|
|
466
|
+
* status Cortex answered it with, apart from how the run it started goes.
|
|
467
|
+
* `persisted` (2xx): Cortex took them up, and sending them again changes
|
|
468
|
+
* nothing. `refused` (4xx, a `ChatDropped` among them): it did not take
|
|
469
|
+
* them up as given, and the stored transcript says what stands. `lost`
|
|
470
|
+
* (5xx, or no answer at all): its word never came, and only the stored
|
|
471
|
+
* transcript can tell.
|
|
472
|
+
*/
|
|
473
|
+
export function answersReceiptOf(status: number) {
|
|
474
|
+
if (status >= 500) return "lost";
|
|
475
|
+
return status >= 400 ? "refused" : "persisted";
|
|
476
|
+
}
|
|
477
|
+
export type AnswersReceipt = ReturnType<typeof answersReceiptOf>;
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* One model call of the turn that wrote a message, raw, in the order the
|
|
481
|
+
* turn ran them: `GET /threads/:threadId/messages/:messageId/llm-requests`
|
|
482
|
+
* answers a list, for the request inspector.
|
|
483
|
+
*/
|
|
484
|
+
export type LlmRequest = {
|
|
485
|
+
id: string;
|
|
486
|
+
prompt: string;
|
|
487
|
+
output: string | null;
|
|
488
|
+
tokenUsage: TokenUsage | null;
|
|
489
|
+
};
|