@m6d/cortex-cli 0.14.0 → 0.16.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 +43 -19
- package/package.json +3 -13
- package/src/commands/apply.ts +171 -38
- package/src/commands/management.ts +73 -20
- package/src/commands/new.ts +8 -10
- package/src/commands/setup.ts +77 -37
- package/src/contracts/datasets/dataset-sql.ts +58 -7
- package/src/contracts/datasets/datasets.ts +220 -195
- package/src/contracts/egress-policy/scrub.ts +22 -10
- package/src/contracts/errors/index.ts +7 -0
- package/src/contracts/json/index.ts +45 -1
- package/src/contracts/manifest/index.ts +506 -134
- package/src/contracts/server/index.ts +45 -56
- package/src/contracts/triggers/index.ts +13 -9
- package/src/contracts/wire/index.ts +153 -18
- package/src/management/client.ts +79 -117
- package/src/management/profiles.ts +11 -22
- package/src/scaffold/files.ts +11 -9
- package/src/ui/prompts.ts +38 -8
- 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 -18
|
@@ -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",
|
|
@@ -97,10 +114,8 @@ export const BUILT_IN_TOOL_NAMES = [
|
|
|
97
114
|
"queryDataset",
|
|
98
115
|
"readAttachment",
|
|
99
116
|
"readReport",
|
|
100
|
-
"
|
|
101
|
-
"searchServices",
|
|
117
|
+
"searchRegistries",
|
|
102
118
|
"searchThreadHistory",
|
|
103
|
-
"searchTools",
|
|
104
119
|
REQUEST_LOCATION_TOOL,
|
|
105
120
|
TRIGGER_ANSWER_TOOL_NAME,
|
|
106
121
|
] as const;
|
|
@@ -109,30 +124,22 @@ export function isBuiltInToolName(name: string) {
|
|
|
109
124
|
return BUILT_IN_TOOL_NAMES.some((builtIn) => builtIn === name);
|
|
110
125
|
}
|
|
111
126
|
|
|
112
|
-
/** The longest name a tool takes, a server's or one defined in Cortex. */
|
|
113
|
-
export const TOOL_NAME_MAX_LENGTH = 64;
|
|
114
|
-
|
|
115
127
|
/** A tool name, a server's or one defined in Cortex: an identifier no built-in tool holds. */
|
|
116
|
-
const declaredToolNameSchema =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
.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
|
+
);
|
|
121
132
|
|
|
122
133
|
const jsonSchemaSchema = z.record(z.string(), z.unknown());
|
|
123
134
|
|
|
124
135
|
/** A variable the server fills for its agents' threads; the built-in ones are Cortex's. */
|
|
125
136
|
const serverVariableSchema = z.object({
|
|
126
|
-
name:
|
|
127
|
-
.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
"the name of a built-in variable",
|
|
133
|
-
),
|
|
134
|
-
description: z.string().max(500),
|
|
135
|
-
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),
|
|
136
143
|
type: variableDeclarationSchema.shape.type,
|
|
137
144
|
});
|
|
138
145
|
export type ServerVariable = z.infer<typeof serverVariableSchema>;
|
|
@@ -145,7 +152,7 @@ export type ServerVariable = z.infer<typeof serverVariableSchema>;
|
|
|
145
152
|
*/
|
|
146
153
|
export const serverToolSchema = z.object({
|
|
147
154
|
name: declaredToolNameSchema,
|
|
148
|
-
description: z.string().trim().min(1).max(
|
|
155
|
+
description: z.string().trim().min(1).max(SERVER_MANIFEST_LIMITS.description),
|
|
149
156
|
inputSchema: jsonSchemaSchema.refine(
|
|
150
157
|
(schema) => schema.type === "object",
|
|
151
158
|
"a tool's input is an object",
|
|
@@ -184,27 +191,9 @@ export const approvalRequestedEventSchema = z.object({
|
|
|
184
191
|
});
|
|
185
192
|
export type ApprovalRequestedEvent = z.infer<typeof approvalRequestedEventSchema>;
|
|
186
193
|
|
|
187
|
-
/** A JSON Pointer token as a key: percent-decoded, then unescaped; undefined when its encoding is broken. */
|
|
188
|
-
function pointerKey(token: string) {
|
|
189
|
-
try {
|
|
190
|
-
return unescapePointerToken(decodeURIComponent(token));
|
|
191
|
-
} catch {
|
|
192
|
-
return undefined;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
194
|
/** What a local `$ref` (`#/$defs/order`) points at in `root`, read as the input compiler reads it; undefined for any other. */
|
|
197
195
|
function pointedAt(root: unknown, ref: string) {
|
|
198
|
-
|
|
199
|
-
let node = root;
|
|
200
|
-
for (const token of ref.slice(1).split("/").slice(1)) {
|
|
201
|
-
const key = pointerKey(token);
|
|
202
|
-
node =
|
|
203
|
-
key !== undefined && (isRecord(node) || Array.isArray(node))
|
|
204
|
-
? new Map<string, unknown>(Object.entries(node)).get(key)
|
|
205
|
-
: undefined;
|
|
206
|
-
}
|
|
207
|
-
return node;
|
|
196
|
+
return ref.startsWith("#") ? readJsonPointer(root, ref) : undefined;
|
|
208
197
|
}
|
|
209
198
|
|
|
210
199
|
/** Whether one schema node names `APPROVAL_TITLE_FIELD` as a property or a required field. */
|
|
@@ -261,18 +250,15 @@ export function blocksApprovalTitle(inputSchema: unknown) {
|
|
|
261
250
|
);
|
|
262
251
|
}
|
|
263
252
|
|
|
264
|
-
/** 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`. */
|
|
265
254
|
export function refuseBlockedApprovalTitle(
|
|
266
255
|
tool: { requiresApproval?: boolean; inputSchema: unknown },
|
|
267
256
|
context: z.RefinementCtx,
|
|
268
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`,
|
|
269
259
|
) {
|
|
270
260
|
if (!tool.requiresApproval || !blocksApprovalTitle(tool.inputSchema)) return;
|
|
271
|
-
context.addIssue({
|
|
272
|
-
code: "custom",
|
|
273
|
-
path: [...path, "inputSchema"],
|
|
274
|
-
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`,
|
|
275
|
-
});
|
|
261
|
+
context.addIssue({ code: "custom", path: [...path, "inputSchema"], message });
|
|
276
262
|
}
|
|
277
263
|
|
|
278
264
|
function uniqueNames(entries: ReadonlyArray<{ name: string }>) {
|
|
@@ -282,10 +268,13 @@ function uniqueNames(entries: ReadonlyArray<{ name: string }>) {
|
|
|
282
268
|
/** `GET /`: everything the server offers, keyed by `version`, which changes whenever anything else does. */
|
|
283
269
|
export const serverManifestSchema = z.object({
|
|
284
270
|
version: z.string().min(1).max(128),
|
|
285
|
-
variables: z
|
|
271
|
+
variables: z
|
|
272
|
+
.array(serverVariableSchema)
|
|
273
|
+
.max(SERVER_MANIFEST_LIMITS.variables)
|
|
274
|
+
.refine(uniqueNames, "variable names repeat"),
|
|
286
275
|
tools: z
|
|
287
276
|
.array(serverToolSchema)
|
|
288
|
-
.max(
|
|
277
|
+
.max(SERVER_MANIFEST_LIMITS.tools)
|
|
289
278
|
.refine(uniqueNames, "tool names repeat")
|
|
290
279
|
.superRefine((tools, context) => {
|
|
291
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,9 @@ export type AttachmentSummary = {
|
|
|
70
85
|
description: string | null;
|
|
71
86
|
};
|
|
72
87
|
|
|
88
|
+
/** The user id every caller shares on an agent that verifies no end user. */
|
|
89
|
+
export const ANONYMOUS_USER_ID = "00000000-0000-0000-0000-000000000000";
|
|
90
|
+
|
|
73
91
|
export type ThreadSummary = {
|
|
74
92
|
id: string;
|
|
75
93
|
title: string | null;
|
|
@@ -131,12 +149,23 @@ export type ClientToolBinding = {
|
|
|
131
149
|
*/
|
|
132
150
|
export type AgentCapabilities = {
|
|
133
151
|
attachments: boolean;
|
|
134
|
-
/**
|
|
152
|
+
/**
|
|
153
|
+
* False when this caller may not send to the chat: the agent's `message`
|
|
154
|
+
* trigger is off, or the caller only reads (`readOnly`). The widget hides
|
|
155
|
+
* its composer and answers no tool call.
|
|
156
|
+
*/
|
|
135
157
|
messages: boolean;
|
|
158
|
+
/** True when the caller only reads, as an operator inspecting a user's thread does. */
|
|
159
|
+
readOnly: boolean;
|
|
136
160
|
/** Prompt chips on the blank-chat landing per locale; clicking one sends its `prompt` as the first message. */
|
|
137
161
|
suggestions: Suggestions;
|
|
138
162
|
/** Headline of the blank-chat landing per locale; "" falls back to the widget's built-in copy. */
|
|
139
163
|
greeting: Greeting;
|
|
164
|
+
/**
|
|
165
|
+
* Whether this caller gets the debug view: tool inputs and outputs, reasoning,
|
|
166
|
+
* model calls. Cortex sends anyone else only what the widget's normal UI draws.
|
|
167
|
+
*/
|
|
168
|
+
debug: boolean;
|
|
140
169
|
};
|
|
141
170
|
|
|
142
171
|
export type MessageMetadata = {
|
|
@@ -178,6 +207,18 @@ export const STATEMENT_TOOLS = ["executeCode", "queryDataset", "describeDataset"
|
|
|
178
207
|
*/
|
|
179
208
|
export const APPROVAL_TITLE_FIELD = "approvalTitle";
|
|
180
209
|
|
|
210
|
+
/** A line the model wrote into a call input's `field`, trimmed; undefined while it streams in or is blank. */
|
|
211
|
+
export function modelLineOf(input: unknown, field: string) {
|
|
212
|
+
if (!input || typeof input !== "object") return undefined;
|
|
213
|
+
const line: unknown = Reflect.get(input, field);
|
|
214
|
+
return typeof line === "string" && line.trim() ? line.trim() : undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** The title the model wrote into an approval call's input, or undefined while it has none. */
|
|
218
|
+
export function approvalTitleOf(input: unknown) {
|
|
219
|
+
return modelLineOf(input, APPROVAL_TITLE_FIELD);
|
|
220
|
+
}
|
|
221
|
+
|
|
181
222
|
/**
|
|
182
223
|
* What Cortex stamps on a tool call's `metadata.cortex` from its first chunk,
|
|
183
224
|
* so the live part and the stored one agree.
|
|
@@ -242,16 +283,27 @@ export function toUiMessage<TPart>(message: CortexMessage<TPart>) {
|
|
|
242
283
|
export const CLIENT_TOOL_ANSWERS_KEY = "clientToolAnswers";
|
|
243
284
|
|
|
244
285
|
/**
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
286
|
+
* How a call was answered: a client tool's output (`complete` / `error`), or
|
|
287
|
+
* the user's decision on a server tool that asked for approval
|
|
288
|
+
* (`approved` / `denied`).
|
|
289
|
+
*/
|
|
290
|
+
export const CLIENT_TOOL_ANSWER_STATES = ["complete", "error", "approved", "denied"] as const;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* One answered call, which Cortex applies to the stored transcript. A
|
|
294
|
+
* decision carries a null output.
|
|
248
295
|
*/
|
|
249
296
|
export type ClientToolAnswer = {
|
|
250
297
|
toolCallId: string;
|
|
251
298
|
output: unknown;
|
|
252
|
-
state:
|
|
299
|
+
state: (typeof CLIENT_TOOL_ANSWER_STATES)[number];
|
|
253
300
|
};
|
|
254
301
|
|
|
302
|
+
/** Whether the answer is the user's decision on a call that asked for approval, rather than a tool's output. */
|
|
303
|
+
export function isDecision(answer: Pick<ClientToolAnswer, "state">) {
|
|
304
|
+
return answer.state === "approved" || answer.state === "denied";
|
|
305
|
+
}
|
|
306
|
+
|
|
255
307
|
/**
|
|
256
308
|
* The id a parked tool call's decision is filed under: the one on its
|
|
257
309
|
* approval record, or `approval_<toolCallId>`, which is how the SDK names
|
|
@@ -261,6 +313,11 @@ export function approvalIdOf(part: { id: string; approval?: { id: string } }) {
|
|
|
261
313
|
return part.approval?.id ?? `approval_${part.id}`;
|
|
262
314
|
}
|
|
263
315
|
|
|
316
|
+
/** Whether a tool call asked the user for approval: it carries an approval record, or waits for one. */
|
|
317
|
+
export function askedApproval(part: { approval?: unknown; state?: string }) {
|
|
318
|
+
return part.approval !== undefined || part.state === "approval-requested";
|
|
319
|
+
}
|
|
320
|
+
|
|
264
321
|
/**
|
|
265
322
|
* Response of `POST /chat/:threadId/tools/:toolCallId/initiate` for a pending
|
|
266
323
|
* call stamped with an embed. The payload is for the widget only; it never
|
|
@@ -310,24 +367,102 @@ export type ThreadMessagesUpdatedEvent = {
|
|
|
310
367
|
payload: { thread: ThreadSummary };
|
|
311
368
|
};
|
|
312
369
|
|
|
313
|
-
export type ToolProgressEvent = {
|
|
314
|
-
type: "thread:tool-progress";
|
|
315
|
-
payload: {
|
|
316
|
-
threadId: string;
|
|
317
|
-
toolCallId: string;
|
|
318
|
-
toolName: string;
|
|
319
|
-
status: "started" | "finished";
|
|
320
|
-
};
|
|
321
|
-
};
|
|
322
|
-
|
|
323
370
|
export type WsEvent =
|
|
324
371
|
| ThreadCreatedEvent
|
|
325
372
|
| ThreadDeletedEvent
|
|
326
373
|
| ThreadTitleUpdatedEvent
|
|
327
374
|
| ThreadRunStartedEvent
|
|
328
375
|
| ThreadRunFinishedEvent
|
|
329
|
-
| ThreadMessagesUpdatedEvent
|
|
330
|
-
| ToolProgressEvent;
|
|
376
|
+
| ThreadMessagesUpdatedEvent;
|
|
331
377
|
|
|
332
378
|
/** The header the widget sends the browser's IANA time zone in; the turn reasons in that clock. */
|
|
333
379
|
export const TIMEZONE_HEADER = "X-Cortex-Timezone";
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* The header carrying a console operator's token, sent by the playground and
|
|
383
|
+
* the thread inspector on every request to an agent and by the fire and
|
|
384
|
+
* answer calls Cortex makes to a trigger's route. A verified one makes the
|
|
385
|
+
* operator the caller unless the request also carries an end-user token to
|
|
386
|
+
* test as, and marks the threads it opens as test threads. A WebSocket
|
|
387
|
+
* upgrade carries it as `?operator=`.
|
|
388
|
+
*/
|
|
389
|
+
export const OPERATOR_HEADER = "X-Cortex-Operator";
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Beside the operator token, the id of a thread the operator inspects: the
|
|
393
|
+
* request then reads as that thread's owner, read-only, with the debug view.
|
|
394
|
+
* The engine refuses any request carrying it but a read (GET, HEAD, OPTIONS),
|
|
395
|
+
* on every agent route.
|
|
396
|
+
* A WebSocket upgrade carries it as `?inspect=`.
|
|
397
|
+
*/
|
|
398
|
+
export const INSPECT_HEADER = "X-Cortex-Inspect";
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Why a firing was dropped: the thread was busy with another run, a call on
|
|
402
|
+
* it waits for a decision (answered 409: decide that firing first), the
|
|
403
|
+
* daily budget was spent, or the agent's request bucket was.
|
|
404
|
+
*/
|
|
405
|
+
export const TRIGGER_DROP_REASONS = ["busy", "approval_pending", "budget", "rate_limited"] as const;
|
|
406
|
+
export type TriggerDropReason = (typeof TRIGGER_DROP_REASONS)[number];
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Every reason a firing records beside its outcome: why it was dropped, why
|
|
410
|
+
* its payload was rejected (`invalid_payload`, `missing_thread_key`), that
|
|
411
|
+
* no turn ran for it (`no_turn`), or why a run with an output schema failed
|
|
412
|
+
* without an answer the schema accepted (`no_output`, `invalid_output`).
|
|
413
|
+
*/
|
|
414
|
+
export const TRIGGER_REASONS = [
|
|
415
|
+
...TRIGGER_DROP_REASONS,
|
|
416
|
+
"invalid_payload",
|
|
417
|
+
"missing_thread_key",
|
|
418
|
+
"no_turn",
|
|
419
|
+
"no_output",
|
|
420
|
+
"invalid_output",
|
|
421
|
+
] as const;
|
|
422
|
+
export type TriggerReason = (typeof TRIGGER_REASONS)[number];
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Body of a `POST /chat` the thread did not take: 409 while a run holds the
|
|
426
|
+
* thread or a call on it waits for a decision, or when the answers it
|
|
427
|
+
* carried fit no open call, were already applied, or answer a call whose
|
|
428
|
+
* origin is out of reach (`no_turn`); 429 once the agent's request bucket is
|
|
429
|
+
* spent, with `Retry-After`.
|
|
430
|
+
*/
|
|
431
|
+
export type ChatDropped = {
|
|
432
|
+
outcome: "dropped";
|
|
433
|
+
reason: Exclude<TriggerDropReason, "budget"> | "no_turn";
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* The drop a widget tells its user about by name: a call on the thread waits
|
|
438
|
+
* for approval, so the message goes back to the composer. Any other drop
|
|
439
|
+
* shows as a failed turn.
|
|
440
|
+
*/
|
|
441
|
+
export type SendRefusal = Extract<ChatDropped["reason"], "approval_pending">;
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* What became of the client tool answers a `POST /chat` carried, by the
|
|
445
|
+
* status Cortex answered it with, apart from how the run it started goes.
|
|
446
|
+
* `persisted` (2xx): Cortex took them up, and sending them again changes
|
|
447
|
+
* nothing. `refused` (4xx, a `ChatDropped` among them): it did not take
|
|
448
|
+
* them up as given, and the stored transcript says what stands. `lost`
|
|
449
|
+
* (5xx, or no answer at all): its word never came, and only the stored
|
|
450
|
+
* transcript can tell.
|
|
451
|
+
*/
|
|
452
|
+
export function answersReceiptOf(status: number) {
|
|
453
|
+
if (status >= 500) return "lost";
|
|
454
|
+
return status >= 400 ? "refused" : "persisted";
|
|
455
|
+
}
|
|
456
|
+
export type AnswersReceipt = ReturnType<typeof answersReceiptOf>;
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* One model call of the turn that wrote a message, raw, in the order the
|
|
460
|
+
* turn ran them: `GET /threads/:threadId/messages/:messageId/llm-requests`
|
|
461
|
+
* answers a list, for the request inspector.
|
|
462
|
+
*/
|
|
463
|
+
export type LlmRequest = {
|
|
464
|
+
id: string;
|
|
465
|
+
prompt: string;
|
|
466
|
+
output: string | null;
|
|
467
|
+
tokenUsage: TokenUsage | null;
|
|
468
|
+
};
|