@m6d/cortex-cli 0.12.0 → 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.
- package/README.md +135 -130
- package/package.json +16 -5
- package/src/cli.ts +17 -12
- package/src/commands/apply.ts +169 -0
- package/src/commands/{cc.ts → management.ts} +150 -100
- package/src/commands/new.ts +50 -74
- package/src/contracts/charts/chart-spec.ts +1 -1
- package/src/contracts/client-tools/index.ts +3 -3
- package/src/contracts/{runtime → datasets}/datasets.ts +7 -42
- package/src/contracts/datasets/index.ts +3 -0
- package/src/contracts/egress-policy/index.ts +16 -0
- package/src/contracts/{egress → egress-policy}/scrub.ts +34 -5
- package/src/contracts/{egress → egress-policy}/url.ts +22 -9
- package/src/contracts/errors/index.ts +38 -0
- package/src/contracts/json/index.ts +5 -0
- package/src/contracts/manifest/index.ts +932 -0
- package/src/contracts/manifest-file/index.ts +16 -0
- package/src/contracts/markdown/index.ts +1 -1
- package/src/contracts/pattern/index.ts +276 -0
- package/src/contracts/server/index.ts +247 -0
- package/src/contracts/server/reserved-names.fixtures.json +18 -0
- package/src/contracts/triggers/index.ts +112 -0
- package/src/contracts/wire/index.ts +61 -32
- package/src/{cc → management}/client.ts +19 -7
- package/src/{cc → management}/flags.ts +2 -1
- package/src/{cc → management}/profiles.ts +5 -5
- package/src/scaffold/files.ts +362 -237
- package/src/scaffold/ports.ts +13 -24
- package/src/scaffold/templates/datasets.ts.txt +52 -0
- package/src/scaffold/templates/dockerignore.txt +7 -0
- package/src/scaffold/templates/fallowrc.jsonc.txt +8 -12
- package/src/scaffold/templates/gitignore.txt +2 -1
- package/src/scaffold/templates/server.ts.txt +40 -0
- package/src/scaffold/templates/setup.ts.txt +52 -0
- package/src/ui/prompts.ts +4 -31
- package/src/ui/report.ts +5 -15
- package/tsconfig.json +2 -3
- package/src/commands/graph.ts +0 -228
- package/src/commands/swagger.ts +0 -247
- package/src/config/load.ts +0 -122
- package/src/config/validate.ts +0 -46
- package/src/contracts/credentials/index.ts +0 -183
- package/src/contracts/egress/index.ts +0 -120
- package/src/contracts/graph/clients/embed.ts +0 -45
- package/src/contracts/graph/clients/neo4j.ts +0 -85
- package/src/contracts/graph/helpers.ts +0 -55
- package/src/contracts/graph/index.ts +0 -30
- package/src/contracts/graph/schema.ts +0 -65
- package/src/contracts/graph/types.ts +0 -125
- package/src/contracts/rich-text/index.ts +0 -220
- package/src/contracts/runtime/api.ts +0 -345
- package/src/contracts/runtime/auth.ts +0 -41
- package/src/contracts/runtime/grammar.ts +0 -169
- package/src/contracts/runtime/index.ts +0 -15
- package/src/contracts/runtime/models.ts +0 -50
- package/src/contracts/runtime/signature.ts +0 -255
- package/src/contracts/runtime/sync.ts +0 -196
- package/src/contracts/runtime/triggers.ts +0 -267
- package/src/graph/expand-domains.ts +0 -286
- package/src/graph/generate-cypher.ts +0 -201
- package/src/graph/seed.ts +0 -199
- package/src/graph/validate.ts +0 -78
- package/src/scaffold/features.ts +0 -190
- package/src/scaffold/templates/domains-README.md.txt +0 -16
- package/src/scaffold/templates/index.ts.txt +0 -18
- package/src/swagger/extract-endpoints.ts +0 -269
- package/src/swagger/generated-block.ts +0 -74
- package/src/swagger/openapi-schema.ts +0 -168
- package/src/swagger/parse-response.ts +0 -88
- package/src/swagger/types.ts +0 -25
- /package/src/contracts/{runtime → datasets}/dataset-sql-guide.ts +0 -0
- /package/src/contracts/{runtime → datasets}/dataset-sql.ts +0 -0
- /package/src/contracts/{runtime → datasets}/datasets.fixtures.json +0 -0
- /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>;
|