@m6d/cortex-cli 0.19.0 → 0.21.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 +4 -4
- package/package.json +3 -3
- package/src/commands/new.ts +4 -4
- package/src/commands/setup.ts +19 -10
- package/src/contracts/datasets/dataset-sql.ts +1 -1
- package/src/contracts/management/index.ts +13 -1
- package/src/contracts/management/procedures.generated.ts +401 -216
- package/src/contracts/manifest/index.ts +130 -137
- package/src/contracts/server/index.ts +33 -9
- package/src/contracts/triggers/index.ts +9 -9
- package/src/contracts/wire/index.ts +11 -0
- package/src/management/client.ts +2 -0
- package/src/scaffold/files.ts +41 -37
- package/src/scaffold/ports.ts +5 -4
- package/src/scaffold/templates/cspell.json.txt +1 -1
- package/src/scaffold/templates/oxlintrc.json.txt +67 -0
- package/src/scaffold/templates/setup.ts.txt +1 -0
- package/src/scaffold/templates/eslint.config.mjs.txt +0 -20
|
@@ -2,10 +2,11 @@ import { z } from "zod";
|
|
|
2
2
|
import { datasetIdentifierSchema } from "../datasets/index";
|
|
3
3
|
import { canCarrySecret } from "../egress-policy/index";
|
|
4
4
|
import {
|
|
5
|
-
|
|
5
|
+
displayNameSchema,
|
|
6
6
|
EVENT_HEADER,
|
|
7
7
|
identifierSchema,
|
|
8
8
|
localeSchema,
|
|
9
|
+
nameSchema,
|
|
9
10
|
SERVER_EVENTS,
|
|
10
11
|
refuseBlockedApprovalTitle,
|
|
11
12
|
serverToolSchema,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
* A manifest: the agents and components a code-first deployment declares,
|
|
26
27
|
* written by hand (`cortex.manifest.ts` or JSON) and applied in one
|
|
27
28
|
* transaction. Everything it points at, it names: a server, a model
|
|
28
|
-
* provider, a service
|
|
29
|
+
* provider, a service, an agent, and a stored secret
|
|
29
30
|
* or credential by the noun of what it names (`secret: "signing-key"`,
|
|
30
31
|
* `credential: "petstore"`). A tool input field's `x-source` is the one
|
|
31
32
|
* exception: `inputSchema` is the JSON Schema the tool stores, verbatim, so
|
|
@@ -187,11 +188,6 @@ function overridable<const TField extends string>(fields: readonly [TField, ...T
|
|
|
187
188
|
.default([]);
|
|
188
189
|
}
|
|
189
190
|
|
|
190
|
-
/** Consumer-facing slugs: the manifest id, service keys, skill names and trigger names share it. */
|
|
191
|
-
export const SLUG_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
192
|
-
export const SLUG_MAX_LENGTH = 64;
|
|
193
|
-
const slugSchema = z.string().regex(SLUG_PATTERN).max(SLUG_MAX_LENGTH);
|
|
194
|
-
|
|
195
191
|
/** A DNS host name in lower case, the way `URL` reports one: dot-separated labels of letters, digits and inner hyphens. */
|
|
196
192
|
export const HOSTNAME_PATTERN =
|
|
197
193
|
/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/;
|
|
@@ -477,11 +473,6 @@ function repeatedHeaders(entries: ReadonlyArray<{ header: string }>) {
|
|
|
477
473
|
});
|
|
478
474
|
}
|
|
479
475
|
|
|
480
|
-
/** The longest name Cortex holds a server, model provider, credential or secret under. */
|
|
481
|
-
export const NAME_MAX_LENGTH = 120;
|
|
482
|
-
/** A server, model provider, credential or secret, by the name Cortex holds it under. */
|
|
483
|
-
const nameSchema = z.string().trim().min(1).max(NAME_MAX_LENGTH);
|
|
484
|
-
|
|
485
476
|
const presentedHeaderSchema = headerNameSchema.refine(
|
|
486
477
|
(name) => !transportOwnedHeaders.has(name.toLowerCase()),
|
|
487
478
|
"a header the transport sets",
|
|
@@ -518,7 +509,10 @@ const variableSourceSchema = z.object({
|
|
|
518
509
|
});
|
|
519
510
|
|
|
520
511
|
/** A credential Cortex holds or acquires, by name. */
|
|
521
|
-
const credentialSourceSchema = z.object({
|
|
512
|
+
const credentialSourceSchema = z.object({
|
|
513
|
+
kind: z.literal("credential"),
|
|
514
|
+
name: nameSchema,
|
|
515
|
+
});
|
|
522
516
|
|
|
523
517
|
/** A value written as is. */
|
|
524
518
|
const constantHeaderSourceSchema = z.object({
|
|
@@ -883,15 +877,15 @@ const objectSchema = objectJsonSchemaOf();
|
|
|
883
877
|
|
|
884
878
|
const EMPTY_OBJECT = { type: "object" } as const satisfies z.input<typeof objectSchema>;
|
|
885
879
|
|
|
886
|
-
/** Who a component reaches: every agent, every agent on one server, or the agents listed by
|
|
880
|
+
/** Who a component reaches: every agent, every agent on one server, or the agents listed by name. */
|
|
887
881
|
const audienceSchema = z.union([
|
|
888
882
|
z.literal("all"),
|
|
889
883
|
z.strictObject({ server: nameSchema }),
|
|
890
884
|
z.strictObject({
|
|
891
885
|
agents: z
|
|
892
|
-
.array(
|
|
886
|
+
.array(nameSchema)
|
|
893
887
|
.min(1)
|
|
894
|
-
.refine((
|
|
888
|
+
.refine((names) => new Set(names).size === names.length, "an agent repeats"),
|
|
895
889
|
}),
|
|
896
890
|
]);
|
|
897
891
|
|
|
@@ -905,14 +899,9 @@ const modelSchema = z.strictObject({
|
|
|
905
899
|
effort: z.string().trim().min(1).max(64).optional(),
|
|
906
900
|
});
|
|
907
901
|
|
|
908
|
-
/** A service's
|
|
909
|
-
export const SERVICE_LIMITS = {
|
|
902
|
+
/** A service's description, in characters per locale. The console caps its editor at the same length. */
|
|
903
|
+
export const SERVICE_LIMITS = { descriptionLength: 4_000 } as const;
|
|
910
904
|
|
|
911
|
-
/** One line per locale, trimmed. */
|
|
912
|
-
const localizedLineSchema = z.record(
|
|
913
|
-
localeSchema,
|
|
914
|
-
z.string().trim().min(1).max(SERVICE_LIMITS.nameLength),
|
|
915
|
-
);
|
|
916
905
|
const localizedTextSchema = z.record(
|
|
917
906
|
localeSchema,
|
|
918
907
|
z.string().trim().min(1).max(SERVICE_LIMITS.descriptionLength),
|
|
@@ -932,6 +921,7 @@ function toolInputSchemaOf(type: (typeof TOOL_TYPE_NAMES)[number]) {
|
|
|
932
921
|
|
|
933
922
|
/** What a live write may change on a manifest's tool, and keep through the next apply. */
|
|
934
923
|
const TOOL_OVERRIDABLES = [
|
|
924
|
+
"displayName",
|
|
935
925
|
"description",
|
|
936
926
|
"timeoutMs",
|
|
937
927
|
"maxResponseBytes",
|
|
@@ -946,6 +936,7 @@ const CALLED_TOOL_OVERRIDABLES = [...TOOL_OVERRIDABLES, "headers"] as const;
|
|
|
946
936
|
/** Shared by every tool type; a tool that calls nothing (`client`) has no endpoint, headers, approval or bound fields. */
|
|
947
937
|
const toolBaseSchema = z.strictObject({
|
|
948
938
|
...serverToolSchema.pick({ name: true, description: true }).shape,
|
|
939
|
+
displayName: displayNameSchema.default({}),
|
|
949
940
|
inputSchema: toolInputSchemaOf("client"),
|
|
950
941
|
outputSchema: objectSchema.default(EMPTY_OBJECT),
|
|
951
942
|
timeoutMs: setting(toolBounds.timeoutMs),
|
|
@@ -1050,15 +1041,15 @@ const manifestOpenApiSchema = z.strictObject({
|
|
|
1050
1041
|
});
|
|
1051
1042
|
export type ManifestOpenApi = z.input<typeof manifestOpenApiSchema>;
|
|
1052
1043
|
|
|
1053
|
-
/**
|
|
1044
|
+
/** Skills and collections scope themselves by a service's `name`. */
|
|
1054
1045
|
const manifestServiceSchema = z.strictObject({
|
|
1055
|
-
/** What consumers parameterize their tools with
|
|
1056
|
-
|
|
1057
|
-
|
|
1046
|
+
/** What consumers parameterize their tools with. */
|
|
1047
|
+
name: nameSchema,
|
|
1048
|
+
displayName: displayNameSchema.default({}),
|
|
1058
1049
|
/** Rich text: it may mention tools and skills. */
|
|
1059
1050
|
description: localizedTextSchema,
|
|
1060
1051
|
audience: audienceSchema,
|
|
1061
|
-
overridable: overridable(["description"]),
|
|
1052
|
+
overridable: overridable(["displayName", "description"]),
|
|
1062
1053
|
});
|
|
1063
1054
|
export type ManifestService = z.input<typeof manifestServiceSchema>;
|
|
1064
1055
|
|
|
@@ -1066,14 +1057,15 @@ export type ManifestService = z.input<typeof manifestServiceSchema>;
|
|
|
1066
1057
|
export const SKILL_LIMITS = { descriptionLength: 500 } as const;
|
|
1067
1058
|
|
|
1068
1059
|
const manifestSkillSchema = z.strictObject({
|
|
1069
|
-
name:
|
|
1060
|
+
name: nameSchema,
|
|
1061
|
+
displayName: displayNameSchema.default({}),
|
|
1070
1062
|
description: z.string().trim().min(1).max(SKILL_LIMITS.descriptionLength),
|
|
1071
1063
|
/** Rich text: it may mention tools, skills and datasets, and read `{{variables}}`. */
|
|
1072
1064
|
body: z.string().trim().min(1),
|
|
1073
|
-
/** The
|
|
1074
|
-
service:
|
|
1065
|
+
/** The name of the service the skill renders inside; fixed once created. */
|
|
1066
|
+
service: nameSchema.optional(),
|
|
1075
1067
|
audience: audienceSchema,
|
|
1076
|
-
overridable: overridable(["description", "body"]),
|
|
1068
|
+
overridable: overridable(["displayName", "description", "body"]),
|
|
1077
1069
|
});
|
|
1078
1070
|
export type ManifestSkill = z.input<typeof manifestSkillSchema>;
|
|
1079
1071
|
|
|
@@ -1100,7 +1092,8 @@ export const TRIGGER_LIMITS = { descriptionLength: 500, instructionsLength: 20_0
|
|
|
1100
1092
|
/** Fired with its secret, which the apply that creates it answers once and no manifest holds. */
|
|
1101
1093
|
const manifestTriggerSchema = z.strictObject({
|
|
1102
1094
|
/** `message` is the built-in trigger every agent has. */
|
|
1103
|
-
name:
|
|
1095
|
+
name: nameSchema.refine((name) => name !== MESSAGE_TRIGGER_NAME, "the built-in trigger's name"),
|
|
1096
|
+
displayName: displayNameSchema.default({}),
|
|
1104
1097
|
description: z.string().trim().min(1).max(TRIGGER_LIMITS.descriptionLength),
|
|
1105
1098
|
inputSchema: objectSchema.default(EMPTY_OBJECT),
|
|
1106
1099
|
/** The shape the run answers in; absent, the run answers in free text. */
|
|
@@ -1125,6 +1118,7 @@ const manifestTriggerSchema = z.strictObject({
|
|
|
1125
1118
|
model: modelSchema.optional(),
|
|
1126
1119
|
audience: audienceSchema,
|
|
1127
1120
|
overridable: overridable([
|
|
1121
|
+
"displayName",
|
|
1128
1122
|
"description",
|
|
1129
1123
|
"instructions",
|
|
1130
1124
|
"threadPolicy",
|
|
@@ -1147,6 +1141,7 @@ export function unexposedPins(host: {
|
|
|
1147
1141
|
const manifestDatasetHostSchema = z
|
|
1148
1142
|
.strictObject({
|
|
1149
1143
|
name: datasetIdentifierSchema,
|
|
1144
|
+
displayName: displayNameSchema.default({}),
|
|
1150
1145
|
url: upstreamUrlSchema,
|
|
1151
1146
|
headers: headersSchema,
|
|
1152
1147
|
timeoutMs: setting(SETTING_BOUNDS.datasetHost.timeoutMs),
|
|
@@ -1175,7 +1170,7 @@ const manifestDatasetHostSchema = z
|
|
|
1175
1170
|
/** Among those, the ones listed in every turn, whatever the question. */
|
|
1176
1171
|
pinned: z.array(datasetIdentifierSchema).max(50).default([]),
|
|
1177
1172
|
audience: audienceSchema,
|
|
1178
|
-
overridable: overridable(["timeoutMs", "rateLimits", "headers"]),
|
|
1173
|
+
overridable: overridable(["displayName", "timeoutMs", "rateLimits", "headers"]),
|
|
1179
1174
|
})
|
|
1180
1175
|
.superRefine((host, context) => {
|
|
1181
1176
|
const unexposed = unexposedPins(host);
|
|
@@ -1204,6 +1199,7 @@ export const mcpToolNamesSchema = z
|
|
|
1204
1199
|
const manifestMcpServerSchema = z.strictObject({
|
|
1205
1200
|
/** Prefixes every tool the server contributes: scripts call `tools.<name>_<tool>()`. */
|
|
1206
1201
|
name: identifierSchema,
|
|
1202
|
+
displayName: displayNameSchema.default({}),
|
|
1207
1203
|
url: upstreamUrlSchema,
|
|
1208
1204
|
headers: declaredHeadersSchema(
|
|
1209
1205
|
presentedHeaderSchema.refine(
|
|
@@ -1216,7 +1212,7 @@ const manifestMcpServerSchema = z.strictObject({
|
|
|
1216
1212
|
include: mcpToolNamesSchema.default([]),
|
|
1217
1213
|
exclude: mcpToolNamesSchema.default([]),
|
|
1218
1214
|
audience: audienceSchema,
|
|
1219
|
-
overridable: overridable(["include", "exclude", "headers"]),
|
|
1215
|
+
overridable: overridable(["displayName", "include", "exclude", "headers"]),
|
|
1220
1216
|
});
|
|
1221
1217
|
export type ManifestMcpServer = z.input<typeof manifestMcpServerSchema>;
|
|
1222
1218
|
|
|
@@ -1226,7 +1222,8 @@ export type ManifestMcpServer = z.input<typeof manifestMcpServerSchema>;
|
|
|
1226
1222
|
* travels under the header that names the event.
|
|
1227
1223
|
*/
|
|
1228
1224
|
const manifestWebhookSchema = z.strictObject({
|
|
1229
|
-
name:
|
|
1225
|
+
name: nameSchema,
|
|
1226
|
+
displayName: displayNameSchema.default({}),
|
|
1230
1227
|
description: z.string().trim().max(500).default(""),
|
|
1231
1228
|
event: z.enum(SERVER_EVENTS),
|
|
1232
1229
|
url: upstreamUrlSchema,
|
|
@@ -1239,19 +1236,15 @@ const manifestWebhookSchema = z.strictObject({
|
|
|
1239
1236
|
),
|
|
1240
1237
|
timeoutMs: setting(SETTING_BOUNDS.webhook.timeoutMs),
|
|
1241
1238
|
audience: audienceSchema,
|
|
1242
|
-
overridable: overridable(["description", "timeoutMs", "headers"]),
|
|
1239
|
+
overridable: overridable(["displayName", "description", "timeoutMs", "headers"]),
|
|
1243
1240
|
});
|
|
1244
1241
|
export type ManifestWebhook = z.input<typeof manifestWebhookSchema>;
|
|
1245
1242
|
|
|
1246
1243
|
/**
|
|
1247
|
-
* A collection's
|
|
1248
|
-
*
|
|
1244
|
+
* A collection's description in characters, and how many documents a
|
|
1245
|
+
* manifest lists in one. The console caps its editor at the same.
|
|
1249
1246
|
*/
|
|
1250
|
-
export const COLLECTION_LIMITS = {
|
|
1251
|
-
nameLength: 80,
|
|
1252
|
-
descriptionLength: 2_000,
|
|
1253
|
-
documents: 100,
|
|
1254
|
-
} as const;
|
|
1247
|
+
export const COLLECTION_LIMITS = { descriptionLength: 2_000, documents: 100 } as const;
|
|
1255
1248
|
|
|
1256
1249
|
/** A knowledge document's name in characters and its size in bytes, however it arrives. */
|
|
1257
1250
|
export const DOCUMENT_LIMITS = { nameLength: 255, bytes: 10 * 1024 * 1024 } as const;
|
|
@@ -1377,15 +1370,16 @@ function collectionSchema<TDocument extends z.ZodType<Parameters<typeof document
|
|
|
1377
1370
|
document: TDocument,
|
|
1378
1371
|
) {
|
|
1379
1372
|
return z.strictObject({
|
|
1380
|
-
name:
|
|
1373
|
+
name: nameSchema,
|
|
1374
|
+
displayName: displayNameSchema.default({}),
|
|
1381
1375
|
description: z.string().trim().max(COLLECTION_LIMITS.descriptionLength).default(""),
|
|
1382
|
-
/** The
|
|
1383
|
-
service:
|
|
1376
|
+
/** The name of the service every document in the collection belongs to. */
|
|
1377
|
+
service: nameSchema.optional(),
|
|
1384
1378
|
/** Whether answers may name its documents; false keeps their names, sections and pages from the model. */
|
|
1385
1379
|
citable: z.boolean().default(true),
|
|
1386
1380
|
documents: documentsSchema(document),
|
|
1387
1381
|
audience: audienceSchema,
|
|
1388
|
-
overridable: overridable(["description", "documents"]),
|
|
1382
|
+
overridable: overridable(["displayName", "description", "documents"]),
|
|
1389
1383
|
});
|
|
1390
1384
|
}
|
|
1391
1385
|
|
|
@@ -1497,58 +1491,77 @@ export const agentSettingsSchema = z.strictObject({
|
|
|
1497
1491
|
.prefault({}),
|
|
1498
1492
|
});
|
|
1499
1493
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
.
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
"
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1494
|
+
const manifestAgentSchema = z
|
|
1495
|
+
.strictObject({
|
|
1496
|
+
name: nameSchema,
|
|
1497
|
+
displayName: displayNameSchema.default({}),
|
|
1498
|
+
/**
|
|
1499
|
+
* The server it belongs to, by name; a manifest never creates one. Left
|
|
1500
|
+
* out, the agent has no server: no native tools, no server variables,
|
|
1501
|
+
* no `server` auth, and no model to inherit, so it names its own `chat`.
|
|
1502
|
+
*/
|
|
1503
|
+
server: nameSchema.optional(),
|
|
1504
|
+
/** Rich text: it may mention tools and skills, and read `{{variables}}`. */
|
|
1505
|
+
prompt: z.string().trim().min(1),
|
|
1506
|
+
auth: agentAuthSchema,
|
|
1507
|
+
/**
|
|
1508
|
+
* `chat` left out runs on the server's pick; `vision` and
|
|
1509
|
+
* `summarization` left out use none of their own.
|
|
1510
|
+
*/
|
|
1511
|
+
models: z
|
|
1512
|
+
.strictObject({
|
|
1513
|
+
chat: modelSchema.optional(),
|
|
1514
|
+
vision: modelSchema.omit({ effort: true }).optional(),
|
|
1515
|
+
summarization: modelSchema.omit({ effort: true }).optional(),
|
|
1516
|
+
})
|
|
1517
|
+
.default({}),
|
|
1518
|
+
settings: agentSettingsSchema.prefault({}),
|
|
1519
|
+
/** Messages a minute and model tokens a UTC day, for all users together and for each; null, no limit. */
|
|
1520
|
+
rateLimits: z
|
|
1521
|
+
.strictObject({
|
|
1522
|
+
allUsers: z
|
|
1523
|
+
.strictObject({
|
|
1524
|
+
messagesPerMinute: rateLimit(agentRateLimits.messagesPerMinute, "allUsers"),
|
|
1525
|
+
tokensPerDay: rateLimit(agentRateLimits.tokensPerDay, "allUsers"),
|
|
1526
|
+
})
|
|
1527
|
+
.prefault({}),
|
|
1528
|
+
perUser: z
|
|
1529
|
+
.strictObject({
|
|
1530
|
+
messagesPerMinute: rateLimit(agentRateLimits.messagesPerMinute, "perUser"),
|
|
1531
|
+
tokensPerDay: rateLimit(agentRateLimits.tokensPerDay, "perUser"),
|
|
1532
|
+
})
|
|
1533
|
+
.prefault({}),
|
|
1534
|
+
})
|
|
1535
|
+
.prefault({}),
|
|
1536
|
+
suggestions: suggestionsSchema.default(() => byLocale(() => [])),
|
|
1537
|
+
greeting: greetingSchema.default(() => byLocale(() => "")),
|
|
1538
|
+
overridable: overridable([
|
|
1539
|
+
"displayName",
|
|
1540
|
+
"prompt",
|
|
1541
|
+
"models",
|
|
1542
|
+
"settings",
|
|
1543
|
+
"rateLimits",
|
|
1544
|
+
"suggestions",
|
|
1545
|
+
"greeting",
|
|
1546
|
+
]),
|
|
1547
|
+
})
|
|
1548
|
+
.superRefine((agent, context) => {
|
|
1549
|
+
if (agent.server !== undefined) return;
|
|
1550
|
+
if (agent.auth.kind === "server") {
|
|
1551
|
+
context.addIssue({
|
|
1552
|
+
code: "custom",
|
|
1553
|
+
path: ["auth"],
|
|
1554
|
+
message: "server auth needs a server",
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
if (agent.models.chat === undefined) {
|
|
1558
|
+
context.addIssue({
|
|
1559
|
+
code: "custom",
|
|
1560
|
+
path: ["models", "chat"],
|
|
1561
|
+
message: "an agent with no server names its own chat model",
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
});
|
|
1552
1565
|
export type ManifestAgent = z.input<typeof manifestAgentSchema>;
|
|
1553
1566
|
|
|
1554
1567
|
/**
|
|
@@ -1570,31 +1583,14 @@ export const MANIFEST_KINDS = [
|
|
|
1570
1583
|
export type ManifestKind = (typeof MANIFEST_KINDS)[number];
|
|
1571
1584
|
|
|
1572
1585
|
/**
|
|
1573
|
-
*
|
|
1574
|
-
*
|
|
1575
|
-
* once, so one entry means one row.
|
|
1586
|
+
* A list of entries, each going by the name its row holds, refusing a name
|
|
1587
|
+
* they repeat: one entry means one row.
|
|
1576
1588
|
*/
|
|
1577
|
-
|
|
1578
|
-
agents: "slug",
|
|
1579
|
-
tools: "name",
|
|
1580
|
-
services: "key",
|
|
1581
|
-
skills: "name",
|
|
1582
|
-
triggers: "name",
|
|
1583
|
-
datasetHosts: "name",
|
|
1584
|
-
mcpServers: "name",
|
|
1585
|
-
webhooks: "name",
|
|
1586
|
-
collections: "name",
|
|
1587
|
-
} as const satisfies Record<ManifestKind, string>;
|
|
1588
|
-
|
|
1589
|
-
/** A list of entries named by their `field`, refusing a name they repeat. */
|
|
1590
|
-
function listOf<TField extends string, TEntry extends z.ZodType<Record<TField, string>>>(
|
|
1591
|
-
field: TField,
|
|
1592
|
-
entry: TEntry,
|
|
1593
|
-
) {
|
|
1589
|
+
function listOf<TEntry extends z.ZodType<{ name: string }>>(entry: TEntry) {
|
|
1594
1590
|
return z
|
|
1595
1591
|
.array(entry)
|
|
1596
1592
|
.refine(
|
|
1597
|
-
(entries) => new Set(entries.map((value) => value
|
|
1593
|
+
(entries) => new Set(entries.map((value) => value.name)).size === entries.length,
|
|
1598
1594
|
"a name repeats",
|
|
1599
1595
|
)
|
|
1600
1596
|
.default([]);
|
|
@@ -1612,17 +1608,17 @@ export const CORTEX_MANIFEST_ID = "cortex";
|
|
|
1612
1608
|
*/
|
|
1613
1609
|
export const manifestSchema = z.strictObject({
|
|
1614
1610
|
/** Fixed for the deployment it describes: the rows an apply owns carry it. */
|
|
1615
|
-
id:
|
|
1616
|
-
agents: listOf(
|
|
1617
|
-
tools: listOf(
|
|
1618
|
-
openapi: listOf(
|
|
1619
|
-
services: listOf(
|
|
1620
|
-
skills: listOf(
|
|
1621
|
-
triggers: listOf(
|
|
1622
|
-
datasetHosts: listOf(
|
|
1623
|
-
mcpServers: listOf(
|
|
1624
|
-
webhooks: listOf(
|
|
1625
|
-
collections: listOf(
|
|
1611
|
+
id: nameSchema,
|
|
1612
|
+
agents: listOf(manifestAgentSchema),
|
|
1613
|
+
tools: listOf(manifestToolSchema),
|
|
1614
|
+
openapi: listOf(manifestOpenApiSchema),
|
|
1615
|
+
services: listOf(manifestServiceSchema),
|
|
1616
|
+
skills: listOf(manifestSkillSchema),
|
|
1617
|
+
triggers: listOf(manifestTriggerSchema),
|
|
1618
|
+
datasetHosts: listOf(manifestDatasetHostSchema),
|
|
1619
|
+
mcpServers: listOf(manifestMcpServerSchema),
|
|
1620
|
+
webhooks: listOf(manifestWebhookSchema),
|
|
1621
|
+
collections: listOf(manifestCollectionSchema),
|
|
1626
1622
|
} satisfies Record<ManifestKind | "id" | "openapi", z.ZodType>);
|
|
1627
1623
|
/** A manifest as written: every field with a default may be left out. */
|
|
1628
1624
|
export type Manifest = z.input<typeof manifestSchema>;
|
|
@@ -1633,15 +1629,12 @@ export type Manifest = z.input<typeof manifestSchema>;
|
|
|
1633
1629
|
* beside it.
|
|
1634
1630
|
*/
|
|
1635
1631
|
export const resolvedManifestSchema = manifestSchema.extend({
|
|
1636
|
-
collections: listOf(
|
|
1632
|
+
collections: listOf(resolvedCollectionSchema),
|
|
1637
1633
|
});
|
|
1638
1634
|
|
|
1639
1635
|
const manifestKindSchema = z.enum(MANIFEST_KINDS);
|
|
1640
1636
|
|
|
1641
|
-
/**
|
|
1642
|
-
* Entry names per kind, each by its `MANIFEST_NAME_FIELDS` field. An OpenAPI
|
|
1643
|
-
* source's operations are counted among `tools`.
|
|
1644
|
-
*/
|
|
1637
|
+
/** Entry names per kind. An OpenAPI source's operations are counted among `tools`. */
|
|
1645
1638
|
const namesByKindSchema = z.record(manifestKindSchema, z.array(z.string()));
|
|
1646
1639
|
|
|
1647
1640
|
/** What an apply creates, updates and deletes, kind by kind; a preview answers the same without doing it. */
|
|
@@ -18,10 +18,6 @@ import { APPROVAL_TITLE_FIELD, LOCALES } from "../wire/index";
|
|
|
18
18
|
/** The run's end-user token, which Cortex forwards verbatim to a tool's backend, a dataset host or the agent's server. */
|
|
19
19
|
export const END_USER_TOKEN_HEADER = "X-End-User-Token";
|
|
20
20
|
|
|
21
|
-
/** The agent slug Cortex's routes name an agent by. */
|
|
22
|
-
export const AGENT_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;
|
|
23
|
-
export const agentSlugSchema = z.string().regex(AGENT_SLUG_PATTERN);
|
|
24
|
-
|
|
25
21
|
/** The longest end-user id Cortex stores. */
|
|
26
22
|
export const USER_ID_MAX_LENGTH = 255;
|
|
27
23
|
|
|
@@ -40,9 +36,35 @@ export const IDENTIFIER_MAX_LENGTH = 64;
|
|
|
40
36
|
|
|
41
37
|
export const identifierSchema = z.string().regex(IDENTIFIER_PATTERN).max(IDENTIFIER_MAX_LENGTH);
|
|
42
38
|
|
|
39
|
+
/**
|
|
40
|
+
* A kebab name: lowercase letters and digits in words joined by single
|
|
41
|
+
* hyphens. What the manifest id and every name Cortex holds must be, save a
|
|
42
|
+
* tool's, an MCP server's and a dataset host's, and what its routes name an
|
|
43
|
+
* agent by.
|
|
44
|
+
*/
|
|
45
|
+
export const NAME_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
46
|
+
|
|
47
|
+
/** The longest kebab name Cortex stores. */
|
|
48
|
+
export const NAME_MAX_LENGTH = 64;
|
|
49
|
+
|
|
50
|
+
export const nameSchema = z.string().regex(NAME_PATTERN).max(NAME_MAX_LENGTH);
|
|
51
|
+
|
|
43
52
|
/** A localized value's key. */
|
|
44
53
|
export const localeSchema = z.enum(LOCALES);
|
|
45
54
|
|
|
55
|
+
/** The longest display name Cortex stores, in any locale. */
|
|
56
|
+
export const DISPLAY_NAME_MAX_LENGTH = 120;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* What a console shows an entity as, per locale; a locale without one falls
|
|
60
|
+
* back as `localized` says, and an entity with none goes by its name. Each
|
|
61
|
+
* value is trimmed, and a blank one is dropped as absent.
|
|
62
|
+
*/
|
|
63
|
+
export const displayNameSchema = z
|
|
64
|
+
.partialRecord(localeSchema, z.string().trim().max(DISPLAY_NAME_MAX_LENGTH))
|
|
65
|
+
.transform((value) => Object.fromEntries(Object.entries(value).filter(([, text]) => text)))
|
|
66
|
+
.pipe(z.partialRecord(localeSchema, z.string()));
|
|
67
|
+
|
|
46
68
|
/** How many variables and tools a server's manifest declares at most, and how long each of their texts runs. */
|
|
47
69
|
export const SERVER_MANIFEST_LIMITS = {
|
|
48
70
|
variables: 100,
|
|
@@ -146,12 +168,14 @@ export type ServerVariable = z.infer<typeof serverVariableSchema>;
|
|
|
146
168
|
|
|
147
169
|
/**
|
|
148
170
|
* A tool the server runs. Its input is an object, the `input` its calls
|
|
149
|
-
* carry. `
|
|
150
|
-
*
|
|
151
|
-
*
|
|
171
|
+
* carry. `displayName` is what the console shows it as; the model only ever
|
|
172
|
+
* sees its name. `requiresApproval` parks every call for the end user's
|
|
173
|
+
* approval; such a tool's input cannot declare `APPROVAL_TITLE_FIELD`, which
|
|
174
|
+
* the model fills for the approval card.
|
|
152
175
|
*/
|
|
153
176
|
export const serverToolSchema = z.object({
|
|
154
177
|
name: declaredToolNameSchema,
|
|
178
|
+
displayName: displayNameSchema.optional(),
|
|
155
179
|
description: z.string().trim().min(1).max(SERVER_MANIFEST_LIMITS.description),
|
|
156
180
|
inputSchema: jsonSchemaSchema.refine(
|
|
157
181
|
(schema) => schema.type === "object",
|
|
@@ -179,7 +203,7 @@ export const EVENT_HEADER = "X-Cortex-Event";
|
|
|
179
203
|
*/
|
|
180
204
|
export const approvalRequestedEventSchema = z.object({
|
|
181
205
|
event: z.literal("approval_requested"),
|
|
182
|
-
|
|
206
|
+
agentName: nameSchema,
|
|
183
207
|
agentId: z.uuid(),
|
|
184
208
|
threadId: z.string(),
|
|
185
209
|
runId: z.string(),
|
|
@@ -285,7 +309,7 @@ export const serverManifestSchema = z.object({
|
|
|
285
309
|
export type ServerManifest = z.infer<typeof serverManifestSchema>;
|
|
286
310
|
|
|
287
311
|
/** The agent a call is for. */
|
|
288
|
-
const agentSchema = z.object({ id: z.uuid(),
|
|
312
|
+
const agentSchema = z.object({ id: z.uuid(), name: nameSchema });
|
|
289
313
|
|
|
290
314
|
/** The end user a run acts for, as their token was verified; null for a trigger's run. */
|
|
291
315
|
const serverUserSchema = z
|
|
@@ -6,7 +6,7 @@ import { TRIGGER_DROP_REASONS, TRIGGER_REASONS } from "../wire/index";
|
|
|
6
6
|
* Triggers: named entry points on an agent. A firing is one invocation with a
|
|
7
7
|
* payload and starts at most one run. The end user's message is the built-in
|
|
8
8
|
* `message` trigger; every other trigger is fired by a caller over
|
|
9
|
-
* `POST /agents/:
|
|
9
|
+
* `POST /agents/:agentName/triggers/:name` with the trigger's secret as its
|
|
10
10
|
* bearer. These are the shapes Cortex answers that caller with.
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -26,14 +26,14 @@ export const TRIGGER_OUTCOMES = [
|
|
|
26
26
|
] as const;
|
|
27
27
|
export type TriggerOutcome = (typeof TRIGGER_OUTCOMES)[number];
|
|
28
28
|
|
|
29
|
-
/** The engine route of agent `
|
|
30
|
-
export function agentRoute(
|
|
31
|
-
return `/agents/${encodeURIComponent(
|
|
29
|
+
/** The engine route of agent `agentName`, which the widget chats with and its triggers sit under. */
|
|
30
|
+
export function agentRoute(agentName: string) {
|
|
31
|
+
return `/agents/${encodeURIComponent(agentName)}`;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/** The engine route a trigger is fired at on agent `
|
|
35
|
-
export function triggerRoute(
|
|
36
|
-
return `${agentRoute(
|
|
34
|
+
/** The engine route a trigger is fired at on agent `agentName`; its firings are read and answered under it. */
|
|
35
|
+
export function triggerRoute(agentName: string, name: string) {
|
|
36
|
+
return `${agentRoute(agentName)}/triggers/${encodeURIComponent(name)}`;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/** One server tool call a parked run waits on; `POST .../firings/:id/answer` decides it. */
|
|
@@ -88,7 +88,7 @@ export const triggerFiringResponseSchema = z.discriminatedUnion("outcome", [
|
|
|
88
88
|
export type TriggerFiringResponse = z.infer<typeof triggerFiringResponseSchema>;
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
|
-
* `POST /agents/:
|
|
91
|
+
* `POST /agents/:agentName/triggers/:name/firings/:id/answer`: the decision on one parked call.
|
|
92
92
|
* A call id is as long as the model provider made it (some carry a signature), so only the
|
|
93
93
|
* request's body limit bounds it.
|
|
94
94
|
*/
|
|
@@ -98,7 +98,7 @@ export const triggerFiringAnswerSchema = z.object({
|
|
|
98
98
|
});
|
|
99
99
|
export type TriggerFiringAnswer = z.infer<typeof triggerFiringAnswerSchema>;
|
|
100
100
|
|
|
101
|
-
/** One row of `GET /agents/:
|
|
101
|
+
/** One row of `GET /agents/:agentName/triggers/:name/firings`, and the answer of `.../firings/:id`. */
|
|
102
102
|
export const triggerFiringRecordSchema = z.object({
|
|
103
103
|
id: z.uuid(),
|
|
104
104
|
triggerName: z.string(),
|
|
@@ -169,6 +169,8 @@ export type ClientToolBinding = {
|
|
|
169
169
|
* The widget hides UI for anything absent; Cortex enforces each.
|
|
170
170
|
*/
|
|
171
171
|
export type AgentCapabilities = {
|
|
172
|
+
/** The agent's display name per locale, which titles a new chat; `{}` for an agent without one. */
|
|
173
|
+
displayName: Partial<LocalizedText>;
|
|
172
174
|
attachments: boolean;
|
|
173
175
|
/**
|
|
174
176
|
* False when this caller may not send to the chat: the agent's `message`
|
|
@@ -214,6 +216,15 @@ export type Greeting = Record<Locale, string>;
|
|
|
214
216
|
/** Author-written copy the widget shows verbatim, one string per locale. */
|
|
215
217
|
export type LocalizedText = Record<Locale, string>;
|
|
216
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Resolves a per-locale value against `locale`: exact match, then English,
|
|
221
|
+
* then any entry, so a sparse map still renders something. An entity's
|
|
222
|
+
* label is `localized(entity.displayName, locale) ?? entity.name`.
|
|
223
|
+
*/
|
|
224
|
+
export function localized<T>(value: Partial<Record<Locale, T>>, locale: Locale) {
|
|
225
|
+
return value[locale] ?? value.en ?? Object.values(value)[0];
|
|
226
|
+
}
|
|
227
|
+
|
|
217
228
|
/**
|
|
218
229
|
* Cortex's own tools whose input carries `status`: the model's one-line
|
|
219
230
|
* statement of the call, which the widget draws in place of a generic label.
|
package/src/management/client.ts
CHANGED
|
@@ -166,6 +166,8 @@ export function inputSchemaOf(operation: OpenApiOperation) {
|
|
|
166
166
|
export function managementTarget(profile: { url: string; key: string }) {
|
|
167
167
|
return {
|
|
168
168
|
...profile,
|
|
169
|
+
// Every day range a command takes is this machine's days.
|
|
170
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
169
171
|
fetch: (url: string, init: RequestInit) =>
|
|
170
172
|
fetch(url, init).catch((error: unknown) => {
|
|
171
173
|
throw new UnreachableError(error);
|