@zhivex-ai/core 1.18.0 → 1.19.1

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.
Files changed (255) hide show
  1. package/README.md +4 -0
  2. package/dist/agent/approvals.d.ts +7 -0
  3. package/dist/agent/approvals.d.ts.map +1 -0
  4. package/dist/agent/approvals.js +97 -0
  5. package/dist/agent/approvals.js.map +1 -0
  6. package/dist/agent/cancellation.d.ts +4 -0
  7. package/dist/agent/cancellation.d.ts.map +1 -0
  8. package/dist/agent/cancellation.js +69 -0
  9. package/dist/agent/cancellation.js.map +1 -0
  10. package/dist/agent/common.d.ts +20 -0
  11. package/dist/agent/common.d.ts.map +1 -0
  12. package/dist/agent/common.js +150 -0
  13. package/dist/agent/common.js.map +1 -0
  14. package/dist/agent/compaction.d.ts +6 -0
  15. package/dist/agent/compaction.d.ts.map +1 -0
  16. package/dist/agent/compaction.js +156 -0
  17. package/dist/agent/compaction.js.map +1 -0
  18. package/dist/agent/context.d.ts +58 -0
  19. package/dist/agent/context.d.ts.map +1 -0
  20. package/dist/agent/context.js +267 -0
  21. package/dist/agent/context.js.map +1 -0
  22. package/dist/agent/definition.d.ts +4 -0
  23. package/dist/agent/definition.d.ts.map +1 -0
  24. package/dist/agent/definition.js +35 -0
  25. package/dist/agent/definition.js.map +1 -0
  26. package/dist/agent/execution.d.ts +14 -0
  27. package/dist/agent/execution.d.ts.map +1 -0
  28. package/dist/agent/execution.js +1062 -0
  29. package/dist/agent/execution.js.map +1 -0
  30. package/dist/agent/groups.d.ts +3 -0
  31. package/dist/agent/groups.d.ts.map +1 -0
  32. package/dist/agent/groups.js +130 -0
  33. package/dist/agent/groups.js.map +1 -0
  34. package/dist/agent/guardrails.d.ts +4 -0
  35. package/dist/agent/guardrails.d.ts.map +1 -0
  36. package/dist/agent/guardrails.js +29 -0
  37. package/dist/agent/guardrails.js.map +1 -0
  38. package/dist/agent/instance.d.ts +42 -0
  39. package/dist/agent/instance.d.ts.map +1 -0
  40. package/dist/agent/instance.js +82 -0
  41. package/dist/agent/instance.js.map +1 -0
  42. package/dist/agent/lifecycle.d.ts +31 -0
  43. package/dist/agent/lifecycle.d.ts.map +1 -0
  44. package/dist/agent/lifecycle.js +184 -0
  45. package/dist/agent/lifecycle.js.map +1 -0
  46. package/dist/agent/state.d.ts +6 -0
  47. package/dist/agent/state.d.ts.map +1 -0
  48. package/dist/agent/state.js +119 -0
  49. package/dist/agent/state.js.map +1 -0
  50. package/dist/agent/telemetry.d.ts +12 -0
  51. package/dist/agent/telemetry.d.ts.map +1 -0
  52. package/dist/agent/telemetry.js +176 -0
  53. package/dist/agent/telemetry.js.map +1 -0
  54. package/dist/agent/tool-execution.d.ts +6 -0
  55. package/dist/agent/tool-execution.d.ts.map +1 -0
  56. package/dist/agent/tool-execution.js +176 -0
  57. package/dist/agent/tool-execution.js.map +1 -0
  58. package/dist/agent-run-view.d.ts +9 -0
  59. package/dist/agent-run-view.d.ts.map +1 -0
  60. package/dist/agent-run-view.js +42 -0
  61. package/dist/agent-run-view.js.map +1 -0
  62. package/dist/agent-store/file.d.ts +11 -0
  63. package/dist/agent-store/file.d.ts.map +1 -0
  64. package/dist/agent-store/file.js +534 -0
  65. package/dist/agent-store/file.js.map +1 -0
  66. package/dist/agent-store/memory.d.ts +9 -0
  67. package/dist/agent-store/memory.d.ts.map +1 -0
  68. package/dist/agent-store/memory.js +198 -0
  69. package/dist/agent-store/memory.js.map +1 -0
  70. package/dist/agent-store/postgres.d.ts +4 -0
  71. package/dist/agent-store/postgres.d.ts.map +1 -0
  72. package/dist/agent-store/postgres.js +374 -0
  73. package/dist/agent-store/postgres.js.map +1 -0
  74. package/dist/agent-store/shared.d.ts +19 -0
  75. package/dist/agent-store/shared.d.ts.map +1 -0
  76. package/dist/agent-store/shared.js +137 -0
  77. package/dist/agent-store/shared.js.map +1 -0
  78. package/dist/agent-store/sqlite.d.ts +4 -0
  79. package/dist/agent-store/sqlite.d.ts.map +1 -0
  80. package/dist/agent-store/sqlite.js +362 -0
  81. package/dist/agent-store/sqlite.js.map +1 -0
  82. package/dist/agent-store.d.ts +5 -21
  83. package/dist/agent-store.d.ts.map +1 -1
  84. package/dist/agent-store.js +5 -1588
  85. package/dist/agent-store.js.map +1 -1
  86. package/dist/agent.d.ts +6 -57
  87. package/dist/agent.d.ts.map +1 -1
  88. package/dist/agent.js +6 -2631
  89. package/dist/agent.js.map +1 -1
  90. package/dist/agents-entry.d.ts +19 -0
  91. package/dist/agents-entry.d.ts.map +1 -0
  92. package/dist/agents-entry.js +14 -0
  93. package/dist/agents-entry.js.map +1 -0
  94. package/dist/artifact/file.d.ts +6 -0
  95. package/dist/artifact/file.d.ts.map +1 -0
  96. package/dist/artifact/file.js +380 -0
  97. package/dist/artifact/file.js.map +1 -0
  98. package/dist/artifact/memory.d.ts +3 -0
  99. package/dist/artifact/memory.d.ts.map +1 -0
  100. package/dist/artifact/memory.js +80 -0
  101. package/dist/artifact/memory.js.map +1 -0
  102. package/dist/artifact/postgres.d.ts +3 -0
  103. package/dist/artifact/postgres.d.ts.map +1 -0
  104. package/dist/artifact/postgres.js +283 -0
  105. package/dist/artifact/postgres.js.map +1 -0
  106. package/dist/artifact/shared.d.ts +208 -0
  107. package/dist/artifact/shared.d.ts.map +1 -0
  108. package/dist/artifact/shared.js +523 -0
  109. package/dist/artifact/shared.js.map +1 -0
  110. package/dist/artifact/sqlite.d.ts +3 -0
  111. package/dist/artifact/sqlite.d.ts.map +1 -0
  112. package/dist/artifact/sqlite.js +283 -0
  113. package/dist/artifact/sqlite.js.map +1 -0
  114. package/dist/artifact.d.ts +6 -186
  115. package/dist/artifact.d.ts.map +1 -1
  116. package/dist/artifact.js +6 -1536
  117. package/dist/artifact.js.map +1 -1
  118. package/dist/catalog-contracts.d.ts +52 -0
  119. package/dist/catalog-contracts.d.ts.map +1 -0
  120. package/dist/catalog-contracts.js +250 -0
  121. package/dist/catalog-contracts.js.map +1 -0
  122. package/dist/catalog.d.ts +3 -52
  123. package/dist/catalog.d.ts.map +1 -1
  124. package/dist/catalog.js +3 -249
  125. package/dist/catalog.js.map +1 -1
  126. package/dist/generate-cache-file.d.ts +11 -0
  127. package/dist/generate-cache-file.d.ts.map +1 -0
  128. package/dist/generate-cache-file.js +124 -0
  129. package/dist/generate-cache-file.js.map +1 -0
  130. package/dist/generate-text.d.ts +2 -5
  131. package/dist/generate-text.d.ts.map +1 -1
  132. package/dist/generate-text.js +3 -18
  133. package/dist/generate-text.js.map +1 -1
  134. package/dist/generation-entry.d.ts +6 -0
  135. package/dist/generation-entry.d.ts.map +1 -0
  136. package/dist/generation-entry.js +6 -0
  137. package/dist/generation-entry.js.map +1 -0
  138. package/dist/middleware-runtime.d.ts +35 -0
  139. package/dist/middleware-runtime.d.ts.map +1 -0
  140. package/dist/middleware-runtime.js +532 -0
  141. package/dist/middleware-runtime.js.map +1 -0
  142. package/dist/middleware.d.ts +3 -43
  143. package/dist/middleware.d.ts.map +1 -1
  144. package/dist/middleware.js +3 -653
  145. package/dist/middleware.js.map +1 -1
  146. package/dist/normalize-messages.d.ts +3 -0
  147. package/dist/normalize-messages.d.ts.map +1 -0
  148. package/dist/normalize-messages.js +16 -0
  149. package/dist/normalize-messages.js.map +1 -0
  150. package/dist/provider-entry.d.ts +17 -0
  151. package/dist/provider-entry.d.ts.map +1 -0
  152. package/dist/provider-entry.js +13 -0
  153. package/dist/provider-entry.js.map +1 -0
  154. package/dist/realtime.d.ts +2 -0
  155. package/dist/realtime.d.ts.map +1 -1
  156. package/dist/realtime.js +6 -0
  157. package/dist/realtime.js.map +1 -1
  158. package/dist/store-key.d.ts +3 -0
  159. package/dist/store-key.d.ts.map +1 -0
  160. package/dist/store-key.js +5 -0
  161. package/dist/store-key.js.map +1 -0
  162. package/dist/store-security.d.ts +1 -2
  163. package/dist/store-security.d.ts.map +1 -1
  164. package/dist/store-security.js +2 -4
  165. package/dist/store-security.js.map +1 -1
  166. package/dist/types/agent-state.d.ts +236 -0
  167. package/dist/types/agent-state.d.ts.map +1 -0
  168. package/dist/types/agent-state.js +2 -0
  169. package/dist/types/agent-state.js.map +1 -0
  170. package/dist/types/agents.d.ts +429 -0
  171. package/dist/types/agents.d.ts.map +1 -0
  172. package/dist/types/agents.js +2 -0
  173. package/dist/types/agents.js.map +1 -0
  174. package/dist/types/common.d.ts +179 -0
  175. package/dist/types/common.d.ts.map +1 -0
  176. package/dist/types/common.js +2 -0
  177. package/dist/types/common.js.map +1 -0
  178. package/dist/types/generation.d.ts +109 -0
  179. package/dist/types/generation.d.ts.map +1 -0
  180. package/dist/types/generation.js +2 -0
  181. package/dist/types/generation.js.map +1 -0
  182. package/dist/types/media-data.d.ts +65 -0
  183. package/dist/types/media-data.d.ts.map +1 -0
  184. package/dist/types/media-data.js +2 -0
  185. package/dist/types/media-data.js.map +1 -0
  186. package/dist/types/media.d.ts +141 -0
  187. package/dist/types/media.d.ts.map +1 -0
  188. package/dist/types/media.js +2 -0
  189. package/dist/types/media.js.map +1 -0
  190. package/dist/types/messages.d.ts +82 -0
  191. package/dist/types/messages.d.ts.map +1 -0
  192. package/dist/types/messages.js +2 -0
  193. package/dist/types/messages.js.map +1 -0
  194. package/dist/types/middleware.d.ts +130 -0
  195. package/dist/types/middleware.d.ts.map +1 -0
  196. package/dist/types/middleware.js +2 -0
  197. package/dist/types/middleware.js.map +1 -0
  198. package/dist/types/model-tools.d.ts +409 -0
  199. package/dist/types/model-tools.d.ts.map +1 -0
  200. package/dist/types/model-tools.js +2 -0
  201. package/dist/types/model-tools.js.map +1 -0
  202. package/dist/types/persistence.d.ts +168 -0
  203. package/dist/types/persistence.d.ts.map +1 -0
  204. package/dist/types/persistence.js +2 -0
  205. package/dist/types/persistence.js.map +1 -0
  206. package/dist/types/provider-resources.d.ts +336 -0
  207. package/dist/types/provider-resources.d.ts.map +1 -0
  208. package/dist/types/provider-resources.js +2 -0
  209. package/dist/types/provider-resources.js.map +1 -0
  210. package/dist/types/provider.d.ts +23 -0
  211. package/dist/types/provider.d.ts.map +1 -0
  212. package/dist/types/provider.js +2 -0
  213. package/dist/types/provider.js.map +1 -0
  214. package/dist/types/resource-options.d.ts +88 -0
  215. package/dist/types/resource-options.d.ts.map +1 -0
  216. package/dist/types/resource-options.js +2 -0
  217. package/dist/types/resource-options.js.map +1 -0
  218. package/dist/types/stream.d.ts +70 -0
  219. package/dist/types/stream.d.ts.map +1 -0
  220. package/dist/types/stream.js +2 -0
  221. package/dist/types/stream.js.map +1 -0
  222. package/dist/types/ui.d.ts +110 -0
  223. package/dist/types/ui.d.ts.map +1 -0
  224. package/dist/types/ui.js +2 -0
  225. package/dist/types/ui.js.map +1 -0
  226. package/dist/types.d.ts +16 -2480
  227. package/dist/types.d.ts.map +1 -1
  228. package/dist/ui.d.ts.map +1 -1
  229. package/dist/ui.js +2 -0
  230. package/dist/ui.js.map +1 -1
  231. package/dist/workflow-state-service/file.d.ts +5 -0
  232. package/dist/workflow-state-service/file.d.ts.map +1 -0
  233. package/dist/workflow-state-service/file.js +142 -0
  234. package/dist/workflow-state-service/file.js.map +1 -0
  235. package/dist/workflow-state-service/memory.d.ts +3 -0
  236. package/dist/workflow-state-service/memory.d.ts.map +1 -0
  237. package/dist/workflow-state-service/memory.js +27 -0
  238. package/dist/workflow-state-service/memory.js.map +1 -0
  239. package/dist/workflow-state-service/postgres.d.ts +4 -0
  240. package/dist/workflow-state-service/postgres.d.ts.map +1 -0
  241. package/dist/workflow-state-service/postgres.js +179 -0
  242. package/dist/workflow-state-service/postgres.js.map +1 -0
  243. package/dist/workflow-state-service/shared.d.ts +43 -0
  244. package/dist/workflow-state-service/shared.d.ts.map +1 -0
  245. package/dist/workflow-state-service/shared.js +107 -0
  246. package/dist/workflow-state-service/shared.js.map +1 -0
  247. package/dist/workflow-state-service/sqlite.d.ts +4 -0
  248. package/dist/workflow-state-service/sqlite.d.ts.map +1 -0
  249. package/dist/workflow-state-service/sqlite.js +183 -0
  250. package/dist/workflow-state-service/sqlite.js.map +1 -0
  251. package/dist/workflow-state-service.d.ts +6 -33
  252. package/dist/workflow-state-service.d.ts.map +1 -1
  253. package/dist/workflow-state-service.js +6 -626
  254. package/dist/workflow-state-service.js.map +1 -1
  255. package/package.json +17 -1
package/dist/agent.js CHANGED
@@ -1,2632 +1,7 @@
1
- import { refreshAgentTaskOutcome } from "./agent-reconciliation.js";
2
- import { createHash } from "node:crypto";
3
- import { createAgentApprovalMessage, getAgentApprovalRequests } from "./agent-approval.js";
4
- import { createAgentHandoffMessage } from "./agent-handoff-contracts.js";
5
- import { createAgentExecutionEnvironmentBinding, fingerprintAgentHarness } from "./agent-harness.js";
6
- import { AGENT_RUN_STATE_SCHEMA_VERSION, normalizeAgentRunState } from "./agent-state.js";
7
- import { BoundedReplayBroadcast } from "./bounded-broadcast.js";
8
- import { ConflictError, GuardrailTriggeredError, ProviderToolCallError, ToolNotRegisteredError, UnsupportedFeatureError, ValidationError } from "./errors.js";
9
- import { aggregateTokenUsage, generateText, getGenerateTextStepTiming, normalizeMessages, streamText } from "./generate-text.js";
10
- import { createTextMessage, isCallableToolDefinition, serializeJsonValue } from "./messages.js";
11
- import { createMergedAbortSignal } from "./runtime.js";
12
- import { evaluateAgentBudgetPreflight, getAgentBudgetStatus } from "./safety-policy.js";
13
- import { createSecureId } from "#secure-id";
14
- import { createStructuredOutputPrompt } from "./structured-output-prompt.js";
15
- import { ToolExecutionSuspendedError } from "./tool-execution-suspension.js";
16
- import { toToolSet } from "./tool-registry.js";
17
- import { z } from "zod";
18
- const randomId = createSecureId;
19
- const AGENT_GROUP_FAIL_FAST_ABORT_MESSAGE = "Agent group member aborted after fail-fast.";
20
- const DEFAULT_AGENT_LEASE_TTL_MS = 30_000;
21
- const DEFAULT_AGENT_CANCELLATION_POLL_MS = 1_000;
22
- const DEFAULT_AGENT_MONITOR_SHUTDOWN_TIMEOUT_MS = 1_000;
23
- const DEFAULT_AGENT_MAX_STATE_BYTES = 4 * 1024 * 1024;
24
- class AgentPolicyTimeoutError extends Error {
25
- constructor(timeoutMs) {
26
- super(`Agent run timed out after ${timeoutMs}ms.`);
27
- this.name = "AgentPolicyTimeoutError";
28
- }
29
- }
30
- const joinInstructions = (...parts) => {
31
- const content = parts.map((part) => part?.trim()).filter((part) => Boolean(part));
32
- return content.length ? content.join("\n\n") : undefined;
33
- };
34
- const resolveAgentOutputMode = (agent) => {
35
- if (!agent.outputSchema) {
36
- return undefined;
37
- }
38
- const requested = agent.outputMode ?? "auto";
39
- if (requested === "native" && !agent.model.capabilities.structuredOutput) {
40
- throw new UnsupportedFeatureError(`Model "${agent.model.provider}/${agent.model.modelId}" does not support native structured output.`);
41
- }
42
- return requested === "auto"
43
- ? agent.model.capabilities.structuredOutput
44
- ? "native"
45
- : "prompted"
46
- : requested;
47
- };
48
- const promptedOutputInstruction = (agent) => agent.outputSchema && resolveAgentOutputMode(agent) === "prompted"
49
- ? createStructuredOutputPrompt(agent.outputSchema, {
50
- name: agent.outputName,
51
- description: agent.outputDescription
52
- })
53
- : undefined;
54
- const hasToolCalls = (messages) => messages.some((message) => message.parts.some((part) => part.type === "tool-call"));
55
- const snapshotRequest = (request, messageOffset = 0, messages = request.messages) => ({
56
- messageOffset,
57
- messages,
58
- toolChoice: request.toolChoice,
59
- toolExecution: request.toolExecution,
60
- temperature: request.temperature,
61
- maxTokens: request.maxTokens,
62
- reasoning: request.reasoning,
63
- providerOptions: request.providerOptions,
64
- timeoutMs: request.timeoutMs,
65
- maxRetries: request.maxRetries,
66
- retryBackoffMs: request.retryBackoffMs
67
- });
68
- const snapshotResponse = (response) => ({
69
- messages: response.messages ?? (response.message ? [response.message] : []),
70
- text: response.text,
71
- finishReason: response.finishReason,
72
- providerFinishReason: response.providerFinishReason,
73
- usage: response.usage
74
- });
75
- const countToolCalls = (messages) => messages.reduce((total, message) => total + message.parts.filter((part) => part.type === "tool-call").length, 0);
76
- const messagePrefixLength = (prefix, messages) => {
77
- if (prefix.length > messages.length) {
78
- return 0;
79
- }
80
- for (let index = 0; index < prefix.length; index += 1) {
81
- if (JSON.stringify(prefix[index]) !== JSON.stringify(messages[index])) {
82
- return 0;
83
- }
84
- }
85
- return prefix.length;
86
- };
87
- const mapSteps = (steps, offset, toolResults) => {
88
- let toolResultCursor = 0;
89
- let previousMessages = [];
90
- return steps.map((step, index) => {
91
- const response = snapshotResponse(step.response);
92
- const toolCallCount = countToolCalls(response.messages);
93
- const stepToolResults = toolResults.slice(toolResultCursor, toolResultCursor + toolCallCount);
94
- toolResultCursor += toolCallCount;
95
- const timing = getGenerateTextStepTiming(step.request);
96
- const finishedAt = timing?.finishedAt ?? Date.now();
97
- const messageOffset = index === 0 ? 0 : messagePrefixLength(previousMessages, step.request.messages);
98
- const incrementalMessages = step.request.messages.slice(messageOffset);
99
- previousMessages = step.request.messages;
100
- return {
101
- index: offset + index + 1,
102
- status: "completed",
103
- startedAt: timing?.startedAt ?? finishedAt,
104
- finishedAt,
105
- request: snapshotRequest(step.request, messageOffset, incrementalMessages),
106
- response,
107
- toolResults: stepToolResults
108
- };
109
- });
110
- };
111
- const cloneMetadata = (...values) => {
112
- const merged = Object.assign({}, ...values.filter(Boolean));
113
- return Object.keys(merged).length ? merged : undefined;
114
- };
115
- const toOutput = (state) => ({
116
- status: state.status,
117
- taskOutcome: state.taskOutcome,
118
- outputText: state.outputText,
119
- finalOutput: state.status === "completed" && state.finalOutput !== undefined
120
- ? state.finalOutput
121
- : undefined,
122
- finishReason: state.finishReason,
123
- providerFinishReason: state.providerFinishReason,
124
- usage: state.usage,
125
- messages: state.messages,
126
- steps: state.steps,
127
- toolResults: state.toolResults,
128
- state,
129
- error: state.error
130
- });
131
- const normalizeApprovalStatus = (status) => status === "suspended" ? "waiting_approval" : status;
132
- const cloneState = (state) => JSON.parse(JSON.stringify(normalizeAgentRunState(state)));
133
- const createBaseState = (provider, modelId, initialMessages, maxSteps, metadata, agentId, runId, handoff, parentRunId, idempotencyKey, scope, outputMode, harness, executionEnvironment) => {
134
- const startedAt = Date.now();
135
- return {
136
- schemaVersion: AGENT_RUN_STATE_SCHEMA_VERSION,
137
- revision: 0,
138
- runId,
139
- scope,
140
- idempotencyKey,
141
- agentId,
142
- parentRunId: parentRunId ?? handoff?.fromRunId,
143
- provider,
144
- modelId,
145
- harness,
146
- executionEnvironment,
147
- status: "running",
148
- messages: initialMessages,
149
- steps: [],
150
- toolResults: [],
151
- currentStep: 0,
152
- maxSteps,
153
- outputText: "",
154
- outputMode,
155
- pendingApprovals: [],
156
- approvalHistory: [],
157
- compactions: [],
158
- metadata,
159
- handoff,
160
- startedAt,
161
- updatedAt: startedAt
162
- };
163
- };
164
- const ensureValidStateInput = (input) => {
165
- if (input.approvals?.length && !input.state) {
166
- throw new ValidationError('The "approvals" option requires an existing agent "state".');
167
- }
168
- if (!input.state) {
169
- return;
170
- }
171
- if (input.prompt !== undefined || input.messages !== undefined || input.system !== undefined || input.handoff !== undefined) {
172
- throw new ValidationError('Pass either "state" or a fresh "prompt"/"messages" input, but not both.');
173
- }
174
- const stateScope = input.state?.scope;
175
- const inputScope = input.scope;
176
- if (stateScope &&
177
- inputScope &&
178
- (stateScope.tenantId !== inputScope.tenantId ||
179
- stateScope.userId !== inputScope.userId ||
180
- stateScope.namespace !== inputScope.namespace)) {
181
- throw new ValidationError('The provided agent state belongs to a different tenant/user scope.');
182
- }
183
- };
184
- const ensureValidIdempotencyInput = (input, store) => {
185
- if (!input.idempotencyKey) {
186
- return;
187
- }
188
- if (!store) {
189
- throw new ValidationError('The "idempotencyKey" option requires an agent run "store".');
190
- }
191
- if (!store.claimIdempotencyKey) {
192
- throw new ValidationError('The agent run "store" must implement "claimIdempotencyKey()" to use "idempotencyKey" safely.');
193
- }
194
- };
195
- const ensureValidScope = (scope) => {
196
- if (!scope)
197
- return;
198
- if (typeof scope.tenantId !== "string" || scope.tenantId.length === 0) {
199
- throw new ValidationError('Agent scope "tenantId" must be a non-empty string.');
200
- }
201
- for (const field of ["userId", "namespace"]) {
202
- if (scope[field] !== undefined && (typeof scope[field] !== "string" || scope[field].length === 0)) {
203
- throw new ValidationError(`Agent scope "${field}" must be a non-empty string when provided.`);
204
- }
205
- }
206
- };
207
- const invokeOperationalHook = async (agent, source, operation, runId, callback, fallback) => {
208
- if (!callback) {
209
- return fallback;
210
- }
211
- try {
212
- return await callback();
213
- }
214
- catch (error) {
215
- const normalizedError = error instanceof Error ? error : new Error(String(error));
216
- try {
217
- await agent.hookFailurePolicy?.onError?.({
218
- source,
219
- operation,
220
- runId,
221
- error: normalizedError
222
- });
223
- }
224
- catch {
225
- // Reporting an observer failure must never recursively fail the run.
226
- }
227
- if (agent.hookFailurePolicy?.[source] === "fail") {
228
- throw normalizedError;
229
- }
230
- return fallback;
231
- }
232
- };
233
- const injectContextMessages = (messages, extraMessages) => {
234
- if (!extraMessages.length) {
235
- return messages;
236
- }
237
- if (messages[0]?.role === "system") {
238
- return [messages[0], ...extraMessages, ...messages.slice(1)];
239
- }
240
- return [...extraMessages, ...messages];
241
- };
242
- const prepareFreshMessages = async (agent, input, runId) => {
243
- let messages = normalizeMessages({
244
- prompt: input.prompt,
245
- messages: input.messages,
246
- system: joinInstructions(agent.instructions, input.system, promptedOutputInstruction(agent))
247
- });
248
- const handoffMessages = input.handoff
249
- ? [createAgentHandoffMessage(input.handoff), ...input.handoff.contextMessages.filter((message) => message.role !== "system")]
250
- : [];
251
- messages = injectContextMessages(messages, handoffMessages);
252
- const memoryMessages = await invokeOperationalHook(agent, "memory", "load", runId, agent.memory
253
- ? () => agent.memory.load({
254
- runId,
255
- agentId: agent.id,
256
- scope: input.scope ?? input.handoff?.scope,
257
- metadata: cloneMetadata(agent.metadata, input.metadata)
258
- })
259
- : undefined, []);
260
- messages = injectContextMessages(messages, memoryMessages);
261
- return {
262
- messages,
263
- memoryMessages
264
- };
265
- };
266
- const localApprovalResolutionPayload = (inputDigest, approve, reason) => JSON.stringify({
267
- inputDigest,
268
- approve,
269
- reason: reason ?? null
270
- });
271
- const approvalKey = (provider, requestId) => `${provider}\0${requestId}`;
272
- const applyApprovalResponses = async (messages, approvals, pendingApprovals, approvalHistory = [], signer) => {
273
- if (!approvals?.length) {
274
- return {
275
- messages,
276
- pendingApprovals,
277
- approvalHistory
278
- };
279
- }
280
- const pendingById = new Map(pendingApprovals.map((approval) => [approvalKey(approval.provider, approval.id), approval]));
281
- for (const approval of approvals) {
282
- const pending = pendingById.get(approvalKey(approval.provider, approval.approvalRequestId));
283
- if (!pending) {
284
- throw new ValidationError(`Unknown approval request "${approval.approvalRequestId}" for provider "${approval.provider}".`);
285
- }
286
- }
287
- const providerApprovals = approvals.filter((approval) => {
288
- const pending = pendingById.get(approvalKey(approval.provider, approval.approvalRequestId));
289
- return pending?.kind === undefined || pending.kind === "provider";
290
- });
291
- const localResolutions = [];
292
- const subagentResolutions = [];
293
- for (const approval of approvals) {
294
- const pending = pendingById.get(approvalKey(approval.provider, approval.approvalRequestId));
295
- if (!pending) {
296
- continue;
297
- }
298
- if (pending.kind === "subagent") {
299
- subagentResolutions.push({
300
- requestId: pending.id,
301
- kind: "subagent",
302
- provider: pending.provider,
303
- approve: approval.approve,
304
- reason: approval.reason,
305
- toolCallId: pending.toolCallId,
306
- childRunId: pending.childRunId,
307
- childAgentId: pending.childAgentId,
308
- childApprovalRequestId: pending.childApprovalRequestId,
309
- resolvedAt: Date.now()
310
- });
311
- continue;
312
- }
313
- if (pending.kind !== "local-tool") {
314
- continue;
315
- }
316
- if (signer) {
317
- if (!pending.inputDigest || !pending.signature) {
318
- throw new ValidationError(`Approval request "${pending.id}" is missing its required signature.`);
319
- }
320
- const requestSignatureValid = signer.verify
321
- ? await signer.verify(pending.inputDigest, pending.signature)
322
- : (await signer.sign(pending.inputDigest)) === pending.signature;
323
- if (!requestSignatureValid) {
324
- throw new ValidationError(`Approval request "${pending.id}" has an invalid signature.`);
325
- }
326
- }
327
- const resolutionSignature = signer && pending.inputDigest
328
- ? await signer.sign(localApprovalResolutionPayload(pending.inputDigest, approval.approve, approval.reason))
329
- : undefined;
330
- localResolutions.push({
331
- requestId: pending.id,
332
- kind: "local-tool",
333
- provider: pending.provider,
334
- approve: approval.approve,
335
- reason: approval.reason,
336
- toolCallId: pending.toolCallId,
337
- step: pending.step,
338
- inputDigest: pending.inputDigest,
339
- toolVersion: pending.toolVersion,
340
- signature: resolutionSignature,
341
- resolvedAt: Date.now()
342
- });
343
- }
344
- return {
345
- messages: providerApprovals.length
346
- ? [...messages, createAgentApprovalMessage(providerApprovals)]
347
- : messages,
348
- pendingApprovals: pendingApprovals.filter((pending) => !approvals.some((approval) => approval.approvalRequestId === pending.id &&
349
- approval.provider === pending.provider)),
350
- approvalHistory: [
351
- ...approvalHistory.filter((existing) => !localResolutions.some((resolution) => resolution.requestId === existing.requestId &&
352
- resolution.provider === existing.provider) &&
353
- !subagentResolutions.some((resolution) => resolution.requestId === existing.requestId &&
354
- resolution.provider === existing.provider)),
355
- ...localResolutions,
356
- ...subagentResolutions
357
- ]
358
- };
359
- };
360
- const finalizeState = (agent, state, result, newSteps, newToolResults) => {
361
- const nextCurrentStep = state.currentStep + newSteps.length;
362
- const exhausted = nextCurrentStep >= state.maxSteps;
363
- const lastStep = newSteps.at(-1);
364
- const unresolvedToolCalls = lastStep?.response ? hasToolCalls(lastStep.response.messages) : false;
365
- const pendingApprovals = [
366
- ...(result.approvalRequests ?? []),
367
- ...getAgentApprovalRequests(newSteps.flatMap((step) => step.response?.messages ?? []))
368
- ];
369
- if (pendingApprovals.length) {
370
- state.status = "waiting_approval";
371
- state.error = undefined;
372
- if (lastStep) {
373
- lastStep.status = "waiting_approval";
374
- }
375
- }
376
- else if (exhausted && unresolvedToolCalls) {
377
- state.status = "failed";
378
- state.error = {
379
- message: "Agent exhausted maxSteps before reaching a terminal response."
380
- };
381
- if (lastStep) {
382
- lastStep.status = "failed";
383
- lastStep.error = state.error;
384
- }
385
- }
386
- else {
387
- state.status = "completed";
388
- state.error = undefined;
389
- }
390
- state.messages = result.messages;
391
- state.steps = [...state.steps, ...newSteps];
392
- state.toolResults = [...state.toolResults, ...newToolResults];
393
- state.currentStep = nextCurrentStep;
394
- state.outputText = result.text;
395
- if (state.status === "completed") {
396
- const terminalText = result.steps.at(-1)?.response.text ?? result.text;
397
- if (agent.outputSchema) {
398
- let parsedJson;
399
- try {
400
- parsedJson = JSON.parse(terminalText);
401
- }
402
- catch (error) {
403
- throw new ValidationError("Agent final output is not valid JSON.", { cause: error });
404
- }
405
- const parsedOutput = agent.outputSchema.safeParse(parsedJson);
406
- if (!parsedOutput.success) {
407
- throw new ValidationError(`Agent final output validation failed: ${parsedOutput.error.message}`);
408
- }
409
- state.finalOutput = serializeJsonValue(parsedOutput.data);
410
- }
411
- }
412
- state.finishReason = result.finishReason;
413
- state.providerFinishReason = result.providerFinishReason;
414
- state.usage = aggregateTokenUsage([state.usage, result.usage]);
415
- state.pendingApprovals = pendingApprovals;
416
- state.updatedAt = Date.now();
417
- return toOutput(state);
418
- };
419
- const emitTelemetryEvent = async (agent, event) => {
420
- await invokeOperationalHook(agent, "telemetry", event.type, event.runId, agent.onTelemetryEvent ? () => agent.onTelemetryEvent(event) : undefined, undefined);
421
- };
422
- const emitInvocationStartTelemetry = async (agent, runId, startedAt, maxSteps) => {
423
- try {
424
- await agent.onTelemetryEvent?.startInvocation?.({
425
- runId,
426
- agentId: agent.id,
427
- agentName: agent.name,
428
- provider: agent.model.provider,
429
- modelId: agent.model.modelId,
430
- maxSteps,
431
- startedAt
432
- });
433
- }
434
- catch {
435
- // Invocation telemetry is best-effort and cannot replace setup/business errors.
436
- }
437
- };
438
- const emitInvocationFinishTelemetry = async (agent, runId, status, error) => {
439
- try {
440
- await agent.onTelemetryEvent?.finishInvocation?.({
441
- runId,
442
- agentId: agent.id,
443
- agentName: agent.name,
444
- status,
445
- error,
446
- finishedAt: Date.now()
447
- });
448
- }
449
- catch {
450
- // Invocation telemetry is best-effort and cannot replace setup/business errors.
451
- }
452
- };
453
- const withAgentTelemetryRunContext = (agent, runId, callback) => {
454
- const telemetryObserver = agent.onTelemetryEvent;
455
- const wrapper = telemetryObserver?.withRunContext;
456
- if (!wrapper)
457
- return Promise.resolve().then(callback);
458
- let execution;
459
- let callbackError;
460
- let callbackFailed = false;
461
- const executeOnce = () => {
462
- execution ??= Promise.resolve()
463
- .then(callback)
464
- .catch((error) => {
465
- callbackFailed = true;
466
- callbackError = error;
467
- throw error;
468
- });
469
- return execution;
470
- };
471
- return Promise.resolve()
472
- .then(async () => {
473
- await wrapper.call(telemetryObserver, runId, executeOnce);
474
- return executeOnce();
475
- })
476
- .catch(async (wrapperError) => {
477
- if (callbackFailed)
478
- throw callbackError;
479
- try {
480
- return await executeOnce();
481
- }
482
- catch (executionError) {
483
- throw callbackFailed ? callbackError : executionError ?? wrapperError;
484
- }
485
- });
486
- };
487
- const subAgentToolInputSchema = z.object({
488
- prompt: z.string().min(1),
489
- system: z.string().optional()
490
- });
491
- const defaultSubAgentToolName = (agent) => {
492
- const id = agent.id ?? `${agent.model.provider}_${agent.model.modelId}`;
493
- return `subagent_${id.replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "agent"}`;
494
- };
495
- const countToolCallsInSteps = (steps) => steps.reduce((total, step) => total + countToolCalls(step.response?.messages ?? []), 0);
496
- const countToolErrors = (toolResults) => toolResults.filter((result) => result.isError).length;
497
- export const createSubAgentTool = (options) => {
498
- const runtimeState = options.runtimeState;
499
- const toolName = options.toolName ?? options.name ?? defaultSubAgentToolName(options.agent);
500
- const metadata = {
501
- type: "subagent"
502
- };
503
- if (options.agent.id) {
504
- metadata.childAgentId = options.agent.id;
505
- }
506
- if (options.parentRunId) {
507
- metadata.parentRunId = options.parentRunId;
508
- }
509
- if (options.parentAgentId) {
510
- metadata.parentAgentId = options.parentAgentId;
511
- }
512
- return {
513
- name: toolName,
514
- description: options.description ??
515
- `Delegate the task to ${options.agent.id ? `subagent "${options.agent.id}"` : "a subagent"} and return its result.`,
516
- schema: subAgentToolInputSchema,
517
- requiresApproval: options.requiresApproval,
518
- metadata: cloneMetadata(metadata, options.metadata),
519
- execute: async (input, executionContext) => {
520
- await options.onStart?.({
521
- toolName,
522
- childAgentId: options.agent.id,
523
- parentRunId: options.parentRunId
524
- });
525
- const childMetadata = {
526
- subagentToolName: toolName
527
- };
528
- if (options.parentRunId) {
529
- childMetadata.parentRunId = options.parentRunId;
530
- }
531
- if (options.parentAgentId) {
532
- childMetadata.parentAgentId = options.parentAgentId;
533
- }
534
- const toolCallId = executionContext?.toolCall.id;
535
- const childIdempotencyKey = options.parentRunId &&
536
- toolCallId &&
537
- executionContext &&
538
- options.agent.store?.claimIdempotencyKey
539
- ? `subagent:${durableToolCallId(options.parentRunId, executionContext.step, toolCallId, toolName, serializeJsonValue(input))}`
540
- : undefined;
541
- const checkpoint = runtimeState?.childRuns?.find((childRun) => childRun.toolCallId === toolCallId && childRun.resumeState);
542
- const childApprovalResponses = checkpoint
543
- ? (runtimeState?.approvalHistory ?? [])
544
- .filter((resolution) => resolution.kind === "subagent" &&
545
- resolution.toolCallId === toolCallId &&
546
- resolution.childRunId === checkpoint.runId &&
547
- resolution.childApprovalRequestId)
548
- .map((resolution) => ({
549
- provider: resolution.provider,
550
- approvalRequestId: resolution.childApprovalRequestId,
551
- approve: resolution.approve,
552
- reason: resolution.reason
553
- }))
554
- : [];
555
- const output = checkpoint?.resumeState
556
- ? await resumeAgent(options.agent, {
557
- state: checkpoint.resumeState,
558
- approvals: childApprovalResponses,
559
- scope: options.scope,
560
- context: executionContext?.context,
561
- maxSteps: options.maxSteps
562
- })
563
- : await runAgent(options.agent, {
564
- prompt: input.prompt,
565
- system: joinInstructions(options.system, input.system),
566
- parentRunId: options.parentRunId,
567
- idempotencyKey: childIdempotencyKey,
568
- scope: options.scope,
569
- context: executionContext?.context,
570
- maxSteps: options.maxSteps,
571
- metadata: cloneMetadata(options.metadata, childMetadata)
572
- });
573
- const childRun = {
574
- runId: output.state.runId,
575
- status: output.status,
576
- outputText: output.outputText,
577
- steps: output.state.currentStep,
578
- toolCalls: countToolCallsInSteps(output.steps),
579
- toolErrors: countToolErrors(output.toolResults)
580
- };
581
- if (toolCallId) {
582
- childRun.toolCallId = toolCallId;
583
- }
584
- if (output.state.agentId) {
585
- childRun.agentId = output.state.agentId;
586
- }
587
- if (options.parentRunId) {
588
- childRun.parentRunId = options.parentRunId;
589
- }
590
- childRun.toolName = toolName;
591
- if (output.usage) {
592
- childRun.usage = output.usage;
593
- }
594
- if (output.state.startedAt !== undefined) {
595
- childRun.startedAt = output.state.startedAt;
596
- }
597
- if (output.state.updatedAt !== undefined) {
598
- childRun.updatedAt = output.state.updatedAt;
599
- }
600
- if (output.error) {
601
- childRun.error = output.error;
602
- }
603
- if (output.state.metadata) {
604
- childRun.metadata = output.state.metadata;
605
- }
606
- if (output.status === "waiting_approval" && output.state.pendingApprovals.length) {
607
- childRun.resumeState = output.state;
608
- }
609
- await options.onFinish?.(childRun);
610
- if (childRun.resumeState) {
611
- const approvals = childRun.resumeState.pendingApprovals.map((approval) => {
612
- const id = `subapproval_${createHash("sha256")
613
- .update(`${options.parentRunId ?? ""}\0${toolCallId ?? ""}\0${childRun.runId}\0${approval.id}`)
614
- .digest("hex")}`;
615
- return {
616
- kind: "subagent",
617
- provider: approval.provider,
618
- id,
619
- name: approval.name,
620
- arguments: approval.arguments,
621
- serverLabel: approval.serverLabel,
622
- toolCallId,
623
- step: executionContext?.step,
624
- childRunId: childRun.runId,
625
- childAgentId: childRun.agentId,
626
- childApprovalRequestId: approval.id,
627
- rawData: {
628
- type: "subagent_approval_request",
629
- childRunId: childRun.runId,
630
- childAgentId: childRun.agentId ?? null,
631
- childApprovalRequestId: approval.id,
632
- approval: approval.rawData
633
- }
634
- };
635
- });
636
- throw new ToolExecutionSuspendedError(approvals);
637
- }
638
- return serializeJsonValue(childRun);
639
- }
640
- };
641
- };
642
- const saveStateWithRevision = async (store, state, serializedNextState) => {
643
- const expectedRevision = state.revision ?? 0;
644
- const nextRevision = expectedRevision + 1;
645
- const nextState = { ...state, revision: nextRevision };
646
- await store.save(serializedNextState === undefined ? cloneState(nextState) : JSON.parse(serializedNextState), { expectedRevision });
647
- state.revision = nextRevision;
648
- };
649
- const claimAgentExecution = async (agent, state) => {
650
- state.status = "running";
651
- state.updatedAt = Date.now();
652
- const serialized = assertStateSize(agent, agent.store ? normalizeAgentRunState({ ...state, revision: (state.revision ?? 0) + 1 }) : state);
653
- if (agent.store) {
654
- await saveStateWithRevision(agent.store, state, serialized);
655
- }
656
- };
657
- const assertStateSize = (agent, state, policy) => {
658
- const limit = policy?.maxStateBytes ?? agent.policy?.maxStateBytes ?? DEFAULT_AGENT_MAX_STATE_BYTES;
659
- if (!Number.isSafeInteger(limit) || limit < 1) {
660
- throw new ValidationError('Agent policy "maxStateBytes" must be a positive integer.');
661
- }
662
- const serialized = JSON.stringify(state);
663
- const bytes = new TextEncoder().encode(serialized).byteLength;
664
- if (bytes > limit) {
665
- throw new ValidationError(`Agent run state is ${bytes} bytes and exceeds maxStateBytes=${limit}. Offload large tool outputs to artifacts or raise the explicit limit.`);
666
- }
667
- return serialized;
668
- };
669
- const persistState = async (agent, state, policy) => {
670
- state.updatedAt = Date.now();
671
- await refreshAgentTaskOutcome(state, agent.store);
672
- const serialized = assertStateSize(agent, agent.store ? normalizeAgentRunState({ ...state, revision: (state.revision ?? 0) + 1 }) : state, policy);
673
- if (agent.store) {
674
- await saveStateWithRevision(agent.store, state, serialized);
675
- }
676
- await emitTelemetryEvent(agent, {
677
- type: "state-saved",
678
- runId: state.runId,
679
- agentId: state.agentId,
680
- status: state.status
681
- });
682
- await invokeOperationalHook(agent, "memory", "save", state.runId, agent.memory?.save
683
- ? () => agent.memory.save({
684
- runId: state.runId,
685
- agentId: state.agentId,
686
- scope: state.scope,
687
- state: cloneState(state),
688
- metadata: state.metadata
689
- })
690
- : undefined, undefined);
691
- };
692
- const approvalsFromEvents = (messages) => getAgentApprovalRequests(messages);
693
- const emitFinalizedStepTelemetry = async (agent, state, steps) => {
694
- for (const step of steps) {
695
- await emitTelemetryEvent(agent, {
696
- type: "step-finish",
697
- runId: state.runId,
698
- agentId: state.agentId,
699
- step
700
- });
701
- }
702
- };
703
- const emitApprovalTelemetry = async (agent, state, approvals) => {
704
- for (const approval of approvals) {
705
- await emitTelemetryEvent(agent, {
706
- type: "approval-request",
707
- runId: state.runId,
708
- agentId: state.agentId,
709
- approval
710
- });
711
- }
712
- };
713
- const emitToolApprovalTelemetry = async (agent, state, event) => {
714
- await emitTelemetryEvent(agent, {
715
- type: "tool-approval",
716
- runId: state.runId,
717
- agentId: state.agentId,
718
- toolCall: event.request.toolCall,
719
- approved: event.decision.approved,
720
- reason: event.decision.reason,
721
- metadata: event.decision.metadata
722
- });
723
- };
724
- const normalizeGuardrailTrigger = (value) => value?.triggered ? value : undefined;
725
- const applyGuardrailFailure = (state, stage, trigger) => ({
726
- ...state,
727
- status: "failed",
728
- error: {
729
- message: trigger.reason ?? `Agent ${stage} guardrail triggered.`
730
- },
731
- updatedAt: Date.now()
732
- });
733
- const runGuardrails = async (agent, state, stage, guardrails, requestFactory) => {
734
- for (const [index, guardrail] of (guardrails ?? []).entries()) {
735
- const trigger = normalizeGuardrailTrigger(await guardrail(requestFactory(index)));
736
- if (!trigger) {
737
- continue;
738
- }
739
- await emitTelemetryEvent(agent, {
740
- type: "guardrail-triggered",
741
- runId: state.runId,
742
- agentId: state.agentId,
743
- stage,
744
- reason: trigger.reason ?? `Agent ${stage} guardrail #${index + 1} triggered.`,
745
- metadata: trigger.metadata
746
- });
747
- return trigger;
748
- }
749
- return undefined;
750
- };
751
- const bindDurableRuntime = (agent, input, state, executionEnvironment) => {
752
- const policy = {
753
- ...(agent.policy ?? {}),
754
- ...(input.policy ?? {})
755
- };
756
- if (state.harness && !agent.harness) {
757
- throw new ConflictError(`Agent run "${state.runId}" is bound to harness "${state.harness.id}", but the current agent has no harness binding.`);
758
- }
759
- if (state.harness && agent.harness) {
760
- if (state.harness.id !== agent.harness.id ||
761
- state.harness.version !== agent.harness.version ||
762
- state.harness.fingerprint !== agent.harness.fingerprint) {
763
- throw new ConflictError(`Agent run "${state.runId}" was created by a different harness fingerprint.`);
764
- }
765
- }
766
- else if (!state.harness && agent.harness) {
767
- if (!policy?.allowLegacyHarnessResume) {
768
- throw new ConflictError(`Agent run "${state.runId}" predates harness binding; set allowLegacyHarnessResume only for an explicit migration.`);
769
- }
770
- state.harness = agent.harness;
771
- }
772
- if (state.executionEnvironment && !executionEnvironment) {
773
- throw new ConflictError(`Agent run "${state.runId}" is bound to execution environment "${state.executionEnvironment.environmentId}".`);
774
- }
775
- if (state.executionEnvironment && executionEnvironment) {
776
- if (state.executionEnvironment.environmentId !== executionEnvironment.environmentId ||
777
- state.executionEnvironment.environmentVersion !== executionEnvironment.environmentVersion ||
778
- state.executionEnvironment.fingerprint !== executionEnvironment.fingerprint ||
779
- state.executionEnvironment.workspaceId !== executionEnvironment.workspaceId) {
780
- throw new ConflictError(`Agent run "${state.runId}" was created in a different execution environment.`);
781
- }
782
- }
783
- else if (!state.executionEnvironment && executionEnvironment) {
784
- if (!policy?.allowLegacyExecutionEnvironmentResume) {
785
- throw new ConflictError(`Agent run "${state.runId}" predates execution-environment binding; enable the explicit migration policy to resume it.`);
786
- }
787
- state.executionEnvironment = executionEnvironment;
788
- }
789
- };
790
- const validateHarnessBinding = (binding) => {
791
- if (!binding) {
792
- return;
793
- }
794
- if (binding.schemaVersion !== 1 ||
795
- binding.algorithm !== "sha256" ||
796
- !binding.id ||
797
- !binding.version ||
798
- !/^sha256:[0-9a-f]{64}$/.test(binding.fingerprint)) {
799
- throw new ValidationError("Agent harness binding is invalid.");
800
- }
801
- };
802
- const resolveContext = async (agent, input) => {
803
- validateHarnessBinding(agent.harness);
804
- const executionEnvironment = input.executionEnvironment ?? agent.executionEnvironment;
805
- const executionEnvironmentBinding = executionEnvironment
806
- ? createAgentExecutionEnvironmentBinding(executionEnvironment.manifest)
807
- : undefined;
808
- let parsedContext = input.context;
809
- if (agent.contextSchema) {
810
- const result = await agent.contextSchema.safeParseAsync(input.context);
811
- if (!result.success) {
812
- throw new ValidationError(`Invalid agent context: ${result.error.message}`);
813
- }
814
- parsedContext = result.data;
815
- }
816
- ensureValidIdempotencyInput(input, agent.store);
817
- const inputScope = input.scope ?? input.handoff?.scope;
818
- ensureValidScope(inputScope);
819
- let loadedState = input.state ? normalizeAgentRunState(input.state) : undefined;
820
- let loadedByIdempotencyKey = false;
821
- if (!loadedState && input.runId && agent.store) {
822
- loadedState = await agent.store.load(input.runId, inputScope);
823
- if (loadedState) {
824
- loadedState = normalizeAgentRunState(loadedState);
825
- }
826
- }
827
- if (!loadedState && input.idempotencyKey) {
828
- const runId = input.runId ?? randomId("run");
829
- const maxSteps = Math.max(1, input.maxSteps ?? agent.maxSteps ?? 1);
830
- const metadata = cloneMetadata(agent.metadata, input.metadata, input.handoff?.metadata);
831
- const prepared = await prepareFreshMessages(agent, input, runId);
832
- 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);
833
- const claim = await agent.store.claimIdempotencyKey(candidate);
834
- if (claim.claimed) {
835
- return {
836
- state: normalizeAgentRunState(claim.state),
837
- messages: prepared.messages,
838
- remainingSteps: maxSteps,
839
- memoryMessages: prepared.memoryMessages,
840
- context: parsedContext,
841
- executionEnvironment,
842
- fresh: true
843
- };
844
- }
845
- loadedState = normalizeAgentRunState(claim.state);
846
- loadedByIdempotencyKey = true;
847
- }
848
- const normalizedInput = loadedState && loadedByIdempotencyKey
849
- ? { ...input, prompt: undefined, messages: undefined, system: undefined, handoff: undefined, state: loadedState }
850
- : loadedState
851
- ? { ...input, state: loadedState }
852
- : input;
853
- ensureValidStateInput(normalizedInput);
854
- const metadata = cloneMetadata(agent.metadata, loadedState?.metadata, input.metadata, input.handoff?.metadata);
855
- if (loadedState) {
856
- const groupIdentity = input.metadata?.agentGroupIdentity;
857
- if (groupIdentity !== undefined && (loadedState.metadata?.agentGroupIdentity !== groupIdentity ||
858
- loadedState.agentId !== agent.id)) {
859
- throw new ConflictError("Agent group idempotency key belongs to a different member or agent.");
860
- }
861
- bindDurableRuntime(agent, input, loadedState, executionEnvironmentBinding);
862
- const maxSteps = input.maxSteps ?? loadedState.maxSteps;
863
- const resumed = await applyApprovalResponses(loadedState.messages, input.approvals, loadedState.pendingApprovals, loadedState.approvalHistory, agent.toolApprovalSigner);
864
- return {
865
- state: {
866
- ...loadedState,
867
- schemaVersion: AGENT_RUN_STATE_SCHEMA_VERSION,
868
- idempotencyKey: loadedState.idempotencyKey ?? input.idempotencyKey,
869
- scope: loadedState.scope ?? inputScope,
870
- agentId: loadedState.agentId ?? agent.id,
871
- parentRunId: loadedState.parentRunId ?? input.parentRunId,
872
- provider: agent.model.provider,
873
- modelId: agent.model.modelId,
874
- maxSteps,
875
- messages: resumed.messages,
876
- pendingApprovals: resumed.pendingApprovals,
877
- approvalHistory: resumed.approvalHistory,
878
- metadata,
879
- updatedAt: Date.now()
880
- },
881
- messages: resumed.messages,
882
- remainingSteps: Math.max(0, maxSteps - loadedState.currentStep),
883
- memoryMessages: [],
884
- context: parsedContext,
885
- executionEnvironment,
886
- fresh: false
887
- };
888
- }
889
- const runId = input.runId ?? randomId("run");
890
- const maxSteps = Math.max(1, input.maxSteps ?? agent.maxSteps ?? 1);
891
- const prepared = await prepareFreshMessages(agent, input, runId);
892
- return {
893
- 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),
894
- messages: prepared.messages,
895
- remainingSteps: maxSteps,
896
- memoryMessages: prepared.memoryMessages,
897
- context: parsedContext,
898
- executionEnvironment,
899
- fresh: true
900
- };
901
- };
902
- const canonicalJson = (value) => {
903
- if (value === null || typeof value !== "object") {
904
- return JSON.stringify(value);
905
- }
906
- if (Array.isArray(value)) {
907
- return `[${value.map(canonicalJson).join(",")}]`;
908
- }
909
- return `{${Object.keys(value)
910
- .sort()
911
- .map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`)
912
- .join(",")}}`;
913
- };
914
- const durableToolCallId = (runId, step, providerToolCallId, toolName, input) => `tool_${createHash("sha256")
915
- .update(`${runId}\0${step}\0${providerToolCallId}\0${toolName}\0${canonicalJson(input)}`)
916
- .digest("hex")}`;
917
- const wrapToolWithExecutionEnvironment = (tool, session) => {
918
- const authorize = async (input, context, phase) => session.authorize({
919
- manifest: session.manifest,
920
- binding: session.binding,
921
- tool,
922
- toolCall: context.toolCall,
923
- input,
924
- context,
925
- phase
926
- });
927
- const environmentGuardrail = async ({ input, context }) => {
928
- const decision = await authorize(input, context, "preflight");
929
- return decision.decision === "deny"
930
- ? {
931
- triggered: true,
932
- reason: decision.reason,
933
- metadata: decision.metadata
934
- }
935
- : undefined;
936
- };
937
- return {
938
- ...tool,
939
- approvalVersion: [
940
- tool.approvalVersion,
941
- `environment:${session.binding.fingerprint}`
942
- ].filter(Boolean).join("|"),
943
- inputGuardrails: [
944
- environmentGuardrail,
945
- ...(tool.inputGuardrails ?? [])
946
- ],
947
- execute: async (input, context) => {
948
- if (!context) {
949
- throw new ValidationError(`Tool "${tool.name}" requires an execution environment context.`);
950
- }
951
- const decision = await authorize(input, context, "execute");
952
- if (decision.decision === "deny") {
953
- throw new GuardrailTriggeredError("tool-input", decision.reason, { metadata: decision.metadata });
954
- }
955
- const request = {
956
- manifest: session.manifest,
957
- binding: session.binding,
958
- tool,
959
- toolCall: context.toolCall,
960
- input,
961
- context,
962
- phase: "execute"
963
- };
964
- return session.execute(request, () => tool.execute(input, context));
965
- }
966
- };
967
- };
968
- const wrapToolWithJournal = (agent, state, tool) => {
969
- const store = agent.store;
970
- if (!store?.claimToolExecution || !store.loadToolExecution || !store.completeToolExecution) {
971
- return tool;
972
- }
973
- return {
974
- ...tool,
975
- execute: async (input, context) => {
976
- if (!context) {
977
- throw new ValidationError(`Durable tool "${tool.name}" requires an execution context.`);
978
- }
979
- const serializedInput = serializeJsonValue(input);
980
- const confirmed = state.reconciliations?.find(record => record.evidence.toolName === tool.name && canonicalJson(record.evidence.input) === canonicalJson(serializedInput));
981
- if (confirmed)
982
- return confirmed.evidence.output;
983
- const step = context.step;
984
- const toolCallId = durableToolCallId(state.runId, step, context.toolCall.id, tool.name, serializedInput);
985
- const idempotencyKey = `${state.runId}:${toolCallId}`;
986
- const now = Date.now();
987
- const candidate = {
988
- runId: state.runId,
989
- scope: state.scope,
990
- toolCallId,
991
- providerToolCallId: context.toolCall.id,
992
- toolName: tool.name,
993
- status: "pending",
994
- idempotencyKey,
995
- revision: 0,
996
- input: serializedInput,
997
- updatedAt: now
998
- };
999
- const claim = await store.claimToolExecution(candidate);
1000
- if (!claim.claimed) {
1001
- if (claim.entry.status === "completed") {
1002
- return claim.entry.output ?? null;
1003
- }
1004
- if (claim.entry.status === "failed") {
1005
- throw new Error(claim.entry.error?.message ?? `Tool "${tool.name}" previously failed.`);
1006
- }
1007
- throw new ConflictError(`Tool "${tool.name}" has an indeterminate durable execution. Reconcile idempotency key "${claim.entry.idempotencyKey}" before retrying.`);
1008
- }
1009
- try {
1010
- const output = serializeJsonValue(await tool.execute(input, {
1011
- ...context,
1012
- runId: state.runId,
1013
- idempotencyKey
1014
- }));
1015
- await store.completeToolExecution({
1016
- ...claim.entry,
1017
- status: "completed",
1018
- output,
1019
- completedAt: Date.now(),
1020
- updatedAt: Date.now()
1021
- }, { expectedRevision: claim.entry.revision });
1022
- return output;
1023
- }
1024
- catch (error) {
1025
- const normalizedError = error instanceof Error ? error : new Error(String(error));
1026
- try {
1027
- await store.completeToolExecution({
1028
- ...claim.entry,
1029
- status: "failed",
1030
- error: { message: normalizedError.message },
1031
- completedAt: Date.now(),
1032
- updatedAt: Date.now()
1033
- }, { expectedRevision: claim.entry.revision });
1034
- }
1035
- catch {
1036
- // The original error is more useful; a running journal row blocks unsafe replay.
1037
- }
1038
- throw normalizedError;
1039
- }
1040
- }
1041
- };
1042
- };
1043
- const defaultEstimateInputTokens = (messages) => Math.ceil(new TextEncoder().encode(JSON.stringify(messages)).byteLength / 4);
1044
- const validateCompactionOptions = (options) => {
1045
- if (options.maxMessages !== undefined &&
1046
- (!Number.isSafeInteger(options.maxMessages) || options.maxMessages < 2)) {
1047
- throw new ValidationError("Agent compaction maxMessages must be an integer greater than or equal to 2.");
1048
- }
1049
- if (options.maxEstimatedInputTokens !== undefined &&
1050
- (!Number.isSafeInteger(options.maxEstimatedInputTokens) || options.maxEstimatedInputTokens < 1)) {
1051
- throw new ValidationError("Agent compaction maxEstimatedInputTokens must be a positive integer.");
1052
- }
1053
- if (options.keepRecentMessages !== undefined &&
1054
- (!Number.isSafeInteger(options.keepRecentMessages) || options.keepRecentMessages < 1)) {
1055
- throw new ValidationError("Agent compaction keepRecentMessages must be a positive integer.");
1056
- }
1057
- if (options.maxMessages === undefined && options.maxEstimatedInputTokens === undefined) {
1058
- throw new ValidationError("Agent compaction requires maxMessages or maxEstimatedInputTokens.");
1059
- }
1060
- };
1061
- const compactAgentMessages = async (options, state, messages, beforeStep, context, abortSignal) => {
1062
- validateCompactionOptions(options);
1063
- if (state.pendingApprovals.length) {
1064
- return undefined;
1065
- }
1066
- const estimateTokens = options.estimateTokens ?? defaultEstimateInputTokens;
1067
- const estimatedTokensBefore = estimateTokens(messages);
1068
- const reasons = [];
1069
- if (options.maxMessages !== undefined && messages.length > options.maxMessages) {
1070
- reasons.push("message-count");
1071
- }
1072
- if (options.maxEstimatedInputTokens !== undefined &&
1073
- estimatedTokensBefore > options.maxEstimatedInputTokens) {
1074
- reasons.push("estimated-input-tokens");
1075
- }
1076
- if (!reasons.length) {
1077
- return undefined;
1078
- }
1079
- let systemCount = 0;
1080
- while (messages[systemCount]?.role === "system") {
1081
- systemCount += 1;
1082
- }
1083
- const maxTailForMessageLimit = options.maxMessages === undefined
1084
- ? Number.POSITIVE_INFINITY
1085
- : Math.max(1, options.maxMessages - systemCount - 1);
1086
- const keepRecentMessages = Math.min(options.keepRecentMessages ?? 8, maxTailForMessageLimit);
1087
- let cut = Math.max(systemCount, messages.length - keepRecentMessages);
1088
- while (cut > systemCount && messages[cut]?.role === "tool") {
1089
- cut -= 1;
1090
- }
1091
- // Preserve whole correlated groups even when approval messages or parallel
1092
- // tool results separate a call from its result. Moving the boundary can expose
1093
- // another crossing dependency, so resolve it to a fixed point.
1094
- const origins = new Map();
1095
- const dependencies = [];
1096
- messages.forEach((message, index) => {
1097
- for (const part of message.parts) {
1098
- let origin;
1099
- let reference;
1100
- if (part.type === "tool-call")
1101
- origin = `tool:${part.toolCall.id}`;
1102
- if (part.type === "tool-result")
1103
- reference = `tool:${part.toolResult.toolCallId}`;
1104
- if (part.type === "provider-data" && typeof part.data === "object" && part.data !== null && !Array.isArray(part.data)) {
1105
- if (part.data.type === "mcp_approval_request" && typeof part.data.id === "string") {
1106
- origin = `approval:${part.provider}:${part.data.id}`;
1107
- }
1108
- if (part.data.type === "mcp_approval_response" && typeof part.data.approval_request_id === "string") {
1109
- reference = `approval:${part.provider}:${part.data.approval_request_id}`;
1110
- }
1111
- }
1112
- if (origin)
1113
- origins.set(origin, index);
1114
- const from = reference ? origins.get(reference) : undefined;
1115
- if (from !== undefined)
1116
- dependencies.push({ from, to: index });
1117
- }
1118
- });
1119
- let previousCut;
1120
- do {
1121
- previousCut = cut;
1122
- for (const { from, to } of dependencies) {
1123
- if (from < cut && to >= cut)
1124
- cut = from;
1125
- }
1126
- } while (cut !== previousCut);
1127
- if (cut <= systemCount) {
1128
- throw new ValidationError("Agent compaction cannot satisfy its limits without removing protected messages.");
1129
- }
1130
- const compactedMessages = structuredClone(messages.slice(systemCount, cut));
1131
- const retainedMessages = structuredClone(messages.slice(cut));
1132
- const sourceDigest = fingerprintAgentHarness(messages);
1133
- const id = `cmp_${createHash("sha256")
1134
- .update(`${state.runId}\0${beforeStep}\0${sourceDigest}`)
1135
- .digest("hex")}`;
1136
- const result = await options.compactor({
1137
- runId: state.runId,
1138
- agentId: state.agentId,
1139
- scope: state.scope,
1140
- beforeStep,
1141
- context,
1142
- messages: compactedMessages,
1143
- retainedMessages,
1144
- reasons,
1145
- estimatedTokensBefore,
1146
- sourceDigest,
1147
- idempotencyKey: id,
1148
- metadata: state.metadata,
1149
- abortSignal
1150
- });
1151
- const summary = result.summary.trim();
1152
- if (!summary) {
1153
- throw new ValidationError("Agent compactor returned an empty summary.");
1154
- }
1155
- const compacted = [
1156
- ...structuredClone(messages.slice(0, systemCount)),
1157
- createTextMessage("assistant", `[Compacted prior conversation]\n${summary}`),
1158
- ...retainedMessages
1159
- ];
1160
- const estimatedTokensAfter = estimateTokens(compacted);
1161
- if (compacted.length >= messages.length || estimatedTokensAfter >= estimatedTokensBefore) {
1162
- throw new ValidationError("Agent compaction must reduce both message count and estimated input tokens.");
1163
- }
1164
- if (options.maxMessages !== undefined && compacted.length > options.maxMessages) {
1165
- throw new ValidationError("Agent compaction result still exceeds maxMessages.");
1166
- }
1167
- if (options.maxEstimatedInputTokens !== undefined &&
1168
- estimatedTokensAfter > options.maxEstimatedInputTokens) {
1169
- throw new ValidationError("Agent compaction result still exceeds maxEstimatedInputTokens.");
1170
- }
1171
- const resultDigest = fingerprintAgentHarness(compacted);
1172
- return {
1173
- messages: compacted,
1174
- record: {
1175
- id,
1176
- beforeStep,
1177
- createdAt: Date.now(),
1178
- reasons,
1179
- sourceDigest,
1180
- resultDigest,
1181
- summaryDigest: fingerprintAgentHarness(summary),
1182
- summary,
1183
- messageCountBefore: messages.length,
1184
- messageCountAfter: compacted.length,
1185
- compactedMessageCount: compactedMessages.length,
1186
- retainedMessageCount: retainedMessages.length,
1187
- estimatedTokensBefore,
1188
- estimatedTokensAfter,
1189
- usage: result.usage,
1190
- metadata: result.metadata
1191
- }
1192
- };
1193
- };
1194
- const createGenerateOptions = (agent, state, input, messages, maxSteps, context, executionEnvironmentSession, abortSignal = input.abortSignal, onCompaction) => {
1195
- const tools = { ...(toToolSet(input.tools ?? agent.tools) ?? {}) };
1196
- for (const subagent of agent.subagents ?? []) {
1197
- const subagentTool = createSubAgentTool({
1198
- ...subagent,
1199
- agent: {
1200
- ...subagent.agent,
1201
- store: subagent.agent.store ?? agent.store,
1202
- memory: subagent.agent.memory ?? agent.memory,
1203
- executionEnvironment: subagent.agent.executionEnvironment ??
1204
- input.executionEnvironment ??
1205
- agent.executionEnvironment,
1206
- compaction: subagent.agent.compaction ?? (input.compaction === false
1207
- ? undefined
1208
- : input.compaction ?? agent.compaction)
1209
- },
1210
- parentRunId: state.runId,
1211
- parentAgentId: state.agentId,
1212
- scope: state.scope,
1213
- runtimeState: state,
1214
- onStart: async ({ toolName, childAgentId }) => {
1215
- await emitTelemetryEvent(agent, {
1216
- type: "subagent-start",
1217
- runId: state.runId,
1218
- agentId: state.agentId,
1219
- childAgentId,
1220
- toolName
1221
- });
1222
- },
1223
- onFinish: async (childRun) => {
1224
- state.childRuns = [
1225
- ...(state.childRuns ?? []).filter((existing) => childRun.toolCallId
1226
- ? existing.toolCallId !== childRun.toolCallId
1227
- : existing.runId !== childRun.runId),
1228
- childRun
1229
- ];
1230
- await emitTelemetryEvent(agent, {
1231
- type: "subagent-finish",
1232
- runId: state.runId,
1233
- agentId: state.agentId,
1234
- childRun
1235
- });
1236
- }
1237
- });
1238
- if (tools[subagentTool.name]) {
1239
- throw new ValidationError(`Subagent tool "${subagentTool.name}" conflicts with an existing tool.`);
1240
- }
1241
- tools[subagentTool.name] = subagentTool;
1242
- }
1243
- for (const [name, tool] of Object.entries(tools)) {
1244
- if (isCallableToolDefinition(tool)) {
1245
- const environmentTool = executionEnvironmentSession
1246
- ? wrapToolWithExecutionEnvironment(tool, executionEnvironmentSession)
1247
- : tool;
1248
- tools[name] = tool.metadata?.type === "subagent"
1249
- ? environmentTool
1250
- : wrapToolWithJournal(agent, state, environmentTool);
1251
- }
1252
- }
1253
- const finalTools = Object.keys(tools).length ? tools : undefined;
1254
- const budget = input.policy?.budget ?? agent.policy?.budget;
1255
- const runPolicy = resolveRunPolicy(agent, input);
1256
- const compaction = input.compaction === false
1257
- ? undefined
1258
- : input.compaction ?? agent.compaction;
1259
- let checkpointState = cloneState(state);
1260
- let liveUsage = state.usage;
1261
- const liveToolResults = [...state.toolResults];
1262
- let reservedToolCalls = 0;
1263
- const requestedMaxTokens = input.maxTokens ?? agent.maxTokens;
1264
- const budgetStatus = budget ? getAgentBudgetStatus(state, budget) : undefined;
1265
- const tokenCeilings = [
1266
- requestedMaxTokens,
1267
- budgetStatus?.remaining.outputTokens,
1268
- budgetStatus?.remaining.totalTokens
1269
- ].filter((value) => value !== undefined);
1270
- const maxTokens = tokenCeilings.length ? Math.min(...tokenCeilings) : undefined;
1271
- const requestedToolExecution = input.toolExecution ?? agent.toolExecution;
1272
- const toolExecution = agent.subagents?.length && !(requestedToolExecution?.parallel && requestedToolExecution.independentOnly)
1273
- ? {
1274
- ...requestedToolExecution,
1275
- parallel: false,
1276
- maxConcurrency: 1
1277
- }
1278
- : requestedToolExecution;
1279
- return {
1280
- model: agent.model,
1281
- messages,
1282
- tools: finalTools,
1283
- toolChoice: input.toolChoice,
1284
- toolExecution,
1285
- toolApprovalPolicy: input.toolApprovalPolicy ?? agent.toolApprovalPolicy,
1286
- toolApprovalSigner: agent.toolApprovalSigner,
1287
- toolApprovalResolutions: state.approvalHistory,
1288
- toolContext: {
1289
- context,
1290
- runId: state.runId,
1291
- agentId: state.agentId,
1292
- agentName: agent.name,
1293
- scope: state.scope,
1294
- metadata: state.metadata,
1295
- executionEnvironment: executionEnvironmentSession
1296
- },
1297
- onToolApprovalDecision: async (event) => {
1298
- await emitToolApprovalTelemetry(agent, state, event);
1299
- },
1300
- prepareModelMessages: compaction
1301
- ? async ({ messages: activeMessages, step }) => {
1302
- const compacted = await compactAgentMessages(compaction, checkpointState, activeMessages, step, context, abortSignal);
1303
- if (!compacted) {
1304
- return undefined;
1305
- }
1306
- checkpointState = {
1307
- ...checkpointState,
1308
- messages: compacted.messages,
1309
- usage: aggregateTokenUsage([
1310
- checkpointState.usage,
1311
- compacted.record.usage
1312
- ]),
1313
- compactions: [
1314
- ...(checkpointState.compactions ?? []).filter((existing) => existing.id !== compacted.record.id),
1315
- compacted.record
1316
- ],
1317
- updatedAt: Date.now()
1318
- };
1319
- state.messages = compacted.messages;
1320
- // Finalization adds this invocation's model usage. Only add the
1321
- // compactor here; checkpoint usage already includes model responses.
1322
- state.usage = aggregateTokenUsage([state.usage, compacted.record.usage]);
1323
- liveUsage = aggregateTokenUsage([liveUsage, compacted.record.usage]);
1324
- state.compactions = checkpointState.compactions;
1325
- await persistState(agent, checkpointState, runPolicy);
1326
- state.revision = checkpointState.revision;
1327
- await onCompaction?.(compacted.record);
1328
- return compacted.messages;
1329
- }
1330
- : undefined,
1331
- onBeforeModelStep: async ({ step }) => {
1332
- if (budget) {
1333
- const trigger = evaluateAgentBudgetPreflight({ ...state, usage: liveUsage, toolResults: liveToolResults }, budget, {
1334
- operation: "model",
1335
- requiredSteps: Math.max(1, step - state.currentStep),
1336
- requestedOutputTokens: maxTokens
1337
- });
1338
- if (trigger) {
1339
- throw new GuardrailTriggeredError("input", trigger.reason ?? "Agent model budget preflight failed.", {
1340
- metadata: trigger.metadata
1341
- });
1342
- }
1343
- }
1344
- await emitTelemetryEvent(agent, {
1345
- type: "step-start",
1346
- runId: state.runId,
1347
- agentId: state.agentId,
1348
- agentName: agent.name,
1349
- stepIndex: step,
1350
- startedAt: Date.now()
1351
- });
1352
- },
1353
- onModelStep: async ({ request, response, step, toolCalls, approvalRequests, failedToolResults }) => {
1354
- liveUsage = aggregateTokenUsage([liveUsage, response.usage]);
1355
- const responseSnapshot = snapshotResponse(response);
1356
- const approvals = [
1357
- ...approvalRequests,
1358
- ...getAgentApprovalRequests(responseSnapshot.messages)
1359
- ];
1360
- const crossedCompactionBoundary = checkpointState.compactions?.some((record) => record.beforeStep === step &&
1361
- record.resultDigest === fingerprintAgentHarness(request.messages));
1362
- const requestOffset = crossedCompactionBoundary
1363
- ? 0
1364
- : messagePrefixLength(checkpointState.messages, request.messages);
1365
- const timing = getGenerateTextStepTiming(request);
1366
- const finishedAt = timing?.finishedAt ?? Date.now();
1367
- const checkpointStep = {
1368
- index: step,
1369
- status: failedToolResults ? "failed" : approvals.length ? "waiting_approval" : "completed",
1370
- startedAt: timing?.startedAt ?? finishedAt,
1371
- finishedAt,
1372
- request: snapshotRequest(request, requestOffset, request.messages.slice(requestOffset)),
1373
- response: responseSnapshot,
1374
- toolResults: failedToolResults ?? []
1375
- };
1376
- checkpointState = {
1377
- ...checkpointState,
1378
- status: approvals.length ? "waiting_approval" : toolCalls.length ? "running" : "completed",
1379
- messages: [...request.messages, ...responseSnapshot.messages,
1380
- ...(failedToolResults ?? []).map(toolResult => ({ role: "tool", parts: [{ type: "tool-result", toolResult }] }))],
1381
- toolResults: [...checkpointState.toolResults, ...(failedToolResults ?? [])],
1382
- steps: [...checkpointState.steps.filter((existing) => existing.index !== step), checkpointStep],
1383
- currentStep: step,
1384
- outputText: response.text ?? checkpointState.outputText,
1385
- finishReason: response.finishReason,
1386
- providerFinishReason: response.providerFinishReason,
1387
- usage: aggregateTokenUsage([checkpointState.usage, response.usage]),
1388
- pendingApprovals: approvals,
1389
- error: undefined,
1390
- updatedAt: Date.now()
1391
- };
1392
- if (agent.store) {
1393
- await persistState(agent, checkpointState, runPolicy);
1394
- state.revision = checkpointState.revision;
1395
- }
1396
- if (failedToolResults)
1397
- Object.assign(state, checkpointState);
1398
- },
1399
- onToolExecutionComplete: async ({ toolResults }) => {
1400
- liveToolResults.push(...toolResults);
1401
- const lastStep = checkpointState.steps.at(-1);
1402
- if (lastStep) {
1403
- lastStep.toolResults = [...lastStep.toolResults, ...toolResults];
1404
- }
1405
- checkpointState = {
1406
- ...checkpointState,
1407
- status: "running",
1408
- messages: [
1409
- ...checkpointState.messages,
1410
- ...toolResults.map((toolResult) => ({
1411
- role: "tool",
1412
- parts: [{ type: "tool-result", toolResult }]
1413
- }))
1414
- ],
1415
- toolResults: [...checkpointState.toolResults, ...toolResults],
1416
- updatedAt: Date.now()
1417
- };
1418
- if (agent.store) {
1419
- await persistState(agent, checkpointState, runPolicy);
1420
- state.revision = checkpointState.revision;
1421
- }
1422
- },
1423
- stepOffset: state.currentStep,
1424
- onBeforeToolExecution: async ({ step, toolCalls }) => {
1425
- if (budget) {
1426
- reservedToolCalls += toolCalls.length;
1427
- const trigger = evaluateAgentBudgetPreflight({ ...state, usage: liveUsage, toolResults: liveToolResults }, budget, {
1428
- operation: "tool",
1429
- requiredToolCalls: reservedToolCalls
1430
- });
1431
- if (trigger) {
1432
- throw new GuardrailTriggeredError("input", trigger.reason ?? "Agent tool budget preflight failed.", {
1433
- metadata: trigger.metadata
1434
- });
1435
- }
1436
- }
1437
- for (const toolCall of toolCalls) {
1438
- await emitTelemetryEvent(agent, {
1439
- type: "tool-start",
1440
- runId: state.runId,
1441
- agentId: state.agentId,
1442
- agentName: agent.name,
1443
- stepIndex: step,
1444
- toolCall,
1445
- startedAt: Date.now()
1446
- });
1447
- }
1448
- },
1449
- maxSteps,
1450
- temperature: input.temperature ?? agent.temperature,
1451
- maxTokens,
1452
- reasoning: input.reasoning ?? agent.reasoning,
1453
- structuredOutput: agent.outputSchema && resolveAgentOutputMode(agent) === "native"
1454
- ? {
1455
- schema: agent.outputSchema,
1456
- mode: "native",
1457
- name: agent.outputName,
1458
- description: agent.outputDescription
1459
- }
1460
- : undefined,
1461
- providerOptions: input.providerOptions ?? agent.providerOptions,
1462
- abortSignal,
1463
- timeoutMs: input.timeoutMs,
1464
- maxRetries: input.maxRetries,
1465
- retryBackoffMs: input.retryBackoffMs
1466
- };
1467
- };
1468
- const emptyAsyncIterable = async function* () {
1469
- return;
1470
- };
1471
- const toAgentRunError = (error) => {
1472
- if (error instanceof ToolNotRegisteredError) {
1473
- return { message: error.message, diagnosticCode: error.code };
1474
- }
1475
- if (error instanceof ProviderToolCallError) {
1476
- return {
1477
- message: error.message,
1478
- diagnosticCode: error.diagnosticCode,
1479
- category: error.category,
1480
- provider: error.provider,
1481
- ...(error.transport ? { transport: error.transport } : {}),
1482
- reason: error.reason,
1483
- retryable: error.retryable,
1484
- effectsPossible: error.effectsPossible
1485
- };
1486
- }
1487
- return {
1488
- message: error instanceof Error ? error.message : String(error)
1489
- };
1490
- };
1491
- const createFailedState = (state, error) => ({
1492
- ...state,
1493
- status: "failed",
1494
- error: toAgentRunError(error),
1495
- updatedAt: Date.now()
1496
- });
1497
- const createTerminalState = (state, status, message) => ({
1498
- ...state,
1499
- status,
1500
- error: {
1501
- message
1502
- },
1503
- cancellationReason: status === "cancel_requested" ? message : state.cancellationReason,
1504
- cancelledAt: status === "cancel_requested" ? Date.now() : state.cancelledAt,
1505
- updatedAt: Date.now()
1506
- });
1507
- const resolveRunPolicy = (agent, input) => {
1508
- const policy = {
1509
- ...(agent.policy ?? {}),
1510
- ...(input.policy ?? {})
1511
- };
1512
- return Object.keys(policy).length ? policy : undefined;
1513
- };
1514
- const withAgentPolicyTimeout = async (operation, timeout) => {
1515
- try {
1516
- return timeout.timeoutPromise
1517
- ? await Promise.race([operation, timeout.timeoutPromise])
1518
- : await operation;
1519
- }
1520
- finally {
1521
- timeout.cleanup();
1522
- }
1523
- };
1524
- const createAgentAbortContext = (inputAbortSignal, policy, ...additionalSignals) => {
1525
- if (!policy?.timeoutMs) {
1526
- const merged = createMergedAbortSignal(inputAbortSignal, ...additionalSignals);
1527
- return {
1528
- signal: merged.signal,
1529
- timeoutPromise: undefined,
1530
- cleanup: merged.cleanup,
1531
- isTimedOut: () => false
1532
- };
1533
- }
1534
- const controller = new AbortController();
1535
- let timedOut = false;
1536
- let timeout;
1537
- const timeoutPromise = new Promise((_, reject) => {
1538
- timeout = setTimeout(() => {
1539
- timedOut = true;
1540
- controller.abort();
1541
- reject(new AgentPolicyTimeoutError(policy.timeoutMs));
1542
- }, policy.timeoutMs);
1543
- });
1544
- const merged = createMergedAbortSignal(inputAbortSignal, ...additionalSignals, controller.signal);
1545
- return {
1546
- signal: merged.signal,
1547
- timeoutPromise,
1548
- cleanup: () => {
1549
- merged.cleanup();
1550
- if (timeout) {
1551
- clearTimeout(timeout);
1552
- }
1553
- },
1554
- isTimedOut: () => timedOut
1555
- };
1556
- };
1557
- const acquireExecutionEnvironment = async (environment, state, context, abortSignal) => {
1558
- if (!environment) {
1559
- return undefined;
1560
- }
1561
- const expected = state.executionEnvironment;
1562
- if (!expected) {
1563
- throw new ConflictError(`Agent run "${state.runId}" has no durable execution-environment binding.`);
1564
- }
1565
- const session = await environment.acquire({
1566
- runId: state.runId,
1567
- agentId: state.agentId,
1568
- scope: state.scope,
1569
- context,
1570
- metadata: state.metadata,
1571
- abortSignal
1572
- });
1573
- const manifestBinding = createAgentExecutionEnvironmentBinding(session.manifest);
1574
- if (session.binding.environmentId !== expected.environmentId ||
1575
- session.binding.environmentVersion !== expected.environmentVersion ||
1576
- session.binding.fingerprint !== expected.fingerprint ||
1577
- session.binding.workspaceId !== expected.workspaceId ||
1578
- manifestBinding.fingerprint !== expected.fingerprint) {
1579
- await session.release?.({
1580
- status: "failed",
1581
- error: { message: "Execution environment returned a binding that differs from the durable run." }
1582
- });
1583
- throw new ConflictError(`Execution environment binding changed for agent run "${state.runId}".`);
1584
- }
1585
- return session;
1586
- };
1587
- const waitForAgentMonitorShutdown = async (monitor, timeoutMs) => {
1588
- let timeout;
1589
- const deadline = new Promise((resolve) => {
1590
- timeout = setTimeout(resolve, timeoutMs);
1591
- });
1592
- try {
1593
- await Promise.race([monitor, deadline]);
1594
- }
1595
- finally {
1596
- if (timeout)
1597
- clearTimeout(timeout);
1598
- }
1599
- };
1600
- const acquireAgentExecutionLease = async (agent, state, policy) => {
1601
- const store = agent.store;
1602
- if (policy?.leaseMode === "disabled" || !store?.acquireLease || !store.renewLease || !store.releaseLease) {
1603
- return {
1604
- supported: false,
1605
- cancelledState: () => undefined,
1606
- leaseLost: () => false,
1607
- release: async () => undefined
1608
- };
1609
- }
1610
- const ttlMs = policy?.leaseTtlMs ?? DEFAULT_AGENT_LEASE_TTL_MS;
1611
- const heartbeatMs = policy?.heartbeatMs ?? Math.max(250, Math.floor(ttlMs / 3));
1612
- const cancellationPollMs = policy?.cancellationPollMs ?? DEFAULT_AGENT_CANCELLATION_POLL_MS;
1613
- for (const [name, value] of [
1614
- ["leaseTtlMs", ttlMs],
1615
- ["heartbeatMs", heartbeatMs],
1616
- ["cancellationPollMs", cancellationPollMs]
1617
- ]) {
1618
- if (!Number.isSafeInteger(value) || value < 1) {
1619
- throw new ValidationError(`Agent policy "${name}" must be a positive integer.`);
1620
- }
1621
- }
1622
- if (heartbeatMs >= ttlMs) {
1623
- throw new ValidationError('Agent policy "heartbeatMs" must be less than "leaseTtlMs".');
1624
- }
1625
- const ownerId = randomId("worker");
1626
- const lease = await store.acquireLease(state.runId, { ownerId, ttlMs }, state.scope);
1627
- if (!lease) {
1628
- return undefined;
1629
- }
1630
- const controller = new AbortController();
1631
- let cancelled;
1632
- let lost = false;
1633
- let stopped = false;
1634
- let activeMonitor;
1635
- let lastHeartbeat = Date.now();
1636
- let lastCancellationPoll = 0;
1637
- const intervalMs = Math.max(25, Math.min(heartbeatMs, cancellationPollMs));
1638
- const monitorShutdownTimeoutMs = Math.min(ttlMs, DEFAULT_AGENT_MONITOR_SHUTDOWN_TIMEOUT_MS);
1639
- const cleanupStoppedMonitorLease = async () => {
1640
- try {
1641
- await store.releaseLease?.(state.runId, ownerId, state.scope);
1642
- }
1643
- catch {
1644
- // The foreground release remains authoritative; late monitor cleanup is best effort.
1645
- }
1646
- };
1647
- const monitorLease = async () => {
1648
- const now = Date.now();
1649
- try {
1650
- if (now - lastHeartbeat >= heartbeatMs) {
1651
- const renewed = await store.renewLease?.(state.runId, { ownerId, ttlMs }, state.scope);
1652
- if (stopped) {
1653
- await cleanupStoppedMonitorLease();
1654
- return;
1655
- }
1656
- if (!renewed) {
1657
- lost = true;
1658
- controller.abort(new ConflictError(`Agent run "${state.runId}" lost its worker lease.`));
1659
- return;
1660
- }
1661
- lastHeartbeat = now;
1662
- }
1663
- if (now - lastCancellationPoll >= cancellationPollMs) {
1664
- const latest = await store.load(state.runId, state.scope);
1665
- if (stopped)
1666
- return;
1667
- lastCancellationPoll = now;
1668
- if (latest?.status === "cancel_requested" || latest?.status === "cancelled") {
1669
- cancelled = normalizeAgentRunState(latest);
1670
- controller.abort(new Error(latest.cancellationReason ?? "Agent run was cancelled."));
1671
- }
1672
- }
1673
- }
1674
- catch (error) {
1675
- if (stopped) {
1676
- await cleanupStoppedMonitorLease();
1677
- return;
1678
- }
1679
- lost = true;
1680
- controller.abort(error);
1681
- }
1682
- };
1683
- const timer = setInterval(() => {
1684
- if (stopped || activeMonitor)
1685
- return;
1686
- activeMonitor = monitorLease().finally(() => {
1687
- activeMonitor = undefined;
1688
- });
1689
- }, intervalMs);
1690
- timer.unref?.();
1691
- return {
1692
- supported: true,
1693
- signal: controller.signal,
1694
- cancelledState: () => cancelled,
1695
- leaseLost: () => lost,
1696
- release: async () => {
1697
- stopped = true;
1698
- clearInterval(timer);
1699
- const monitor = activeMonitor;
1700
- if (monitor) {
1701
- await waitForAgentMonitorShutdown(monitor, monitorShutdownTimeoutMs);
1702
- }
1703
- await store.releaseLease?.(state.runId, ownerId, state.scope);
1704
- }
1705
- };
1706
- };
1707
- const emitRunStartTelemetry = async (agent, state, memoryMessages, approvals, invocationStartedAt) => {
1708
- await emitTelemetryEvent(agent, {
1709
- type: "run-start",
1710
- runId: state.runId,
1711
- agentId: state.agentId,
1712
- agentName: agent.name,
1713
- provider: state.provider,
1714
- modelId: state.modelId,
1715
- maxSteps: state.maxSteps,
1716
- startedAt: invocationStartedAt
1717
- });
1718
- if (state.handoff) {
1719
- await emitTelemetryEvent(agent, {
1720
- type: "handoff",
1721
- runId: state.runId,
1722
- agentId: state.agentId,
1723
- handoff: state.handoff
1724
- });
1725
- }
1726
- if (memoryMessages.length) {
1727
- await emitTelemetryEvent(agent, {
1728
- type: "memory-loaded",
1729
- runId: state.runId,
1730
- agentId: state.agentId,
1731
- messageCount: memoryMessages.length
1732
- });
1733
- }
1734
- for (const approval of approvals ?? []) {
1735
- await emitTelemetryEvent(agent, {
1736
- type: "approval-resolved",
1737
- runId: state.runId,
1738
- agentId: state.agentId,
1739
- approval
1740
- });
1741
- }
1742
- };
1743
- const emitRunFinishTelemetry = async (agent, state) => {
1744
- await emitTelemetryEvent(agent, {
1745
- type: "run-finish",
1746
- runId: state.runId,
1747
- agentId: state.agentId,
1748
- agentName: agent.name,
1749
- status: state.status,
1750
- state: cloneState(state),
1751
- finishedAt: Date.now()
1752
- });
1753
- };
1754
- export const createAgent = (definition) => ({
1755
- ...definition,
1756
- metadata: cloneMetadata(definition.metadata)
1757
- });
1758
- export class Agent {
1759
- id;
1760
- name;
1761
- model;
1762
- instructions;
1763
- contextSchema;
1764
- tools;
1765
- maxSteps;
1766
- temperature;
1767
- maxTokens;
1768
- reasoning;
1769
- outputSchema;
1770
- outputMode;
1771
- outputName;
1772
- outputDescription;
1773
- toolExecution;
1774
- toolApprovalPolicy;
1775
- toolApprovalSigner;
1776
- inputGuardrails;
1777
- outputGuardrails;
1778
- providerOptions;
1779
- subagents;
1780
- harness;
1781
- executionEnvironment;
1782
- compaction;
1783
- policy;
1784
- metadata;
1785
- store;
1786
- memory;
1787
- onTelemetryEvent;
1788
- hookFailurePolicy;
1789
- constructor(definition) {
1790
- Object.assign(this, createAgent(definition));
1791
- this.model = definition.model;
1792
- }
1793
- toDefinition() {
1794
- return createAgent({
1795
- id: this.id,
1796
- name: this.name,
1797
- model: this.model,
1798
- instructions: this.instructions,
1799
- contextSchema: this.contextSchema,
1800
- tools: this.tools,
1801
- maxSteps: this.maxSteps,
1802
- temperature: this.temperature,
1803
- maxTokens: this.maxTokens,
1804
- reasoning: this.reasoning,
1805
- outputSchema: this.outputSchema,
1806
- outputMode: this.outputMode,
1807
- outputName: this.outputName,
1808
- outputDescription: this.outputDescription,
1809
- toolExecution: this.toolExecution,
1810
- toolApprovalPolicy: this.toolApprovalPolicy,
1811
- toolApprovalSigner: this.toolApprovalSigner,
1812
- inputGuardrails: this.inputGuardrails,
1813
- outputGuardrails: this.outputGuardrails,
1814
- providerOptions: this.providerOptions,
1815
- subagents: this.subagents,
1816
- harness: this.harness,
1817
- executionEnvironment: this.executionEnvironment,
1818
- compaction: this.compaction,
1819
- policy: this.policy,
1820
- metadata: this.metadata,
1821
- store: this.store,
1822
- memory: this.memory,
1823
- onTelemetryEvent: this.onTelemetryEvent,
1824
- hookFailurePolicy: this.hookFailurePolicy
1825
- });
1826
- }
1827
- run(input = {}) {
1828
- return runAgent(this.toDefinition(), input);
1829
- }
1830
- resume(input) {
1831
- return resumeAgent(this.toDefinition(), input);
1832
- }
1833
- stream(input = {}) {
1834
- return streamAgent(this.toDefinition(), input);
1835
- }
1836
- }
1837
- export const prepareSubagentsForAgent = (agent, options = {}) => {
1838
- const store = options.store ?? agent.store;
1839
- const memory = options.memory ?? agent.memory;
1840
- const onTelemetryEvent = options.onTelemetryEvent ?? agent.onTelemetryEvent;
1841
- const toolApprovalPolicy = options.toolApprovalPolicy ?? agent.toolApprovalPolicy;
1842
- const toolExecution = options.toolExecution ?? agent.toolExecution;
1843
- const executionEnvironment = options.executionEnvironment ?? agent.executionEnvironment;
1844
- const compaction = options.compaction ?? agent.compaction;
1845
- const defaultMetadata = cloneMetadata(agent.metadata, options.metadata);
1846
- return {
1847
- ...agent,
1848
- metadata: cloneMetadata(agent.metadata),
1849
- subagents: (agent.subagents ?? []).map((subagent) => ({
1850
- ...subagent,
1851
- metadata: cloneMetadata(defaultMetadata, subagent.metadata),
1852
- agent: {
1853
- ...subagent.agent,
1854
- store: subagent.agent.store ?? store,
1855
- memory: subagent.agent.memory ?? memory,
1856
- onTelemetryEvent: subagent.agent.onTelemetryEvent ?? onTelemetryEvent,
1857
- toolApprovalPolicy: subagent.agent.toolApprovalPolicy ?? toolApprovalPolicy,
1858
- toolExecution: subagent.agent.toolExecution ?? toolExecution,
1859
- executionEnvironment: subagent.agent.executionEnvironment ?? executionEnvironment,
1860
- compaction: subagent.agent.compaction ?? compaction,
1861
- metadata: cloneMetadata(defaultMetadata, subagent.agent.metadata)
1862
- }
1863
- }))
1864
- };
1865
- };
1866
- export const runAgentGroup = async (agents, input = {}) => {
1867
- const { stopOnError, maxConcurrency, runId: _runId, state: _state, approvals: _approvals, handoff: _handoff, ...sharedInput } = input;
1868
- if (maxConcurrency !== undefined && (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1)) {
1869
- throw new ValidationError('Agent group "maxConcurrency" must be a positive safe integer.');
1870
- }
1871
- // Validate the complete group before any member can claim a key or invoke a model.
1872
- const identities = new Set();
1873
- const claims = [];
1874
- const memberInputs = agents.map((member) => {
1875
- const merged = { ...sharedInput, ...member.input };
1876
- ensureValidScope(merged.scope);
1877
- ensureValidIdempotencyInput(merged, member.agent.store);
1878
- if (!merged.idempotencyKey)
1879
- return merged;
1880
- const identity = member.name ?? member.agent.id;
1881
- if (!identity || identities.has(identity)) {
1882
- throw new ValidationError("Idempotent agent group members require unique names or agent IDs.");
1883
- }
1884
- identities.add(identity);
1885
- const key = member.input?.idempotencyKey ?? `agent-group:${JSON.stringify([input.idempotencyKey, identity])}`;
1886
- const scope = merged.scope;
1887
- const scopedKey = JSON.stringify([scope?.namespace ?? "default", scope?.tenantId, scope?.userId, key]);
1888
- if (claims.some((claim) => claim.store === member.agent.store && claim.key === scopedKey)) {
1889
- throw new ConflictError("Agent group members cannot share an explicit idempotency key in the same store and scope.");
1890
- }
1891
- claims.push({ store: member.agent.store, key: scopedKey });
1892
- return {
1893
- ...merged,
1894
- idempotencyKey: key,
1895
- metadata: cloneMetadata(input.metadata, member.input?.metadata, {
1896
- agentGroupIdentity: JSON.stringify([input.idempotencyKey ?? null, identity])
1897
- })
1898
- };
1899
- });
1900
- const parentRunId = input.parentRunId;
1901
- const controllers = agents.map(() => new AbortController());
1902
- let failFastTriggered = false;
1903
- const isFailingOutput = (output) => output.status === "failed" || output.status === "timed_out";
1904
- const abortPending = (currentIndex) => {
1905
- if (!stopOnError || failFastTriggered) {
1906
- return;
1907
- }
1908
- failFastTriggered = true;
1909
- controllers.forEach((controller, index) => {
1910
- if (index !== currentIndex) {
1911
- controller.abort();
1912
- }
1913
- });
1914
- };
1915
- const runMember = async (member, index) => {
1916
- const merged = createMergedAbortSignal(input.abortSignal, member.input?.abortSignal, controllers[index].signal);
1917
- try {
1918
- if (merged.signal?.aborted) {
1919
- throw new DOMException("Agent group member aborted before execution.", "AbortError");
1920
- }
1921
- const runInput = {
1922
- ...memberInputs[index],
1923
- parentRunId: member.input?.parentRunId ?? parentRunId,
1924
- abortSignal: merged.signal,
1925
- metadata: cloneMetadata(input.metadata, memberInputs[index]?.metadata, {
1926
- ...(member.name ? { agentGroupMember: member.name } : {})
1927
- })
1928
- };
1929
- const output = await runAgent(member.agent, runInput);
1930
- if (isFailingOutput(output)) {
1931
- abortPending(index);
1932
- }
1933
- return output;
1934
- }
1935
- catch (error) {
1936
- abortPending(index);
1937
- throw error;
1938
- }
1939
- finally {
1940
- merged.cleanup();
1941
- }
1942
- };
1943
- const settled = new Array(agents.length);
1944
- let nextIndex = 0;
1945
- await Promise.all(Array.from({ length: Math.min(maxConcurrency ?? agents.length, agents.length) }, async () => {
1946
- while (nextIndex < agents.length) {
1947
- const index = nextIndex++;
1948
- try {
1949
- settled[index] = { status: "fulfilled", value: await runMember(agents[index], index) };
1950
- }
1951
- catch (reason) {
1952
- settled[index] = { status: "rejected", reason };
1953
- }
1954
- }
1955
- }));
1956
- const outputs = settled.map((result, index) => {
1957
- const member = agents[index];
1958
- if (result.status === "fulfilled") {
1959
- return {
1960
- name: member.name,
1961
- agentId: result.value.state.agentId ?? member.agent.id,
1962
- status: "fulfilled",
1963
- output: result.value
1964
- };
1965
- }
1966
- return {
1967
- name: member.name,
1968
- agentId: member.agent.id,
1969
- status: "rejected",
1970
- error: {
1971
- message: stopOnError && failFastTriggered && controllers[index].signal.aborted
1972
- ? AGENT_GROUP_FAIL_FAST_ABORT_MESSAGE
1973
- : result.reason instanceof Error
1974
- ? result.reason.message
1975
- : String(result.reason)
1976
- }
1977
- };
1978
- });
1979
- const precedence = [
1980
- "failed", "timed_out", "cancel_requested", "running", "queued", "waiting_approval", "suspended", "cancelled", "completed"
1981
- ];
1982
- const status = precedence.find((status) => outputs.some((output) => output.status === "rejected" ? status === "failed" : output.output?.status === status)) ?? "completed";
1983
- return {
1984
- status,
1985
- parentRunId,
1986
- outputs
1987
- };
1988
- };
1989
- export const cancelAgentRun = async (store, runId, options = {}) => {
1990
- const loadedState = await store.load(runId, options.scope);
1991
- if (!loadedState) {
1992
- return undefined;
1993
- }
1994
- const cancelledAt = Date.now();
1995
- const status = options.mode === "final" ? "cancelled" : "cancel_requested";
1996
- const state = normalizeAgentRunState({
1997
- ...loadedState,
1998
- status,
1999
- cancelledAt,
2000
- cancellationReason: options.reason,
2001
- updatedAt: cancelledAt,
2002
- error: undefined
2003
- });
2004
- await saveStateWithRevision(store, state);
2005
- return cloneState(state);
2006
- };
2007
- export const cancelAgentRunTree = async (store, runId, options = {}) => {
2008
- if (!store.findByParentRunId) {
2009
- throw new ValidationError('The agent run "store" must implement "findByParentRunId()" to cancel an agent run tree.');
2010
- }
2011
- const cancelledAt = Date.now();
2012
- const status = options.mode === "final" ? "cancelled" : "cancel_requested";
2013
- const cancelState = (state) => normalizeAgentRunState({
2014
- ...state,
2015
- status,
2016
- cancelledAt,
2017
- cancellationReason: options.reason,
2018
- updatedAt: cancelledAt,
2019
- error: undefined
2020
- });
2021
- const parent = await store.load(runId, options.scope);
2022
- if (!parent) {
2023
- return {
2024
- parent: undefined,
2025
- children: []
2026
- };
2027
- }
2028
- const visited = new Set([runId]);
2029
- const children = [];
2030
- const collectChildren = async (parentRunId) => {
2031
- const directChildren = await store.findByParentRunId?.(parentRunId, options.scope);
2032
- for (const child of directChildren ?? []) {
2033
- if (visited.has(child.runId)) {
2034
- continue;
2035
- }
2036
- visited.add(child.runId);
2037
- children.push(child);
2038
- await collectChildren(child.runId);
2039
- }
2040
- };
2041
- await collectChildren(runId);
2042
- const cancelledParent = cancelState(parent);
2043
- const cancelledChildren = children.map(cancelState);
2044
- await saveStateWithRevision(store, cancelledParent);
2045
- for (const child of cancelledChildren) {
2046
- await saveStateWithRevision(store, child);
2047
- }
2048
- return {
2049
- parent: cloneState(cancelledParent),
2050
- children: cancelledChildren.map(cloneState)
2051
- };
2052
- };
2053
- export const runAgent = async (agent, input = {}) => {
2054
- const invocationStartedAt = Date.now();
2055
- const telemetryRunId = input.runId ?? input.state?.runId ?? randomId("run");
2056
- const invocationInput = input.runId || input.state
2057
- ? input
2058
- : { ...input, runId: telemetryRunId };
2059
- let invocationStatus = "completed";
2060
- let invocationError;
2061
- const returnInvocationOutput = (output) => {
2062
- invocationStatus = output.status;
2063
- if (output.status === "failed" || output.status === "timed_out") {
2064
- invocationError = new Error(output.error?.message ?? `Agent invocation ${output.status}.`);
2065
- if (output.status === "timed_out")
2066
- invocationError.name = "TimeoutError";
2067
- }
2068
- else {
2069
- invocationError = undefined;
2070
- }
2071
- return output;
2072
- };
2073
- await emitInvocationStartTelemetry(agent, telemetryRunId, invocationStartedAt, Math.max(1, input.maxSteps ?? input.state?.maxSteps ?? agent.maxSteps ?? 1));
2074
- try {
2075
- const context = await resolveContext(agent, invocationInput);
2076
- const currentStatus = normalizeApprovalStatus(context.state.status);
2077
- const policy = resolveRunPolicy(agent, input);
2078
- if (currentStatus === "completed" ||
2079
- currentStatus === "cancelled" ||
2080
- currentStatus === "cancel_requested" ||
2081
- currentStatus === "timed_out") {
2082
- context.state.status = currentStatus;
2083
- invocationStatus = currentStatus;
2084
- return returnInvocationOutput(toOutput(context.state));
2085
- }
2086
- if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0) {
2087
- context.state.status = currentStatus;
2088
- invocationStatus = currentStatus;
2089
- return returnInvocationOutput(toOutput(context.state));
2090
- }
2091
- const supportsLeases = Boolean(agent.store?.acquireLease && agent.store.renewLease && agent.store.releaseLease);
2092
- if (!context.fresh && currentStatus === "running" && !supportsLeases) {
2093
- invocationStatus = currentStatus;
2094
- return returnInvocationOutput(toOutput(context.state));
2095
- }
2096
- const freshRequiresExistingClaim = context.fresh && Boolean(context.state.idempotencyKey);
2097
- if (context.fresh && !freshRequiresExistingClaim) {
2098
- await claimAgentExecution(agent, context.state);
2099
- }
2100
- const executionLease = await acquireAgentExecutionLease(agent, context.state, policy);
2101
- if (!executionLease) {
2102
- if (input.state) {
2103
- throw new ConflictError(`Agent run "${context.state.runId}" is already owned by another worker.`);
2104
- }
2105
- const activeState = await agent.store?.load(context.state.runId, context.state.scope);
2106
- const outputState = activeState ? normalizeAgentRunState(activeState) : context.state;
2107
- invocationStatus = outputState.status;
2108
- return returnInvocationOutput(toOutput(outputState));
2109
- }
2110
- try {
2111
- if (!context.fresh || freshRequiresExistingClaim) {
2112
- await claimAgentExecution(agent, context.state);
2113
- }
2114
- await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals, invocationStartedAt);
2115
- }
2116
- catch (error) {
2117
- await executionLease.release();
2118
- throw error;
2119
- }
2120
- if (context.remainingSteps === 0) {
2121
- const state = createFailedState(context.state, "Agent exhausted maxSteps before reaching a terminal response.");
2122
- await persistState(agent, state, policy);
2123
- await emitRunFinishTelemetry(agent, state);
2124
- await executionLease.release();
2125
- return returnInvocationOutput(toOutput(state));
2126
- }
2127
- let inputGuardrail;
2128
- try {
2129
- inputGuardrail = await runGuardrails(agent, context.state, "input", agent.inputGuardrails, () => ({
2130
- runId: context.state.runId,
2131
- agentId: context.state.agentId,
2132
- context: context.context,
2133
- state: cloneState(context.state),
2134
- messages: context.messages,
2135
- metadata: context.state.metadata
2136
- }));
2137
- }
2138
- catch (error) {
2139
- await executionLease.release();
2140
- throw error;
2141
- }
2142
- if (inputGuardrail) {
2143
- const failedState = applyGuardrailFailure(context.state, "input", inputGuardrail);
2144
- await persistState(agent, failedState, policy);
2145
- await emitRunFinishTelemetry(agent, failedState);
2146
- await executionLease.release();
2147
- return returnInvocationOutput(toOutput(failedState));
2148
- }
2149
- const abortContext = createAgentAbortContext(input.abortSignal, policy, executionLease.signal);
2150
- let executionEnvironmentSession;
2151
- let executionEnvironmentStatus = "failed";
2152
- let executionEnvironmentError;
2153
- try {
2154
- executionEnvironmentSession = await acquireExecutionEnvironment(context.executionEnvironment, context.state, context.context, abortContext.signal);
2155
- }
2156
- catch (error) {
2157
- abortContext.cleanup();
2158
- await executionLease.release();
2159
- throw error;
2160
- }
2161
- try {
2162
- const result = await withAgentTelemetryRunContext(agent, context.state.runId, () => withAgentPolicyTimeout(generateText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, context.context, executionEnvironmentSession, abortContext.signal)), abortContext));
2163
- const cancelled = executionLease.cancelledState();
2164
- if (cancelled) {
2165
- executionEnvironmentStatus = cancelled.status;
2166
- await emitRunFinishTelemetry(agent, cancelled);
2167
- return returnInvocationOutput(toOutput(cancelled));
2168
- }
2169
- if (executionLease.leaseLost()) {
2170
- throw new ConflictError(`Agent run "${context.state.runId}" lost its worker lease.`);
2171
- }
2172
- const newSteps = mapSteps(result.steps, context.state.currentStep, result.toolResults);
2173
- let output = finalizeState(agent, context.state, result, newSteps, result.toolResults);
2174
- const outputGuardrail = await runGuardrails(agent, output.state, "output", agent.outputGuardrails, () => ({
2175
- runId: output.state.runId,
2176
- agentId: output.state.agentId,
2177
- context: context.context,
2178
- state: cloneState(output.state),
2179
- output,
2180
- metadata: output.state.metadata
2181
- }));
2182
- if (outputGuardrail) {
2183
- output = toOutput(applyGuardrailFailure(output.state, "output", outputGuardrail));
2184
- }
2185
- await emitFinalizedStepTelemetry(agent, output.state, newSteps);
2186
- await emitApprovalTelemetry(agent, output.state, [
2187
- ...(result.approvalRequests ?? []),
2188
- ...approvalsFromEvents(newSteps.flatMap((step) => step.response?.messages ?? []))
2189
- ]);
2190
- await persistState(agent, output.state, policy);
2191
- output.taskOutcome = output.state.taskOutcome;
2192
- await emitRunFinishTelemetry(agent, output.state);
2193
- executionEnvironmentStatus = output.status;
2194
- return returnInvocationOutput(output);
2195
- }
2196
- catch (error) {
2197
- executionEnvironmentError = {
2198
- message: error instanceof Error ? error.message : String(error)
2199
- };
2200
- const cancelled = executionLease.cancelledState();
2201
- if (cancelled) {
2202
- executionEnvironmentStatus = cancelled.status;
2203
- await emitRunFinishTelemetry(agent, cancelled);
2204
- return returnInvocationOutput(toOutput(cancelled));
2205
- }
2206
- if (executionLease.leaseLost()) {
2207
- throw new ConflictError(`Agent run "${context.state.runId}" lost its worker lease.`);
2208
- }
2209
- if (error instanceof AgentPolicyTimeoutError || abortContext.isTimedOut()) {
2210
- const status = policy?.onTimeout === "cancel-requested" ? "cancel_requested" : "timed_out";
2211
- const message = error instanceof Error ? error.message : `Agent run timed out after ${policy?.timeoutMs}ms.`;
2212
- const durableState = agent.store
2213
- ? normalizeAgentRunState((await agent.store.load(context.state.runId, context.state.scope)) ?? context.state)
2214
- : context.state;
2215
- const timedOutState = createTerminalState(durableState, status, message);
2216
- await persistState(agent, timedOutState, policy);
2217
- await emitRunFinishTelemetry(agent, timedOutState);
2218
- executionEnvironmentStatus = timedOutState.status;
2219
- return returnInvocationOutput(toOutput(timedOutState));
2220
- }
2221
- const durableState = agent.store
2222
- ? normalizeAgentRunState((await agent.store.load(context.state.runId, context.state.scope)) ?? context.state)
2223
- : context.state;
2224
- const failedState = createFailedState(durableState, error);
2225
- await persistState(agent, failedState, policy);
2226
- await emitRunFinishTelemetry(agent, failedState);
2227
- executionEnvironmentStatus = failedState.status;
2228
- throw error;
2229
- }
2230
- finally {
2231
- await executionEnvironmentSession?.release?.({
2232
- status: executionEnvironmentStatus,
2233
- error: executionEnvironmentError
2234
- });
2235
- await executionLease.release();
2236
- }
2237
- }
2238
- catch (error) {
2239
- invocationStatus = "failed";
2240
- invocationError = error instanceof Error ? error : new Error(String(error));
2241
- throw error;
2242
- }
2243
- finally {
2244
- await emitInvocationFinishTelemetry(agent, telemetryRunId, invocationStatus, invocationError);
2245
- }
2246
- };
2247
- export const streamAgent = (agent, input = {}) => {
2248
- const invocationStartedAt = Date.now();
2249
- const telemetryRunId = input.runId ?? input.state?.runId ?? randomId("run");
2250
- const invocationInput = input.runId || input.state
2251
- ? input
2252
- : { ...input, runId: telemetryRunId };
2253
- const policy = resolveRunPolicy(agent, input);
2254
- const broadcast = new BoundedReplayBroadcast({
2255
- maxHistory: policy?.maxStreamEvents ?? 4096
2256
- });
2257
- const publish = (event, terminal = false) => broadcast.publish(event, { terminal });
2258
- let activeLease;
2259
- let activeExecutionEnvironment;
2260
- let invocationFinishPromise;
2261
- const finishInvocation = (status, error) => {
2262
- invocationFinishPromise ??= emitInvocationFinishTelemetry(agent, telemetryRunId, status, error);
2263
- return invocationFinishPromise;
2264
- };
2265
- const runner = (async () => {
2266
- await emitInvocationStartTelemetry(agent, telemetryRunId, invocationStartedAt, Math.max(1, input.maxSteps ?? input.state?.maxSteps ?? agent.maxSteps ?? 1));
2267
- const context = await resolveContext(agent, invocationInput);
2268
- const currentStatus = normalizeApprovalStatus(context.state.status);
2269
- const supportsLeases = Boolean(agent.store?.acquireLease && agent.store.renewLease && agent.store.releaseLease);
2270
- if (!context.fresh && currentStatus === "running" && !supportsLeases) {
2271
- broadcast.close();
2272
- await finishInvocation(currentStatus);
2273
- return {
2274
- output: toOutput(context.state),
2275
- textStream: emptyAsyncIterable()
2276
- };
2277
- }
2278
- if (currentStatus === "completed" ||
2279
- currentStatus === "cancelled" ||
2280
- currentStatus === "cancel_requested" ||
2281
- currentStatus === "timed_out") {
2282
- context.state.status = currentStatus;
2283
- broadcast.close();
2284
- await finishInvocation(currentStatus);
2285
- return {
2286
- output: toOutput(context.state),
2287
- textStream: emptyAsyncIterable()
2288
- };
2289
- }
2290
- if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0) {
2291
- context.state.status = currentStatus;
2292
- broadcast.close();
2293
- await finishInvocation(currentStatus);
2294
- return {
2295
- output: toOutput(context.state),
2296
- textStream: emptyAsyncIterable()
2297
- };
2298
- }
2299
- const freshRequiresExistingClaim = context.fresh && Boolean(context.state.idempotencyKey);
2300
- if (context.fresh && !freshRequiresExistingClaim) {
2301
- await claimAgentExecution(agent, context.state);
2302
- }
2303
- const executionLease = await acquireAgentExecutionLease(agent, context.state, policy);
2304
- if (!executionLease) {
2305
- if (input.state) {
2306
- throw new ConflictError(`Agent run "${context.state.runId}" is already owned by another worker.`);
2307
- }
2308
- const activeState = await agent.store?.load(context.state.runId, context.state.scope);
2309
- broadcast.close();
2310
- const outputState = activeState ? normalizeAgentRunState(activeState) : context.state;
2311
- await finishInvocation(outputState.status);
2312
- return {
2313
- output: toOutput(outputState),
2314
- textStream: emptyAsyncIterable()
2315
- };
2316
- }
2317
- activeLease = executionLease;
2318
- try {
2319
- if (!context.fresh || freshRequiresExistingClaim) {
2320
- await claimAgentExecution(agent, context.state);
2321
- }
2322
- await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals, invocationStartedAt);
2323
- }
2324
- catch (error) {
2325
- await executionLease.release();
2326
- throw error;
2327
- }
2328
- if (context.remainingSteps === 0) {
2329
- const state = createFailedState(context.state, "Agent exhausted maxSteps before reaching a terminal response.");
2330
- await persistState(agent, state, policy);
2331
- await emitRunFinishTelemetry(agent, state);
2332
- await executionLease.release();
2333
- broadcast.close();
2334
- await finishInvocation(state.status);
2335
- return {
2336
- output: toOutput(state),
2337
- textStream: emptyAsyncIterable()
2338
- };
2339
- }
2340
- let inputGuardrail;
2341
- try {
2342
- inputGuardrail = await runGuardrails(agent, context.state, "input", agent.inputGuardrails, () => ({
2343
- runId: context.state.runId,
2344
- agentId: context.state.agentId,
2345
- context: context.context,
2346
- state: cloneState(context.state),
2347
- messages: context.messages,
2348
- metadata: context.state.metadata
2349
- }));
2350
- }
2351
- catch (error) {
2352
- await executionLease.release();
2353
- throw error;
2354
- }
2355
- if (inputGuardrail) {
2356
- const failedState = applyGuardrailFailure(context.state, "input", inputGuardrail);
2357
- await persistState(agent, failedState, policy);
2358
- await emitRunFinishTelemetry(agent, failedState);
2359
- await publish({
2360
- type: "error",
2361
- error: new GuardrailTriggeredError("input", failedState.error?.message ?? "Agent input guardrail triggered.", {
2362
- metadata: inputGuardrail.metadata
2363
- })
2364
- }, true);
2365
- await publish({
2366
- type: "agent-run-finish",
2367
- status: failedState.status,
2368
- state: failedState
2369
- }, true);
2370
- broadcast.close();
2371
- await executionLease.release();
2372
- await finishInvocation(failedState.status);
2373
- return {
2374
- output: toOutput(failedState),
2375
- textStream: emptyAsyncIterable()
2376
- };
2377
- }
2378
- await publish({
2379
- type: "agent-run-start",
2380
- currentStep: context.state.currentStep + 1,
2381
- maxSteps: context.state.maxSteps
2382
- });
2383
- for (const approval of input.approvals ?? []) {
2384
- await publish({
2385
- type: "agent-approval-resolved",
2386
- approval
2387
- });
2388
- }
2389
- await publish({
2390
- type: "agent-step-start",
2391
- stepIndex: context.state.currentStep + 1
2392
- });
2393
- const abortContext = createAgentAbortContext(input.abortSignal, policy, executionLease.signal);
2394
- let executionEnvironmentSession;
2395
- try {
2396
- executionEnvironmentSession = await acquireExecutionEnvironment(context.executionEnvironment, context.state, context.context, abortContext.signal);
2397
- }
2398
- catch (error) {
2399
- abortContext.cleanup();
2400
- await executionLease.release();
2401
- throw error;
2402
- }
2403
- activeExecutionEnvironment = executionEnvironmentSession;
2404
- let executionEnvironmentStatus = "failed";
2405
- let executionEnvironmentError;
2406
- let streamResult;
2407
- try {
2408
- streamResult = streamText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, context.context, executionEnvironmentSession, abortContext.signal, async (record) => {
2409
- await publish({
2410
- type: "agent-compaction",
2411
- compaction: record
2412
- });
2413
- }));
2414
- }
2415
- catch (error) {
2416
- abortContext.cleanup();
2417
- executionEnvironmentError = {
2418
- message: error instanceof Error ? error.message : String(error)
2419
- };
2420
- await executionEnvironmentSession?.release?.({
2421
- status: "failed",
2422
- error: executionEnvironmentError
2423
- });
2424
- activeExecutionEnvironment = undefined;
2425
- await executionLease.release();
2426
- throw error;
2427
- }
2428
- const approvalRequests = [];
2429
- const eventRelay = withAgentTelemetryRunContext(agent, context.state.runId, async () => {
2430
- for await (const event of streamResult.eventStream) {
2431
- await publish(event);
2432
- if (event.type === "tool-approval-request") {
2433
- approvalRequests.push(event.approval);
2434
- await publish({
2435
- type: "agent-approval-request",
2436
- approval: event.approval
2437
- });
2438
- await emitTelemetryEvent(agent, {
2439
- type: "approval-request",
2440
- runId: context.state.runId,
2441
- agentId: context.state.agentId,
2442
- approval: event.approval
2443
- });
2444
- }
2445
- if (event.type === "provider-data" &&
2446
- typeof event.data === "object" &&
2447
- event.data !== null &&
2448
- !Array.isArray(event.data) &&
2449
- event.data.type === "mcp_approval_request" &&
2450
- typeof event.data.id === "string" &&
2451
- typeof event.data.name === "string" &&
2452
- typeof event.data.arguments === "string") {
2453
- const approval = {
2454
- provider: event.provider,
2455
- id: event.data.id,
2456
- name: event.data.name,
2457
- arguments: event.data.arguments,
2458
- serverLabel: typeof event.data.server_label === "string" ? event.data.server_label : undefined,
2459
- rawData: event.data
2460
- };
2461
- approvalRequests.push(approval);
2462
- await publish({
2463
- type: "agent-approval-request",
2464
- approval
2465
- });
2466
- await emitTelemetryEvent(agent, {
2467
- type: "approval-request",
2468
- runId: context.state.runId,
2469
- agentId: context.state.agentId,
2470
- approval
2471
- });
2472
- }
2473
- }
2474
- });
2475
- const output = (async () => {
2476
- try {
2477
- const final = await withAgentPolicyTimeout(eventRelay.then(() => streamResult.collect()), abortContext);
2478
- const cancelled = executionLease.cancelledState();
2479
- if (cancelled) {
2480
- executionEnvironmentStatus = cancelled.status;
2481
- await emitRunFinishTelemetry(agent, cancelled);
2482
- await publish({
2483
- type: "agent-run-finish",
2484
- status: cancelled.status,
2485
- state: cancelled
2486
- }, true);
2487
- broadcast.close();
2488
- return toOutput(cancelled);
2489
- }
2490
- if (executionLease.leaseLost()) {
2491
- const conflict = new ConflictError(`Agent run "${context.state.runId}" lost its worker lease.`);
2492
- broadcast.fail(conflict);
2493
- throw conflict;
2494
- }
2495
- const newSteps = mapSteps(final.steps, context.state.currentStep, final.toolResults);
2496
- let result = finalizeState(agent, context.state, final, newSteps, final.toolResults);
2497
- const outputGuardrail = await runGuardrails(agent, result.state, "output", agent.outputGuardrails, () => ({
2498
- runId: result.state.runId,
2499
- agentId: result.state.agentId,
2500
- context: context.context,
2501
- state: cloneState(result.state),
2502
- output: result,
2503
- metadata: result.state.metadata
2504
- }));
2505
- if (outputGuardrail) {
2506
- result = toOutput(applyGuardrailFailure(result.state, "output", outputGuardrail));
2507
- await publish({
2508
- type: "error",
2509
- error: new GuardrailTriggeredError("output", result.state.error?.message ?? "Agent output guardrail triggered.", { metadata: outputGuardrail.metadata })
2510
- }, true);
2511
- }
2512
- for (const step of newSteps) {
2513
- await publish({
2514
- type: "agent-step-finish",
2515
- step
2516
- });
2517
- }
2518
- await emitFinalizedStepTelemetry(agent, result.state, newSteps);
2519
- if (!approvalRequests.length) {
2520
- await emitApprovalTelemetry(agent, result.state, approvalsFromEvents(newSteps.flatMap((step) => step.response?.messages ?? [])));
2521
- }
2522
- await persistState(agent, result.state, policy);
2523
- result.taskOutcome = result.state.taskOutcome;
2524
- await emitRunFinishTelemetry(agent, result.state);
2525
- await publish({
2526
- type: "agent-run-finish",
2527
- status: result.status,
2528
- state: result.state
2529
- }, true);
2530
- broadcast.close();
2531
- executionEnvironmentStatus = result.status;
2532
- return result;
2533
- }
2534
- catch (error) {
2535
- executionEnvironmentError = {
2536
- message: error instanceof Error ? error.message : String(error)
2537
- };
2538
- const cancelled = executionLease.cancelledState();
2539
- if (cancelled) {
2540
- executionEnvironmentStatus = cancelled.status;
2541
- await emitRunFinishTelemetry(agent, cancelled);
2542
- await publish({
2543
- type: "agent-run-finish",
2544
- status: cancelled.status,
2545
- state: cancelled
2546
- }, true);
2547
- broadcast.close();
2548
- return toOutput(cancelled);
2549
- }
2550
- if (executionLease.leaseLost()) {
2551
- const conflict = new ConflictError(`Agent run "${context.state.runId}" lost its worker lease.`);
2552
- broadcast.fail(conflict);
2553
- throw conflict;
2554
- }
2555
- if (error instanceof AgentPolicyTimeoutError || abortContext.isTimedOut()) {
2556
- const status = policy?.onTimeout === "cancel-requested" ? "cancel_requested" : "timed_out";
2557
- const message = error instanceof Error ? error.message : `Agent run timed out after ${policy?.timeoutMs}ms.`;
2558
- const durableState = agent.store
2559
- ? normalizeAgentRunState((await agent.store.load(context.state.runId, context.state.scope)) ?? context.state)
2560
- : context.state;
2561
- const timedOutState = createTerminalState(durableState, status, message);
2562
- await persistState(agent, timedOutState, policy);
2563
- await emitRunFinishTelemetry(agent, timedOutState);
2564
- await publish({
2565
- type: "error",
2566
- error: new AgentPolicyTimeoutError(policy?.timeoutMs ?? 0)
2567
- }, true);
2568
- await publish({
2569
- type: "agent-run-finish",
2570
- status: timedOutState.status,
2571
- state: timedOutState
2572
- }, true);
2573
- broadcast.close();
2574
- executionEnvironmentStatus = timedOutState.status;
2575
- return toOutput(timedOutState);
2576
- }
2577
- const durableState = agent.store
2578
- ? normalizeAgentRunState((await agent.store.load(context.state.runId, context.state.scope)) ?? context.state)
2579
- : context.state;
2580
- const failedState = createFailedState(durableState, error);
2581
- await persistState(agent, failedState, policy);
2582
- await emitRunFinishTelemetry(agent, failedState);
2583
- await publish({
2584
- type: "error",
2585
- error: error instanceof Error ? error : new Error(String(error))
2586
- }, true);
2587
- await publish({
2588
- type: "agent-run-finish",
2589
- status: failedState.status,
2590
- state: failedState
2591
- }, true);
2592
- broadcast.close();
2593
- executionEnvironmentStatus = failedState.status;
2594
- throw error;
2595
- }
2596
- finally {
2597
- await executionEnvironmentSession?.release?.({
2598
- status: executionEnvironmentStatus,
2599
- error: executionEnvironmentError
2600
- });
2601
- activeExecutionEnvironment = undefined;
2602
- await executionLease.release();
2603
- }
2604
- })().finally(() => finishInvocation(executionEnvironmentStatus, executionEnvironmentError ? new Error(executionEnvironmentError.message) : undefined));
2605
- return {
2606
- output,
2607
- textStream: streamResult.textStream
2608
- };
2609
- })().catch(async (error) => {
2610
- await finishInvocation("failed", error instanceof Error ? error : new Error(String(error)));
2611
- await activeExecutionEnvironment?.release?.({
2612
- status: "failed",
2613
- error: { message: error instanceof Error ? error.message : String(error) }
2614
- });
2615
- activeExecutionEnvironment = undefined;
2616
- await activeLease?.release();
2617
- broadcast.fail(error);
2618
- throw error;
2619
- });
2620
- return {
2621
- eventStream: broadcast.stream(),
2622
- textStream: (async function* () {
2623
- const started = await runner;
2624
- for await (const chunk of started.textStream) {
2625
- yield chunk;
2626
- }
2627
- })(),
2628
- collect: async () => (await runner).output
2629
- };
2630
- };
2631
- export const resumeAgent = async (agent, input) => runAgent(agent, input);
1
+ /** Compatibility facade. Implementations are organized by responsibility internally. */
2
+ export { runAgentGroup } from "./agent/groups.js";
3
+ export { createSubAgentTool, runAgent, streamAgent, resumeAgent } from "./agent/execution.js";
4
+ export { createAgent, prepareSubagentsForAgent } from "./agent/definition.js";
5
+ export { Agent } from "./agent/instance.js";
6
+ export { cancelAgentRun, cancelAgentRunTree } from "./agent/cancellation.js";
2632
7
  //# sourceMappingURL=agent.js.map