@m6d/cortex-cli 0.12.1 → 0.13.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.
Files changed (74) hide show
  1. package/README.md +135 -130
  2. package/package.json +16 -5
  3. package/src/cli.ts +17 -12
  4. package/src/commands/apply.ts +169 -0
  5. package/src/commands/{cc.ts → management.ts} +150 -100
  6. package/src/commands/new.ts +50 -74
  7. package/src/contracts/charts/chart-spec.ts +1 -1
  8. package/src/contracts/client-tools/index.ts +3 -3
  9. package/src/contracts/{runtime → datasets}/datasets.ts +7 -42
  10. package/src/contracts/datasets/index.ts +3 -0
  11. package/src/contracts/egress-policy/index.ts +16 -0
  12. package/src/contracts/{egress → egress-policy}/scrub.ts +34 -5
  13. package/src/contracts/{egress → egress-policy}/url.ts +22 -9
  14. package/src/contracts/errors/index.ts +38 -0
  15. package/src/contracts/json/index.ts +5 -0
  16. package/src/contracts/manifest/index.ts +932 -0
  17. package/src/contracts/manifest-file/index.ts +16 -0
  18. package/src/contracts/markdown/index.ts +1 -1
  19. package/src/contracts/pattern/index.ts +276 -0
  20. package/src/contracts/server/index.ts +247 -0
  21. package/src/contracts/server/reserved-names.fixtures.json +18 -0
  22. package/src/contracts/triggers/index.ts +112 -0
  23. package/src/contracts/wire/index.ts +61 -32
  24. package/src/{cc → management}/client.ts +19 -7
  25. package/src/{cc → management}/flags.ts +2 -1
  26. package/src/{cc → management}/profiles.ts +5 -5
  27. package/src/scaffold/files.ts +362 -237
  28. package/src/scaffold/ports.ts +13 -24
  29. package/src/scaffold/templates/datasets.ts.txt +52 -0
  30. package/src/scaffold/templates/dockerignore.txt +7 -0
  31. package/src/scaffold/templates/fallowrc.jsonc.txt +8 -12
  32. package/src/scaffold/templates/gitignore.txt +2 -1
  33. package/src/scaffold/templates/server.ts.txt +40 -0
  34. package/src/scaffold/templates/setup.ts.txt +52 -0
  35. package/src/ui/prompts.ts +4 -31
  36. package/src/ui/report.ts +5 -15
  37. package/tsconfig.json +2 -3
  38. package/src/commands/graph.ts +0 -228
  39. package/src/commands/swagger.ts +0 -247
  40. package/src/config/load.ts +0 -122
  41. package/src/config/validate.ts +0 -46
  42. package/src/contracts/credentials/index.ts +0 -183
  43. package/src/contracts/egress/index.ts +0 -120
  44. package/src/contracts/graph/clients/embed.ts +0 -62
  45. package/src/contracts/graph/clients/neo4j.ts +0 -85
  46. package/src/contracts/graph/helpers.ts +0 -55
  47. package/src/contracts/graph/index.ts +0 -30
  48. package/src/contracts/graph/schema.ts +0 -65
  49. package/src/contracts/graph/types.ts +0 -125
  50. package/src/contracts/rich-text/index.ts +0 -220
  51. package/src/contracts/runtime/api.ts +0 -345
  52. package/src/contracts/runtime/auth.ts +0 -41
  53. package/src/contracts/runtime/grammar.ts +0 -169
  54. package/src/contracts/runtime/index.ts +0 -15
  55. package/src/contracts/runtime/models.ts +0 -50
  56. package/src/contracts/runtime/signature.ts +0 -255
  57. package/src/contracts/runtime/sync.ts +0 -196
  58. package/src/contracts/runtime/triggers.ts +0 -267
  59. package/src/graph/expand-domains.ts +0 -286
  60. package/src/graph/generate-cypher.ts +0 -201
  61. package/src/graph/seed.ts +0 -199
  62. package/src/graph/validate.ts +0 -78
  63. package/src/scaffold/features.ts +0 -190
  64. package/src/scaffold/templates/domains-README.md.txt +0 -16
  65. package/src/scaffold/templates/index.ts.txt +0 -18
  66. package/src/swagger/extract-endpoints.ts +0 -269
  67. package/src/swagger/generated-block.ts +0 -74
  68. package/src/swagger/openapi-schema.ts +0 -168
  69. package/src/swagger/parse-response.ts +0 -88
  70. package/src/swagger/types.ts +0 -25
  71. /package/src/contracts/{runtime → datasets}/dataset-sql-guide.ts +0 -0
  72. /package/src/contracts/{runtime → datasets}/dataset-sql.ts +0 -0
  73. /package/src/contracts/{runtime → datasets}/datasets.fixtures.json +0 -0
  74. /package/src/{cc → management}/output.ts +0 -0
@@ -1,220 +0,0 @@
1
- /*
2
- * The grammar and rendering pipeline for user-authored rich text that reaches
3
- * the LLM (agent prompts, service descriptions, skill bodies). Both sides of
4
- * the wire share it: the console edits and validates these strings, the
5
- * console's runtime API pre-renders them (mentions + heading demotion), and
6
- * `@m6d/cortex-server` applies per-request variable values. Three token
7
- * grammars live here:
8
- *
9
- * - Tool mentions `@[name](uuid)` and skill mentions `/[name](uuid)` — the
10
- * uuid is authoritative; the inline name is only a display cache refreshed
11
- * on save ({@link normalizeMentions}) and resolved live when text is
12
- * rendered for the LLM ({@link renderMentions}).
13
- * - Dataset mentions `#[name](name)` — a dataset's name is its identity, so
14
- * the token carries it twice and renders to it as is.
15
- * - Variables `{{name}}` — validated against a known list at authoring time
16
- * ({@link analyzeVariables}) and substituted per request on the server
17
- * ({@link substituteVariables}).
18
- */
19
-
20
- import { fenceTracker } from "../markdown";
21
-
22
- const UUID = String.raw`[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`;
23
-
24
- /** One tool mention token, unanchored and flagless — the editor's tokenizer anchors it, the helpers below add `g`. */
25
- export const MENTION_PATTERN_SOURCE = String.raw`@\[([^\]\n]*)\]\((${UUID})\)`;
26
- /** One skill mention token; `/` is the slash-command convention skills already carry elsewhere. */
27
- export const SKILL_MENTION_PATTERN_SOURCE = String.raw`/\[([^\]\n]*)\]\((${UUID})\)`;
28
-
29
- /** One dataset mention token; the id is the dataset's name, which is already its identity. */
30
- export const DATASET_MENTION_PATTERN_SOURCE = String.raw`#\[([^\]\n]*)\]\(([a-zA-Z][a-zA-Z0-9_]{0,63})\)`;
31
-
32
- const MENTION_PATTERN = new RegExp(MENTION_PATTERN_SOURCE, "g");
33
- const SKILL_MENTION_PATTERN = new RegExp(SKILL_MENTION_PATTERN_SOURCE, "g");
34
- const DATASET_MENTION_PATTERN = new RegExp(DATASET_MENTION_PATTERN_SOURCE, "g");
35
-
36
- /** `{{name}}` or `{{/json/pointer}}` with inner whitespace tolerated: what substitution and analysis match. */
37
- export const VARIABLE_PATTERN_SOURCE = String.raw`{{\s*([^{}]*?)\s*}}`;
38
- /** The subset an editor turns into a chip: a non-empty, whitespace-free name. */
39
- export const VARIABLE_TOKEN_SOURCE = String.raw`{{\s*([^{}\s]+)\s*}}`;
40
- const VARIABLE_PATTERN = new RegExp(VARIABLE_PATTERN_SOURCE, "g");
41
-
42
- /** Entity names by id for the two mention grammars; a missing map leaves that grammar's cached names in place. */
43
- export type MentionNames = {
44
- tools?: ReadonlyMap<string, string>;
45
- skills?: ReadonlyMap<string, string>;
46
- };
47
-
48
- const NO_NAMES: ReadonlyMap<string, string> = new Map();
49
-
50
- function extractIds(text: string, pattern: RegExp) {
51
- const ids: string[] = [];
52
- for (const match of text.matchAll(pattern)) {
53
- const id = match[2];
54
- if (id && !ids.includes(id)) ids.push(id);
55
- }
56
- return ids;
57
- }
58
-
59
- export function extractMentionedToolIds(text: string) {
60
- return extractIds(text, MENTION_PATTERN);
61
- }
62
-
63
- export function extractMentionedSkillIds(text: string) {
64
- return extractIds(text, SKILL_MENTION_PATTERN);
65
- }
66
-
67
- /** The dataset names a text mentions, in order of first appearance. */
68
- export function extractMentionedDatasets(text: string) {
69
- return extractIds(text, DATASET_MENTION_PATTERN);
70
- }
71
-
72
- /** Replaces each mention token with the entity's current name (falling back to the cached inline name). */
73
- export function renderMentions(text: string, names: MentionNames) {
74
- const tools = names.tools ?? NO_NAMES;
75
- const skills = names.skills ?? NO_NAMES;
76
- return text
77
- .replace(MENTION_PATTERN, (_, cached: string, id: string) => tools.get(id) ?? cached)
78
- .replace(SKILL_MENTION_PATTERN, (_, cached: string, id: string) => skills.get(id) ?? cached)
79
- .replace(DATASET_MENTION_PATTERN, (_, _cached: string, name: string) => name);
80
- }
81
-
82
- /** Refreshes stale display names inside stored text, keeping the tokens intact. */
83
- export function normalizeMentions(text: string, names: MentionNames) {
84
- const tools = names.tools ?? NO_NAMES;
85
- const skills = names.skills ?? NO_NAMES;
86
- return text
87
- .replace(MENTION_PATTERN, (token, _cached: string, id: string) => {
88
- const name = tools.get(id);
89
- return name === undefined ? token : `@[${name}](${id})`;
90
- })
91
- .replace(SKILL_MENTION_PATTERN, (token, _cached: string, id: string) => {
92
- const name = skills.get(id);
93
- return name === undefined ? token : `/[${name}](${id})`;
94
- });
95
- }
96
-
97
- /** Splits the `{{variables}}` referenced in a text into known and unknown ones. */
98
- export function analyzeVariables<Variable extends string>(
99
- text: string,
100
- known: ReadonlyArray<Variable>,
101
- ) {
102
- const referenced = new Set(
103
- Array.from(text.matchAll(VARIABLE_PATTERN), ([, name]) => name?.trim()),
104
- );
105
-
106
- return {
107
- variables: known.filter((variable) => referenced.has(variable)),
108
- unknownVariables: [...referenced].filter(
109
- (variable): variable is string =>
110
- variable !== undefined &&
111
- !known.some((knownVariable) => knownVariable === variable),
112
- ),
113
- };
114
- }
115
-
116
- /** Fills `{{variables}}` with per-request values; unknown names stay verbatim. */
117
- export function substituteVariables(text: string, values: Record<string, string>) {
118
- return text.replace(VARIABLE_PATTERN, (match, name: string) => values[name] ?? match);
119
- }
120
-
121
- /** ATX heading, CommonMark-style: up to three leading spaces, `#` run closed by space or line end. */
122
- const ATX_HEADING = /^( {0,3})(#{1,6})(?=[ \t]|$)/;
123
-
124
- /** Setext underline: a run of `=` or `-` alone on its line, up to three leading spaces. */
125
- const SETEXT_UNDERLINE = /^ {0,3}(=+|-+)[ \t]*$/;
126
-
127
- /** The skeleton owns `##` and `###`, so user headings start three levels below it. */
128
- function demotedHashes(level: number) {
129
- return "#".repeat(Math.min(level + 3, 6));
130
- }
131
-
132
- /**
133
- * Whether a line can be the text of a setext heading — i.e. it is an ordinary
134
- * paragraph line, not a blank, a fence, another heading, or the start of a
135
- * different block (quote/list), in which case the underline below it is a
136
- * thematic break or list content rather than a heading marker.
137
- */
138
- function isSetextText(line: string) {
139
- return (
140
- line.trim() !== "" &&
141
- !ATX_HEADING.test(line) &&
142
- !SETEXT_UNDERLINE.test(line) &&
143
- !/^ {0,3}(`{3,}|~{3,})/.test(line) &&
144
- !/^ {0,3}(>|[-*+][ \t]|\d+[.)][ \t])/.test(line)
145
- );
146
- }
147
-
148
- /**
149
- * Shifts user-authored headings down so they can never collide with the prompt
150
- * skeleton, which owns `##` (sections) and `###` (card titles): `#` becomes
151
- * `####`, deeper levels cap at `######`. Setext headings are rewritten as the
152
- * equivalent demoted ATX heading, since two-level setext cannot express the
153
- * shift. Fenced code is left alone.
154
- */
155
- export function demoteHeadings(text: string) {
156
- const lineState = fenceTracker();
157
- const output: string[] = [];
158
- for (const line of text.split("\n")) {
159
- if (lineState(line) !== "outside") {
160
- output.push(line);
161
- continue;
162
- }
163
-
164
- const underline = SETEXT_UNDERLINE.exec(line)?.[1];
165
- const previous = output[output.length - 1];
166
- if (underline !== undefined && previous !== undefined && isSetextText(previous)) {
167
- const level = underline.startsWith("=") ? 1 : 2;
168
- output[output.length - 1] = `${demotedHashes(level)} ${previous.trim()}`;
169
- continue;
170
- }
171
-
172
- output.push(
173
- line.replace(ATX_HEADING, (_, indent: string, hashes: string) => {
174
- return `${indent}${demotedHashes(hashes.length)}`;
175
- }),
176
- );
177
- }
178
- return output.join("\n");
179
- }
180
-
181
- /** The one render step for LLM-bound rich text: resolve mentions, then demote headings. */
182
- export function renderRichText(text: string, names: MentionNames) {
183
- return demoteHeadings(renderMentions(text, names));
184
- }
185
-
186
- /**
187
- * Reduces rich text to plain prose for embeddings: mentions become entity
188
- * names (cached inline names when no map is given) and markdown syntax is
189
- * dropped.
190
- * A regex stripper, not a markdown parser: nested emphasis or exotic
191
- * constructs may leave residue; swap in a real parser if retrieval quality
192
- * ever shows it.
193
- */
194
- export function stripToPlaintext(text: string, names: MentionNames = {}) {
195
- const lineState = fenceTracker();
196
- return renderMentions(text, names)
197
- .split("\n")
198
- .filter((line) => {
199
- const state = lineState(line);
200
- return state !== "open" && state !== "close";
201
- })
202
- .map((line) =>
203
- line
204
- .replace(/^ {0,3}#{1,6}[ \t]+/, "")
205
- .replace(SETEXT_UNDERLINE, "")
206
- .replace(/^\s{0,3}(>\s?)+/, "")
207
- .replace(/^(\s*)([-*+]|\d+[.)])\s+/, "$1")
208
- .replace(/^\s*([-*_]\s*){3,}$/, ""),
209
- )
210
- .join("\n")
211
- .replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1")
212
- .replace(/\[([^\]]*)\]\([^)]*\)/g, "$1")
213
- .replace(/(\*\*|__)([^*_]+)\1/g, "$2")
214
- .replace(/(^|\W)[*_]([^*_]+)[*_](?=\W|$)/gm, "$1$2")
215
- .replace(/~~([^~]+)~~/g, "$1")
216
- .replace(/`([^`]*)`/g, "$1")
217
- .replace(/\\([\\*_#[\]()>!`~+.-])/g, "$1")
218
- .replace(/\n{3,}/g, "\n\n")
219
- .trim();
220
- }
@@ -1,345 +0,0 @@
1
- import { z } from "zod";
2
- import {
3
- agentRefSchema,
4
- boundVariablesSchema,
5
- catalogVersionSchema,
6
- greetingSchema,
7
- IDENTIFIER_PATTERN,
8
- localeSchema,
9
- localizedTextSchema,
10
- mediaFormSchema,
11
- mentionedToolSchema,
12
- slugSchema,
13
- suggestionsSchema,
14
- toolSurfaceSchema,
15
- variableDeclarationSchema,
16
- } from "./grammar";
17
- import { publishedAuthSchema } from "./auth";
18
- import { runtimeModelsSchema } from "./models";
19
- import { triggerDeclarationSchema } from "./triggers";
20
- import { datasetHostDeclarationSchema, datasetIdentifierSchema } from "./datasets";
21
-
22
- /*
23
- * The cc runtime API (`/api/runtime/agents/:agentSlug/*`): the console
24
- * validates requests against these schemas and `@m6d/cortex-server` validates
25
- * the responses it gets back. Request types use `z.input` because the callers
26
- * build payloads the runtime's parser then fills defaults into.
27
- */
28
-
29
- export const RUNTIME_ERROR_KINDS = [
30
- "timeout",
31
- "upstream_4xx",
32
- "upstream_5xx",
33
- "schema_mismatch",
34
- "rate_limited",
35
- "not_found",
36
- "unauthorized",
37
- "invalid_request",
38
- "egress_blocked",
39
- "internal",
40
- ] as const;
41
- export const runtimeErrorKindSchema = z.enum(RUNTIME_ERROR_KINDS);
42
- export type RuntimeErrorKind = z.infer<typeof runtimeErrorKindSchema>;
43
-
44
- /** The HTTP status and default detail each error kind ships with; both sides rely on these exact values. */
45
- export const RUNTIME_ERRORS = {
46
- invalid_request: { status: 400, detail: "invalid request", retryable: false },
47
- unauthorized: { status: 401, detail: "unauthorized", retryable: false },
48
- egress_blocked: { status: 422, detail: "egress blocked", retryable: false },
49
- not_found: { status: 404, detail: "not found", retryable: false },
50
- rate_limited: { status: 429, detail: "rate limited", retryable: true },
51
- internal: { status: 500, detail: "internal error", retryable: true },
52
- upstream_4xx: { status: 502, detail: "upstream request failed", retryable: false },
53
- upstream_5xx: { status: 502, detail: "upstream request failed", retryable: true },
54
- schema_mismatch: {
55
- status: 502,
56
- detail: "upstream response did not match the schema",
57
- retryable: false,
58
- },
59
- timeout: { status: 504, detail: "request timed out", retryable: true },
60
- } as const satisfies Record<
61
- RuntimeErrorKind,
62
- { status: number; detail: string; retryable: boolean }
63
- >;
64
-
65
- export const runtimeErrorSchema = z.object({
66
- error: z.object({
67
- kind: runtimeErrorKindSchema,
68
- detail: z.string().max(512),
69
- retryable: z.boolean(),
70
- retryAfterSeconds: z.number().int().positive().optional(),
71
- upstreamStatus: z.number().int().min(100).max(599).optional(),
72
- }),
73
- });
74
-
75
- /** The ways an execute output may disagree with the tool's declared schema. */
76
- export const JSON_SCHEMA_ISSUE_KINDS = ["missing", "unexpected", "type_mismatch"] as const;
77
-
78
- export const sharedShapeSchema = z.object({
79
- ref: z.string().regex(/^\$R\d+$/),
80
- shape: z.string(),
81
- });
82
-
83
- export const knowledgeChunkSchema = z.object({
84
- chunkId: z.uuid(),
85
- text: z.string(),
86
- score: z.number(),
87
- citation: z.object({
88
- documentId: z.uuid(),
89
- documentTitle: z.string(),
90
- page: z.number().int().positive().nullable(),
91
- section: z.string().nullable(),
92
- uri: z.url().nullable(),
93
- service: z.string().nullable(),
94
- }),
95
- });
96
-
97
- /**
98
- * Present on signatures of `embedded` tools: flows the end user completes in
99
- * an embedded surface inside the chat widget. The tool's endpoint fields act
100
- * as the *initiate* call (returns the embed URL); the result the page reports
101
- * back is relayed as-is, verifying it is the integrating backend's job.
102
- */
103
- export const toolEmbedSchema = z.object({
104
- surface: toolSurfaceSchema,
105
- embedOrigin: z.url(),
106
- });
107
- export type ToolEmbed = z.infer<typeof toolEmbedSchema>;
108
-
109
- const toolSignatureBaseSchema = z.object({
110
- toolId: z.uuid(),
111
- name: z.string().regex(IDENTIFIER_PATTERN),
112
- version: z.number().int().positive(),
113
- readOnly: z.boolean(),
114
- /**
115
- * The call parks the run until the end user approves it. Declared to the
116
- * model as a function call like an embedded tool, never as a sandbox
117
- * binding, so the schema travels with it.
118
- */
119
- requiresApproval: z.boolean(),
120
- /** The action the widget's approval card names before an approval-gated call runs; only such tools carry one. */
121
- approvalMessage: localizedTextSchema.optional(),
122
- tags: z.array(z.string()),
123
- signature: z.string(),
124
- score: z.number().nullable(),
125
- pinned: z.boolean(),
126
- /** Variables the tool's input reads (`x-source` of kind `variable`); filled server-side, never seen by the model. */
127
- variables: boundVariablesSchema,
128
- });
129
-
130
- /**
131
- * A sandbox tool is described to the model by its rendered `signature`; a
132
- * client tool is declared to the model as a function call, which needs the
133
- * published input JSON Schema verbatim.
134
- */
135
- export const toolSignatureSchema = z.union([
136
- toolSignatureBaseSchema.extend({
137
- embed: z.undefined().optional(),
138
- inputSchema: z.record(z.string(), z.unknown()).optional(),
139
- }),
140
- toolSignatureBaseSchema.extend({
141
- embed: toolEmbedSchema,
142
- inputSchema: z.record(z.string(), z.unknown()),
143
- }),
144
- ]);
145
- export type ToolSignature = z.infer<typeof toolSignatureSchema>;
146
-
147
- /**
148
- * One always-listed skill line. The description is the whole trigger: the
149
- * model reads it every turn and calls `loadSkill` when it matches the task.
150
- */
151
- export const skillLineSchema = z.object({
152
- name: slugSchema,
153
- description: z.string().max(500),
154
- });
155
- export type SkillLine = z.infer<typeof skillLineSchema>;
156
-
157
- export const serviceCardSchema = z.object({
158
- serviceId: z.uuid(),
159
- key: slugSchema,
160
- name: z.string(),
161
- card: z.string(),
162
- score: z.number(),
163
- /** Skills scoped to this service; listed only while the card is in context. */
164
- skills: z.array(skillLineSchema),
165
- });
166
-
167
- /**
168
- * A skill body served on `loadSkill`: mentions rendered to current names and
169
- * headings demoted by the console, `{{variables}}` left for the server to
170
- * fill per request. `mentionedTools` are the sandbox bindings the body
171
- * refers to, so the turn can register them before the model acts on it.
172
- */
173
- export const skillBodyResponseSchema = z.object({
174
- name: z.string(),
175
- body: z.string(),
176
- mentionedTools: z.array(mentionedToolSchema),
177
- /** Datasets the body mentions; the server lists them under the body so the model can query them at once. */
178
- mentionedDatasets: z.array(datasetIdentifierSchema),
179
- catalogVersion: catalogVersionSchema,
180
- });
181
- export type SkillBodyResponse = z.infer<typeof skillBodyResponseSchema>;
182
-
183
- export const runtimeAgentConfigSchema = z.object({
184
- /**
185
- * End-user token verification for this agent, managed by the agent's
186
- * owner. Absent = the server's own auth default applies.
187
- */
188
- auth: publishedAuthSchema.optional(),
189
- systemPrompt: z.string(),
190
- /** Every declared variable the published prompt, skills, or triggers reference. */
191
- promptVariables: z.array(variableDeclarationSchema.shape.name),
192
- defaultLocale: localeSchema,
193
- /**
194
- * Whether end users may upload files to this agent. Operational config
195
- * like `auth`: read from the agent row, so toggling applies without
196
- * republishing. The runtime rejects new uploads when false; existing
197
- * attachments stay downloadable and deletable.
198
- */
199
- attachmentsEnabled: z.boolean(),
200
- /**
201
- * The media forms the agent may draw, which its prompt then describes.
202
- * Operational like `attachmentsEnabled`: toggling applies without
203
- * republishing.
204
- */
205
- media: z.array(mediaFormSchema),
206
- metaTools: z.object({
207
- searchKnowledge: z.boolean(),
208
- searchTools: z.boolean(),
209
- searchServices: z.boolean(),
210
- }),
211
- limits: z.object({
212
- resolveTokenBudget: z.number().int().positive(),
213
- executeTimeoutMs: z.number().int().positive(),
214
- maxResponseBytes: z.number().int().positive(),
215
- }),
216
- /** Unscoped published skills; service-scoped ones travel on their card. */
217
- skills: z.array(skillLineSchema),
218
- triggers: z.array(triggerDeclarationSchema),
219
- /** Published dataset hosts with the exposed definitions as of their pinned manifest version. */
220
- datasetHosts: z.array(datasetHostDeclarationSchema),
221
- /** Whether end users may message this agent; false makes it headless. */
222
- messageTrigger: z.boolean(),
223
- /** Operational like `messageTrigger`: edited on the agent row without republishing. */
224
- suggestions: suggestionsSchema,
225
- /** Operational like `suggestions`. */
226
- greeting: greetingSchema,
227
- /**
228
- * The model picks of the agent's server and of the agent, with the
229
- * providers they and the agent's triggers name. Operational like
230
- * `greeting`: edited without republishing.
231
- */
232
- models: runtimeModelsSchema,
233
- /** The resolved agent's id; reports about it carry this so they attach to it whatever the slug or alias later means. */
234
- agentRef: agentRefSchema,
235
- catalogVersion: catalogVersionSchema,
236
- publishedAt: z.iso.datetime(),
237
- });
238
- export type RuntimeAgentConfig = z.infer<typeof runtimeAgentConfigSchema>;
239
-
240
- const retrievalQuerySchema = z.string().trim().min(1).max(4000);
241
- const threadIdSchema = z.string().max(128);
242
-
243
- /** How many recent tool names one resolve request may carry. */
244
- export const RECENT_TOOL_NAMES_MAX = 20;
245
-
246
- export const resolveRequestSchema = z.object({
247
- query: retrievalQuerySchema,
248
- threadId: threadIdSchema,
249
- /** Sandbox tools the thread called recently; resolve ranks them ahead of cold ones. */
250
- recentToolNames: z.array(z.string()).max(RECENT_TOOL_NAMES_MAX),
251
- limits: z
252
- .object({
253
- knowledge: z.number().int().min(0).max(20),
254
- tools: z.number().int().min(0).max(20),
255
- services: z.number().int().min(0).max(20),
256
- datasets: z.number().int().min(0).max(20).default(6),
257
- })
258
- .prefault({ knowledge: 6, tools: 8, services: 5, datasets: 6 }),
259
- });
260
- export type ResolveRequest = z.input<typeof resolveRequestSchema>;
261
-
262
- /** A dataset the turn should list: ranked in by the question, or pinned by the author. */
263
- export const resolvedDatasetSchema = z.object({
264
- name: datasetIdentifierSchema,
265
- score: z.number().nullable(),
266
- pinned: z.boolean(),
267
- });
268
-
269
- export type ResolvedDataset = z.infer<typeof resolvedDatasetSchema>;
270
-
271
- export const resolveResponseSchema = z.object({
272
- resolveId: z.uuid(),
273
- catalogVersion: catalogVersionSchema,
274
- locale: localeSchema,
275
- knowledge: z.array(knowledgeChunkSchema),
276
- tools: z.array(toolSignatureSchema),
277
- services: z.array(serviceCardSchema),
278
- /** One line each in the turn context; the definitions come from the config's dataset hosts. */
279
- datasets: z.array(resolvedDatasetSchema),
280
- sharedShapes: z.array(sharedShapeSchema),
281
- budget: z.object({
282
- requested: z.number().int(),
283
- used: z.number().int(),
284
- truncated: z.boolean(),
285
- }),
286
- });
287
- export type ResolveResponse = z.infer<typeof resolveResponseSchema>;
288
-
289
- export const searchRequestSchema = z.object({
290
- query: retrievalQuerySchema,
291
- limit: z.number().int().min(1).max(20).default(5),
292
- locale: localeSchema.optional(),
293
- threadId: threadIdSchema.optional(),
294
- service: slugSchema.optional(),
295
- });
296
- export type SearchRequest = z.input<typeof searchRequestSchema>;
297
-
298
- export const searchToolsResponseSchema = z.object({
299
- catalogVersion: catalogVersionSchema,
300
- tools: z.array(toolSignatureSchema),
301
- sharedShapes: z.array(sharedShapeSchema),
302
- });
303
-
304
- export const searchServicesResponseSchema = z.object({
305
- catalogVersion: catalogVersionSchema,
306
- services: z.array(serviceCardSchema),
307
- tools: z.array(toolSignatureSchema),
308
- sharedShapes: z.array(sharedShapeSchema),
309
- });
310
-
311
- export const searchKnowledgeResponseSchema = z.object({
312
- catalogVersion: catalogVersionSchema,
313
- knowledge: z.array(knowledgeChunkSchema),
314
- });
315
-
316
- export const executeRequestSchema = z.object({
317
- input: z.record(z.string(), z.unknown()),
318
- context: z.object({
319
- threadId: threadIdSchema,
320
- userId: z.string().max(128),
321
- /** Values of the variables the tool's input reads; only those names travel. */
322
- variables: z.record(z.string(), z.unknown()),
323
- }),
324
- });
325
- export type ExecuteRequest = z.input<typeof executeRequestSchema>;
326
-
327
- export const executeResponseSchema = z.object({
328
- toolId: z.uuid(),
329
- name: z.string(),
330
- version: z.number().int().positive(),
331
- output: z.unknown(),
332
- meta: z.object({
333
- upstreamStatus: z.number().int().min(100).max(599),
334
- durationMs: z.number().int().min(0),
335
- replayed: z.boolean(),
336
- truncated: z.boolean(),
337
- truncatedPaths: z.array(z.string()),
338
- drift: z.array(
339
- z.object({
340
- path: z.string(),
341
- kind: z.enum(JSON_SCHEMA_ISSUE_KINDS),
342
- }),
343
- ),
344
- }),
345
- });
@@ -1,41 +0,0 @@
1
- import { z } from "zod";
2
-
3
- /*
4
- * How a server, or one agent on it, verifies the end user's token. One union
5
- * for every reader: a code config, an agent the Control Center created, and
6
- * the runtime config cc publishes. A `custom` verifier exists in code only,
7
- * so it is not part of this contract.
8
- */
9
-
10
- export const AUTH_JWT_SECRET_ALGORITHMS = ["HS256", "HS384", "HS512"] as const;
11
-
12
- const issuerSchema = z.string().trim().min(1).max(2048);
13
- /** When set, the token's `aud` must contain it, so a token minted for another audience under the same issuer cannot be replayed here. */
14
- const audienceSchema = z.string().trim().min(1).max(2048).optional();
15
-
16
- /** Asymmetric tokens: the issuer publishes its public keys at the JWKS URI. */
17
- export const jwksAuthSchema = z.object({
18
- kind: z.literal("jwks"),
19
- jwksUri: z.url(),
20
- issuer: issuerSchema,
21
- audience: audienceSchema,
22
- });
23
-
24
- /**
25
- * Symmetric tokens: the issuer and this server share the secret. At least
26
- * 32 characters, the JOSE minimum for HMAC keys; the algorithms are pinned
27
- * so a token signed another way is refused before the key is tried.
28
- */
29
- export const jwtSecretAuthSchema = z.object({
30
- kind: z.literal("jwt-secret"),
31
- secret: z.string().min(32),
32
- algorithms: z.array(z.enum(AUTH_JWT_SECRET_ALGORITHMS)).min(1),
33
- issuer: issuerSchema,
34
- audience: audienceSchema,
35
- });
36
-
37
- export const publishedAuthSchema = z.discriminatedUnion("kind", [
38
- jwksAuthSchema,
39
- jwtSecretAuthSchema,
40
- ]);
41
- export type PublishedAuth = z.infer<typeof publishedAuthSchema>;