@sequenceholdings/agent-spec 0.1.2 → 0.1.4
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 +152 -0
- package/dist/agent-definition.d.ts +626 -0
- package/dist/agent-definition.js +250 -0
- package/dist/canonical-json.d.ts +2 -0
- package/dist/canonical-json.js +13 -0
- package/dist/client-proposals.d.ts +20 -0
- package/dist/client-proposals.js +172 -0
- package/dist/index.d.ts +4 -116
- package/dist/index.js +4 -247
- package/dist/input-schema.d.ts +19 -0
- package/dist/input-schema.js +57 -0
- package/dist/job-request-hash.d.ts +27 -0
- package/dist/job-request-hash.js +54 -0
- package/dist/voice-access.d.ts +40 -0
- package/dist/voice-access.js +34 -0
- package/dist/voice-actions-contracts.d.ts +423 -0
- package/dist/voice-actions-contracts.js +95 -0
- package/dist/voice-actions-transitions.d.ts +52 -0
- package/dist/voice-actions-transitions.js +170 -0
- package/dist/voice-actions.d.ts +2 -0
- package/dist/voice-actions.js +2 -0
- package/dist/voice-api.d.ts +1102 -0
- package/dist/voice-api.js +164 -0
- package/dist/voice-authoring.d.ts +463 -0
- package/dist/voice-authoring.js +132 -0
- package/dist/voice-authority-contracts.d.ts +247 -0
- package/dist/voice-authority-contracts.js +177 -0
- package/dist/voice-authority-evaluation.d.ts +29 -0
- package/dist/voice-authority-evaluation.js +80 -0
- package/dist/voice-authority-verification.d.ts +47 -0
- package/dist/voice-authority-verification.js +184 -0
- package/dist/voice-authority.d.ts +3 -0
- package/dist/voice-authority.js +3 -0
- package/dist/voice-common.d.ts +21 -0
- package/dist/voice-common.js +23 -0
- package/dist/voice-drafts.d.ts +441 -0
- package/dist/voice-drafts.js +75 -0
- package/dist/voice-grant.d.ts +19 -0
- package/dist/voice-grant.js +21 -0
- package/dist/voice-lifecycle-control.d.ts +130 -0
- package/dist/voice-lifecycle-control.js +138 -0
- package/dist/voice-lifecycle-session.d.ts +168 -0
- package/dist/voice-lifecycle-session.js +114 -0
- package/dist/voice-lifecycle-states.d.ts +96 -0
- package/dist/voice-lifecycle-states.js +139 -0
- package/dist/voice-lifecycle.d.ts +3 -0
- package/dist/voice-lifecycle.js +3 -0
- package/dist/voice-policy-duration.d.ts +46 -0
- package/dist/voice-policy-duration.js +166 -0
- package/dist/voice-policy-limits.d.ts +174 -0
- package/dist/voice-policy-limits.js +188 -0
- package/dist/voice-policy.d.ts +2 -0
- package/dist/voice-policy.js +2 -0
- package/dist/voice-provider-evidence.d.ts +80 -0
- package/dist/voice-provider-evidence.js +115 -0
- package/dist/voice-provider-publication.d.ts +319 -0
- package/dist/voice-provider-publication.js +117 -0
- package/dist/voice-provider-release.d.ts +386 -0
- package/dist/voice-provider-release.js +154 -0
- package/dist/voice-provider.d.ts +3 -0
- package/dist/voice-provider.js +3 -0
- package/dist/voice-release.d.ts +808 -0
- package/dist/voice-release.js +110 -0
- package/dist/voice-runs-audio.d.ts +157 -0
- package/dist/voice-runs-audio.js +85 -0
- package/dist/voice-runs-evidence.d.ts +183 -0
- package/dist/voice-runs-evidence.js +154 -0
- package/dist/voice-runs-model.d.ts +967 -0
- package/dist/voice-runs-model.js +199 -0
- package/dist/voice-runs-tooling.d.ts +88 -0
- package/dist/voice-runs-tooling.js +100 -0
- package/dist/voice-runs.d.ts +4 -0
- package/dist/voice-runs.js +4 -0
- package/dist/voice.d.ts +13 -0
- package/dist/voice.js +13 -0
- package/package.json +8 -3
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { inputSchemaSchema } from './input-schema.js';
|
|
4
|
+
import { canonicalJson, sortJson } from './canonical-json.js';
|
|
5
|
+
import { voiceDeclarationSchema } from './voice-authoring.js';
|
|
6
|
+
import { clientProposalsSchema } from './client-proposals.js';
|
|
7
|
+
export { canonicalJson } from './canonical-json.js';
|
|
8
|
+
export { voiceDeclarationSchema } from './voice-authoring.js';
|
|
9
|
+
const OBJECT_ID_PATTERN = /^[0-9a-f]{24}$/;
|
|
10
|
+
const IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
11
|
+
const DEFAULT_MODEL = {
|
|
12
|
+
provider: 'anthropic',
|
|
13
|
+
name: 'claude-sonnet-4-5-20250929',
|
|
14
|
+
};
|
|
15
|
+
const jsonObjectSchema = z.record(z.string(), z.unknown());
|
|
16
|
+
const unsupportedConstraints = {
|
|
17
|
+
integer: ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum'],
|
|
18
|
+
number: ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum'],
|
|
19
|
+
array: ['minItems', 'maxItems', 'uniqueItems'],
|
|
20
|
+
string: ['minLength', 'maxLength', 'format', 'pattern'],
|
|
21
|
+
};
|
|
22
|
+
export function collectUnsupportedConstraints({ schema, path = '', }) {
|
|
23
|
+
if (schema === null || typeof schema !== 'object')
|
|
24
|
+
return [];
|
|
25
|
+
const object = schema;
|
|
26
|
+
const type = typeof object.type === 'string' ? object.type : undefined;
|
|
27
|
+
const violations = [];
|
|
28
|
+
if (type !== undefined) {
|
|
29
|
+
for (const keyword of unsupportedConstraints[type] ?? []) {
|
|
30
|
+
if (keyword in object) {
|
|
31
|
+
violations.push({ path: path || '(root)', type, keyword });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (type === 'object' &&
|
|
36
|
+
object.properties !== null &&
|
|
37
|
+
typeof object.properties === 'object') {
|
|
38
|
+
for (const [key, value] of Object.entries(object.properties)) {
|
|
39
|
+
violations.push(...collectUnsupportedConstraints({
|
|
40
|
+
schema: value,
|
|
41
|
+
path: `${path}.properties.${key}`,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (type === 'array' && object.items !== undefined) {
|
|
46
|
+
violations.push(...collectUnsupportedConstraints({
|
|
47
|
+
schema: object.items,
|
|
48
|
+
path: `${path}.items`,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
for (const combinator of ['anyOf', 'oneOf', 'allOf', 'prefixItems']) {
|
|
52
|
+
const entries = object[combinator];
|
|
53
|
+
if (!Array.isArray(entries))
|
|
54
|
+
continue;
|
|
55
|
+
entries.forEach((entry, index) => {
|
|
56
|
+
violations.push(...collectUnsupportedConstraints({
|
|
57
|
+
schema: entry,
|
|
58
|
+
path: `${path}.${combinator}[${index}]`,
|
|
59
|
+
}));
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return violations;
|
|
63
|
+
}
|
|
64
|
+
const modelSchema = z.object({
|
|
65
|
+
provider: z.string().min(1),
|
|
66
|
+
name: z.string().min(1),
|
|
67
|
+
});
|
|
68
|
+
export const AGENT_PERMISSION_ROLES = [
|
|
69
|
+
'owner',
|
|
70
|
+
'editor',
|
|
71
|
+
'invoker',
|
|
72
|
+
'outputs_viewer',
|
|
73
|
+
];
|
|
74
|
+
const permissionGrantSchema = z.object({
|
|
75
|
+
type: z.enum(['user', 'group']),
|
|
76
|
+
id: z.string().min(1),
|
|
77
|
+
role: z.enum(AGENT_PERMISSION_ROLES),
|
|
78
|
+
});
|
|
79
|
+
const subagentSchema = z.union([
|
|
80
|
+
z.string().min(1),
|
|
81
|
+
z.object({
|
|
82
|
+
id: z.string().min(1),
|
|
83
|
+
forkContext: z.boolean().optional(),
|
|
84
|
+
}),
|
|
85
|
+
]);
|
|
86
|
+
export const AGENT_REFERENCE_KINDS = ['mcp_server', 'skill', 'subagent', 'managed_function'];
|
|
87
|
+
export const agentReferenceSchema = z.strictObject({
|
|
88
|
+
kind: z.enum(AGENT_REFERENCE_KINDS),
|
|
89
|
+
id: z.string().min(1),
|
|
90
|
+
});
|
|
91
|
+
const openaiCodeInterpreterSchema = z.preprocess((value) => {
|
|
92
|
+
if (value === true)
|
|
93
|
+
return { enabled: true };
|
|
94
|
+
if (value === false)
|
|
95
|
+
return { enabled: false };
|
|
96
|
+
return value;
|
|
97
|
+
}, z.object({
|
|
98
|
+
enabled: z.boolean(),
|
|
99
|
+
containerId: z.string().min(1).optional(),
|
|
100
|
+
fileIds: z.array(z.string().min(1)).optional(),
|
|
101
|
+
}));
|
|
102
|
+
const textDefinitionBaseSchema = z
|
|
103
|
+
.strictObject({
|
|
104
|
+
id: z
|
|
105
|
+
.string()
|
|
106
|
+
.regex(OBJECT_ID_PATTERN, 'id must be a 24-character lowercase hex ObjectId'),
|
|
107
|
+
name: z.string().min(1),
|
|
108
|
+
description: z.string().optional(),
|
|
109
|
+
framework: z.enum(['vercel', 'claude-agent']).optional(),
|
|
110
|
+
vmBackend: z.enum(['gce', 'e2b']).optional(),
|
|
111
|
+
idleTimeoutSeconds: z.number().int().min(60).max(86_400).optional(),
|
|
112
|
+
agentCapabilities: jsonObjectSchema.nullish(),
|
|
113
|
+
claudeToolSearch: z.enum(['enabled', 'disabled', 'auto']).optional(),
|
|
114
|
+
claudeBuiltInTools: z.array(z.string()).default([]),
|
|
115
|
+
openaiCodeInterpreter: openaiCodeInterpreterSchema.nullish(),
|
|
116
|
+
model: modelSchema.default(DEFAULT_MODEL),
|
|
117
|
+
thinking: jsonObjectSchema.nullish(),
|
|
118
|
+
reasoningEffort: z.string().nullish(),
|
|
119
|
+
temperature: z.number().min(0).max(1).nullish(),
|
|
120
|
+
systemPrompt: z.string().optional(),
|
|
121
|
+
tools: z.array(z.string()).default([]),
|
|
122
|
+
clientProposals: clientProposalsSchema.optional(),
|
|
123
|
+
mcpServers: z.array(z.string()).default([]),
|
|
124
|
+
requiredMcpServers: z.array(z.string()).default([]),
|
|
125
|
+
requiredReferences: z.array(agentReferenceSchema).optional(),
|
|
126
|
+
managedFunctions: z.array(z.string().min(1)).optional(),
|
|
127
|
+
excludedMcpTools: z.array(z.string()).default([]),
|
|
128
|
+
hideIntermediateText: z.boolean().optional(),
|
|
129
|
+
middleware: z.array(z.string()).default([]),
|
|
130
|
+
subagents: z.array(subagentSchema).default([]),
|
|
131
|
+
skills: z.array(z.string()).default([]),
|
|
132
|
+
contextFileIds: z.array(z.string()).default([]),
|
|
133
|
+
structuredOutput: jsonObjectSchema.optional(),
|
|
134
|
+
inputSchema: inputSchemaSchema.nullish(),
|
|
135
|
+
promptTemplate: z.string().optional(),
|
|
136
|
+
webhooks: jsonObjectSchema.optional(),
|
|
137
|
+
mcpServerConfigs: jsonObjectSchema.optional(),
|
|
138
|
+
stubConfig: jsonObjectSchema.nullish(),
|
|
139
|
+
maxTtlSeconds: z.number().int().positive().optional(),
|
|
140
|
+
agentPermissions: z.array(jsonObjectSchema).optional(),
|
|
141
|
+
apiPermissions: z.array(z.string()).optional(),
|
|
142
|
+
managedBy: z.literal('automation').nullish(),
|
|
143
|
+
isPublic: z.boolean().optional(),
|
|
144
|
+
publicOutputs: z.boolean().optional(),
|
|
145
|
+
permissions: z.array(permissionGrantSchema).optional(),
|
|
146
|
+
});
|
|
147
|
+
export const textAgentDefinitionSchema = textDefinitionBaseSchema.safeExtend({
|
|
148
|
+
mode: z.literal('text').optional(),
|
|
149
|
+
voice: z.never().optional(),
|
|
150
|
+
}).superRefine((definition, context) => {
|
|
151
|
+
if (definition.framework === 'claude-agent' && (definition.clientProposals?.length ?? 0) > 0) {
|
|
152
|
+
context.addIssue({ code: 'custom', path: ['clientProposals'], message: 'Client proposals require the Vercel tool runtime' });
|
|
153
|
+
}
|
|
154
|
+
for (const [index, proposal] of (definition.clientProposals ?? []).entries()) {
|
|
155
|
+
if (definition.tools.includes(proposal.name)) {
|
|
156
|
+
context.addIssue({ code: 'custom', path: ['clientProposals', index, 'name'], message: 'Client proposal name collides with a selected built-in tool' });
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
for (const [index, serverId] of definition.requiredMcpServers.entries()) {
|
|
160
|
+
if (definition.mcpServers.includes(serverId))
|
|
161
|
+
continue;
|
|
162
|
+
context.addIssue({
|
|
163
|
+
code: 'custom',
|
|
164
|
+
path: ['requiredMcpServers', index],
|
|
165
|
+
message: `requiredMcpServers entry "${serverId}" must also appear in mcpServers`,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const declaredByKind = {
|
|
169
|
+
managed_function: new Set(definition.managedFunctions ?? []),
|
|
170
|
+
mcp_server: new Set(definition.mcpServers),
|
|
171
|
+
skill: new Set(definition.skills),
|
|
172
|
+
subagent: new Set(definition.subagents.map((entry) => typeof entry === 'string' ? entry : entry.id)),
|
|
173
|
+
};
|
|
174
|
+
for (const [index, reference] of (definition.requiredReferences ?? []).entries()) {
|
|
175
|
+
if (declaredByKind[reference.kind].has(reference.id))
|
|
176
|
+
continue;
|
|
177
|
+
context.addIssue({
|
|
178
|
+
code: 'custom',
|
|
179
|
+
path: ['requiredReferences', index],
|
|
180
|
+
message: `requiredReferences entry ${reference.kind} "${reference.id}" must also be declared on the agent`,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
if (definition.structuredOutput?.enabled === true) {
|
|
184
|
+
const schema = definition.structuredOutput.schema;
|
|
185
|
+
for (const violation of collectUnsupportedConstraints({ schema })) {
|
|
186
|
+
context.addIssue({
|
|
187
|
+
code: 'custom',
|
|
188
|
+
path: ['structuredOutput', 'schema'],
|
|
189
|
+
message: `${violation.path}: "${violation.keyword}" on ${violation.type} is not supported`,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
export const voiceAgentDefinitionSchema = textDefinitionBaseSchema.pick({
|
|
195
|
+
id: true, name: true, description: true, permissions: true,
|
|
196
|
+
}).extend({
|
|
197
|
+
mode: z.literal('voice'),
|
|
198
|
+
model: z.strictObject({ provider: z.string().trim().min(1).max(200), name: z.string().trim().min(1).max(200) }),
|
|
199
|
+
systemPrompt: z.string().min(1).max(100_000).refine((text) => text.trim().length > 0, 'Prompt must contain text'),
|
|
200
|
+
skills: z.array(z.string().trim().min(1).max(200)).max(32).default([])
|
|
201
|
+
.refine((ids) => new Set(ids).size === ids.length, 'Duplicate native Skill reference')
|
|
202
|
+
.transform((ids) => [...ids].sort()),
|
|
203
|
+
voice: voiceDeclarationSchema,
|
|
204
|
+
isPublic: z.literal(false).optional(),
|
|
205
|
+
publicOutputs: z.literal(false).optional(),
|
|
206
|
+
}).superRefine((definition, context) => {
|
|
207
|
+
const captured = new Set(definition.voice.procedures.map((item) => item.source.skillId));
|
|
208
|
+
if (definition.skills.length !== captured.size || definition.skills.some((id) => !captured.has(id))) {
|
|
209
|
+
context.addIssue({ code: 'custom', path: ['skills'], message: 'Native Skill references must match captured Voice procedures' });
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
export const agentDefinitionSchema = z.discriminatedUnion('mode', [
|
|
213
|
+
textAgentDefinitionSchema, voiceAgentDefinitionSchema,
|
|
214
|
+
]);
|
|
215
|
+
export function defineAgent(input) {
|
|
216
|
+
return agentDefinitionSchema.parse(input);
|
|
217
|
+
}
|
|
218
|
+
export function getAgentMode({ definition }) {
|
|
219
|
+
return definition.mode ?? 'text';
|
|
220
|
+
}
|
|
221
|
+
export function validateAgentModeUpdate({ existing, incoming }) {
|
|
222
|
+
const parsed = agentDefinitionSchema.parse(incoming);
|
|
223
|
+
if (existing.id !== parsed.id)
|
|
224
|
+
throw new Error('Agent identity cannot change across versions');
|
|
225
|
+
if (getAgentMode({ definition: existing }) !== getAgentMode({ definition: parsed })) {
|
|
226
|
+
throw new Error('Agent mode cannot change across versions');
|
|
227
|
+
}
|
|
228
|
+
return parsed;
|
|
229
|
+
}
|
|
230
|
+
export function canonicalAgentJson(definition) {
|
|
231
|
+
return canonicalJson(definition);
|
|
232
|
+
}
|
|
233
|
+
export function hashAgentBundle({ definitions, }) {
|
|
234
|
+
const canonical = [...definitions]
|
|
235
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
236
|
+
.map(canonicalAgentJson)
|
|
237
|
+
.join('\n');
|
|
238
|
+
return createHash('sha256').update('sequence-agent-bundle-v1\0').update(canonical).digest('hex');
|
|
239
|
+
}
|
|
240
|
+
export function formatAgentSource({ definition, exportName = 'agent', }) {
|
|
241
|
+
if (!IDENTIFIER_PATTERN.test(exportName)) {
|
|
242
|
+
throw new Error('exportName must be a valid TypeScript identifier');
|
|
243
|
+
}
|
|
244
|
+
const object = JSON.stringify(sortJson(definition), null, 2);
|
|
245
|
+
return ("import { defineAgent } from '@sequenceholdings/agent-spec'\n\n" +
|
|
246
|
+
`export const ${exportName} = defineAgent(${object})\n`);
|
|
247
|
+
}
|
|
248
|
+
export function generateAgentSource({ definition, exportName = 'agent', }) {
|
|
249
|
+
return formatAgentSource({ definition: defineAgent(definition), exportName });
|
|
250
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function sortJson(value) {
|
|
2
|
+
if (Array.isArray(value))
|
|
3
|
+
return value.map(sortJson);
|
|
4
|
+
if (value === null || typeof value !== 'object')
|
|
5
|
+
return value;
|
|
6
|
+
return Object.fromEntries(Object.entries(value)
|
|
7
|
+
.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
|
|
8
|
+
.filter(([, nested]) => nested !== undefined)
|
|
9
|
+
.map(([key, nested]) => [key, sortJson(nested)]));
|
|
10
|
+
}
|
|
11
|
+
export function canonicalJson(value) {
|
|
12
|
+
return JSON.stringify(sortJson(value));
|
|
13
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const CLIENT_PROPOSAL_LIMITS: {
|
|
3
|
+
readonly count: 64;
|
|
4
|
+
readonly descriptionLength: 32000;
|
|
5
|
+
readonly schemaBytes: number;
|
|
6
|
+
readonly totalBytes: number;
|
|
7
|
+
readonly schemaDepth: 32;
|
|
8
|
+
readonly schemaNodes: 10000;
|
|
9
|
+
};
|
|
10
|
+
export declare const clientProposalSchema: z.ZodObject<{
|
|
11
|
+
name: z.ZodString;
|
|
12
|
+
description: z.ZodString;
|
|
13
|
+
inputSchema: z.ZodPipe<z.ZodUnknown, z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
export type ClientProposal = z.infer<typeof clientProposalSchema>;
|
|
16
|
+
export declare const clientProposalsSchema: z.ZodArray<z.ZodObject<{
|
|
17
|
+
name: z.ZodString;
|
|
18
|
+
description: z.ZodString;
|
|
19
|
+
inputSchema: z.ZodPipe<z.ZodUnknown, z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
20
|
+
}, z.core.$strict>>;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const CLIENT_PROPOSAL_LIMITS = {
|
|
3
|
+
count: 64, descriptionLength: 32_000, schemaBytes: 128 * 1024,
|
|
4
|
+
totalBytes: 512 * 1024, schemaDepth: 32, schemaNodes: 10_000,
|
|
5
|
+
};
|
|
6
|
+
const TYPES = new Set(['object', 'array', 'string', 'number', 'integer', 'boolean', 'null']);
|
|
7
|
+
const RESERVED_NAMES = new Set(['__proto__', 'constructor', 'prototype']);
|
|
8
|
+
const ANNOTATIONS = new Set(['$schema', 'title', 'description']);
|
|
9
|
+
const NUMBER_KEYS = new Set(['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum']);
|
|
10
|
+
const SIZE_KEYS = new Set(['minLength', 'maxLength', 'minItems', 'maxItems']);
|
|
11
|
+
const CHILD_KEYS = new Set(['items', 'additionalProperties', 'propertyNames']);
|
|
12
|
+
const UNION_KEYS = new Set(['anyOf', 'oneOf', 'allOf']);
|
|
13
|
+
const KEYWORDS = new Set([...ANNOTATIONS, ...NUMBER_KEYS, ...SIZE_KEYS, ...CHILD_KEYS, ...UNION_KEYS,
|
|
14
|
+
'type', 'properties', 'required', 'enum', 'const']);
|
|
15
|
+
function isObject(value) {
|
|
16
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
17
|
+
}
|
|
18
|
+
function requireBoundedJson(value) {
|
|
19
|
+
const seen = new Set();
|
|
20
|
+
const pending = [{ value, depth: 0 }];
|
|
21
|
+
let nodes = 0;
|
|
22
|
+
while (pending.length > 0) {
|
|
23
|
+
const item = pending.pop();
|
|
24
|
+
if (!item)
|
|
25
|
+
break;
|
|
26
|
+
if (++nodes > CLIENT_PROPOSAL_LIMITS.schemaNodes || item.depth > CLIENT_PROPOSAL_LIMITS.schemaDepth) {
|
|
27
|
+
throw new Error('Client proposal schema exceeds its depth or node limit');
|
|
28
|
+
}
|
|
29
|
+
const current = item.value;
|
|
30
|
+
if (typeof current === 'string' && Buffer.byteLength(current) > CLIENT_PROPOSAL_LIMITS.schemaBytes) {
|
|
31
|
+
throw new Error('Client proposal schema exceeds its byte limit');
|
|
32
|
+
}
|
|
33
|
+
if (current === null || typeof current === 'string' || typeof current === 'boolean')
|
|
34
|
+
continue;
|
|
35
|
+
if (typeof current === 'number' && Number.isFinite(current))
|
|
36
|
+
continue;
|
|
37
|
+
if (typeof current !== 'object' || current === null)
|
|
38
|
+
throw new Error('Client proposal schema must be literal JSON');
|
|
39
|
+
requireLiteralContainer({ current, seen, pending, depth: item.depth });
|
|
40
|
+
}
|
|
41
|
+
if (Buffer.byteLength(JSON.stringify(value)) > CLIENT_PROPOSAL_LIMITS.schemaBytes) {
|
|
42
|
+
throw new Error('Client proposal schema exceeds its byte limit');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function requireLiteralContainer({ current, seen, pending, depth }) {
|
|
46
|
+
const prototype = Object.getPrototypeOf(current);
|
|
47
|
+
if ((!Array.isArray(current) && prototype !== Object.prototype && prototype !== null) || seen.has(current)) {
|
|
48
|
+
throw new Error('Client proposal schema must be acyclic literal JSON');
|
|
49
|
+
}
|
|
50
|
+
seen.add(current);
|
|
51
|
+
const keys = Reflect.ownKeys(current);
|
|
52
|
+
if (Array.isArray(current))
|
|
53
|
+
requireLiteralArray({ value: current, keys });
|
|
54
|
+
for (const key of keys) {
|
|
55
|
+
if (Array.isArray(current) && key === 'length')
|
|
56
|
+
continue;
|
|
57
|
+
if (typeof key !== 'string' || RESERVED_NAMES.has(key))
|
|
58
|
+
throw new Error('Client proposal schema contains an unsupported key');
|
|
59
|
+
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
60
|
+
if (!descriptor || !('value' in descriptor) || !descriptor.enumerable)
|
|
61
|
+
throw new Error('Client proposal schema must contain JSON values');
|
|
62
|
+
pending.push({ value: descriptor.value, depth: depth + 1 });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function requireLiteralArray({ value, keys }) {
|
|
66
|
+
if (Object.getPrototypeOf(value) !== Array.prototype || keys.length !== value.length + 1) {
|
|
67
|
+
throw new Error('Client proposal schema arrays must be dense literal JSON');
|
|
68
|
+
}
|
|
69
|
+
for (let index = 0; index < value.length; index++) {
|
|
70
|
+
if (!Object.hasOwn(value, String(index)))
|
|
71
|
+
throw new Error('Client proposal schema arrays must be dense literal JSON');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function requireType(value) {
|
|
75
|
+
const types = Array.isArray(value) ? value : [value];
|
|
76
|
+
if (types.length === 0 || types.length > TYPES.size || types.some(type => typeof type !== 'string' || !TYPES.has(type))
|
|
77
|
+
|| new Set(types).size !== types.length)
|
|
78
|
+
throw new Error('Client proposal schema has an invalid type');
|
|
79
|
+
}
|
|
80
|
+
function requireProperties(value) {
|
|
81
|
+
if (!isObject(value) || Object.keys(value).length > 512)
|
|
82
|
+
throw new Error('Client proposal properties must be a bounded object');
|
|
83
|
+
for (const child of Object.values(value))
|
|
84
|
+
requireSchema(child);
|
|
85
|
+
}
|
|
86
|
+
function requireRequired({ value, properties }) {
|
|
87
|
+
if (!Array.isArray(value) || value.some(name => typeof name !== 'string') || new Set(value).size !== value.length) {
|
|
88
|
+
throw new Error('Client proposal required must contain unique property names');
|
|
89
|
+
}
|
|
90
|
+
if (value.some(name => !isObject(properties) || !Object.hasOwn(properties, name))) {
|
|
91
|
+
throw new Error('Client proposal required must name declared properties');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function requireBound({ key, value }) {
|
|
95
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
96
|
+
throw new Error(`Client proposal ${key} must be a finite number`);
|
|
97
|
+
if (SIZE_KEYS.has(key) && (!Number.isSafeInteger(value) || value < 0)) {
|
|
98
|
+
throw new Error(`Client proposal ${key} must be a nonnegative integer`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function requireEntry({ key, entry, schema }) {
|
|
102
|
+
if (!KEYWORDS.has(key))
|
|
103
|
+
throw new Error(`Unsupported client proposal schema keyword: ${key}`);
|
|
104
|
+
if (ANNOTATIONS.has(key)) {
|
|
105
|
+
if (typeof entry !== 'string')
|
|
106
|
+
throw new Error(`Client proposal ${key} must be a string`);
|
|
107
|
+
}
|
|
108
|
+
else if (NUMBER_KEYS.has(key) || SIZE_KEYS.has(key))
|
|
109
|
+
requireBound({ key, value: entry });
|
|
110
|
+
else if (key === 'type')
|
|
111
|
+
requireType(entry);
|
|
112
|
+
else if (CHILD_KEYS.has(key))
|
|
113
|
+
requireSchema(entry);
|
|
114
|
+
else if (UNION_KEYS.has(key)) {
|
|
115
|
+
if (!Array.isArray(entry) || entry.length === 0 || entry.length > 32)
|
|
116
|
+
throw new Error(`Client proposal ${key} requires 1–32 schemas`);
|
|
117
|
+
for (const child of entry)
|
|
118
|
+
requireSchema(child);
|
|
119
|
+
}
|
|
120
|
+
else if (key === 'properties')
|
|
121
|
+
requireProperties(entry);
|
|
122
|
+
else if (key === 'required')
|
|
123
|
+
requireRequired({ value: entry, properties: schema.properties });
|
|
124
|
+
else if (key === 'enum') {
|
|
125
|
+
if (!Array.isArray(entry) || entry.length === 0 || entry.length > 512)
|
|
126
|
+
throw new Error('Client proposal enum must be a bounded nonempty array');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function requireSchema(value) {
|
|
130
|
+
if (typeof value === 'boolean')
|
|
131
|
+
return;
|
|
132
|
+
if (!isObject(value))
|
|
133
|
+
throw new Error('Client proposal schema nodes must be objects or booleans');
|
|
134
|
+
for (const [key, entry] of Object.entries(value))
|
|
135
|
+
requireEntry({ key, entry, schema: value });
|
|
136
|
+
}
|
|
137
|
+
const literalJsonSchema = z.unknown().superRefine((value, context) => {
|
|
138
|
+
try {
|
|
139
|
+
requireBoundedJson(value);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
context.addIssue({ code: 'custom', message: error instanceof Error ? error.message : 'Invalid client proposal JSON' });
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
const proposalInputSchema = literalJsonSchema.pipe(z.record(z.string(), z.unknown())).superRefine((value, context) => {
|
|
146
|
+
try {
|
|
147
|
+
if (value.type !== 'object')
|
|
148
|
+
throw new Error('Client proposal root schema type must be object');
|
|
149
|
+
requireSchema(value);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
context.addIssue({ code: 'custom', message: error instanceof Error ? error.message : 'Invalid client proposal schema' });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
export const clientProposalSchema = z.strictObject({
|
|
156
|
+
name: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,63}$/).refine(name => !RESERVED_NAMES.has(name), 'Reserved client proposal name'),
|
|
157
|
+
description: z.string().min(1).max(CLIENT_PROPOSAL_LIMITS.descriptionLength),
|
|
158
|
+
inputSchema: proposalInputSchema,
|
|
159
|
+
});
|
|
160
|
+
export const clientProposalsSchema = z.array(clientProposalSchema).max(CLIENT_PROPOSAL_LIMITS.count).superRefine((value, context) => {
|
|
161
|
+
if (new Set(value.map(proposal => proposal.name)).size !== value.length) {
|
|
162
|
+
context.addIssue({ code: 'custom', message: 'Client proposal names must be unique' });
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
if (Buffer.byteLength(JSON.stringify(value)) > CLIENT_PROPOSAL_LIMITS.totalBytes) {
|
|
166
|
+
context.addIssue({ code: 'custom', message: 'Client proposal descriptors exceed their total byte limit' });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
context.addIssue({ code: 'custom', message: 'Client proposal descriptors must be literal JSON' });
|
|
171
|
+
}
|
|
172
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,116 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
keyword: string;
|
|
6
|
-
};
|
|
7
|
-
export declare function collectUnsupportedConstraints({ schema, path, }: {
|
|
8
|
-
schema: unknown;
|
|
9
|
-
path?: string;
|
|
10
|
-
}): UnsupportedConstraint[];
|
|
11
|
-
export declare const AGENT_PERMISSION_ROLES: readonly ["owner", "editor", "invoker", "outputs_viewer"];
|
|
12
|
-
export type AgentPermissionRole = (typeof AGENT_PERMISSION_ROLES)[number];
|
|
13
|
-
export declare const agentDefinitionSchema: z.ZodObject<{
|
|
14
|
-
id: z.ZodString;
|
|
15
|
-
name: z.ZodString;
|
|
16
|
-
description: z.ZodOptional<z.ZodString>;
|
|
17
|
-
framework: z.ZodOptional<z.ZodEnum<{
|
|
18
|
-
vercel: "vercel";
|
|
19
|
-
"claude-agent": "claude-agent";
|
|
20
|
-
}>>;
|
|
21
|
-
vmBackend: z.ZodOptional<z.ZodEnum<{
|
|
22
|
-
gce: "gce";
|
|
23
|
-
e2b: "e2b";
|
|
24
|
-
}>>;
|
|
25
|
-
idleTimeoutSeconds: z.ZodOptional<z.ZodNumber>;
|
|
26
|
-
agentCapabilities: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
27
|
-
claudeToolSearch: z.ZodOptional<z.ZodEnum<{
|
|
28
|
-
enabled: "enabled";
|
|
29
|
-
disabled: "disabled";
|
|
30
|
-
auto: "auto";
|
|
31
|
-
}>>;
|
|
32
|
-
claudeBuiltInTools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
33
|
-
openaiCodeInterpreter: z.ZodOptional<z.ZodNullable<z.ZodPreprocess<z.ZodObject<{
|
|
34
|
-
enabled: z.ZodBoolean;
|
|
35
|
-
containerId: z.ZodOptional<z.ZodString>;
|
|
36
|
-
fileIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
37
|
-
}, z.core.$strip>>>>;
|
|
38
|
-
model: z.ZodDefault<z.ZodObject<{
|
|
39
|
-
provider: z.ZodString;
|
|
40
|
-
name: z.ZodString;
|
|
41
|
-
}, z.core.$strip>>;
|
|
42
|
-
thinking: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
43
|
-
reasoningEffort: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
44
|
-
temperature: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
45
|
-
systemPrompt: z.ZodOptional<z.ZodString>;
|
|
46
|
-
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
47
|
-
mcpServers: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
48
|
-
requiredMcpServers: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
49
|
-
excludedMcpTools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
50
|
-
hideIntermediateText: z.ZodOptional<z.ZodBoolean>;
|
|
51
|
-
middleware: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
52
|
-
subagents: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
53
|
-
id: z.ZodString;
|
|
54
|
-
forkContext: z.ZodOptional<z.ZodBoolean>;
|
|
55
|
-
}, z.core.$strip>]>>>;
|
|
56
|
-
skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
57
|
-
contextFileIds: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
58
|
-
structuredOutput: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
59
|
-
inputSchema: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
60
|
-
fields: z.ZodArray<z.ZodObject<{
|
|
61
|
-
name: z.ZodString;
|
|
62
|
-
label: z.ZodOptional<z.ZodString>;
|
|
63
|
-
description: z.ZodOptional<z.ZodString>;
|
|
64
|
-
type: z.ZodEnum<{
|
|
65
|
-
string: "string";
|
|
66
|
-
number: "number";
|
|
67
|
-
boolean: "boolean";
|
|
68
|
-
enum: "enum";
|
|
69
|
-
text: "text";
|
|
70
|
-
}>;
|
|
71
|
-
required: z.ZodOptional<z.ZodBoolean>;
|
|
72
|
-
default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
73
|
-
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
74
|
-
placeholder: z.ZodOptional<z.ZodString>;
|
|
75
|
-
}, z.core.$strip>>;
|
|
76
|
-
}, z.core.$strip>>>;
|
|
77
|
-
promptTemplate: z.ZodOptional<z.ZodString>;
|
|
78
|
-
webhooks: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
79
|
-
mcpServerConfigs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
80
|
-
stubConfig: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
81
|
-
maxTtlSeconds: z.ZodOptional<z.ZodNumber>;
|
|
82
|
-
agentPermissions: z.ZodOptional<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
83
|
-
apiPermissions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
84
|
-
managedBy: z.ZodOptional<z.ZodNullable<z.ZodLiteral<"automation">>>;
|
|
85
|
-
isPublic: z.ZodOptional<z.ZodBoolean>;
|
|
86
|
-
publicOutputs: z.ZodOptional<z.ZodBoolean>;
|
|
87
|
-
permissions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
88
|
-
type: z.ZodEnum<{
|
|
89
|
-
user: "user";
|
|
90
|
-
group: "group";
|
|
91
|
-
}>;
|
|
92
|
-
id: z.ZodString;
|
|
93
|
-
role: z.ZodEnum<{
|
|
94
|
-
owner: "owner";
|
|
95
|
-
editor: "editor";
|
|
96
|
-
invoker: "invoker";
|
|
97
|
-
outputs_viewer: "outputs_viewer";
|
|
98
|
-
}>;
|
|
99
|
-
}, z.core.$strip>>>;
|
|
100
|
-
}, z.core.$strict>;
|
|
101
|
-
export type AgentDefinition = z.infer<typeof agentDefinitionSchema>;
|
|
102
|
-
export type AgentDefinitionInput = z.input<typeof agentDefinitionSchema>;
|
|
103
|
-
export declare function defineAgent(input: AgentDefinitionInput): AgentDefinition;
|
|
104
|
-
export declare function canonicalJson(value: unknown): string;
|
|
105
|
-
export declare function canonicalAgentJson(definition: AgentDefinition): string;
|
|
106
|
-
export declare function hashAgentBundle({ definitions, }: {
|
|
107
|
-
definitions: AgentDefinition[];
|
|
108
|
-
}): string;
|
|
109
|
-
export declare function formatAgentSource({ definition, exportName, }: {
|
|
110
|
-
definition: unknown;
|
|
111
|
-
exportName?: string;
|
|
112
|
-
}): string;
|
|
113
|
-
export declare function generateAgentSource({ definition, exportName, }: {
|
|
114
|
-
definition: AgentDefinition;
|
|
115
|
-
exportName?: string;
|
|
116
|
-
}): string;
|
|
1
|
+
export * from './agent-definition.js';
|
|
2
|
+
export * from './voice.js';
|
|
3
|
+
export * from './job-request-hash.js';
|
|
4
|
+
export * from './client-proposals.js';
|