@zhivex-ai/core 0.19.0 → 1.0.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 +19 -1
- package/dist/agent-control-plane.d.ts +4 -1
- package/dist/agent-control-plane.d.ts.map +1 -1
- package/dist/agent-control-plane.js +61 -32
- package/dist/agent-control-plane.js.map +1 -1
- package/dist/agent-evaluation.d.ts +5 -1
- package/dist/agent-evaluation.d.ts.map +1 -1
- package/dist/agent-evaluation.js +17 -0
- package/dist/agent-evaluation.js.map +1 -1
- package/dist/agent-harness.d.ts +9 -0
- package/dist/agent-harness.d.ts.map +1 -0
- package/dist/agent-harness.js +66 -0
- package/dist/agent-harness.js.map +1 -0
- package/dist/agent-state.d.ts.map +1 -1
- package/dist/agent-state.js +112 -0
- package/dist/agent-state.js.map +1 -1
- package/dist/agent.d.ts +24 -15
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +692 -39
- package/dist/agent.js.map +1 -1
- package/dist/api-stability.d.ts.map +1 -1
- package/dist/api-stability.js +4 -0
- package/dist/api-stability.js.map +1 -1
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +10 -0
- package/dist/catalog.js.map +1 -1
- package/dist/errors.d.ts +2 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/generate-object.d.ts.map +1 -1
- package/dist/generate-object.js +7 -2
- package/dist/generate-object.js.map +1 -1
- package/dist/generate-text.d.ts +5 -4
- package/dist/generate-text.d.ts.map +1 -1
- package/dist/generate-text.js +482 -70
- package/dist/generate-text.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +22 -3
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +164 -18
- package/dist/mcp.js.map +1 -1
- package/dist/messages.d.ts +5 -5
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js.map +1 -1
- package/dist/safety-policy.d.ts.map +1 -1
- package/dist/safety-policy.js +2 -3
- package/dist/safety-policy.js.map +1 -1
- package/dist/stream.d.ts +4 -0
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js +29 -0
- package/dist/stream.js.map +1 -1
- package/dist/structured-output-prompt.d.ts +6 -0
- package/dist/structured-output-prompt.d.ts.map +1 -0
- package/dist/structured-output-prompt.js +26 -0
- package/dist/structured-output-prompt.js.map +1 -0
- package/dist/tool-execution-suspension.d.ts +8 -0
- package/dist/tool-execution-suspension.d.ts.map +1 -0
- package/dist/tool-execution-suspension.js +12 -0
- package/dist/tool-execution-suspension.js.map +1 -0
- package/dist/types.d.ts +335 -28
- package/dist/types.d.ts.map +1 -1
- package/dist/ui.d.ts +5 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +68 -2
- package/dist/ui.js.map +1 -1
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { createAgentApprovalMessage, getAgentApprovalRequests } from "./agent-approval.js";
|
|
3
3
|
import { createAgentHandoffMessage } from "./agent-handoff.js";
|
|
4
|
+
import { createAgentExecutionEnvironmentBinding, fingerprintAgentHarness } from "./agent-harness.js";
|
|
4
5
|
import { AGENT_RUN_STATE_SCHEMA_VERSION, normalizeAgentRunState } from "./agent-state.js";
|
|
5
6
|
import { BoundedReplayBroadcast } from "./bounded-broadcast.js";
|
|
6
|
-
import { ConflictError, GuardrailTriggeredError, ValidationError } from "./errors.js";
|
|
7
|
+
import { ConflictError, GuardrailTriggeredError, UnsupportedFeatureError, ValidationError } from "./errors.js";
|
|
7
8
|
import { aggregateTokenUsage, generateText, getGenerateTextStepTiming, normalizeMessages, streamText } from "./generate-text.js";
|
|
8
|
-
import { isCallableToolDefinition, serializeJsonValue } from "./messages.js";
|
|
9
|
+
import { createTextMessage, isCallableToolDefinition, serializeJsonValue } from "./messages.js";
|
|
9
10
|
import { mergeAbortSignals } from "./runtime.js";
|
|
10
11
|
import { evaluateAgentBudgetPreflight, getAgentBudgetStatus } from "./safety-policy.js";
|
|
11
12
|
import { createSecureId } from "./secure-id.js";
|
|
13
|
+
import { createStructuredOutputPrompt } from "./structured-output-prompt.js";
|
|
14
|
+
import { ToolExecutionSuspendedError } from "./tool-execution-suspension.js";
|
|
12
15
|
import { toToolSet } from "./tool-registry.js";
|
|
13
16
|
import { z } from "zod";
|
|
14
17
|
const randomId = createSecureId;
|
|
@@ -26,6 +29,26 @@ const joinInstructions = (...parts) => {
|
|
|
26
29
|
const content = parts.map((part) => part?.trim()).filter((part) => Boolean(part));
|
|
27
30
|
return content.length ? content.join("\n\n") : undefined;
|
|
28
31
|
};
|
|
32
|
+
const resolveAgentOutputMode = (agent) => {
|
|
33
|
+
if (!agent.outputSchema) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const requested = agent.outputMode ?? "auto";
|
|
37
|
+
if (requested === "native" && !agent.model.capabilities.structuredOutput) {
|
|
38
|
+
throw new UnsupportedFeatureError(`Model "${agent.model.provider}/${agent.model.modelId}" does not support native structured output.`);
|
|
39
|
+
}
|
|
40
|
+
return requested === "auto"
|
|
41
|
+
? agent.model.capabilities.structuredOutput
|
|
42
|
+
? "native"
|
|
43
|
+
: "prompted"
|
|
44
|
+
: requested;
|
|
45
|
+
};
|
|
46
|
+
const promptedOutputInstruction = (agent) => agent.outputSchema && resolveAgentOutputMode(agent) === "prompted"
|
|
47
|
+
? createStructuredOutputPrompt(agent.outputSchema, {
|
|
48
|
+
name: agent.outputName,
|
|
49
|
+
description: agent.outputDescription
|
|
50
|
+
})
|
|
51
|
+
: undefined;
|
|
29
52
|
const hasToolCalls = (messages) => messages.some((message) => message.parts.some((part) => part.type === "tool-call"));
|
|
30
53
|
const snapshotRequest = (request, messageOffset = 0, messages = request.messages) => ({
|
|
31
54
|
messageOffset,
|
|
@@ -48,9 +71,20 @@ const snapshotResponse = (response) => ({
|
|
|
48
71
|
usage: response.usage
|
|
49
72
|
});
|
|
50
73
|
const countToolCalls = (messages) => messages.reduce((total, message) => total + message.parts.filter((part) => part.type === "tool-call").length, 0);
|
|
74
|
+
const messagePrefixLength = (prefix, messages) => {
|
|
75
|
+
if (prefix.length > messages.length) {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
for (let index = 0; index < prefix.length; index += 1) {
|
|
79
|
+
if (JSON.stringify(prefix[index]) !== JSON.stringify(messages[index])) {
|
|
80
|
+
return 0;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return prefix.length;
|
|
84
|
+
};
|
|
51
85
|
const mapSteps = (steps, offset, toolResults) => {
|
|
52
86
|
let toolResultCursor = 0;
|
|
53
|
-
let
|
|
87
|
+
let previousMessages = [];
|
|
54
88
|
return steps.map((step, index) => {
|
|
55
89
|
const response = snapshotResponse(step.response);
|
|
56
90
|
const toolCallCount = countToolCalls(response.messages);
|
|
@@ -58,9 +92,9 @@ const mapSteps = (steps, offset, toolResults) => {
|
|
|
58
92
|
toolResultCursor += toolCallCount;
|
|
59
93
|
const timing = getGenerateTextStepTiming(step.request);
|
|
60
94
|
const finishedAt = timing?.finishedAt ?? Date.now();
|
|
61
|
-
const messageOffset = index === 0 ? 0 :
|
|
95
|
+
const messageOffset = index === 0 ? 0 : messagePrefixLength(previousMessages, step.request.messages);
|
|
62
96
|
const incrementalMessages = step.request.messages.slice(messageOffset);
|
|
63
|
-
|
|
97
|
+
previousMessages = step.request.messages;
|
|
64
98
|
return {
|
|
65
99
|
index: offset + index + 1,
|
|
66
100
|
status: "completed",
|
|
@@ -79,6 +113,9 @@ const cloneMetadata = (...values) => {
|
|
|
79
113
|
const toOutput = (state) => ({
|
|
80
114
|
status: state.status,
|
|
81
115
|
outputText: state.outputText,
|
|
116
|
+
finalOutput: state.status === "completed" && state.finalOutput !== undefined
|
|
117
|
+
? state.finalOutput
|
|
118
|
+
: undefined,
|
|
82
119
|
finishReason: state.finishReason,
|
|
83
120
|
providerFinishReason: state.providerFinishReason,
|
|
84
121
|
usage: state.usage,
|
|
@@ -90,7 +127,7 @@ const toOutput = (state) => ({
|
|
|
90
127
|
});
|
|
91
128
|
const normalizeApprovalStatus = (status) => status === "suspended" ? "waiting_approval" : status;
|
|
92
129
|
const cloneState = (state) => JSON.parse(JSON.stringify(normalizeAgentRunState(state)));
|
|
93
|
-
const createBaseState = (provider, modelId, initialMessages, maxSteps, metadata, agentId, runId, handoff, parentRunId, idempotencyKey, scope) => {
|
|
130
|
+
const createBaseState = (provider, modelId, initialMessages, maxSteps, metadata, agentId, runId, handoff, parentRunId, idempotencyKey, scope, outputMode, harness, executionEnvironment) => {
|
|
94
131
|
const startedAt = Date.now();
|
|
95
132
|
return {
|
|
96
133
|
schemaVersion: AGENT_RUN_STATE_SCHEMA_VERSION,
|
|
@@ -102,6 +139,8 @@ const createBaseState = (provider, modelId, initialMessages, maxSteps, metadata,
|
|
|
102
139
|
parentRunId: parentRunId ?? handoff?.fromRunId,
|
|
103
140
|
provider,
|
|
104
141
|
modelId,
|
|
142
|
+
harness,
|
|
143
|
+
executionEnvironment,
|
|
105
144
|
status: "running",
|
|
106
145
|
messages: initialMessages,
|
|
107
146
|
steps: [],
|
|
@@ -109,7 +148,10 @@ const createBaseState = (provider, modelId, initialMessages, maxSteps, metadata,
|
|
|
109
148
|
currentStep: 0,
|
|
110
149
|
maxSteps,
|
|
111
150
|
outputText: "",
|
|
151
|
+
outputMode,
|
|
112
152
|
pendingApprovals: [],
|
|
153
|
+
approvalHistory: [],
|
|
154
|
+
compactions: [],
|
|
113
155
|
metadata,
|
|
114
156
|
handoff,
|
|
115
157
|
startedAt,
|
|
@@ -198,7 +240,7 @@ const prepareFreshMessages = async (agent, input, runId) => {
|
|
|
198
240
|
let messages = normalizeMessages({
|
|
199
241
|
prompt: input.prompt,
|
|
200
242
|
messages: input.messages,
|
|
201
|
-
system: joinInstructions(agent.instructions, input.system)
|
|
243
|
+
system: joinInstructions(agent.instructions, input.system, promptedOutputInstruction(agent))
|
|
202
244
|
});
|
|
203
245
|
const handoffMessages = input.handoff
|
|
204
246
|
? [createAgentHandoffMessage(input.handoff), ...input.handoff.contextMessages.filter((message) => message.role !== "system")]
|
|
@@ -218,11 +260,17 @@ const prepareFreshMessages = async (agent, input, runId) => {
|
|
|
218
260
|
memoryMessages
|
|
219
261
|
};
|
|
220
262
|
};
|
|
221
|
-
const
|
|
263
|
+
const localApprovalResolutionPayload = (inputDigest, approve, reason) => JSON.stringify({
|
|
264
|
+
inputDigest,
|
|
265
|
+
approve,
|
|
266
|
+
reason: reason ?? null
|
|
267
|
+
});
|
|
268
|
+
const applyApprovalResponses = async (messages, approvals, pendingApprovals, approvalHistory = [], signer) => {
|
|
222
269
|
if (!approvals?.length) {
|
|
223
270
|
return {
|
|
224
271
|
messages,
|
|
225
|
-
pendingApprovals
|
|
272
|
+
pendingApprovals,
|
|
273
|
+
approvalHistory
|
|
226
274
|
};
|
|
227
275
|
}
|
|
228
276
|
const pendingById = new Map(pendingApprovals.map((approval) => [approval.id, approval]));
|
|
@@ -235,17 +283,85 @@ const applyApprovalResponses = (messages, approvals, pendingApprovals) => {
|
|
|
235
283
|
throw new ValidationError(`Approval request "${approval.approvalRequestId}" belongs to provider "${pending.provider}", not "${approval.provider}".`);
|
|
236
284
|
}
|
|
237
285
|
}
|
|
286
|
+
const providerApprovals = approvals.filter((approval) => {
|
|
287
|
+
const pending = pendingById.get(approval.approvalRequestId);
|
|
288
|
+
return pending?.kind === undefined || pending.kind === "provider";
|
|
289
|
+
});
|
|
290
|
+
const localResolutions = [];
|
|
291
|
+
const subagentResolutions = [];
|
|
292
|
+
for (const approval of approvals) {
|
|
293
|
+
const pending = pendingById.get(approval.approvalRequestId);
|
|
294
|
+
if (!pending) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (pending.kind === "subagent") {
|
|
298
|
+
subagentResolutions.push({
|
|
299
|
+
requestId: pending.id,
|
|
300
|
+
kind: "subagent",
|
|
301
|
+
provider: pending.provider,
|
|
302
|
+
approve: approval.approve,
|
|
303
|
+
reason: approval.reason,
|
|
304
|
+
toolCallId: pending.toolCallId,
|
|
305
|
+
childRunId: pending.childRunId,
|
|
306
|
+
childAgentId: pending.childAgentId,
|
|
307
|
+
childApprovalRequestId: pending.childApprovalRequestId,
|
|
308
|
+
resolvedAt: Date.now()
|
|
309
|
+
});
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (pending.kind !== "local-tool") {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (signer) {
|
|
316
|
+
if (!pending.inputDigest || !pending.signature) {
|
|
317
|
+
throw new ValidationError(`Approval request "${pending.id}" is missing its required signature.`);
|
|
318
|
+
}
|
|
319
|
+
const requestSignatureValid = signer.verify
|
|
320
|
+
? await signer.verify(pending.inputDigest, pending.signature)
|
|
321
|
+
: (await signer.sign(pending.inputDigest)) === pending.signature;
|
|
322
|
+
if (!requestSignatureValid) {
|
|
323
|
+
throw new ValidationError(`Approval request "${pending.id}" has an invalid signature.`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const resolutionSignature = signer && pending.inputDigest
|
|
327
|
+
? await signer.sign(localApprovalResolutionPayload(pending.inputDigest, approval.approve, approval.reason))
|
|
328
|
+
: undefined;
|
|
329
|
+
localResolutions.push({
|
|
330
|
+
requestId: pending.id,
|
|
331
|
+
kind: "local-tool",
|
|
332
|
+
provider: pending.provider,
|
|
333
|
+
approve: approval.approve,
|
|
334
|
+
reason: approval.reason,
|
|
335
|
+
toolCallId: pending.toolCallId,
|
|
336
|
+
step: pending.step,
|
|
337
|
+
inputDigest: pending.inputDigest,
|
|
338
|
+
toolVersion: pending.toolVersion,
|
|
339
|
+
signature: resolutionSignature,
|
|
340
|
+
resolvedAt: Date.now()
|
|
341
|
+
});
|
|
342
|
+
}
|
|
238
343
|
return {
|
|
239
|
-
messages:
|
|
240
|
-
|
|
344
|
+
messages: providerApprovals.length
|
|
345
|
+
? [...messages, createAgentApprovalMessage(providerApprovals)]
|
|
346
|
+
: messages,
|
|
347
|
+
pendingApprovals: pendingApprovals.filter((pending) => !approvals.some((approval) => approval.approvalRequestId === pending.id)),
|
|
348
|
+
approvalHistory: [
|
|
349
|
+
...approvalHistory.filter((existing) => !localResolutions.some((resolution) => resolution.requestId === existing.requestId) &&
|
|
350
|
+
!subagentResolutions.some((resolution) => resolution.requestId === existing.requestId)),
|
|
351
|
+
...localResolutions,
|
|
352
|
+
...subagentResolutions
|
|
353
|
+
]
|
|
241
354
|
};
|
|
242
355
|
};
|
|
243
|
-
const finalizeState = (state, result, newSteps, newToolResults) => {
|
|
356
|
+
const finalizeState = (agent, state, result, newSteps, newToolResults) => {
|
|
244
357
|
const nextCurrentStep = state.currentStep + newSteps.length;
|
|
245
358
|
const exhausted = nextCurrentStep >= state.maxSteps;
|
|
246
359
|
const lastStep = newSteps.at(-1);
|
|
247
360
|
const unresolvedToolCalls = lastStep?.response ? hasToolCalls(lastStep.response.messages) : false;
|
|
248
|
-
const pendingApprovals =
|
|
361
|
+
const pendingApprovals = [
|
|
362
|
+
...(result.approvalRequests ?? []),
|
|
363
|
+
...getAgentApprovalRequests(newSteps.flatMap((step) => step.response?.messages ?? []))
|
|
364
|
+
];
|
|
249
365
|
if (pendingApprovals.length) {
|
|
250
366
|
state.status = "waiting_approval";
|
|
251
367
|
state.error = undefined;
|
|
@@ -272,6 +388,23 @@ const finalizeState = (state, result, newSteps, newToolResults) => {
|
|
|
272
388
|
state.toolResults = [...state.toolResults, ...newToolResults];
|
|
273
389
|
state.currentStep = nextCurrentStep;
|
|
274
390
|
state.outputText = result.text;
|
|
391
|
+
if (state.status === "completed") {
|
|
392
|
+
const terminalText = result.steps.at(-1)?.response.text ?? result.text;
|
|
393
|
+
if (agent.outputSchema) {
|
|
394
|
+
let parsedJson;
|
|
395
|
+
try {
|
|
396
|
+
parsedJson = JSON.parse(terminalText);
|
|
397
|
+
}
|
|
398
|
+
catch (error) {
|
|
399
|
+
throw new ValidationError("Agent final output is not valid JSON.", { cause: error });
|
|
400
|
+
}
|
|
401
|
+
const parsedOutput = agent.outputSchema.safeParse(parsedJson);
|
|
402
|
+
if (!parsedOutput.success) {
|
|
403
|
+
throw new ValidationError(`Agent final output validation failed: ${parsedOutput.error.message}`);
|
|
404
|
+
}
|
|
405
|
+
state.finalOutput = serializeJsonValue(parsedOutput.data);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
275
408
|
state.finishReason = result.finishReason;
|
|
276
409
|
state.providerFinishReason = result.providerFinishReason;
|
|
277
410
|
state.usage = aggregateTokenUsage([state.usage, result.usage]);
|
|
@@ -293,6 +426,7 @@ const defaultSubAgentToolName = (agent) => {
|
|
|
293
426
|
const countToolCallsInSteps = (steps) => steps.reduce((total, step) => total + countToolCalls(step.response?.messages ?? []), 0);
|
|
294
427
|
const countToolErrors = (toolResults) => toolResults.filter((result) => result.isError).length;
|
|
295
428
|
export const createSubAgentTool = (options) => {
|
|
429
|
+
const runtimeState = options.runtimeState;
|
|
296
430
|
const toolName = options.toolName ?? options.name ?? defaultSubAgentToolName(options.agent);
|
|
297
431
|
const metadata = {
|
|
298
432
|
type: "subagent"
|
|
@@ -313,7 +447,7 @@ export const createSubAgentTool = (options) => {
|
|
|
313
447
|
schema: subAgentToolInputSchema,
|
|
314
448
|
requiresApproval: options.requiresApproval,
|
|
315
449
|
metadata: cloneMetadata(metadata, options.metadata),
|
|
316
|
-
execute: async (input) => {
|
|
450
|
+
execute: async (input, executionContext) => {
|
|
317
451
|
await options.onStart?.({
|
|
318
452
|
toolName,
|
|
319
453
|
childAgentId: options.agent.id,
|
|
@@ -328,14 +462,38 @@ export const createSubAgentTool = (options) => {
|
|
|
328
462
|
if (options.parentAgentId) {
|
|
329
463
|
childMetadata.parentAgentId = options.parentAgentId;
|
|
330
464
|
}
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
465
|
+
const toolCallId = executionContext?.toolCall.id;
|
|
466
|
+
const checkpoint = runtimeState?.childRuns?.find((childRun) => childRun.toolCallId === toolCallId && childRun.resumeState);
|
|
467
|
+
const childApprovalResponses = checkpoint
|
|
468
|
+
? (runtimeState?.approvalHistory ?? [])
|
|
469
|
+
.filter((resolution) => resolution.kind === "subagent" &&
|
|
470
|
+
resolution.toolCallId === toolCallId &&
|
|
471
|
+
resolution.childRunId === checkpoint.runId &&
|
|
472
|
+
resolution.childApprovalRequestId)
|
|
473
|
+
.map((resolution) => ({
|
|
474
|
+
provider: resolution.provider,
|
|
475
|
+
approvalRequestId: resolution.childApprovalRequestId,
|
|
476
|
+
approve: resolution.approve,
|
|
477
|
+
reason: resolution.reason
|
|
478
|
+
}))
|
|
479
|
+
: [];
|
|
480
|
+
const output = checkpoint?.resumeState
|
|
481
|
+
? await resumeAgent(options.agent, {
|
|
482
|
+
state: checkpoint.resumeState,
|
|
483
|
+
approvals: childApprovalResponses,
|
|
484
|
+
scope: options.scope,
|
|
485
|
+
context: executionContext?.context,
|
|
486
|
+
maxSteps: options.maxSteps
|
|
487
|
+
})
|
|
488
|
+
: await runAgent(options.agent, {
|
|
489
|
+
prompt: input.prompt,
|
|
490
|
+
system: joinInstructions(options.system, input.system),
|
|
491
|
+
parentRunId: options.parentRunId,
|
|
492
|
+
scope: options.scope,
|
|
493
|
+
context: executionContext?.context,
|
|
494
|
+
maxSteps: options.maxSteps,
|
|
495
|
+
metadata: cloneMetadata(options.metadata, childMetadata)
|
|
496
|
+
});
|
|
339
497
|
const childRun = {
|
|
340
498
|
runId: output.state.runId,
|
|
341
499
|
status: output.status,
|
|
@@ -344,6 +502,9 @@ export const createSubAgentTool = (options) => {
|
|
|
344
502
|
toolCalls: countToolCallsInSteps(output.steps),
|
|
345
503
|
toolErrors: countToolErrors(output.toolResults)
|
|
346
504
|
};
|
|
505
|
+
if (toolCallId) {
|
|
506
|
+
childRun.toolCallId = toolCallId;
|
|
507
|
+
}
|
|
347
508
|
if (output.state.agentId) {
|
|
348
509
|
childRun.agentId = output.state.agentId;
|
|
349
510
|
}
|
|
@@ -366,7 +527,38 @@ export const createSubAgentTool = (options) => {
|
|
|
366
527
|
if (output.state.metadata) {
|
|
367
528
|
childRun.metadata = output.state.metadata;
|
|
368
529
|
}
|
|
530
|
+
if (output.status === "waiting_approval" && output.state.pendingApprovals.length) {
|
|
531
|
+
childRun.resumeState = output.state;
|
|
532
|
+
}
|
|
369
533
|
await options.onFinish?.(childRun);
|
|
534
|
+
if (childRun.resumeState) {
|
|
535
|
+
const approvals = childRun.resumeState.pendingApprovals.map((approval) => {
|
|
536
|
+
const id = `subapproval_${createHash("sha256")
|
|
537
|
+
.update(`${options.parentRunId ?? ""}\0${toolCallId ?? ""}\0${childRun.runId}\0${approval.id}`)
|
|
538
|
+
.digest("hex")}`;
|
|
539
|
+
return {
|
|
540
|
+
kind: "subagent",
|
|
541
|
+
provider: approval.provider,
|
|
542
|
+
id,
|
|
543
|
+
name: approval.name,
|
|
544
|
+
arguments: approval.arguments,
|
|
545
|
+
serverLabel: approval.serverLabel,
|
|
546
|
+
toolCallId,
|
|
547
|
+
step: executionContext?.step,
|
|
548
|
+
childRunId: childRun.runId,
|
|
549
|
+
childAgentId: childRun.agentId,
|
|
550
|
+
childApprovalRequestId: approval.id,
|
|
551
|
+
rawData: {
|
|
552
|
+
type: "subagent_approval_request",
|
|
553
|
+
childRunId: childRun.runId,
|
|
554
|
+
childAgentId: childRun.agentId ?? null,
|
|
555
|
+
childApprovalRequestId: approval.id,
|
|
556
|
+
approval: approval.rawData
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
});
|
|
560
|
+
throw new ToolExecutionSuspendedError(approvals);
|
|
561
|
+
}
|
|
370
562
|
return serializeJsonValue(childRun);
|
|
371
563
|
}
|
|
372
564
|
};
|
|
@@ -477,7 +669,71 @@ const runGuardrails = async (agent, state, stage, guardrails, requestFactory) =>
|
|
|
477
669
|
}
|
|
478
670
|
return undefined;
|
|
479
671
|
};
|
|
672
|
+
const bindDurableRuntime = (agent, input, state, executionEnvironment) => {
|
|
673
|
+
const policy = {
|
|
674
|
+
...(agent.policy ?? {}),
|
|
675
|
+
...(input.policy ?? {})
|
|
676
|
+
};
|
|
677
|
+
if (state.harness && !agent.harness) {
|
|
678
|
+
throw new ConflictError(`Agent run "${state.runId}" is bound to harness "${state.harness.id}", but the current agent has no harness binding.`);
|
|
679
|
+
}
|
|
680
|
+
if (state.harness && agent.harness) {
|
|
681
|
+
if (state.harness.id !== agent.harness.id ||
|
|
682
|
+
state.harness.version !== agent.harness.version ||
|
|
683
|
+
state.harness.fingerprint !== agent.harness.fingerprint) {
|
|
684
|
+
throw new ConflictError(`Agent run "${state.runId}" was created by a different harness fingerprint.`);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
else if (!state.harness && agent.harness) {
|
|
688
|
+
if (!policy?.allowLegacyHarnessResume) {
|
|
689
|
+
throw new ConflictError(`Agent run "${state.runId}" predates harness binding; set allowLegacyHarnessResume only for an explicit migration.`);
|
|
690
|
+
}
|
|
691
|
+
state.harness = agent.harness;
|
|
692
|
+
}
|
|
693
|
+
if (state.executionEnvironment && !executionEnvironment) {
|
|
694
|
+
throw new ConflictError(`Agent run "${state.runId}" is bound to execution environment "${state.executionEnvironment.environmentId}".`);
|
|
695
|
+
}
|
|
696
|
+
if (state.executionEnvironment && executionEnvironment) {
|
|
697
|
+
if (state.executionEnvironment.environmentId !== executionEnvironment.environmentId ||
|
|
698
|
+
state.executionEnvironment.environmentVersion !== executionEnvironment.environmentVersion ||
|
|
699
|
+
state.executionEnvironment.fingerprint !== executionEnvironment.fingerprint ||
|
|
700
|
+
state.executionEnvironment.workspaceId !== executionEnvironment.workspaceId) {
|
|
701
|
+
throw new ConflictError(`Agent run "${state.runId}" was created in a different execution environment.`);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
else if (!state.executionEnvironment && executionEnvironment) {
|
|
705
|
+
if (!policy?.allowLegacyExecutionEnvironmentResume) {
|
|
706
|
+
throw new ConflictError(`Agent run "${state.runId}" predates execution-environment binding; enable the explicit migration policy to resume it.`);
|
|
707
|
+
}
|
|
708
|
+
state.executionEnvironment = executionEnvironment;
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
const validateHarnessBinding = (binding) => {
|
|
712
|
+
if (!binding) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (binding.schemaVersion !== 1 ||
|
|
716
|
+
binding.algorithm !== "sha256" ||
|
|
717
|
+
!binding.id ||
|
|
718
|
+
!binding.version ||
|
|
719
|
+
!/^sha256:[0-9a-f]{64}$/.test(binding.fingerprint)) {
|
|
720
|
+
throw new ValidationError("Agent harness binding is invalid.");
|
|
721
|
+
}
|
|
722
|
+
};
|
|
480
723
|
const resolveContext = async (agent, input) => {
|
|
724
|
+
validateHarnessBinding(agent.harness);
|
|
725
|
+
const executionEnvironment = input.executionEnvironment ?? agent.executionEnvironment;
|
|
726
|
+
const executionEnvironmentBinding = executionEnvironment
|
|
727
|
+
? createAgentExecutionEnvironmentBinding(executionEnvironment.manifest)
|
|
728
|
+
: undefined;
|
|
729
|
+
let parsedContext = input.context;
|
|
730
|
+
if (agent.contextSchema) {
|
|
731
|
+
const result = await agent.contextSchema.safeParseAsync(input.context);
|
|
732
|
+
if (!result.success) {
|
|
733
|
+
throw new ValidationError(`Invalid agent context: ${result.error.message}`);
|
|
734
|
+
}
|
|
735
|
+
parsedContext = result.data;
|
|
736
|
+
}
|
|
481
737
|
ensureValidIdempotencyInput(input, agent.store);
|
|
482
738
|
const inputScope = input.scope ?? input.handoff?.scope;
|
|
483
739
|
ensureValidScope(inputScope);
|
|
@@ -494,7 +750,7 @@ const resolveContext = async (agent, input) => {
|
|
|
494
750
|
const maxSteps = Math.max(1, input.maxSteps ?? agent.maxSteps ?? 1);
|
|
495
751
|
const metadata = cloneMetadata(agent.metadata, input.metadata, input.handoff?.metadata);
|
|
496
752
|
const prepared = await prepareFreshMessages(agent, input, runId);
|
|
497
|
-
const candidate = createBaseState(agent.model.provider, agent.model.modelId, prepared.messages, maxSteps, metadata, agent.id, runId, input.handoff, input.parentRunId, input.idempotencyKey, inputScope);
|
|
753
|
+
const candidate = createBaseState(agent.model.provider, agent.model.modelId, prepared.messages, maxSteps, metadata, agent.id, runId, input.handoff, input.parentRunId, input.idempotencyKey, inputScope, resolveAgentOutputMode(agent), agent.harness, executionEnvironmentBinding);
|
|
498
754
|
const claim = await agent.store.claimIdempotencyKey(candidate);
|
|
499
755
|
if (claim.claimed) {
|
|
500
756
|
return {
|
|
@@ -502,6 +758,8 @@ const resolveContext = async (agent, input) => {
|
|
|
502
758
|
messages: prepared.messages,
|
|
503
759
|
remainingSteps: maxSteps,
|
|
504
760
|
memoryMessages: prepared.memoryMessages,
|
|
761
|
+
context: parsedContext,
|
|
762
|
+
executionEnvironment,
|
|
505
763
|
fresh: true
|
|
506
764
|
};
|
|
507
765
|
}
|
|
@@ -516,8 +774,9 @@ const resolveContext = async (agent, input) => {
|
|
|
516
774
|
ensureValidStateInput(normalizedInput);
|
|
517
775
|
const metadata = cloneMetadata(agent.metadata, loadedState?.metadata, input.metadata, input.handoff?.metadata);
|
|
518
776
|
if (loadedState) {
|
|
777
|
+
bindDurableRuntime(agent, input, loadedState, executionEnvironmentBinding);
|
|
519
778
|
const maxSteps = input.maxSteps ?? loadedState.maxSteps;
|
|
520
|
-
const resumed = applyApprovalResponses(loadedState.messages, input.approvals, loadedState.pendingApprovals);
|
|
779
|
+
const resumed = await applyApprovalResponses(loadedState.messages, input.approvals, loadedState.pendingApprovals, loadedState.approvalHistory, agent.toolApprovalSigner);
|
|
521
780
|
return {
|
|
522
781
|
state: {
|
|
523
782
|
...loadedState,
|
|
@@ -531,12 +790,15 @@ const resolveContext = async (agent, input) => {
|
|
|
531
790
|
maxSteps,
|
|
532
791
|
messages: resumed.messages,
|
|
533
792
|
pendingApprovals: resumed.pendingApprovals,
|
|
793
|
+
approvalHistory: resumed.approvalHistory,
|
|
534
794
|
metadata,
|
|
535
795
|
updatedAt: Date.now()
|
|
536
796
|
},
|
|
537
797
|
messages: resumed.messages,
|
|
538
798
|
remainingSteps: Math.max(0, maxSteps - loadedState.currentStep),
|
|
539
799
|
memoryMessages: [],
|
|
800
|
+
context: parsedContext,
|
|
801
|
+
executionEnvironment,
|
|
540
802
|
fresh: false
|
|
541
803
|
};
|
|
542
804
|
}
|
|
@@ -544,10 +806,12 @@ const resolveContext = async (agent, input) => {
|
|
|
544
806
|
const maxSteps = Math.max(1, input.maxSteps ?? agent.maxSteps ?? 1);
|
|
545
807
|
const prepared = await prepareFreshMessages(agent, input, runId);
|
|
546
808
|
return {
|
|
547
|
-
state: createBaseState(agent.model.provider, agent.model.modelId, prepared.messages, maxSteps, metadata, agent.id, runId, input.handoff, input.parentRunId, input.idempotencyKey, inputScope),
|
|
809
|
+
state: createBaseState(agent.model.provider, agent.model.modelId, prepared.messages, maxSteps, metadata, agent.id, runId, input.handoff, input.parentRunId, input.idempotencyKey, inputScope, resolveAgentOutputMode(agent), agent.harness, executionEnvironmentBinding),
|
|
548
810
|
messages: prepared.messages,
|
|
549
811
|
remainingSteps: maxSteps,
|
|
550
812
|
memoryMessages: prepared.memoryMessages,
|
|
813
|
+
context: parsedContext,
|
|
814
|
+
executionEnvironment,
|
|
551
815
|
fresh: true
|
|
552
816
|
};
|
|
553
817
|
};
|
|
@@ -566,6 +830,57 @@ const canonicalJson = (value) => {
|
|
|
566
830
|
const durableToolCallId = (runId, step, providerToolCallId, toolName, input) => `tool_${createHash("sha256")
|
|
567
831
|
.update(`${runId}\0${step}\0${providerToolCallId}\0${toolName}\0${canonicalJson(input)}`)
|
|
568
832
|
.digest("hex")}`;
|
|
833
|
+
const wrapToolWithExecutionEnvironment = (tool, session) => {
|
|
834
|
+
const authorize = async (input, context, phase) => session.authorize({
|
|
835
|
+
manifest: session.manifest,
|
|
836
|
+
binding: session.binding,
|
|
837
|
+
tool,
|
|
838
|
+
toolCall: context.toolCall,
|
|
839
|
+
input,
|
|
840
|
+
context,
|
|
841
|
+
phase
|
|
842
|
+
});
|
|
843
|
+
const environmentGuardrail = async ({ input, context }) => {
|
|
844
|
+
const decision = await authorize(input, context, "preflight");
|
|
845
|
+
return decision.decision === "deny"
|
|
846
|
+
? {
|
|
847
|
+
triggered: true,
|
|
848
|
+
reason: decision.reason,
|
|
849
|
+
metadata: decision.metadata
|
|
850
|
+
}
|
|
851
|
+
: undefined;
|
|
852
|
+
};
|
|
853
|
+
return {
|
|
854
|
+
...tool,
|
|
855
|
+
approvalVersion: [
|
|
856
|
+
tool.approvalVersion,
|
|
857
|
+
`environment:${session.binding.fingerprint}`
|
|
858
|
+
].filter(Boolean).join("|"),
|
|
859
|
+
inputGuardrails: [
|
|
860
|
+
environmentGuardrail,
|
|
861
|
+
...(tool.inputGuardrails ?? [])
|
|
862
|
+
],
|
|
863
|
+
execute: async (input, context) => {
|
|
864
|
+
if (!context) {
|
|
865
|
+
throw new ValidationError(`Tool "${tool.name}" requires an execution environment context.`);
|
|
866
|
+
}
|
|
867
|
+
const decision = await authorize(input, context, "execute");
|
|
868
|
+
if (decision.decision === "deny") {
|
|
869
|
+
throw new GuardrailTriggeredError("tool-input", decision.reason, { metadata: decision.metadata });
|
|
870
|
+
}
|
|
871
|
+
const request = {
|
|
872
|
+
manifest: session.manifest,
|
|
873
|
+
binding: session.binding,
|
|
874
|
+
tool,
|
|
875
|
+
toolCall: context.toolCall,
|
|
876
|
+
input,
|
|
877
|
+
context,
|
|
878
|
+
phase: "execute"
|
|
879
|
+
};
|
|
880
|
+
return session.execute(request, () => tool.execute(input, context));
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
};
|
|
569
884
|
const wrapToolWithJournal = (agent, state, tool) => {
|
|
570
885
|
const store = agent.store;
|
|
571
886
|
if (!store?.claimToolExecution || !store.loadToolExecution || !store.completeToolExecution) {
|
|
@@ -637,14 +952,141 @@ const wrapToolWithJournal = (agent, state, tool) => {
|
|
|
637
952
|
}
|
|
638
953
|
};
|
|
639
954
|
};
|
|
640
|
-
const
|
|
955
|
+
const defaultEstimateInputTokens = (messages) => Math.ceil(new TextEncoder().encode(JSON.stringify(messages)).byteLength / 4);
|
|
956
|
+
const validateCompactionOptions = (options) => {
|
|
957
|
+
if (options.maxMessages !== undefined &&
|
|
958
|
+
(!Number.isSafeInteger(options.maxMessages) || options.maxMessages < 2)) {
|
|
959
|
+
throw new ValidationError("Agent compaction maxMessages must be an integer greater than or equal to 2.");
|
|
960
|
+
}
|
|
961
|
+
if (options.maxEstimatedInputTokens !== undefined &&
|
|
962
|
+
(!Number.isSafeInteger(options.maxEstimatedInputTokens) || options.maxEstimatedInputTokens < 1)) {
|
|
963
|
+
throw new ValidationError("Agent compaction maxEstimatedInputTokens must be a positive integer.");
|
|
964
|
+
}
|
|
965
|
+
if (options.keepRecentMessages !== undefined &&
|
|
966
|
+
(!Number.isSafeInteger(options.keepRecentMessages) || options.keepRecentMessages < 1)) {
|
|
967
|
+
throw new ValidationError("Agent compaction keepRecentMessages must be a positive integer.");
|
|
968
|
+
}
|
|
969
|
+
if (options.maxMessages === undefined && options.maxEstimatedInputTokens === undefined) {
|
|
970
|
+
throw new ValidationError("Agent compaction requires maxMessages or maxEstimatedInputTokens.");
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
const compactAgentMessages = async (options, state, messages, beforeStep, context, abortSignal) => {
|
|
974
|
+
validateCompactionOptions(options);
|
|
975
|
+
if (state.pendingApprovals.length) {
|
|
976
|
+
return undefined;
|
|
977
|
+
}
|
|
978
|
+
const estimateTokens = options.estimateTokens ?? defaultEstimateInputTokens;
|
|
979
|
+
const estimatedTokensBefore = estimateTokens(messages);
|
|
980
|
+
const reasons = [];
|
|
981
|
+
if (options.maxMessages !== undefined && messages.length > options.maxMessages) {
|
|
982
|
+
reasons.push("message-count");
|
|
983
|
+
}
|
|
984
|
+
if (options.maxEstimatedInputTokens !== undefined &&
|
|
985
|
+
estimatedTokensBefore > options.maxEstimatedInputTokens) {
|
|
986
|
+
reasons.push("estimated-input-tokens");
|
|
987
|
+
}
|
|
988
|
+
if (!reasons.length) {
|
|
989
|
+
return undefined;
|
|
990
|
+
}
|
|
991
|
+
let systemCount = 0;
|
|
992
|
+
while (messages[systemCount]?.role === "system") {
|
|
993
|
+
systemCount += 1;
|
|
994
|
+
}
|
|
995
|
+
const maxTailForMessageLimit = options.maxMessages === undefined
|
|
996
|
+
? Number.POSITIVE_INFINITY
|
|
997
|
+
: Math.max(1, options.maxMessages - systemCount - 1);
|
|
998
|
+
const keepRecentMessages = Math.min(options.keepRecentMessages ?? 8, maxTailForMessageLimit);
|
|
999
|
+
let cut = Math.max(systemCount, messages.length - keepRecentMessages);
|
|
1000
|
+
while (cut > systemCount && messages[cut]?.role === "tool") {
|
|
1001
|
+
cut -= 1;
|
|
1002
|
+
}
|
|
1003
|
+
if (cut <= systemCount) {
|
|
1004
|
+
throw new ValidationError("Agent compaction cannot satisfy its limits without removing protected messages.");
|
|
1005
|
+
}
|
|
1006
|
+
const compactedMessages = structuredClone(messages.slice(systemCount, cut));
|
|
1007
|
+
const retainedMessages = structuredClone(messages.slice(cut));
|
|
1008
|
+
const sourceDigest = fingerprintAgentHarness(messages);
|
|
1009
|
+
const id = `cmp_${createHash("sha256")
|
|
1010
|
+
.update(`${state.runId}\0${beforeStep}\0${sourceDigest}`)
|
|
1011
|
+
.digest("hex")}`;
|
|
1012
|
+
const result = await options.compactor({
|
|
1013
|
+
runId: state.runId,
|
|
1014
|
+
agentId: state.agentId,
|
|
1015
|
+
scope: state.scope,
|
|
1016
|
+
beforeStep,
|
|
1017
|
+
context,
|
|
1018
|
+
messages: compactedMessages,
|
|
1019
|
+
retainedMessages,
|
|
1020
|
+
reasons,
|
|
1021
|
+
estimatedTokensBefore,
|
|
1022
|
+
sourceDigest,
|
|
1023
|
+
idempotencyKey: id,
|
|
1024
|
+
metadata: state.metadata,
|
|
1025
|
+
abortSignal
|
|
1026
|
+
});
|
|
1027
|
+
const summary = result.summary.trim();
|
|
1028
|
+
if (!summary) {
|
|
1029
|
+
throw new ValidationError("Agent compactor returned an empty summary.");
|
|
1030
|
+
}
|
|
1031
|
+
const compacted = [
|
|
1032
|
+
...structuredClone(messages.slice(0, systemCount)),
|
|
1033
|
+
createTextMessage("assistant", `[Compacted prior conversation]\n${summary}`),
|
|
1034
|
+
...retainedMessages
|
|
1035
|
+
];
|
|
1036
|
+
const estimatedTokensAfter = estimateTokens(compacted);
|
|
1037
|
+
if (compacted.length >= messages.length || estimatedTokensAfter >= estimatedTokensBefore) {
|
|
1038
|
+
throw new ValidationError("Agent compaction must reduce both message count and estimated input tokens.");
|
|
1039
|
+
}
|
|
1040
|
+
if (options.maxMessages !== undefined && compacted.length > options.maxMessages) {
|
|
1041
|
+
throw new ValidationError("Agent compaction result still exceeds maxMessages.");
|
|
1042
|
+
}
|
|
1043
|
+
if (options.maxEstimatedInputTokens !== undefined &&
|
|
1044
|
+
estimatedTokensAfter > options.maxEstimatedInputTokens) {
|
|
1045
|
+
throw new ValidationError("Agent compaction result still exceeds maxEstimatedInputTokens.");
|
|
1046
|
+
}
|
|
1047
|
+
const resultDigest = fingerprintAgentHarness(compacted);
|
|
1048
|
+
return {
|
|
1049
|
+
messages: compacted,
|
|
1050
|
+
record: {
|
|
1051
|
+
id,
|
|
1052
|
+
beforeStep,
|
|
1053
|
+
createdAt: Date.now(),
|
|
1054
|
+
reasons,
|
|
1055
|
+
sourceDigest,
|
|
1056
|
+
resultDigest,
|
|
1057
|
+
summaryDigest: fingerprintAgentHarness(summary),
|
|
1058
|
+
summary,
|
|
1059
|
+
messageCountBefore: messages.length,
|
|
1060
|
+
messageCountAfter: compacted.length,
|
|
1061
|
+
compactedMessageCount: compactedMessages.length,
|
|
1062
|
+
retainedMessageCount: retainedMessages.length,
|
|
1063
|
+
estimatedTokensBefore,
|
|
1064
|
+
estimatedTokensAfter,
|
|
1065
|
+
usage: result.usage,
|
|
1066
|
+
metadata: result.metadata
|
|
1067
|
+
}
|
|
1068
|
+
};
|
|
1069
|
+
};
|
|
1070
|
+
const createGenerateOptions = (agent, state, input, messages, maxSteps, context, executionEnvironmentSession, abortSignal = input.abortSignal, onCompaction) => {
|
|
641
1071
|
const tools = { ...(toToolSet(input.tools ?? agent.tools) ?? {}) };
|
|
642
1072
|
for (const subagent of agent.subagents ?? []) {
|
|
643
1073
|
const subagentTool = createSubAgentTool({
|
|
644
1074
|
...subagent,
|
|
1075
|
+
agent: {
|
|
1076
|
+
...subagent.agent,
|
|
1077
|
+
store: subagent.agent.store ?? agent.store,
|
|
1078
|
+
memory: subagent.agent.memory ?? agent.memory,
|
|
1079
|
+
executionEnvironment: subagent.agent.executionEnvironment ??
|
|
1080
|
+
input.executionEnvironment ??
|
|
1081
|
+
agent.executionEnvironment,
|
|
1082
|
+
compaction: subagent.agent.compaction ?? (input.compaction === false
|
|
1083
|
+
? undefined
|
|
1084
|
+
: input.compaction ?? agent.compaction)
|
|
1085
|
+
},
|
|
645
1086
|
parentRunId: state.runId,
|
|
646
1087
|
parentAgentId: state.agentId,
|
|
647
1088
|
scope: state.scope,
|
|
1089
|
+
runtimeState: state,
|
|
648
1090
|
onStart: async ({ toolName, childAgentId }) => {
|
|
649
1091
|
await emitTelemetryEvent(agent, {
|
|
650
1092
|
type: "subagent-start",
|
|
@@ -655,7 +1097,12 @@ const createGenerateOptions = (agent, state, input, messages, maxSteps, abortSig
|
|
|
655
1097
|
});
|
|
656
1098
|
},
|
|
657
1099
|
onFinish: async (childRun) => {
|
|
658
|
-
state.childRuns = [
|
|
1100
|
+
state.childRuns = [
|
|
1101
|
+
...(state.childRuns ?? []).filter((existing) => childRun.toolCallId
|
|
1102
|
+
? existing.toolCallId !== childRun.toolCallId
|
|
1103
|
+
: existing.runId !== childRun.runId),
|
|
1104
|
+
childRun
|
|
1105
|
+
];
|
|
659
1106
|
await emitTelemetryEvent(agent, {
|
|
660
1107
|
type: "subagent-finish",
|
|
661
1108
|
runId: state.runId,
|
|
@@ -671,12 +1118,20 @@ const createGenerateOptions = (agent, state, input, messages, maxSteps, abortSig
|
|
|
671
1118
|
}
|
|
672
1119
|
for (const [name, tool] of Object.entries(tools)) {
|
|
673
1120
|
if (isCallableToolDefinition(tool)) {
|
|
674
|
-
|
|
1121
|
+
const environmentTool = executionEnvironmentSession
|
|
1122
|
+
? wrapToolWithExecutionEnvironment(tool, executionEnvironmentSession)
|
|
1123
|
+
: tool;
|
|
1124
|
+
tools[name] = tool.metadata?.type === "subagent"
|
|
1125
|
+
? environmentTool
|
|
1126
|
+
: wrapToolWithJournal(agent, state, environmentTool);
|
|
675
1127
|
}
|
|
676
1128
|
}
|
|
677
1129
|
const finalTools = Object.keys(tools).length ? tools : undefined;
|
|
678
1130
|
const budget = input.policy?.budget ?? agent.policy?.budget;
|
|
679
1131
|
const runPolicy = resolveRunPolicy(agent, input);
|
|
1132
|
+
const compaction = input.compaction === false
|
|
1133
|
+
? undefined
|
|
1134
|
+
: input.compaction ?? agent.compaction;
|
|
680
1135
|
let checkpointState = cloneState(state);
|
|
681
1136
|
let reservedToolCalls = 0;
|
|
682
1137
|
const requestedMaxTokens = input.maxTokens ?? agent.maxTokens;
|
|
@@ -687,16 +1142,62 @@ const createGenerateOptions = (agent, state, input, messages, maxSteps, abortSig
|
|
|
687
1142
|
budgetStatus?.remaining.totalTokens
|
|
688
1143
|
].filter((value) => value !== undefined);
|
|
689
1144
|
const maxTokens = tokenCeilings.length ? Math.min(...tokenCeilings) : undefined;
|
|
1145
|
+
const requestedToolExecution = input.toolExecution ?? agent.toolExecution;
|
|
1146
|
+
const toolExecution = agent.subagents?.length
|
|
1147
|
+
? {
|
|
1148
|
+
...requestedToolExecution,
|
|
1149
|
+
parallel: false,
|
|
1150
|
+
maxConcurrency: 1
|
|
1151
|
+
}
|
|
1152
|
+
: requestedToolExecution;
|
|
690
1153
|
return {
|
|
691
1154
|
model: agent.model,
|
|
692
1155
|
messages,
|
|
693
1156
|
tools: finalTools,
|
|
694
1157
|
toolChoice: input.toolChoice,
|
|
695
|
-
toolExecution
|
|
1158
|
+
toolExecution,
|
|
696
1159
|
toolApprovalPolicy: input.toolApprovalPolicy ?? agent.toolApprovalPolicy,
|
|
1160
|
+
toolApprovalSigner: agent.toolApprovalSigner,
|
|
1161
|
+
toolApprovalResolutions: state.approvalHistory,
|
|
1162
|
+
toolContext: {
|
|
1163
|
+
context,
|
|
1164
|
+
runId: state.runId,
|
|
1165
|
+
agentId: state.agentId,
|
|
1166
|
+
scope: state.scope,
|
|
1167
|
+
metadata: state.metadata,
|
|
1168
|
+
executionEnvironment: executionEnvironmentSession
|
|
1169
|
+
},
|
|
697
1170
|
onToolApprovalDecision: async (event) => {
|
|
698
1171
|
await emitToolApprovalTelemetry(agent, state, event);
|
|
699
1172
|
},
|
|
1173
|
+
prepareModelMessages: compaction
|
|
1174
|
+
? async ({ messages: activeMessages, step }) => {
|
|
1175
|
+
const compacted = await compactAgentMessages(compaction, checkpointState, activeMessages, step, context, abortSignal);
|
|
1176
|
+
if (!compacted) {
|
|
1177
|
+
return undefined;
|
|
1178
|
+
}
|
|
1179
|
+
checkpointState = {
|
|
1180
|
+
...checkpointState,
|
|
1181
|
+
messages: compacted.messages,
|
|
1182
|
+
usage: aggregateTokenUsage([
|
|
1183
|
+
checkpointState.usage,
|
|
1184
|
+
compacted.record.usage
|
|
1185
|
+
]),
|
|
1186
|
+
compactions: [
|
|
1187
|
+
...(checkpointState.compactions ?? []).filter((existing) => existing.id !== compacted.record.id),
|
|
1188
|
+
compacted.record
|
|
1189
|
+
],
|
|
1190
|
+
updatedAt: Date.now()
|
|
1191
|
+
};
|
|
1192
|
+
state.messages = compacted.messages;
|
|
1193
|
+
state.usage = checkpointState.usage;
|
|
1194
|
+
state.compactions = checkpointState.compactions;
|
|
1195
|
+
await persistState(agent, checkpointState, runPolicy);
|
|
1196
|
+
state.revision = checkpointState.revision;
|
|
1197
|
+
await onCompaction?.(compacted.record);
|
|
1198
|
+
return compacted.messages;
|
|
1199
|
+
}
|
|
1200
|
+
: undefined,
|
|
700
1201
|
onBeforeModelStep: ({ step }) => {
|
|
701
1202
|
if (!budget)
|
|
702
1203
|
return;
|
|
@@ -711,12 +1212,19 @@ const createGenerateOptions = (agent, state, input, messages, maxSteps, abortSig
|
|
|
711
1212
|
});
|
|
712
1213
|
}
|
|
713
1214
|
},
|
|
714
|
-
onModelStep: async ({ request, response, step, toolCalls }) => {
|
|
1215
|
+
onModelStep: async ({ request, response, step, toolCalls, approvalRequests }) => {
|
|
715
1216
|
if (!agent.store)
|
|
716
1217
|
return;
|
|
717
1218
|
const responseSnapshot = snapshotResponse(response);
|
|
718
|
-
const approvals =
|
|
719
|
-
|
|
1219
|
+
const approvals = [
|
|
1220
|
+
...approvalRequests,
|
|
1221
|
+
...getAgentApprovalRequests(responseSnapshot.messages)
|
|
1222
|
+
];
|
|
1223
|
+
const crossedCompactionBoundary = checkpointState.compactions?.some((record) => record.beforeStep === step &&
|
|
1224
|
+
record.resultDigest === fingerprintAgentHarness(request.messages));
|
|
1225
|
+
const requestOffset = crossedCompactionBoundary
|
|
1226
|
+
? 0
|
|
1227
|
+
: messagePrefixLength(checkpointState.messages, request.messages);
|
|
720
1228
|
const timing = getGenerateTextStepTiming(request);
|
|
721
1229
|
const finishedAt = timing?.finishedAt ?? Date.now();
|
|
722
1230
|
const checkpointStep = {
|
|
@@ -787,6 +1295,14 @@ const createGenerateOptions = (agent, state, input, messages, maxSteps, abortSig
|
|
|
787
1295
|
temperature: input.temperature ?? agent.temperature,
|
|
788
1296
|
maxTokens,
|
|
789
1297
|
reasoning: input.reasoning ?? agent.reasoning,
|
|
1298
|
+
structuredOutput: agent.outputSchema && resolveAgentOutputMode(agent) === "native"
|
|
1299
|
+
? {
|
|
1300
|
+
schema: agent.outputSchema,
|
|
1301
|
+
mode: "native",
|
|
1302
|
+
name: agent.outputName,
|
|
1303
|
+
description: agent.outputDescription
|
|
1304
|
+
}
|
|
1305
|
+
: undefined,
|
|
790
1306
|
providerOptions: input.providerOptions ?? agent.providerOptions,
|
|
791
1307
|
abortSignal,
|
|
792
1308
|
timeoutMs: input.timeoutMs,
|
|
@@ -863,6 +1379,36 @@ const createAgentAbortContext = (inputAbortSignal, policy) => {
|
|
|
863
1379
|
isTimedOut: () => timedOut
|
|
864
1380
|
};
|
|
865
1381
|
};
|
|
1382
|
+
const acquireExecutionEnvironment = async (environment, state, context, abortSignal) => {
|
|
1383
|
+
if (!environment) {
|
|
1384
|
+
return undefined;
|
|
1385
|
+
}
|
|
1386
|
+
const expected = state.executionEnvironment;
|
|
1387
|
+
if (!expected) {
|
|
1388
|
+
throw new ConflictError(`Agent run "${state.runId}" has no durable execution-environment binding.`);
|
|
1389
|
+
}
|
|
1390
|
+
const session = await environment.acquire({
|
|
1391
|
+
runId: state.runId,
|
|
1392
|
+
agentId: state.agentId,
|
|
1393
|
+
scope: state.scope,
|
|
1394
|
+
context,
|
|
1395
|
+
metadata: state.metadata,
|
|
1396
|
+
abortSignal
|
|
1397
|
+
});
|
|
1398
|
+
const manifestBinding = createAgentExecutionEnvironmentBinding(session.manifest);
|
|
1399
|
+
if (session.binding.environmentId !== expected.environmentId ||
|
|
1400
|
+
session.binding.environmentVersion !== expected.environmentVersion ||
|
|
1401
|
+
session.binding.fingerprint !== expected.fingerprint ||
|
|
1402
|
+
session.binding.workspaceId !== expected.workspaceId ||
|
|
1403
|
+
manifestBinding.fingerprint !== expected.fingerprint) {
|
|
1404
|
+
await session.release?.({
|
|
1405
|
+
status: "failed",
|
|
1406
|
+
error: { message: "Execution environment returned a binding that differs from the durable run." }
|
|
1407
|
+
});
|
|
1408
|
+
throw new ConflictError(`Execution environment binding changed for agent run "${state.runId}".`);
|
|
1409
|
+
}
|
|
1410
|
+
return session;
|
|
1411
|
+
};
|
|
866
1412
|
const acquireAgentExecutionLease = async (agent, state, policy) => {
|
|
867
1413
|
const store = agent.store;
|
|
868
1414
|
if (policy?.leaseMode === "disabled" || !store?.acquireLease || !store.renewLease || !store.releaseLease) {
|
|
@@ -997,17 +1543,26 @@ export class Agent {
|
|
|
997
1543
|
id;
|
|
998
1544
|
model;
|
|
999
1545
|
instructions;
|
|
1546
|
+
contextSchema;
|
|
1000
1547
|
tools;
|
|
1001
1548
|
maxSteps;
|
|
1002
1549
|
temperature;
|
|
1003
1550
|
maxTokens;
|
|
1004
1551
|
reasoning;
|
|
1552
|
+
outputSchema;
|
|
1553
|
+
outputMode;
|
|
1554
|
+
outputName;
|
|
1555
|
+
outputDescription;
|
|
1005
1556
|
toolExecution;
|
|
1006
1557
|
toolApprovalPolicy;
|
|
1558
|
+
toolApprovalSigner;
|
|
1007
1559
|
inputGuardrails;
|
|
1008
1560
|
outputGuardrails;
|
|
1009
1561
|
providerOptions;
|
|
1010
1562
|
subagents;
|
|
1563
|
+
harness;
|
|
1564
|
+
executionEnvironment;
|
|
1565
|
+
compaction;
|
|
1011
1566
|
policy;
|
|
1012
1567
|
metadata;
|
|
1013
1568
|
store;
|
|
@@ -1023,17 +1578,26 @@ export class Agent {
|
|
|
1023
1578
|
id: this.id,
|
|
1024
1579
|
model: this.model,
|
|
1025
1580
|
instructions: this.instructions,
|
|
1581
|
+
contextSchema: this.contextSchema,
|
|
1026
1582
|
tools: this.tools,
|
|
1027
1583
|
maxSteps: this.maxSteps,
|
|
1028
1584
|
temperature: this.temperature,
|
|
1029
1585
|
maxTokens: this.maxTokens,
|
|
1030
1586
|
reasoning: this.reasoning,
|
|
1587
|
+
outputSchema: this.outputSchema,
|
|
1588
|
+
outputMode: this.outputMode,
|
|
1589
|
+
outputName: this.outputName,
|
|
1590
|
+
outputDescription: this.outputDescription,
|
|
1031
1591
|
toolExecution: this.toolExecution,
|
|
1032
1592
|
toolApprovalPolicy: this.toolApprovalPolicy,
|
|
1593
|
+
toolApprovalSigner: this.toolApprovalSigner,
|
|
1033
1594
|
inputGuardrails: this.inputGuardrails,
|
|
1034
1595
|
outputGuardrails: this.outputGuardrails,
|
|
1035
1596
|
providerOptions: this.providerOptions,
|
|
1036
1597
|
subagents: this.subagents,
|
|
1598
|
+
harness: this.harness,
|
|
1599
|
+
executionEnvironment: this.executionEnvironment,
|
|
1600
|
+
compaction: this.compaction,
|
|
1037
1601
|
policy: this.policy,
|
|
1038
1602
|
metadata: this.metadata,
|
|
1039
1603
|
store: this.store,
|
|
@@ -1058,6 +1622,8 @@ export const prepareSubagentsForAgent = (agent, options = {}) => {
|
|
|
1058
1622
|
const onTelemetryEvent = options.onTelemetryEvent ?? agent.onTelemetryEvent;
|
|
1059
1623
|
const toolApprovalPolicy = options.toolApprovalPolicy ?? agent.toolApprovalPolicy;
|
|
1060
1624
|
const toolExecution = options.toolExecution ?? agent.toolExecution;
|
|
1625
|
+
const executionEnvironment = options.executionEnvironment ?? agent.executionEnvironment;
|
|
1626
|
+
const compaction = options.compaction ?? agent.compaction;
|
|
1061
1627
|
const defaultMetadata = cloneMetadata(agent.metadata, options.metadata);
|
|
1062
1628
|
return {
|
|
1063
1629
|
...agent,
|
|
@@ -1072,6 +1638,8 @@ export const prepareSubagentsForAgent = (agent, options = {}) => {
|
|
|
1072
1638
|
onTelemetryEvent: subagent.agent.onTelemetryEvent ?? onTelemetryEvent,
|
|
1073
1639
|
toolApprovalPolicy: subagent.agent.toolApprovalPolicy ?? toolApprovalPolicy,
|
|
1074
1640
|
toolExecution: subagent.agent.toolExecution ?? toolExecution,
|
|
1641
|
+
executionEnvironment: subagent.agent.executionEnvironment ?? executionEnvironment,
|
|
1642
|
+
compaction: subagent.agent.compaction ?? compaction,
|
|
1075
1643
|
metadata: cloneMetadata(defaultMetadata, subagent.agent.metadata)
|
|
1076
1644
|
}
|
|
1077
1645
|
}))
|
|
@@ -1222,7 +1790,7 @@ export const runAgent = async (agent, input = {}) => {
|
|
|
1222
1790
|
context.state.status = currentStatus;
|
|
1223
1791
|
return toOutput(context.state);
|
|
1224
1792
|
}
|
|
1225
|
-
if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0
|
|
1793
|
+
if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0) {
|
|
1226
1794
|
context.state.status = currentStatus;
|
|
1227
1795
|
return toOutput(context.state);
|
|
1228
1796
|
}
|
|
@@ -1264,6 +1832,7 @@ export const runAgent = async (agent, input = {}) => {
|
|
|
1264
1832
|
inputGuardrail = await runGuardrails(agent, context.state, "input", agent.inputGuardrails, () => ({
|
|
1265
1833
|
runId: context.state.runId,
|
|
1266
1834
|
agentId: context.state.agentId,
|
|
1835
|
+
context: context.context,
|
|
1267
1836
|
state: cloneState(context.state),
|
|
1268
1837
|
messages: context.messages,
|
|
1269
1838
|
metadata: context.state.metadata
|
|
@@ -1293,10 +1862,21 @@ export const runAgent = async (agent, input = {}) => {
|
|
|
1293
1862
|
throw error;
|
|
1294
1863
|
}
|
|
1295
1864
|
const abortContext = createAgentAbortContext(mergeAbortSignals(input.abortSignal, executionLease.signal), policy);
|
|
1865
|
+
let executionEnvironmentSession;
|
|
1866
|
+
let executionEnvironmentStatus = "failed";
|
|
1867
|
+
let executionEnvironmentError;
|
|
1868
|
+
try {
|
|
1869
|
+
executionEnvironmentSession = await acquireExecutionEnvironment(context.executionEnvironment, context.state, context.context, abortContext.signal);
|
|
1870
|
+
}
|
|
1871
|
+
catch (error) {
|
|
1872
|
+
await executionLease.release();
|
|
1873
|
+
throw error;
|
|
1874
|
+
}
|
|
1296
1875
|
try {
|
|
1297
|
-
const result = await withAgentPolicyTimeout(generateText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, abortContext.signal)), abortContext);
|
|
1876
|
+
const result = await withAgentPolicyTimeout(generateText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, context.context, executionEnvironmentSession, abortContext.signal)), abortContext);
|
|
1298
1877
|
const cancelled = executionLease.cancelledState();
|
|
1299
1878
|
if (cancelled) {
|
|
1879
|
+
executionEnvironmentStatus = cancelled.status;
|
|
1300
1880
|
await emitRunFinishTelemetry(agent, cancelled);
|
|
1301
1881
|
return toOutput(cancelled);
|
|
1302
1882
|
}
|
|
@@ -1304,10 +1884,11 @@ export const runAgent = async (agent, input = {}) => {
|
|
|
1304
1884
|
throw new ConflictError(`Agent run "${context.state.runId}" lost its worker lease.`);
|
|
1305
1885
|
}
|
|
1306
1886
|
const newSteps = mapSteps(result.steps, context.state.currentStep, result.toolResults);
|
|
1307
|
-
let output = finalizeState(context.state, result, newSteps, result.toolResults);
|
|
1887
|
+
let output = finalizeState(agent, context.state, result, newSteps, result.toolResults);
|
|
1308
1888
|
const outputGuardrail = await runGuardrails(agent, output.state, "output", agent.outputGuardrails, () => ({
|
|
1309
1889
|
runId: output.state.runId,
|
|
1310
1890
|
agentId: output.state.agentId,
|
|
1891
|
+
context: context.context,
|
|
1311
1892
|
state: cloneState(output.state),
|
|
1312
1893
|
output,
|
|
1313
1894
|
metadata: output.state.metadata
|
|
@@ -1316,14 +1897,22 @@ export const runAgent = async (agent, input = {}) => {
|
|
|
1316
1897
|
output = toOutput(applyGuardrailFailure(output.state, "output", outputGuardrail));
|
|
1317
1898
|
}
|
|
1318
1899
|
await emitFinalizedStepTelemetry(agent, output.state, newSteps);
|
|
1319
|
-
await emitApprovalTelemetry(agent, output.state,
|
|
1900
|
+
await emitApprovalTelemetry(agent, output.state, [
|
|
1901
|
+
...(result.approvalRequests ?? []),
|
|
1902
|
+
...approvalsFromEvents(newSteps.flatMap((step) => step.response?.messages ?? []))
|
|
1903
|
+
]);
|
|
1320
1904
|
await persistState(agent, output.state, policy);
|
|
1321
1905
|
await emitRunFinishTelemetry(agent, output.state);
|
|
1906
|
+
executionEnvironmentStatus = output.status;
|
|
1322
1907
|
return output;
|
|
1323
1908
|
}
|
|
1324
1909
|
catch (error) {
|
|
1910
|
+
executionEnvironmentError = {
|
|
1911
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1912
|
+
};
|
|
1325
1913
|
const cancelled = executionLease.cancelledState();
|
|
1326
1914
|
if (cancelled) {
|
|
1915
|
+
executionEnvironmentStatus = cancelled.status;
|
|
1327
1916
|
await emitRunFinishTelemetry(agent, cancelled);
|
|
1328
1917
|
return toOutput(cancelled);
|
|
1329
1918
|
}
|
|
@@ -1339,6 +1928,7 @@ export const runAgent = async (agent, input = {}) => {
|
|
|
1339
1928
|
const timedOutState = createTerminalState(durableState, status, message);
|
|
1340
1929
|
await persistState(agent, timedOutState, policy);
|
|
1341
1930
|
await emitRunFinishTelemetry(agent, timedOutState);
|
|
1931
|
+
executionEnvironmentStatus = timedOutState.status;
|
|
1342
1932
|
return toOutput(timedOutState);
|
|
1343
1933
|
}
|
|
1344
1934
|
const durableState = agent.store
|
|
@@ -1347,9 +1937,14 @@ export const runAgent = async (agent, input = {}) => {
|
|
|
1347
1937
|
const failedState = createFailedState(durableState, error instanceof Error ? error.message : String(error));
|
|
1348
1938
|
await persistState(agent, failedState, policy);
|
|
1349
1939
|
await emitRunFinishTelemetry(agent, failedState);
|
|
1940
|
+
executionEnvironmentStatus = failedState.status;
|
|
1350
1941
|
throw error;
|
|
1351
1942
|
}
|
|
1352
1943
|
finally {
|
|
1944
|
+
await executionEnvironmentSession?.release?.({
|
|
1945
|
+
status: executionEnvironmentStatus,
|
|
1946
|
+
error: executionEnvironmentError
|
|
1947
|
+
});
|
|
1353
1948
|
await executionLease.release();
|
|
1354
1949
|
}
|
|
1355
1950
|
};
|
|
@@ -1360,6 +1955,7 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
1360
1955
|
});
|
|
1361
1956
|
const publish = (event, terminal = false) => broadcast.publish(event, { terminal });
|
|
1362
1957
|
let activeLease;
|
|
1958
|
+
let activeExecutionEnvironment;
|
|
1363
1959
|
const runner = (async () => {
|
|
1364
1960
|
const context = await resolveContext(agent, input);
|
|
1365
1961
|
const currentStatus = normalizeApprovalStatus(context.state.status);
|
|
@@ -1382,7 +1978,7 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
1382
1978
|
textStream: emptyAsyncIterable()
|
|
1383
1979
|
};
|
|
1384
1980
|
}
|
|
1385
|
-
if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0
|
|
1981
|
+
if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0) {
|
|
1386
1982
|
context.state.status = currentStatus;
|
|
1387
1983
|
broadcast.close();
|
|
1388
1984
|
return {
|
|
@@ -1433,6 +2029,7 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
1433
2029
|
inputGuardrail = await runGuardrails(agent, context.state, "input", agent.inputGuardrails, () => ({
|
|
1434
2030
|
runId: context.state.runId,
|
|
1435
2031
|
agentId: context.state.agentId,
|
|
2032
|
+
context: context.context,
|
|
1436
2033
|
state: cloneState(context.state),
|
|
1437
2034
|
messages: context.messages,
|
|
1438
2035
|
metadata: context.state.metadata
|
|
@@ -1486,11 +2083,48 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
1486
2083
|
stepIndex: context.state.currentStep + 1
|
|
1487
2084
|
});
|
|
1488
2085
|
const abortContext = createAgentAbortContext(mergeAbortSignals(input.abortSignal, executionLease.signal), policy);
|
|
1489
|
-
const
|
|
2086
|
+
const executionEnvironmentSession = await acquireExecutionEnvironment(context.executionEnvironment, context.state, context.context, abortContext.signal);
|
|
2087
|
+
activeExecutionEnvironment = executionEnvironmentSession;
|
|
2088
|
+
let executionEnvironmentStatus = "failed";
|
|
2089
|
+
let executionEnvironmentError;
|
|
2090
|
+
let streamResult;
|
|
2091
|
+
try {
|
|
2092
|
+
streamResult = streamText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, context.context, executionEnvironmentSession, abortContext.signal, async (record) => {
|
|
2093
|
+
await publish({
|
|
2094
|
+
type: "agent-compaction",
|
|
2095
|
+
compaction: record
|
|
2096
|
+
});
|
|
2097
|
+
}));
|
|
2098
|
+
}
|
|
2099
|
+
catch (error) {
|
|
2100
|
+
executionEnvironmentError = {
|
|
2101
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2102
|
+
};
|
|
2103
|
+
await executionEnvironmentSession?.release?.({
|
|
2104
|
+
status: "failed",
|
|
2105
|
+
error: executionEnvironmentError
|
|
2106
|
+
});
|
|
2107
|
+
activeExecutionEnvironment = undefined;
|
|
2108
|
+
await executionLease.release();
|
|
2109
|
+
throw error;
|
|
2110
|
+
}
|
|
1490
2111
|
const approvalRequests = [];
|
|
1491
2112
|
const eventRelay = (async () => {
|
|
1492
2113
|
for await (const event of streamResult.eventStream) {
|
|
1493
2114
|
await publish(event);
|
|
2115
|
+
if (event.type === "tool-approval-request") {
|
|
2116
|
+
approvalRequests.push(event.approval);
|
|
2117
|
+
await publish({
|
|
2118
|
+
type: "agent-approval-request",
|
|
2119
|
+
approval: event.approval
|
|
2120
|
+
});
|
|
2121
|
+
await emitTelemetryEvent(agent, {
|
|
2122
|
+
type: "approval-request",
|
|
2123
|
+
runId: context.state.runId,
|
|
2124
|
+
agentId: context.state.agentId,
|
|
2125
|
+
approval: event.approval
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
1494
2128
|
if (event.type === "provider-data" &&
|
|
1495
2129
|
typeof event.data === "object" &&
|
|
1496
2130
|
event.data !== null &&
|
|
@@ -1526,6 +2160,7 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
1526
2160
|
const final = await withAgentPolicyTimeout(eventRelay.then(() => streamResult.collect()), abortContext);
|
|
1527
2161
|
const cancelled = executionLease.cancelledState();
|
|
1528
2162
|
if (cancelled) {
|
|
2163
|
+
executionEnvironmentStatus = cancelled.status;
|
|
1529
2164
|
await emitRunFinishTelemetry(agent, cancelled);
|
|
1530
2165
|
await publish({
|
|
1531
2166
|
type: "agent-run-finish",
|
|
@@ -1541,10 +2176,11 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
1541
2176
|
throw conflict;
|
|
1542
2177
|
}
|
|
1543
2178
|
const newSteps = mapSteps(final.steps, context.state.currentStep, final.toolResults);
|
|
1544
|
-
let result = finalizeState(context.state, final, newSteps, final.toolResults);
|
|
2179
|
+
let result = finalizeState(agent, context.state, final, newSteps, final.toolResults);
|
|
1545
2180
|
const outputGuardrail = await runGuardrails(agent, result.state, "output", agent.outputGuardrails, () => ({
|
|
1546
2181
|
runId: result.state.runId,
|
|
1547
2182
|
agentId: result.state.agentId,
|
|
2183
|
+
context: context.context,
|
|
1548
2184
|
state: cloneState(result.state),
|
|
1549
2185
|
output: result,
|
|
1550
2186
|
metadata: result.state.metadata
|
|
@@ -1574,11 +2210,16 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
1574
2210
|
state: result.state
|
|
1575
2211
|
}, true);
|
|
1576
2212
|
broadcast.close();
|
|
2213
|
+
executionEnvironmentStatus = result.status;
|
|
1577
2214
|
return result;
|
|
1578
2215
|
}
|
|
1579
2216
|
catch (error) {
|
|
2217
|
+
executionEnvironmentError = {
|
|
2218
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2219
|
+
};
|
|
1580
2220
|
const cancelled = executionLease.cancelledState();
|
|
1581
2221
|
if (cancelled) {
|
|
2222
|
+
executionEnvironmentStatus = cancelled.status;
|
|
1582
2223
|
await emitRunFinishTelemetry(agent, cancelled);
|
|
1583
2224
|
await publish({
|
|
1584
2225
|
type: "agent-run-finish",
|
|
@@ -1612,6 +2253,7 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
1612
2253
|
state: timedOutState
|
|
1613
2254
|
}, true);
|
|
1614
2255
|
broadcast.close();
|
|
2256
|
+
executionEnvironmentStatus = timedOutState.status;
|
|
1615
2257
|
return toOutput(timedOutState);
|
|
1616
2258
|
}
|
|
1617
2259
|
const durableState = agent.store
|
|
@@ -1630,9 +2272,15 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
1630
2272
|
state: failedState
|
|
1631
2273
|
}, true);
|
|
1632
2274
|
broadcast.close();
|
|
2275
|
+
executionEnvironmentStatus = failedState.status;
|
|
1633
2276
|
throw error;
|
|
1634
2277
|
}
|
|
1635
2278
|
finally {
|
|
2279
|
+
await executionEnvironmentSession?.release?.({
|
|
2280
|
+
status: executionEnvironmentStatus,
|
|
2281
|
+
error: executionEnvironmentError
|
|
2282
|
+
});
|
|
2283
|
+
activeExecutionEnvironment = undefined;
|
|
1636
2284
|
await executionLease.release();
|
|
1637
2285
|
}
|
|
1638
2286
|
})();
|
|
@@ -1641,6 +2289,11 @@ export const streamAgent = (agent, input = {}) => {
|
|
|
1641
2289
|
textStream: streamResult.textStream
|
|
1642
2290
|
};
|
|
1643
2291
|
})().catch(async (error) => {
|
|
2292
|
+
await activeExecutionEnvironment?.release?.({
|
|
2293
|
+
status: "failed",
|
|
2294
|
+
error: { message: error instanceof Error ? error.message : String(error) }
|
|
2295
|
+
});
|
|
2296
|
+
activeExecutionEnvironment = undefined;
|
|
1644
2297
|
await activeLease?.release();
|
|
1645
2298
|
broadcast.fail(error);
|
|
1646
2299
|
throw error;
|