@zhivex-ai/core 1.4.0 → 1.6.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 +3 -0
- package/dist/agent-control-plane.d.ts +31 -2
- package/dist/agent-control-plane.d.ts.map +1 -1
- package/dist/agent-control-plane.js +545 -30
- package/dist/agent-control-plane.js.map +1 -1
- package/dist/agent.d.ts +1 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +320 -193
- package/dist/agent.js.map +1 -1
- package/dist/api-stability.d.ts.map +1 -1
- package/dist/api-stability.js +65 -47
- package/dist/api-stability.js.map +1 -1
- package/dist/artifact.d.ts +32 -9
- package/dist/artifact.d.ts.map +1 -1
- package/dist/artifact.js +427 -81
- package/dist/artifact.js.map +1 -1
- package/dist/catalog.d.ts +34 -2
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +262 -7
- package/dist/catalog.js.map +1 -1
- package/dist/index.d.ts +11 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +138 -52
- package/dist/middleware.js.map +1 -1
- package/dist/observability.d.ts +57 -10
- package/dist/observability.d.ts.map +1 -1
- package/dist/observability.js +535 -114
- package/dist/observability.js.map +1 -1
- package/dist/types.d.ts +71 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/workflow-evaluation-gate.d.ts +130 -0
- package/dist/workflow-evaluation-gate.d.ts.map +1 -0
- package/dist/workflow-evaluation-gate.js +337 -0
- package/dist/workflow-evaluation-gate.js.map +1 -0
- package/dist/workflow-evaluation.d.ts +8 -0
- package/dist/workflow-evaluation.d.ts.map +1 -1
- package/dist/workflow-evaluation.js +14 -1
- package/dist/workflow-evaluation.js.map +1 -1
- package/dist/workflow-observability.d.ts +13 -0
- package/dist/workflow-observability.d.ts.map +1 -0
- package/dist/workflow-observability.js +186 -0
- package/dist/workflow-observability.js.map +1 -0
- package/dist/workflow.d.ts +49 -0
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +255 -78
- package/dist/workflow.js.map +1 -1
- package/package.json +9 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { randomBytes } from "node:crypto";
|
|
1
|
+
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { cancelAgentRun, cancelAgentRunTree, resumeAgent, runAgent, streamAgent } from "./agent.js";
|
|
3
|
-
import { createAgentHarnessBinding } from "./agent-harness.js";
|
|
3
|
+
import { createAgentExecutionEnvironmentBinding, createAgentHarnessBinding, fingerprintAgentHarness } from "./agent-harness.js";
|
|
4
4
|
import { createAgentRunSnapshot, replayAgentRun } from "./agent-evaluation.js";
|
|
5
5
|
import { createAgentTraceArtifact, createProductionTraceOptions, createHierarchicalAgentTrace, estimateAgentRunCost, summarizeAgentTrace } from "./agent-trace.js";
|
|
6
6
|
import { createRedactionPolicy } from "./safety-policy.js";
|
|
@@ -68,6 +68,127 @@ const supervisedPermissions = new Set([
|
|
|
68
68
|
...writePermissions,
|
|
69
69
|
"network"
|
|
70
70
|
]);
|
|
71
|
+
const allToolPermissions = new Set([
|
|
72
|
+
"read",
|
|
73
|
+
"write",
|
|
74
|
+
"network",
|
|
75
|
+
"filesystem",
|
|
76
|
+
"code-execution",
|
|
77
|
+
"shell",
|
|
78
|
+
"external-side-effect"
|
|
79
|
+
]);
|
|
80
|
+
const allToolRiskLevels = new Set(["low", "medium", "high", "critical"]);
|
|
81
|
+
const allToolPolicyModes = new Set(["allow-all", "read-only", "deny-write", "supervised"]);
|
|
82
|
+
const allAgentTiers = new Set(["tier-a", "tier-b", "tier-c"]);
|
|
83
|
+
const allAgentStatuses = new Set([
|
|
84
|
+
"queued",
|
|
85
|
+
"running",
|
|
86
|
+
"completed",
|
|
87
|
+
"suspended",
|
|
88
|
+
"waiting_approval",
|
|
89
|
+
"cancel_requested",
|
|
90
|
+
"failed",
|
|
91
|
+
"cancelled",
|
|
92
|
+
"timed_out"
|
|
93
|
+
]);
|
|
94
|
+
const allApprovalKinds = new Set([
|
|
95
|
+
"provider",
|
|
96
|
+
"local-tool",
|
|
97
|
+
"subagent"
|
|
98
|
+
]);
|
|
99
|
+
const controlPlaneRecord = (value, name) => {
|
|
100
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
101
|
+
throw new ValidationError(`${name} must be an object.`);
|
|
102
|
+
}
|
|
103
|
+
return value;
|
|
104
|
+
};
|
|
105
|
+
const controlPlaneString = (value, name, allowEmpty = false) => {
|
|
106
|
+
if (typeof value !== "string" || (!allowEmpty && !value.trim())) {
|
|
107
|
+
throw new ValidationError(`${name} must be a${allowEmpty ? "" : " non-empty"} string.`);
|
|
108
|
+
}
|
|
109
|
+
return value;
|
|
110
|
+
};
|
|
111
|
+
const controlPlaneOptionalString = (value, name, allowEmpty = false) => value === undefined ? undefined : controlPlaneString(value, name, allowEmpty);
|
|
112
|
+
const controlPlaneFiniteNumber = (value, name, minimum = 0) => {
|
|
113
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < minimum) {
|
|
114
|
+
throw new ValidationError(`${name} must be a finite number greater than or equal to ${minimum}.`);
|
|
115
|
+
}
|
|
116
|
+
return value;
|
|
117
|
+
};
|
|
118
|
+
const cloneControlPlaneJson = (value, name) => {
|
|
119
|
+
try {
|
|
120
|
+
const serialized = JSON.stringify(value);
|
|
121
|
+
if (serialized === undefined) {
|
|
122
|
+
throw new Error("undefined is not JSON");
|
|
123
|
+
}
|
|
124
|
+
return JSON.parse(serialized);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
throw new ValidationError(`${name} must be finite JSON.`, { cause: error });
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const cloneControlPlaneMetadata = (value, name) => {
|
|
131
|
+
if (value === undefined) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
controlPlaneRecord(value, name);
|
|
135
|
+
return cloneControlPlaneJson(value, name);
|
|
136
|
+
};
|
|
137
|
+
const normalizeControlPlaneSchemaVersion = (value, name) => {
|
|
138
|
+
if (value === undefined) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (value !== AGENT_CONTROL_PLANE_SCHEMA_VERSION) {
|
|
142
|
+
if (typeof value === "number" && value > AGENT_CONTROL_PLANE_SCHEMA_VERSION) {
|
|
143
|
+
throw new ValidationError(`Unsupported ${name} schemaVersion ${value}.`);
|
|
144
|
+
}
|
|
145
|
+
throw new ValidationError(`${name} schemaVersion must be ${AGENT_CONTROL_PLANE_SCHEMA_VERSION}; only records without schemaVersion are treated as legacy.`);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
const normalizeStringList = (value, name) => {
|
|
149
|
+
if (!Array.isArray(value)) {
|
|
150
|
+
throw new ValidationError(`${name} must be an array.`);
|
|
151
|
+
}
|
|
152
|
+
return value.map((entry, index) => controlPlaneString(entry, `${name}[${index}]`));
|
|
153
|
+
};
|
|
154
|
+
const normalizePermissionList = (value, name) => {
|
|
155
|
+
const permissions = normalizeStringList(value, name);
|
|
156
|
+
for (const permission of permissions) {
|
|
157
|
+
if (!allToolPermissions.has(permission)) {
|
|
158
|
+
throw new ValidationError(`${name} contains unsupported permission "${permission}".`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return [...new Set(permissions)].sort();
|
|
162
|
+
};
|
|
163
|
+
const approvalRequestFingerprint = (approval) => fingerprintAgentHarness({
|
|
164
|
+
kind: approval.kind ?? "provider",
|
|
165
|
+
provider: approval.provider,
|
|
166
|
+
id: approval.id,
|
|
167
|
+
name: approval.name,
|
|
168
|
+
arguments: approval.arguments,
|
|
169
|
+
serverLabel: approval.serverLabel,
|
|
170
|
+
toolCallId: approval.toolCallId,
|
|
171
|
+
step: approval.step,
|
|
172
|
+
inputDigest: approval.inputDigest,
|
|
173
|
+
toolVersion: approval.toolVersion,
|
|
174
|
+
signature: approval.signature,
|
|
175
|
+
childRunId: approval.childRunId,
|
|
176
|
+
childAgentId: approval.childAgentId,
|
|
177
|
+
childApprovalRequestId: approval.childApprovalRequestId,
|
|
178
|
+
rawData: approval.rawData
|
|
179
|
+
});
|
|
180
|
+
const legacyApprovalRequestFingerprint = (approval) => fingerprintAgentHarness({
|
|
181
|
+
provider: approval.provider,
|
|
182
|
+
id: approval.id,
|
|
183
|
+
name: approval.name,
|
|
184
|
+
arguments: approval.arguments,
|
|
185
|
+
rawData: approval.rawData
|
|
186
|
+
});
|
|
187
|
+
const assertControlPlaneMigrationTarget = (targetVersion, name) => {
|
|
188
|
+
if (targetVersion !== AGENT_CONTROL_PLANE_SCHEMA_VERSION) {
|
|
189
|
+
throw new ValidationError(`Unsupported ${name} migration target ${targetVersion}.`);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
71
192
|
const normalizeNames = (names) => new Set((names ?? []).map((name) => name.toLowerCase()));
|
|
72
193
|
const normalizePermissions = (permissions) => new Set(permissions ?? []);
|
|
73
194
|
const normalizeRiskLevels = (levels) => new Set(levels ?? []);
|
|
@@ -79,9 +200,9 @@ const readAdvancedMetadata = (tool) => {
|
|
|
79
200
|
};
|
|
80
201
|
const toolPermissions = (tool) => {
|
|
81
202
|
const permissions = readAdvancedMetadata(tool).permissions;
|
|
82
|
-
return
|
|
83
|
-
?
|
|
84
|
-
:
|
|
203
|
+
return permissions === undefined
|
|
204
|
+
? []
|
|
205
|
+
: normalizePermissionList(permissions, `Tool "${tool.name}" permissions`);
|
|
85
206
|
};
|
|
86
207
|
const toolAudit = (tool) => {
|
|
87
208
|
const audit = readAdvancedMetadata(tool).audit;
|
|
@@ -110,6 +231,159 @@ const validateCapsuleId = (id) => {
|
|
|
110
231
|
throw new ValidationError('Agent capsule "id" may only contain letters, numbers, dots, colons, underscores, and hyphens.');
|
|
111
232
|
}
|
|
112
233
|
};
|
|
234
|
+
const normalizeCapsuleSkill = (value, index) => {
|
|
235
|
+
const name = `AgentCapsuleManifest.skills[${index}]`;
|
|
236
|
+
const skill = controlPlaneRecord(value, name);
|
|
237
|
+
return {
|
|
238
|
+
id: controlPlaneString(skill.id, `${name}.id`),
|
|
239
|
+
name: controlPlaneOptionalString(skill.name, `${name}.name`),
|
|
240
|
+
version: controlPlaneOptionalString(skill.version, `${name}.version`),
|
|
241
|
+
description: controlPlaneOptionalString(skill.description, `${name}.description`, true),
|
|
242
|
+
path: controlPlaneOptionalString(skill.path, `${name}.path`),
|
|
243
|
+
metadata: cloneControlPlaneMetadata(skill.metadata, `${name}.metadata`)
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
const normalizeCapsuleMcpServer = (value, index) => {
|
|
247
|
+
const name = `AgentCapsuleManifest.mcpServers[${index}]`;
|
|
248
|
+
const server = controlPlaneRecord(value, name);
|
|
249
|
+
if (server.transport !== "stdio" && server.transport !== "http" && server.transport !== "sse" && server.transport !== "custom") {
|
|
250
|
+
throw new ValidationError(`${name}.transport is not supported.`);
|
|
251
|
+
}
|
|
252
|
+
if (server.riskLevel !== undefined && !allToolRiskLevels.has(server.riskLevel)) {
|
|
253
|
+
throw new ValidationError(`${name}.riskLevel is not supported.`);
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
name: controlPlaneString(server.name, `${name}.name`),
|
|
257
|
+
transport: server.transport,
|
|
258
|
+
command: controlPlaneOptionalString(server.command, `${name}.command`),
|
|
259
|
+
url: controlPlaneOptionalString(server.url, `${name}.url`),
|
|
260
|
+
permissions: server.permissions === undefined
|
|
261
|
+
? undefined
|
|
262
|
+
: normalizePermissionList(server.permissions, `${name}.permissions`),
|
|
263
|
+
riskLevel: server.riskLevel,
|
|
264
|
+
metadata: cloneControlPlaneMetadata(server.metadata, `${name}.metadata`)
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
const normalizeCapsuleEvaluation = (value, index) => {
|
|
268
|
+
const name = `AgentCapsuleManifest.evaluations[${index}]`;
|
|
269
|
+
const evaluation = controlPlaneRecord(value, name);
|
|
270
|
+
const datasetSize = evaluation.datasetSize === undefined
|
|
271
|
+
? undefined
|
|
272
|
+
: controlPlaneFiniteNumber(evaluation.datasetSize, `${name}.datasetSize`);
|
|
273
|
+
if (datasetSize !== undefined && !Number.isSafeInteger(datasetSize)) {
|
|
274
|
+
throw new ValidationError(`${name}.datasetSize must be a safe integer.`);
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
name: controlPlaneString(evaluation.name, `${name}.name`),
|
|
278
|
+
path: controlPlaneOptionalString(evaluation.path, `${name}.path`),
|
|
279
|
+
datasetSize,
|
|
280
|
+
metadata: cloneControlPlaneMetadata(evaluation.metadata, `${name}.metadata`)
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
const normalizeCapsulePolicy = (value) => {
|
|
284
|
+
if (value === undefined) {
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
const policy = controlPlaneRecord(value, "AgentCapsuleManifest.policy");
|
|
288
|
+
if (policy.toolPolicyMode !== undefined && !allToolPolicyModes.has(policy.toolPolicyMode)) {
|
|
289
|
+
throw new ValidationError("AgentCapsuleManifest.policy.toolPolicyMode is not supported.");
|
|
290
|
+
}
|
|
291
|
+
if (policy.defaultRequiresApproval !== undefined && typeof policy.defaultRequiresApproval !== "boolean") {
|
|
292
|
+
throw new ValidationError("AgentCapsuleManifest.policy.defaultRequiresApproval must be a boolean.");
|
|
293
|
+
}
|
|
294
|
+
if (policy.redaction !== undefined && typeof policy.redaction !== "boolean") {
|
|
295
|
+
throw new ValidationError("AgentCapsuleManifest.policy.redaction must be a boolean.");
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
toolPolicyMode: policy.toolPolicyMode,
|
|
299
|
+
defaultRequiresApproval: policy.defaultRequiresApproval,
|
|
300
|
+
redaction: policy.redaction,
|
|
301
|
+
metadata: cloneControlPlaneMetadata(policy.metadata, "AgentCapsuleManifest.policy.metadata")
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
const normalizeCapsuleTool = (value, index) => {
|
|
305
|
+
const name = `AgentCapsuleManifest.tools[${index}]`;
|
|
306
|
+
const toolManifest = controlPlaneRecord(value, name);
|
|
307
|
+
if (toolManifest.kind !== "callable" && toolManifest.kind !== "hosted") {
|
|
308
|
+
throw new ValidationError(`${name}.kind is not supported.`);
|
|
309
|
+
}
|
|
310
|
+
if (toolManifest.riskLevel !== undefined && !allToolRiskLevels.has(toolManifest.riskLevel)) {
|
|
311
|
+
throw new ValidationError(`${name}.riskLevel is not supported.`);
|
|
312
|
+
}
|
|
313
|
+
if (typeof toolManifest.requiresApproval !== "boolean") {
|
|
314
|
+
throw new ValidationError(`${name}.requiresApproval must be a boolean.`);
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
name: controlPlaneString(toolManifest.name, `${name}.name`),
|
|
318
|
+
kind: toolManifest.kind,
|
|
319
|
+
provider: controlPlaneOptionalString(toolManifest.provider, `${name}.provider`),
|
|
320
|
+
hostedType: controlPlaneOptionalString(toolManifest.hostedType, `${name}.hostedType`),
|
|
321
|
+
source: controlPlaneString(toolManifest.source, `${name}.source`),
|
|
322
|
+
permissions: normalizePermissionList(toolManifest.permissions, `${name}.permissions`),
|
|
323
|
+
riskLevel: toolManifest.riskLevel,
|
|
324
|
+
owner: controlPlaneOptionalString(toolManifest.owner, `${name}.owner`),
|
|
325
|
+
labels: [...new Set(normalizeStringList(toolManifest.labels, `${name}.labels`))].sort(),
|
|
326
|
+
requiresApproval: toolManifest.requiresApproval,
|
|
327
|
+
approvalVersion: controlPlaneOptionalString(toolManifest.approvalVersion, `${name}.approvalVersion`),
|
|
328
|
+
description: controlPlaneOptionalString(toolManifest.description, `${name}.description`, true)
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
const normalizeAgentCapsuleManifestFields = (value) => {
|
|
332
|
+
const input = controlPlaneRecord(value, "AgentCapsuleManifest");
|
|
333
|
+
normalizeControlPlaneSchemaVersion(input.schemaVersion, "AgentCapsuleManifest");
|
|
334
|
+
const id = controlPlaneString(input.id, "AgentCapsuleManifest.id");
|
|
335
|
+
validateCapsuleId(id);
|
|
336
|
+
if (!allAgentTiers.has(input.agentTier)) {
|
|
337
|
+
throw new ValidationError("AgentCapsuleManifest.agentTier is not supported.");
|
|
338
|
+
}
|
|
339
|
+
if (!Array.isArray(input.tools) || !Array.isArray(input.skills) || !Array.isArray(input.mcpServers) || !Array.isArray(input.evaluations)) {
|
|
340
|
+
throw new ValidationError("AgentCapsuleManifest tool, skill, MCP server, and evaluation collections must be arrays.");
|
|
341
|
+
}
|
|
342
|
+
const executionEnvironment = input.executionEnvironment === undefined
|
|
343
|
+
? undefined
|
|
344
|
+
: cloneControlPlaneJson(controlPlaneRecord(input.executionEnvironment, "AgentCapsuleManifest.executionEnvironment"), "AgentCapsuleManifest.executionEnvironment");
|
|
345
|
+
if (executionEnvironment) {
|
|
346
|
+
createAgentExecutionEnvironmentBinding(executionEnvironment);
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
schemaVersion: AGENT_CONTROL_PLANE_SCHEMA_VERSION,
|
|
350
|
+
id,
|
|
351
|
+
name: controlPlaneString(input.name, "AgentCapsuleManifest.name"),
|
|
352
|
+
version: controlPlaneString(input.version, "AgentCapsuleManifest.version"),
|
|
353
|
+
description: controlPlaneOptionalString(input.description, "AgentCapsuleManifest.description", true),
|
|
354
|
+
provider: controlPlaneString(input.provider, "AgentCapsuleManifest.provider"),
|
|
355
|
+
modelId: controlPlaneString(input.modelId, "AgentCapsuleManifest.modelId"),
|
|
356
|
+
agentTier: input.agentTier,
|
|
357
|
+
tools: input.tools.map(normalizeCapsuleTool).sort((left, right) => left.name.localeCompare(right.name)),
|
|
358
|
+
skills: input.skills.map(normalizeCapsuleSkill).sort((left, right) => left.id.localeCompare(right.id)),
|
|
359
|
+
mcpServers: input.mcpServers.map(normalizeCapsuleMcpServer).sort((left, right) => left.name.localeCompare(right.name)),
|
|
360
|
+
evaluations: input.evaluations.map(normalizeCapsuleEvaluation).sort((left, right) => left.name.localeCompare(right.name)),
|
|
361
|
+
policy: normalizeCapsulePolicy(input.policy),
|
|
362
|
+
executionEnvironment,
|
|
363
|
+
metadata: cloneControlPlaneMetadata(input.metadata, "AgentCapsuleManifest.metadata")
|
|
364
|
+
};
|
|
365
|
+
};
|
|
366
|
+
export const normalizeAgentCapsuleManifest = (value) => {
|
|
367
|
+
const input = controlPlaneRecord(value, "AgentCapsuleManifest");
|
|
368
|
+
const manifestWithoutFingerprint = normalizeAgentCapsuleManifestFields(input);
|
|
369
|
+
const fingerprint = controlPlaneString(input.fingerprint, "AgentCapsuleManifest.fingerprint");
|
|
370
|
+
const expectedFingerprint = createAgentHarnessBinding({
|
|
371
|
+
id: manifestWithoutFingerprint.id,
|
|
372
|
+
version: manifestWithoutFingerprint.version,
|
|
373
|
+
manifest: manifestWithoutFingerprint
|
|
374
|
+
}).fingerprint;
|
|
375
|
+
if (fingerprint !== expectedFingerprint) {
|
|
376
|
+
throw new ValidationError("AgentCapsuleManifest fingerprint does not match its normalized contract.");
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
...manifestWithoutFingerprint,
|
|
380
|
+
fingerprint
|
|
381
|
+
};
|
|
382
|
+
};
|
|
383
|
+
export const migrateAgentCapsuleManifest = (value, targetVersion = AGENT_CONTROL_PLANE_SCHEMA_VERSION) => {
|
|
384
|
+
assertControlPlaneMigrationTarget(targetVersion, "AgentCapsuleManifest");
|
|
385
|
+
return normalizeAgentCapsuleManifest(value);
|
|
386
|
+
};
|
|
113
387
|
const inspectTools = (tools) => {
|
|
114
388
|
const toolSet = toToolSet(tools);
|
|
115
389
|
if (!toolSet) {
|
|
@@ -141,7 +415,7 @@ export const createAgentCapsule = (options) => {
|
|
|
141
415
|
validateCapsuleId(id);
|
|
142
416
|
const providerSupport = inspectProviderAgentSupport(options.agent.model);
|
|
143
417
|
const tools = inspectTools(options.tools ?? options.agent.tools);
|
|
144
|
-
const manifestWithoutFingerprint = {
|
|
418
|
+
const manifestWithoutFingerprint = normalizeAgentCapsuleManifestFields({
|
|
145
419
|
schemaVersion: AGENT_CONTROL_PLANE_SCHEMA_VERSION,
|
|
146
420
|
id,
|
|
147
421
|
name: options.name ?? id,
|
|
@@ -162,16 +436,16 @@ export const createAgentCapsule = (options) => {
|
|
|
162
436
|
policy: options.policy,
|
|
163
437
|
executionEnvironment: options.agent.executionEnvironment?.manifest,
|
|
164
438
|
metadata: options.metadata
|
|
165
|
-
};
|
|
439
|
+
});
|
|
166
440
|
const harness = createAgentHarnessBinding({
|
|
167
441
|
id,
|
|
168
442
|
version: manifestWithoutFingerprint.version,
|
|
169
443
|
manifest: manifestWithoutFingerprint
|
|
170
444
|
});
|
|
171
|
-
const manifest = {
|
|
445
|
+
const manifest = normalizeAgentCapsuleManifest({
|
|
172
446
|
...manifestWithoutFingerprint,
|
|
173
447
|
fingerprint: harness.fingerprint
|
|
174
|
-
};
|
|
448
|
+
});
|
|
175
449
|
return {
|
|
176
450
|
manifest,
|
|
177
451
|
agent: {
|
|
@@ -190,6 +464,9 @@ export const inspectAgentCapsule = (capsule) => {
|
|
|
190
464
|
if ((hasWritePermission(toolManifest.permissions) || toolManifest.riskLevel === "critical") && !toolManifest.requiresApproval) {
|
|
191
465
|
warnings.push(`Tool "${toolManifest.name}" has write or critical risk without approval.`);
|
|
192
466
|
}
|
|
467
|
+
if (!toolManifest.permissions.length && !toolManifest.requiresApproval) {
|
|
468
|
+
warnings.push(`Tool "${toolManifest.name}" does not declare permissions and has no approval requirement.`);
|
|
469
|
+
}
|
|
193
470
|
}
|
|
194
471
|
if (capsule.providerSupport.agentTier === "tier-c") {
|
|
195
472
|
warnings.push(`Model "${capsule.manifest.modelId}" is Tier C for agent workloads.`);
|
|
@@ -218,9 +495,9 @@ const requestPermissions = (request) => {
|
|
|
218
495
|
const value = permissions && typeof permissions === "object" && !Array.isArray(permissions)
|
|
219
496
|
? permissions.permissions
|
|
220
497
|
: undefined;
|
|
221
|
-
return
|
|
222
|
-
?
|
|
223
|
-
:
|
|
498
|
+
return value === undefined
|
|
499
|
+
? []
|
|
500
|
+
: normalizePermissionList(value, `Tool "${request.tool.name}" permissions`);
|
|
224
501
|
};
|
|
225
502
|
const requestRiskLevel = (request) => {
|
|
226
503
|
const advanced = request.tool.metadata?.advancedRegistry;
|
|
@@ -293,25 +570,197 @@ export const createAgentToolPolicy = (options = {}) => {
|
|
|
293
570
|
if (permissions.some((permission) => supervisedPermissions.has(permission) && !allowPermissions.has(permission))) {
|
|
294
571
|
return approvalRequiredDecision(`Tool "${request.tool.name}" requests sensitive permissions.`, mode);
|
|
295
572
|
}
|
|
573
|
+
if (permissions.length === 0) {
|
|
574
|
+
return approvalRequiredDecision(`Tool "${request.tool.name}" does not declare permissions and requires approval under supervised policy.`, mode);
|
|
575
|
+
}
|
|
296
576
|
}
|
|
297
577
|
return { approved: true, metadata: { policy: "agent-control-plane", mode } };
|
|
298
578
|
};
|
|
299
579
|
};
|
|
300
|
-
export const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
580
|
+
export const normalizeAgentApprovalQueueItem = (value) => {
|
|
581
|
+
const input = controlPlaneRecord(value, "AgentApprovalQueueItem");
|
|
582
|
+
const legacy = input.schemaVersion === undefined;
|
|
583
|
+
const legacyShape = input.kind === undefined && input.requestFingerprint === undefined;
|
|
584
|
+
normalizeControlPlaneSchemaVersion(input.schemaVersion, "AgentApprovalQueueItem");
|
|
585
|
+
if (input.type !== "agent_approval_queue_item" && !(legacy && input.type === undefined)) {
|
|
586
|
+
throw new ValidationError('AgentApprovalQueueItem.type must be "agent_approval_queue_item".');
|
|
587
|
+
}
|
|
588
|
+
const kind = input.kind === undefined && legacyShape ? "provider" : input.kind;
|
|
589
|
+
if (!allApprovalKinds.has(kind)) {
|
|
590
|
+
throw new ValidationError("AgentApprovalQueueItem.kind is not supported.");
|
|
591
|
+
}
|
|
592
|
+
const provider = controlPlaneString(input.provider, "AgentApprovalQueueItem.provider");
|
|
593
|
+
const approvalRequestId = controlPlaneString(input.approvalRequestId, "AgentApprovalQueueItem.approvalRequestId");
|
|
594
|
+
const name = controlPlaneString(input.name, "AgentApprovalQueueItem.name");
|
|
595
|
+
const approvalToken = controlPlaneString(input.approvalToken, "AgentApprovalQueueItem.approvalToken");
|
|
596
|
+
if (approvalToken.length > 8_192) {
|
|
597
|
+
throw new ValidationError("AgentApprovalQueueItem.approvalToken exceeds the 8192-character limit.");
|
|
598
|
+
}
|
|
599
|
+
const argumentsValue = controlPlaneOptionalString(input.arguments, "AgentApprovalQueueItem.arguments", true);
|
|
600
|
+
const rawData = input.rawData === undefined
|
|
601
|
+
? undefined
|
|
602
|
+
: cloneControlPlaneJson(input.rawData, "AgentApprovalQueueItem.rawData");
|
|
603
|
+
const requestFingerprint = input.requestFingerprint === undefined && legacyShape
|
|
604
|
+
? legacyApprovalRequestFingerprint({
|
|
605
|
+
kind: "provider",
|
|
606
|
+
provider,
|
|
607
|
+
id: approvalRequestId,
|
|
608
|
+
name,
|
|
609
|
+
arguments: argumentsValue ?? "",
|
|
610
|
+
rawData: rawData ?? null
|
|
611
|
+
})
|
|
612
|
+
: controlPlaneString(input.requestFingerprint, "AgentApprovalQueueItem.requestFingerprint");
|
|
613
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(requestFingerprint)) {
|
|
614
|
+
throw new ValidationError("AgentApprovalQueueItem.requestFingerprint must be a sha256 fingerprint.");
|
|
615
|
+
}
|
|
616
|
+
return {
|
|
617
|
+
schemaVersion: AGENT_CONTROL_PLANE_SCHEMA_VERSION,
|
|
618
|
+
type: "agent_approval_queue_item",
|
|
619
|
+
runId: controlPlaneString(input.runId, "AgentApprovalQueueItem.runId"),
|
|
620
|
+
agentId: controlPlaneOptionalString(input.agentId, "AgentApprovalQueueItem.agentId"),
|
|
621
|
+
kind: kind,
|
|
622
|
+
provider,
|
|
623
|
+
approvalRequestId,
|
|
624
|
+
name,
|
|
625
|
+
requestFingerprint,
|
|
626
|
+
arguments: argumentsValue,
|
|
627
|
+
approvalToken,
|
|
628
|
+
resumeUrl: controlPlaneOptionalString(input.resumeUrl, "AgentApprovalQueueItem.resumeUrl"),
|
|
629
|
+
reason: controlPlaneOptionalString(input.reason, "AgentApprovalQueueItem.reason", true),
|
|
630
|
+
expiresAt: input.expiresAt === undefined
|
|
631
|
+
? undefined
|
|
632
|
+
: controlPlaneFiniteNumber(input.expiresAt, "AgentApprovalQueueItem.expiresAt"),
|
|
633
|
+
rawData
|
|
634
|
+
};
|
|
635
|
+
};
|
|
636
|
+
export const migrateAgentApprovalQueueItem = (value, targetVersion = AGENT_CONTROL_PLANE_SCHEMA_VERSION) => {
|
|
637
|
+
assertControlPlaneMigrationTarget(targetVersion, "AgentApprovalQueueItem");
|
|
638
|
+
return normalizeAgentApprovalQueueItem(value);
|
|
639
|
+
};
|
|
640
|
+
export const createAgentApprovalQueue = (state, options = {}) => {
|
|
641
|
+
const redaction = resolveLedgerRedaction(options.redaction);
|
|
642
|
+
return state.pendingApprovals.map((approval) => {
|
|
643
|
+
const reason = typeof options.reason === "function" ? options.reason(approval, state) : options.reason;
|
|
644
|
+
return normalizeAgentApprovalQueueItem({
|
|
645
|
+
schemaVersion: AGENT_CONTROL_PLANE_SCHEMA_VERSION,
|
|
646
|
+
type: "agent_approval_queue_item",
|
|
647
|
+
runId: state.runId,
|
|
648
|
+
agentId: state.agentId,
|
|
649
|
+
kind: approval.kind ?? "provider",
|
|
650
|
+
provider: approval.provider,
|
|
651
|
+
approvalRequestId: approval.id,
|
|
652
|
+
name: approval.name,
|
|
653
|
+
requestFingerprint: approvalRequestFingerprint(approval),
|
|
654
|
+
arguments: options.includeArguments
|
|
655
|
+
? (redaction ? redaction.redactText(approval.arguments) : approval.arguments)
|
|
656
|
+
: undefined,
|
|
657
|
+
approvalToken: `${options.tokenPrefix ?? "appr"}_${randomBytes(32).toString("base64url")}`,
|
|
658
|
+
resumeUrl: typeof options.resumeUrl === "function" ? options.resumeUrl(approval, state) : options.resumeUrl,
|
|
659
|
+
reason: reason === undefined ? undefined : (redaction ? redaction.redactText(reason) : reason),
|
|
660
|
+
expiresAt: typeof options.expiresAt === "function" ? options.expiresAt(approval, state) : options.expiresAt,
|
|
661
|
+
rawData: options.includeRawData
|
|
662
|
+
? (redaction ? redaction.redactJson(approval.rawData) : approval.rawData)
|
|
663
|
+
: undefined
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
};
|
|
667
|
+
const assertLedgerIdentity = (value, name, identity) => {
|
|
668
|
+
if (value.runId !== identity.runId ||
|
|
669
|
+
value.provider !== identity.provider ||
|
|
670
|
+
value.modelId !== identity.modelId ||
|
|
671
|
+
value.status !== identity.status) {
|
|
672
|
+
throw new ValidationError(`${name} identity does not match its AgentRunLedger.`);
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
export const normalizeAgentRunLedger = (value) => {
|
|
676
|
+
const input = controlPlaneRecord(value, "AgentRunLedger");
|
|
677
|
+
const legacy = input.schemaVersion === undefined;
|
|
678
|
+
normalizeControlPlaneSchemaVersion(input.schemaVersion, "AgentRunLedger");
|
|
679
|
+
if (input.type !== "agent_run_ledger" && !(legacy && input.type === undefined)) {
|
|
680
|
+
throw new ValidationError('AgentRunLedger.type must be "agent_run_ledger".');
|
|
681
|
+
}
|
|
682
|
+
const runId = controlPlaneString(input.runId, "AgentRunLedger.runId");
|
|
683
|
+
const provider = controlPlaneString(input.provider, "AgentRunLedger.provider");
|
|
684
|
+
const modelId = controlPlaneString(input.modelId, "AgentRunLedger.modelId");
|
|
685
|
+
if (!allAgentStatuses.has(input.status)) {
|
|
686
|
+
throw new ValidationError("AgentRunLedger.status is not supported.");
|
|
687
|
+
}
|
|
688
|
+
const status = input.status;
|
|
689
|
+
const identity = { runId, provider, modelId, status };
|
|
690
|
+
const snapshotRecord = controlPlaneRecord(input.snapshot, "AgentRunLedger.snapshot");
|
|
691
|
+
assertLedgerIdentity(snapshotRecord, "AgentRunLedger.snapshot", identity);
|
|
692
|
+
for (const field of ["toolCalls", "childRuns", "compactions", "pendingApprovals"]) {
|
|
693
|
+
if (!Array.isArray(snapshotRecord[field])) {
|
|
694
|
+
throw new ValidationError(`AgentRunLedger.snapshot.${field} must be an array.`);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
controlPlaneFiniteNumber(snapshotRecord.steps, "AgentRunLedger.snapshot.steps");
|
|
698
|
+
controlPlaneString(snapshotRecord.outputText, "AgentRunLedger.snapshot.outputText", true);
|
|
699
|
+
const auditRecord = controlPlaneRecord(input.audit, "AgentRunLedger.audit");
|
|
700
|
+
assertLedgerIdentity(auditRecord, "AgentRunLedger.audit", identity);
|
|
701
|
+
if (auditRecord.type !== "agent_run_audit") {
|
|
702
|
+
throw new ValidationError('AgentRunLedger.audit.type must be "agent_run_audit".');
|
|
703
|
+
}
|
|
704
|
+
for (const field of ["steps", "toolCalls", "toolErrors", "approvals", "childRuns"]) {
|
|
705
|
+
controlPlaneFiniteNumber(auditRecord[field], `AgentRunLedger.audit.${field}`);
|
|
706
|
+
}
|
|
707
|
+
if (!Array.isArray(input.toolAudit)) {
|
|
708
|
+
throw new ValidationError("AgentRunLedger.toolAudit must be an array.");
|
|
709
|
+
}
|
|
710
|
+
for (const [index, entry] of input.toolAudit.entries()) {
|
|
711
|
+
const toolAuditRecord = controlPlaneRecord(entry, `AgentRunLedger.toolAudit[${index}]`);
|
|
712
|
+
if (toolAuditRecord.type !== "agent_tool_audit" ||
|
|
713
|
+
toolAuditRecord.runId !== runId ||
|
|
714
|
+
toolAuditRecord.provider !== provider ||
|
|
715
|
+
toolAuditRecord.modelId !== modelId) {
|
|
716
|
+
throw new ValidationError(`AgentRunLedger.toolAudit[${index}] identity does not match its ledger.`);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
const traceRecord = controlPlaneRecord(input.trace, "AgentRunLedger.trace");
|
|
720
|
+
assertLedgerIdentity(traceRecord, "AgentRunLedger.trace", identity);
|
|
721
|
+
for (const field of ["steps", "events", "approvals"]) {
|
|
722
|
+
if (!Array.isArray(traceRecord[field])) {
|
|
723
|
+
throw new ValidationError(`AgentRunLedger.trace.${field} must be an array.`);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
const summaryRecord = controlPlaneRecord(input.summary, "AgentRunLedger.summary");
|
|
727
|
+
assertLedgerIdentity(summaryRecord, "AgentRunLedger.summary", identity);
|
|
728
|
+
controlPlaneRecord(summaryRecord.latency, "AgentRunLedger.summary.latency");
|
|
729
|
+
for (const field of ["steps", "childRuns", "toolCalls", "toolErrors", "approvals"]) {
|
|
730
|
+
controlPlaneFiniteNumber(summaryRecord[field], `AgentRunLedger.summary.${field}`);
|
|
731
|
+
}
|
|
732
|
+
if (input.timeline !== undefined && !Array.isArray(input.timeline)) {
|
|
733
|
+
throw new ValidationError("AgentRunLedger.timeline must be an array.");
|
|
734
|
+
}
|
|
735
|
+
if (input.cost !== undefined) {
|
|
736
|
+
controlPlaneRecord(input.cost, "AgentRunLedger.cost");
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
schemaVersion: AGENT_CONTROL_PLANE_SCHEMA_VERSION,
|
|
740
|
+
type: "agent_run_ledger",
|
|
741
|
+
runId,
|
|
742
|
+
agentId: controlPlaneOptionalString(input.agentId, "AgentRunLedger.agentId"),
|
|
743
|
+
provider,
|
|
744
|
+
modelId,
|
|
745
|
+
status,
|
|
746
|
+
snapshot: cloneControlPlaneJson(input.snapshot, "AgentRunLedger.snapshot"),
|
|
747
|
+
audit: cloneControlPlaneJson(input.audit, "AgentRunLedger.audit"),
|
|
748
|
+
toolAudit: cloneControlPlaneJson(input.toolAudit, "AgentRunLedger.toolAudit"),
|
|
749
|
+
timeline: input.timeline === undefined
|
|
750
|
+
? undefined
|
|
751
|
+
: cloneControlPlaneJson(input.timeline, "AgentRunLedger.timeline"),
|
|
752
|
+
trace: cloneControlPlaneJson(input.trace, "AgentRunLedger.trace"),
|
|
753
|
+
summary: cloneControlPlaneJson(input.summary, "AgentRunLedger.summary"),
|
|
754
|
+
cost: input.cost === undefined
|
|
755
|
+
? undefined
|
|
756
|
+
: cloneControlPlaneJson(input.cost, "AgentRunLedger.cost"),
|
|
757
|
+
metadata: cloneControlPlaneMetadata(input.metadata, "AgentRunLedger.metadata")
|
|
758
|
+
};
|
|
759
|
+
};
|
|
760
|
+
export const migrateAgentRunLedger = (value, targetVersion = AGENT_CONTROL_PLANE_SCHEMA_VERSION) => {
|
|
761
|
+
assertControlPlaneMigrationTarget(targetVersion, "AgentRunLedger");
|
|
762
|
+
return normalizeAgentRunLedger(value);
|
|
763
|
+
};
|
|
315
764
|
export const createAgentRunLedger = (state, options = {}) => {
|
|
316
765
|
const traceOptions = createProductionTraceOptions({
|
|
317
766
|
...options.trace,
|
|
@@ -327,13 +776,20 @@ export const createAgentRunLedger = (state, options = {}) => {
|
|
|
327
776
|
includeOutputText: traceOptions.includeOutputText ?? false
|
|
328
777
|
};
|
|
329
778
|
const redaction = resolveLedgerRedaction(traceOptions.redaction);
|
|
330
|
-
const snapshot =
|
|
779
|
+
const snapshot = {
|
|
780
|
+
...sanitizeLedgerValue(createAgentRunSnapshot(state), sanitizationOptions, redaction),
|
|
781
|
+
runId: state.runId,
|
|
782
|
+
agentId: state.agentId,
|
|
783
|
+
provider: state.provider,
|
|
784
|
+
modelId: state.modelId,
|
|
785
|
+
status: state.status
|
|
786
|
+
};
|
|
331
787
|
const replay = replayAgentRun(state);
|
|
332
788
|
const trace = createAgentTraceArtifact(state, traceOptions);
|
|
333
789
|
const summary = summarizeAgentTrace(trace, { pricing: options.pricing });
|
|
334
790
|
const auditOptions = { ...options, redaction: traceOptions.redaction };
|
|
335
791
|
const audit = createAgentAuditRecord(state, auditOptions);
|
|
336
|
-
return {
|
|
792
|
+
return normalizeAgentRunLedger({
|
|
337
793
|
schemaVersion: AGENT_CONTROL_PLANE_SCHEMA_VERSION,
|
|
338
794
|
type: "agent_run_ledger",
|
|
339
795
|
runId: state.runId,
|
|
@@ -353,7 +809,7 @@ export const createAgentRunLedger = (state, options = {}) => {
|
|
|
353
809
|
metadata: options.includeMetadata && audit.metadata
|
|
354
810
|
? audit.metadata
|
|
355
811
|
: undefined
|
|
356
|
-
};
|
|
812
|
+
});
|
|
357
813
|
};
|
|
358
814
|
const addDiff = (changes, field, left, right) => {
|
|
359
815
|
if (JSON.stringify(left) !== JSON.stringify(right)) {
|
|
@@ -492,6 +948,11 @@ const toRunnerInput = (input) => {
|
|
|
492
948
|
const { state: _state, handoff: _handoff, parentRunId: _parentRunId, ...runnerInput } = input;
|
|
493
949
|
return runnerInput;
|
|
494
950
|
};
|
|
951
|
+
const approvalTokensMatch = (expected, actual) => {
|
|
952
|
+
const expectedBytes = Buffer.from(expected);
|
|
953
|
+
const actualBytes = Buffer.from(actual);
|
|
954
|
+
return expectedBytes.length === actualBytes.length && timingSafeEqual(expectedBytes, actualBytes);
|
|
955
|
+
};
|
|
495
956
|
export const createAgentControlPlane = (options) => {
|
|
496
957
|
const runtimeAgent = options.agent.harness
|
|
497
958
|
? options.agent
|
|
@@ -523,6 +984,60 @@ export const createAgentControlPlane = (options) => {
|
|
|
523
984
|
const output = await resumeAgent(runtimeAgent, input);
|
|
524
985
|
return record(output.state);
|
|
525
986
|
},
|
|
987
|
+
async resumeApproval(input) {
|
|
988
|
+
const { queueItem: rawQueueItem, approvalToken, approve, reason, now = Date.now(), ...resumeInput } = input;
|
|
989
|
+
const queueItem = normalizeAgentApprovalQueueItem(rawQueueItem);
|
|
990
|
+
const presentedToken = controlPlaneString(approvalToken, "Agent approval token");
|
|
991
|
+
if (!approvalTokensMatch(queueItem.approvalToken, presentedToken)) {
|
|
992
|
+
throw new ValidationError("Agent approval token is invalid.");
|
|
993
|
+
}
|
|
994
|
+
if (typeof approve !== "boolean") {
|
|
995
|
+
throw new ValidationError("Agent approval decision must be a boolean.");
|
|
996
|
+
}
|
|
997
|
+
const decisionReason = controlPlaneOptionalString(reason, "Agent approval reason", true);
|
|
998
|
+
controlPlaneFiniteNumber(now, "Agent approval clock");
|
|
999
|
+
if (queueItem.expiresAt !== undefined && now >= queueItem.expiresAt) {
|
|
1000
|
+
throw new ValidationError("Agent approval has expired.");
|
|
1001
|
+
}
|
|
1002
|
+
if (!runtimeAgent.store) {
|
|
1003
|
+
throw new ValidationError("Durable approval resume requires an AgentRunStore.");
|
|
1004
|
+
}
|
|
1005
|
+
const state = await runtimeAgent.store.load(queueItem.runId, resumeInput.scope);
|
|
1006
|
+
if (!state) {
|
|
1007
|
+
throw new ValidationError(`Agent run "${queueItem.runId}" was not found.`);
|
|
1008
|
+
}
|
|
1009
|
+
if (queueItem.agentId !== undefined && state.agentId !== queueItem.agentId) {
|
|
1010
|
+
throw new ValidationError("Agent approval queue item does not match the persisted agent.");
|
|
1011
|
+
}
|
|
1012
|
+
const pending = state.pendingApprovals.find((approval) => approval.provider === queueItem.provider &&
|
|
1013
|
+
approval.id === queueItem.approvalRequestId);
|
|
1014
|
+
if (!pending) {
|
|
1015
|
+
throw new ValidationError("Agent approval is no longer pending or was already consumed.");
|
|
1016
|
+
}
|
|
1017
|
+
if (pending.name !== queueItem.name) {
|
|
1018
|
+
throw new ValidationError("Agent approval queue item does not match the pending request.");
|
|
1019
|
+
}
|
|
1020
|
+
const currentFingerprint = approvalRequestFingerprint(pending);
|
|
1021
|
+
const legacyFingerprint = legacyApprovalRequestFingerprint(pending);
|
|
1022
|
+
const isLegacyProjection = queueItem.requestFingerprint === legacyFingerprint;
|
|
1023
|
+
if (queueItem.requestFingerprint !== currentFingerprint && !isLegacyProjection) {
|
|
1024
|
+
throw new ValidationError("Agent approval request fingerprint does not match the persisted request.");
|
|
1025
|
+
}
|
|
1026
|
+
if (!isLegacyProjection && queueItem.kind !== (pending.kind ?? "provider")) {
|
|
1027
|
+
throw new ValidationError("Agent approval queue item kind does not match the persisted request.");
|
|
1028
|
+
}
|
|
1029
|
+
const output = await resumeAgent(runtimeAgent, {
|
|
1030
|
+
...resumeInput,
|
|
1031
|
+
state,
|
|
1032
|
+
approvals: [{
|
|
1033
|
+
provider: pending.provider,
|
|
1034
|
+
approvalRequestId: pending.id,
|
|
1035
|
+
approve,
|
|
1036
|
+
reason: decisionReason
|
|
1037
|
+
}]
|
|
1038
|
+
});
|
|
1039
|
+
return record(output.state);
|
|
1040
|
+
},
|
|
526
1041
|
stream(input = {}) {
|
|
527
1042
|
if (runner && hasSessionInput(options, input)) {
|
|
528
1043
|
return runner.stream(toRunnerInput(input));
|