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