@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
package/dist/index.js
CHANGED
|
@@ -1,247 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const DEFAULT_MODEL = {
|
|
6
|
-
provider: 'anthropic',
|
|
7
|
-
name: 'claude-sonnet-4-5-20250929',
|
|
8
|
-
};
|
|
9
|
-
const jsonObjectSchema = z.record(z.string(), z.unknown());
|
|
10
|
-
const unsupportedConstraints = {
|
|
11
|
-
integer: ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum'],
|
|
12
|
-
number: ['minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum'],
|
|
13
|
-
array: ['minItems', 'maxItems', 'uniqueItems'],
|
|
14
|
-
string: ['minLength', 'maxLength', 'format', 'pattern'],
|
|
15
|
-
};
|
|
16
|
-
export function collectUnsupportedConstraints({ schema, path = '', }) {
|
|
17
|
-
if (schema === null || typeof schema !== 'object')
|
|
18
|
-
return [];
|
|
19
|
-
const object = schema;
|
|
20
|
-
const type = typeof object.type === 'string' ? object.type : undefined;
|
|
21
|
-
const violations = [];
|
|
22
|
-
if (type !== undefined) {
|
|
23
|
-
for (const keyword of unsupportedConstraints[type] ?? []) {
|
|
24
|
-
if (keyword in object) {
|
|
25
|
-
violations.push({ path: path || '(root)', type, keyword });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if (type === 'object' &&
|
|
30
|
-
object.properties !== null &&
|
|
31
|
-
typeof object.properties === 'object') {
|
|
32
|
-
for (const [key, value] of Object.entries(object.properties)) {
|
|
33
|
-
violations.push(...collectUnsupportedConstraints({
|
|
34
|
-
schema: value,
|
|
35
|
-
path: `${path}.properties.${key}`,
|
|
36
|
-
}));
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (type === 'array' && object.items !== undefined) {
|
|
40
|
-
violations.push(...collectUnsupportedConstraints({
|
|
41
|
-
schema: object.items,
|
|
42
|
-
path: `${path}.items`,
|
|
43
|
-
}));
|
|
44
|
-
}
|
|
45
|
-
for (const combinator of ['anyOf', 'oneOf', 'allOf', 'prefixItems']) {
|
|
46
|
-
const entries = object[combinator];
|
|
47
|
-
if (!Array.isArray(entries))
|
|
48
|
-
continue;
|
|
49
|
-
entries.forEach((entry, index) => {
|
|
50
|
-
violations.push(...collectUnsupportedConstraints({
|
|
51
|
-
schema: entry,
|
|
52
|
-
path: `${path}.${combinator}[${index}]`,
|
|
53
|
-
}));
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
return violations;
|
|
57
|
-
}
|
|
58
|
-
const modelSchema = z.object({
|
|
59
|
-
provider: z.string().min(1),
|
|
60
|
-
name: z.string().min(1),
|
|
61
|
-
});
|
|
62
|
-
export const AGENT_PERMISSION_ROLES = [
|
|
63
|
-
'owner',
|
|
64
|
-
'editor',
|
|
65
|
-
'invoker',
|
|
66
|
-
'outputs_viewer',
|
|
67
|
-
];
|
|
68
|
-
const permissionGrantSchema = z.object({
|
|
69
|
-
type: z.enum(['user', 'group']),
|
|
70
|
-
id: z.string().min(1),
|
|
71
|
-
role: z.enum(AGENT_PERMISSION_ROLES),
|
|
72
|
-
});
|
|
73
|
-
const subagentSchema = z.union([
|
|
74
|
-
z.string().min(1),
|
|
75
|
-
z.object({
|
|
76
|
-
id: z.string().min(1),
|
|
77
|
-
forkContext: z.boolean().optional(),
|
|
78
|
-
}),
|
|
79
|
-
]);
|
|
80
|
-
const inputFieldSchema = z
|
|
81
|
-
.object({
|
|
82
|
-
name: z
|
|
83
|
-
.string()
|
|
84
|
-
.min(1)
|
|
85
|
-
.regex(/^[A-Za-z_][A-Za-z0-9_]*$/, 'name must be a valid identifier'),
|
|
86
|
-
label: z.string().optional(),
|
|
87
|
-
description: z.string().optional(),
|
|
88
|
-
type: z.enum(['string', 'text', 'number', 'boolean', 'enum']),
|
|
89
|
-
required: z.boolean().optional(),
|
|
90
|
-
default: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
91
|
-
enum: z.array(z.string()).optional(),
|
|
92
|
-
placeholder: z.string().optional(),
|
|
93
|
-
})
|
|
94
|
-
.superRefine((field, context) => {
|
|
95
|
-
if (field.type === 'enum') {
|
|
96
|
-
if (field.enum === undefined || field.enum.length === 0) {
|
|
97
|
-
context.addIssue({
|
|
98
|
-
code: 'custom',
|
|
99
|
-
path: ['enum'],
|
|
100
|
-
message: 'enum fields require at least one option',
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
if (field.default !== undefined &&
|
|
104
|
-
(typeof field.default !== 'string' ||
|
|
105
|
-
!field.enum?.includes(field.default))) {
|
|
106
|
-
context.addIssue({
|
|
107
|
-
code: 'custom',
|
|
108
|
-
path: ['default'],
|
|
109
|
-
message: 'default must be one of the declared enum options',
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
if (field.default === undefined)
|
|
115
|
-
return;
|
|
116
|
-
const expected = field.type === 'number'
|
|
117
|
-
? 'number'
|
|
118
|
-
: field.type === 'boolean'
|
|
119
|
-
? 'boolean'
|
|
120
|
-
: 'string';
|
|
121
|
-
if (typeof field.default !== expected) {
|
|
122
|
-
context.addIssue({
|
|
123
|
-
code: 'custom',
|
|
124
|
-
path: ['default'],
|
|
125
|
-
message: `default for ${field.type} fields must be a ${expected}`,
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
const inputSchemaSchema = z
|
|
130
|
-
.object({ fields: z.array(inputFieldSchema) })
|
|
131
|
-
.refine((schema) => new Set(schema.fields.map((field) => field.name)).size ===
|
|
132
|
-
schema.fields.length, {
|
|
133
|
-
path: ['fields'],
|
|
134
|
-
message: 'fields[].name must be unique within an inputSchema',
|
|
135
|
-
});
|
|
136
|
-
const openaiCodeInterpreterSchema = z.preprocess((value) => {
|
|
137
|
-
if (value === true)
|
|
138
|
-
return { enabled: true };
|
|
139
|
-
if (value === false)
|
|
140
|
-
return { enabled: false };
|
|
141
|
-
return value;
|
|
142
|
-
}, z.object({
|
|
143
|
-
enabled: z.boolean(),
|
|
144
|
-
containerId: z.string().min(1).optional(),
|
|
145
|
-
fileIds: z.array(z.string().min(1)).optional(),
|
|
146
|
-
}));
|
|
147
|
-
export const agentDefinitionSchema = z
|
|
148
|
-
.strictObject({
|
|
149
|
-
id: z
|
|
150
|
-
.string()
|
|
151
|
-
.regex(OBJECT_ID_PATTERN, 'id must be a 24-character lowercase hex ObjectId'),
|
|
152
|
-
name: z.string().min(1),
|
|
153
|
-
description: z.string().optional(),
|
|
154
|
-
framework: z.enum(['vercel', 'claude-agent']).optional(),
|
|
155
|
-
vmBackend: z.enum(['gce', 'e2b']).optional(),
|
|
156
|
-
idleTimeoutSeconds: z.number().int().min(60).max(86_400).optional(),
|
|
157
|
-
agentCapabilities: jsonObjectSchema.nullish(),
|
|
158
|
-
claudeToolSearch: z.enum(['enabled', 'disabled', 'auto']).optional(),
|
|
159
|
-
claudeBuiltInTools: z.array(z.string()).default([]),
|
|
160
|
-
openaiCodeInterpreter: openaiCodeInterpreterSchema.nullish(),
|
|
161
|
-
model: modelSchema.default(DEFAULT_MODEL),
|
|
162
|
-
thinking: jsonObjectSchema.nullish(),
|
|
163
|
-
reasoningEffort: z.string().nullish(),
|
|
164
|
-
temperature: z.number().min(0).max(1).nullish(),
|
|
165
|
-
systemPrompt: z.string().optional(),
|
|
166
|
-
tools: z.array(z.string()).default([]),
|
|
167
|
-
mcpServers: z.array(z.string()).default([]),
|
|
168
|
-
requiredMcpServers: z.array(z.string()).default([]),
|
|
169
|
-
excludedMcpTools: z.array(z.string()).default([]),
|
|
170
|
-
hideIntermediateText: z.boolean().optional(),
|
|
171
|
-
middleware: z.array(z.string()).default([]),
|
|
172
|
-
subagents: z.array(subagentSchema).default([]),
|
|
173
|
-
skills: z.array(z.string()).default([]),
|
|
174
|
-
contextFileIds: z.array(z.string()).default([]),
|
|
175
|
-
structuredOutput: jsonObjectSchema.optional(),
|
|
176
|
-
inputSchema: inputSchemaSchema.nullish(),
|
|
177
|
-
promptTemplate: z.string().optional(),
|
|
178
|
-
webhooks: jsonObjectSchema.optional(),
|
|
179
|
-
mcpServerConfigs: jsonObjectSchema.optional(),
|
|
180
|
-
stubConfig: jsonObjectSchema.nullish(),
|
|
181
|
-
maxTtlSeconds: z.number().int().positive().optional(),
|
|
182
|
-
agentPermissions: z.array(jsonObjectSchema).optional(),
|
|
183
|
-
apiPermissions: z.array(z.string()).optional(),
|
|
184
|
-
managedBy: z.literal('automation').nullish(),
|
|
185
|
-
isPublic: z.boolean().optional(),
|
|
186
|
-
publicOutputs: z.boolean().optional(),
|
|
187
|
-
permissions: z.array(permissionGrantSchema).optional(),
|
|
188
|
-
})
|
|
189
|
-
.superRefine((definition, context) => {
|
|
190
|
-
for (const [index, serverId] of definition.requiredMcpServers.entries()) {
|
|
191
|
-
if (definition.mcpServers.includes(serverId))
|
|
192
|
-
continue;
|
|
193
|
-
context.addIssue({
|
|
194
|
-
code: 'custom',
|
|
195
|
-
path: ['requiredMcpServers', index],
|
|
196
|
-
message: `requiredMcpServers entry "${serverId}" must also appear in mcpServers`,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
if (definition.structuredOutput?.enabled === true) {
|
|
200
|
-
const schema = definition.structuredOutput.schema;
|
|
201
|
-
for (const violation of collectUnsupportedConstraints({ schema })) {
|
|
202
|
-
context.addIssue({
|
|
203
|
-
code: 'custom',
|
|
204
|
-
path: ['structuredOutput', 'schema'],
|
|
205
|
-
message: `${violation.path}: "${violation.keyword}" on ${violation.type} is not supported`,
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
});
|
|
210
|
-
export function defineAgent(input) {
|
|
211
|
-
return agentDefinitionSchema.parse(input);
|
|
212
|
-
}
|
|
213
|
-
function sortJson(value) {
|
|
214
|
-
if (Array.isArray(value))
|
|
215
|
-
return value.map(sortJson);
|
|
216
|
-
if (value === null || typeof value !== 'object')
|
|
217
|
-
return value;
|
|
218
|
-
const object = value;
|
|
219
|
-
return Object.fromEntries(Object.keys(object)
|
|
220
|
-
.sort()
|
|
221
|
-
.filter((key) => object[key] !== undefined)
|
|
222
|
-
.map((key) => [key, sortJson(object[key])]));
|
|
223
|
-
}
|
|
224
|
-
export function canonicalJson(value) {
|
|
225
|
-
return JSON.stringify(sortJson(value));
|
|
226
|
-
}
|
|
227
|
-
export function canonicalAgentJson(definition) {
|
|
228
|
-
return canonicalJson(definition);
|
|
229
|
-
}
|
|
230
|
-
export function hashAgentBundle({ definitions, }) {
|
|
231
|
-
const canonical = [...definitions]
|
|
232
|
-
.sort((a, b) => a.id.localeCompare(b.id))
|
|
233
|
-
.map(canonicalAgentJson)
|
|
234
|
-
.join('\n');
|
|
235
|
-
return createHash('sha256').update('sequence-agent-bundle-v1\0').update(canonical).digest('hex');
|
|
236
|
-
}
|
|
237
|
-
export function formatAgentSource({ definition, exportName = 'agent', }) {
|
|
238
|
-
if (!IDENTIFIER_PATTERN.test(exportName)) {
|
|
239
|
-
throw new Error('exportName must be a valid TypeScript identifier');
|
|
240
|
-
}
|
|
241
|
-
const object = JSON.stringify(sortJson(definition), null, 2);
|
|
242
|
-
return ("import { defineAgent } from '@sequenceholdings/agent-spec'\n\n" +
|
|
243
|
-
`export const ${exportName} = defineAgent(${object})\n`);
|
|
244
|
-
}
|
|
245
|
-
export function generateAgentSource({ definition, exportName = 'agent', }) {
|
|
246
|
-
return formatAgentSource({ definition: defineAgent(definition), exportName });
|
|
247
|
-
}
|
|
1
|
+
export * from './agent-definition.js';
|
|
2
|
+
export * from './voice.js';
|
|
3
|
+
export * from './job-request-hash.js';
|
|
4
|
+
export * from './client-proposals.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const inputSchemaSchema: z.ZodObject<{
|
|
3
|
+
fields: z.ZodArray<z.ZodObject<{
|
|
4
|
+
name: z.ZodString;
|
|
5
|
+
label: z.ZodOptional<z.ZodString>;
|
|
6
|
+
description: z.ZodOptional<z.ZodString>;
|
|
7
|
+
type: z.ZodEnum<{
|
|
8
|
+
string: "string";
|
|
9
|
+
number: "number";
|
|
10
|
+
boolean: "boolean";
|
|
11
|
+
text: "text";
|
|
12
|
+
enum: "enum";
|
|
13
|
+
}>;
|
|
14
|
+
required: z.ZodOptional<z.ZodBoolean>;
|
|
15
|
+
default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
16
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
17
|
+
placeholder: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, z.core.$strip>>;
|
|
19
|
+
}, z.core.$strip>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const inputFieldSchema = z
|
|
3
|
+
.object({
|
|
4
|
+
name: z
|
|
5
|
+
.string()
|
|
6
|
+
.min(1)
|
|
7
|
+
.regex(/^[A-Za-z_][A-Za-z0-9_]*$/, 'name must be a valid identifier'),
|
|
8
|
+
label: z.string().optional(),
|
|
9
|
+
description: z.string().optional(),
|
|
10
|
+
type: z.enum(['string', 'text', 'number', 'boolean', 'enum']),
|
|
11
|
+
required: z.boolean().optional(),
|
|
12
|
+
default: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
13
|
+
enum: z.array(z.string()).optional(),
|
|
14
|
+
placeholder: z.string().optional(),
|
|
15
|
+
})
|
|
16
|
+
.superRefine((field, context) => {
|
|
17
|
+
if (field.type === 'enum') {
|
|
18
|
+
if (field.enum === undefined || field.enum.length === 0) {
|
|
19
|
+
context.addIssue({
|
|
20
|
+
code: 'custom',
|
|
21
|
+
path: ['enum'],
|
|
22
|
+
message: 'enum fields require at least one option',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
if (field.default !== undefined &&
|
|
26
|
+
(typeof field.default !== 'string' ||
|
|
27
|
+
!field.enum?.includes(field.default))) {
|
|
28
|
+
context.addIssue({
|
|
29
|
+
code: 'custom',
|
|
30
|
+
path: ['default'],
|
|
31
|
+
message: 'default must be one of the declared enum options',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if (field.default === undefined)
|
|
37
|
+
return;
|
|
38
|
+
const expected = field.type === 'number'
|
|
39
|
+
? 'number'
|
|
40
|
+
: field.type === 'boolean'
|
|
41
|
+
? 'boolean'
|
|
42
|
+
: 'string';
|
|
43
|
+
if (typeof field.default !== expected) {
|
|
44
|
+
context.addIssue({
|
|
45
|
+
code: 'custom',
|
|
46
|
+
path: ['default'],
|
|
47
|
+
message: `default for ${field.type} fields must be a ${expected}`,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
export const inputSchemaSchema = z
|
|
52
|
+
.object({ fields: z.array(inputFieldSchema) })
|
|
53
|
+
.refine((schema) => new Set(schema.fields.map((field) => field.name)).size ===
|
|
54
|
+
schema.fields.length, {
|
|
55
|
+
path: ['fields'],
|
|
56
|
+
message: 'fields[].name must be unique within an inputSchema',
|
|
57
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const JobCreateKindSchema: z.ZodEnum<{
|
|
3
|
+
agent: "agent";
|
|
4
|
+
workflow: "workflow";
|
|
5
|
+
}>;
|
|
6
|
+
export type JobCreateKind = z.infer<typeof JobCreateKindSchema>;
|
|
7
|
+
export type JobRequestJsonValue = null | boolean | number | string | JobRequestJsonValue[] | {
|
|
8
|
+
[key: string]: JobRequestJsonValue;
|
|
9
|
+
};
|
|
10
|
+
export declare const JobRequestExecutionIdentitySchema: z.ZodObject<{
|
|
11
|
+
id: z.ZodString;
|
|
12
|
+
version: z.ZodString;
|
|
13
|
+
definitionSha256: z.ZodString;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
export type JobRequestExecutionIdentity = z.infer<typeof JobRequestExecutionIdentitySchema>;
|
|
16
|
+
export interface JobCreateRequestFingerprintInput {
|
|
17
|
+
kind: JobCreateKind;
|
|
18
|
+
resourceId: string;
|
|
19
|
+
expectedIdentity: JobRequestExecutionIdentity;
|
|
20
|
+
payload: unknown;
|
|
21
|
+
}
|
|
22
|
+
export declare class JobRequestHashError extends Error {
|
|
23
|
+
readonly status: 400 | 413;
|
|
24
|
+
constructor(status: 400 | 413, message: string);
|
|
25
|
+
}
|
|
26
|
+
export declare function normalizeJobRequestJson(value: unknown): JobRequestJsonValue;
|
|
27
|
+
export declare function hashJobCreateRequest({ kind, resourceId, expectedIdentity, payload }: JobCreateRequestFingerprintInput): string;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { canonicalJson } from './canonical-json.js';
|
|
4
|
+
const MAX_JSON_BYTES = 16 * 1024 * 1024;
|
|
5
|
+
export const JobCreateKindSchema = z.enum(['agent', 'workflow']);
|
|
6
|
+
export const JobRequestExecutionIdentitySchema = z.strictObject({
|
|
7
|
+
id: z.string().min(1).max(128).regex(/^[A-Za-z0-9_-]+$/),
|
|
8
|
+
version: z.string().min(1).max(128),
|
|
9
|
+
definitionSha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
10
|
+
});
|
|
11
|
+
export class JobRequestHashError extends Error {
|
|
12
|
+
status;
|
|
13
|
+
constructor(status, message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.status = status;
|
|
16
|
+
this.name = 'JobRequestHashError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function assertJsonValue({ value, ancestors, depth }) {
|
|
20
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean'
|
|
21
|
+
|| typeof value === 'number' && Number.isFinite(value))
|
|
22
|
+
return;
|
|
23
|
+
if (typeof value !== 'object' || !value || depth > 64 || ancestors.has(value)) {
|
|
24
|
+
throw new JobRequestHashError(400, 'Job admission requires bounded JSON values.');
|
|
25
|
+
}
|
|
26
|
+
if (value instanceof Date) {
|
|
27
|
+
if (Number.isNaN(value.getTime()))
|
|
28
|
+
throw new JobRequestHashError(400, 'Job admission contains an invalid date.');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
32
|
+
throw new JobRequestHashError(400, 'Job admission requires plain JSON objects.');
|
|
33
|
+
}
|
|
34
|
+
ancestors.add(value);
|
|
35
|
+
const values = Array.isArray(value) ? value : Object.values(value).filter((entry) => entry !== undefined);
|
|
36
|
+
for (const entry of values)
|
|
37
|
+
assertJsonValue({ value: entry, ancestors, depth: depth + 1 });
|
|
38
|
+
ancestors.delete(value);
|
|
39
|
+
}
|
|
40
|
+
export function normalizeJobRequestJson(value) {
|
|
41
|
+
assertJsonValue({ value, ancestors: new Set(), depth: 0 });
|
|
42
|
+
const encoded = JSON.stringify(value);
|
|
43
|
+
if (Buffer.byteLength(encoded) > MAX_JSON_BYTES)
|
|
44
|
+
throw new JobRequestHashError(413, 'Job admission exceeds the 16 MiB JSON budget.');
|
|
45
|
+
return JSON.parse(encoded);
|
|
46
|
+
}
|
|
47
|
+
export function hashJobCreateRequest({ kind, resourceId, expectedIdentity, payload }) {
|
|
48
|
+
const parsed = z.strictObject({ kind: JobCreateKindSchema, resourceId: z.string().min(1).max(128),
|
|
49
|
+
expectedIdentity: JobRequestExecutionIdentitySchema }).safeParse({ kind, resourceId, expectedIdentity });
|
|
50
|
+
if (!parsed.success)
|
|
51
|
+
throw new JobRequestHashError(400, 'Job request fingerprint requires a valid expected execution identity.');
|
|
52
|
+
const envelope = normalizeJobRequestJson({ format: 'agents-job-create-request-v1', ...parsed.data, payload });
|
|
53
|
+
return createHash('sha256').update(canonicalJson(envelope)).digest('hex');
|
|
54
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type VoiceScope } from './voice-common.js';
|
|
3
|
+
export declare const voiceParticipantPolicySchema: z.ZodObject<{
|
|
4
|
+
tenantId: z.ZodString;
|
|
5
|
+
environmentId: z.ZodString;
|
|
6
|
+
revision: z.ZodNumber;
|
|
7
|
+
participantPrincipalIds: z.ZodArray<z.ZodString>;
|
|
8
|
+
operatorPrincipalIds: z.ZodArray<z.ZodString>;
|
|
9
|
+
updatedByPrincipalId: z.ZodString;
|
|
10
|
+
updatedAt: z.ZodISODateTime;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
export type VoiceParticipantPolicy = z.infer<typeof voiceParticipantPolicySchema>;
|
|
13
|
+
export declare function isVoiceContextEligible({ featureEnabled, scope, eligibleScope }: {
|
|
14
|
+
featureEnabled: boolean;
|
|
15
|
+
scope: VoiceScope;
|
|
16
|
+
eligibleScope: unknown;
|
|
17
|
+
}): boolean;
|
|
18
|
+
export declare const voiceOutboundDestinationSchema: z.ZodObject<{
|
|
19
|
+
tenantId: z.ZodString;
|
|
20
|
+
environmentId: z.ZodString;
|
|
21
|
+
destinationId: z.ZodString;
|
|
22
|
+
staffPrincipalId: z.ZodString;
|
|
23
|
+
phoneNumberE164: z.ZodString;
|
|
24
|
+
registeredByPrincipalId: z.ZodString;
|
|
25
|
+
verifiedAt: z.ZodISODateTime;
|
|
26
|
+
enabled: z.ZodBoolean;
|
|
27
|
+
}, z.core.$strict>;
|
|
28
|
+
export type VoiceOutboundDestination = z.infer<typeof voiceOutboundDestinationSchema>;
|
|
29
|
+
export declare const voiceEnvironmentBindingsSchema: z.ZodObject<{
|
|
30
|
+
tenantId: z.ZodString;
|
|
31
|
+
environmentId: z.ZodString;
|
|
32
|
+
runtimeAccountId: z.ZodString;
|
|
33
|
+
syntheticNamespaceRef: z.ZodString;
|
|
34
|
+
elevenLabsWorkspaceId: z.ZodString;
|
|
35
|
+
elevenLabsCredentialRef: z.ZodString;
|
|
36
|
+
twilioSubaccountSid: z.ZodString;
|
|
37
|
+
twilioCredentialRef: z.ZodString;
|
|
38
|
+
twilioRecordingEnabled: z.ZodLiteral<false>;
|
|
39
|
+
}, z.core.$strict>;
|
|
40
|
+
export type VoiceEnvironmentBindings = z.infer<typeof voiceEnvironmentBindingsSchema>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { voiceIdSchema, voiceRevisionSchema, voiceScopeSchema, voiceTimestampSchema } from './voice-common.js';
|
|
3
|
+
const principalIdsSchema = z.array(voiceIdSchema).max(200)
|
|
4
|
+
.refine((ids) => new Set(ids).size === ids.length, 'Duplicate principal ID');
|
|
5
|
+
export const voiceParticipantPolicySchema = voiceScopeSchema.extend({
|
|
6
|
+
revision: voiceRevisionSchema,
|
|
7
|
+
participantPrincipalIds: principalIdsSchema,
|
|
8
|
+
operatorPrincipalIds: principalIdsSchema,
|
|
9
|
+
updatedByPrincipalId: voiceIdSchema,
|
|
10
|
+
updatedAt: voiceTimestampSchema,
|
|
11
|
+
}).refine((policy) => policy.operatorPrincipalIds.every((id) => policy.participantPrincipalIds.includes(id)), { path: ['operatorPrincipalIds'], message: 'Operators must also be explicit participants' });
|
|
12
|
+
export function isVoiceContextEligible({ featureEnabled, scope, eligibleScope }) {
|
|
13
|
+
if (!featureEnabled)
|
|
14
|
+
return false;
|
|
15
|
+
const parsed = voiceScopeSchema.safeParse(eligibleScope);
|
|
16
|
+
return parsed.success && parsed.data.tenantId === scope.tenantId && parsed.data.environmentId === scope.environmentId;
|
|
17
|
+
}
|
|
18
|
+
export const voiceOutboundDestinationSchema = voiceScopeSchema.extend({
|
|
19
|
+
destinationId: voiceIdSchema,
|
|
20
|
+
staffPrincipalId: voiceIdSchema,
|
|
21
|
+
phoneNumberE164: z.string().regex(/^\+[1-9]\d{1,14}$/),
|
|
22
|
+
registeredByPrincipalId: voiceIdSchema,
|
|
23
|
+
verifiedAt: voiceTimestampSchema,
|
|
24
|
+
enabled: z.boolean(),
|
|
25
|
+
});
|
|
26
|
+
export const voiceEnvironmentBindingsSchema = voiceScopeSchema.extend({
|
|
27
|
+
runtimeAccountId: voiceIdSchema,
|
|
28
|
+
syntheticNamespaceRef: voiceIdSchema,
|
|
29
|
+
elevenLabsWorkspaceId: voiceIdSchema,
|
|
30
|
+
elevenLabsCredentialRef: voiceIdSchema,
|
|
31
|
+
twilioSubaccountSid: voiceIdSchema,
|
|
32
|
+
twilioCredentialRef: voiceIdSchema,
|
|
33
|
+
twilioRecordingEnabled: z.literal(false),
|
|
34
|
+
});
|