@zhivex-ai/core 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +5 -0
  2. package/dist/advanced-tool-registry.d.ts +112 -0
  3. package/dist/advanced-tool-registry.d.ts.map +1 -0
  4. package/dist/advanced-tool-registry.js +407 -0
  5. package/dist/advanced-tool-registry.js.map +1 -0
  6. package/dist/agent-evaluation.d.ts +176 -0
  7. package/dist/agent-evaluation.d.ts.map +1 -0
  8. package/dist/agent-evaluation.js +334 -0
  9. package/dist/agent-evaluation.js.map +1 -0
  10. package/dist/agent-store.d.ts.map +1 -1
  11. package/dist/agent-store.js +226 -8
  12. package/dist/agent-store.js.map +1 -1
  13. package/dist/agent-trace.d.ts +127 -0
  14. package/dist/agent-trace.d.ts.map +1 -0
  15. package/dist/agent-trace.js +331 -0
  16. package/dist/agent-trace.js.map +1 -0
  17. package/dist/agent.d.ts +12 -1
  18. package/dist/agent.d.ts.map +1 -1
  19. package/dist/agent.js +469 -36
  20. package/dist/agent.js.map +1 -1
  21. package/dist/api-stability.d.ts +9 -0
  22. package/dist/api-stability.d.ts.map +1 -0
  23. package/dist/api-stability.js +261 -0
  24. package/dist/api-stability.js.map +1 -0
  25. package/dist/artifact.d.ts +165 -0
  26. package/dist/artifact.d.ts.map +1 -0
  27. package/dist/artifact.js +994 -0
  28. package/dist/artifact.js.map +1 -0
  29. package/dist/errors.d.ts +2 -0
  30. package/dist/errors.d.ts.map +1 -1
  31. package/dist/errors.js +2 -0
  32. package/dist/errors.js.map +1 -1
  33. package/dist/generate-text.d.ts.map +1 -1
  34. package/dist/generate-text.js +6 -2
  35. package/dist/generate-text.js.map +1 -1
  36. package/dist/index.d.ts +27 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +14 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/live-agent.d.ts.map +1 -1
  41. package/dist/live-agent.js +7 -1
  42. package/dist/live-agent.js.map +1 -1
  43. package/dist/provider-parity.d.ts +63 -0
  44. package/dist/provider-parity.d.ts.map +1 -0
  45. package/dist/provider-parity.js +175 -0
  46. package/dist/provider-parity.js.map +1 -0
  47. package/dist/runner.d.ts +111 -0
  48. package/dist/runner.d.ts.map +1 -0
  49. package/dist/runner.js +635 -0
  50. package/dist/runner.js.map +1 -0
  51. package/dist/safety-policy.d.ts +65 -0
  52. package/dist/safety-policy.d.ts.map +1 -0
  53. package/dist/safety-policy.js +308 -0
  54. package/dist/safety-policy.js.map +1 -0
  55. package/dist/types.d.ts +119 -3
  56. package/dist/types.d.ts.map +1 -1
  57. package/dist/workflow-artifacts.d.ts +28 -0
  58. package/dist/workflow-artifacts.d.ts.map +1 -0
  59. package/dist/workflow-artifacts.js +86 -0
  60. package/dist/workflow-artifacts.js.map +1 -0
  61. package/dist/workflow-evaluation-diff.d.ts +51 -0
  62. package/dist/workflow-evaluation-diff.d.ts.map +1 -0
  63. package/dist/workflow-evaluation-diff.js +141 -0
  64. package/dist/workflow-evaluation-diff.js.map +1 -0
  65. package/dist/workflow-evaluation.d.ts +94 -0
  66. package/dist/workflow-evaluation.d.ts.map +1 -0
  67. package/dist/workflow-evaluation.js +210 -0
  68. package/dist/workflow-evaluation.js.map +1 -0
  69. package/dist/workflow-state-service.d.ts +67 -0
  70. package/dist/workflow-state-service.d.ts.map +1 -0
  71. package/dist/workflow-state-service.js +498 -0
  72. package/dist/workflow-state-service.js.map +1 -0
  73. package/dist/workflow.d.ts +206 -0
  74. package/dist/workflow.d.ts.map +1 -0
  75. package/dist/workflow.js +727 -0
  76. package/dist/workflow.js.map +1 -0
  77. package/package.json +1 -1
package/dist/agent.js CHANGED
@@ -2,8 +2,19 @@ import { createAgentApprovalMessage, getAgentApprovalRequests } from "./agent-ap
2
2
  import { createAgentHandoffMessage } from "./agent-handoff.js";
3
3
  import { GuardrailTriggeredError, ValidationError } from "./errors.js";
4
4
  import { generateText, normalizeMessages, streamText } from "./generate-text.js";
5
+ import { serializeJsonValue } from "./messages.js";
6
+ import { mergeAbortSignals } from "./runtime.js";
5
7
  import { toToolSet } from "./tool-registry.js";
8
+ import { z } from "zod";
6
9
  const randomId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 10)}`;
10
+ const AGENT_RUN_STATE_SCHEMA_VERSION = 1;
11
+ const AGENT_GROUP_FAIL_FAST_ABORT_MESSAGE = "Agent group member aborted after fail-fast.";
12
+ class AgentPolicyTimeoutError extends Error {
13
+ constructor(timeoutMs) {
14
+ super(`Agent run timed out after ${timeoutMs}ms.`);
15
+ this.name = "AgentPolicyTimeoutError";
16
+ }
17
+ }
7
18
  const joinInstructions = (...parts) => {
8
19
  const content = parts.map((part) => part?.trim()).filter((part) => Boolean(part));
9
20
  return content.length ? content.join("\n\n") : undefined;
@@ -64,13 +75,20 @@ const toOutput = (state) => ({
64
75
  state,
65
76
  error: state.error
66
77
  });
67
- const cloneState = (state) => JSON.parse(JSON.stringify(state));
68
- const createBaseState = (provider, modelId, initialMessages, maxSteps, metadata, agentId, runId, handoff) => {
78
+ const normalizeRunState = (state) => ({
79
+ ...state,
80
+ schemaVersion: AGENT_RUN_STATE_SCHEMA_VERSION
81
+ });
82
+ const normalizeApprovalStatus = (status) => status === "suspended" ? "waiting_approval" : status;
83
+ const cloneState = (state) => JSON.parse(JSON.stringify(normalizeRunState(state)));
84
+ const createBaseState = (provider, modelId, initialMessages, maxSteps, metadata, agentId, runId, handoff, parentRunId, idempotencyKey) => {
69
85
  const startedAt = Date.now();
70
86
  return {
87
+ schemaVersion: AGENT_RUN_STATE_SCHEMA_VERSION,
71
88
  runId,
89
+ idempotencyKey,
72
90
  agentId,
73
- parentRunId: handoff?.fromRunId,
91
+ parentRunId: parentRunId ?? handoff?.fromRunId,
74
92
  provider,
75
93
  modelId,
76
94
  status: "running",
@@ -98,6 +116,17 @@ const ensureValidStateInput = (input) => {
98
116
  throw new ValidationError('Pass either "state" or a fresh "prompt"/"messages" input, but not both.');
99
117
  }
100
118
  };
119
+ const ensureValidIdempotencyInput = (input, store) => {
120
+ if (!input.idempotencyKey) {
121
+ return;
122
+ }
123
+ if (!store) {
124
+ throw new ValidationError('The "idempotencyKey" option requires an agent run "store".');
125
+ }
126
+ if (!store.findByIdempotencyKey) {
127
+ throw new ValidationError('The agent run "store" must implement "findByIdempotencyKey()" to use "idempotencyKey".');
128
+ }
129
+ };
101
130
  const injectContextMessages = (messages, extraMessages) => {
102
131
  if (!extraMessages.length) {
103
132
  return messages;
@@ -159,10 +188,10 @@ const finalizeState = (state, result, newSteps, newToolResults) => {
159
188
  const unresolvedToolCalls = lastStep?.response ? hasToolCalls(lastStep.response.messages) : false;
160
189
  const pendingApprovals = getAgentApprovalRequests(newSteps.flatMap((step) => step.response?.messages ?? []));
161
190
  if (pendingApprovals.length) {
162
- state.status = "suspended";
191
+ state.status = "waiting_approval";
163
192
  state.error = undefined;
164
193
  if (lastStep) {
165
- lastStep.status = "suspended";
194
+ lastStep.status = "waiting_approval";
166
195
  }
167
196
  }
168
197
  else if (exhausted && unresolvedToolCalls) {
@@ -194,6 +223,94 @@ const finalizeState = (state, result, newSteps, newToolResults) => {
194
223
  const emitTelemetryEvent = async (agent, event) => {
195
224
  await agent.onTelemetryEvent?.(event);
196
225
  };
226
+ const subAgentToolInputSchema = z.object({
227
+ prompt: z.string().min(1),
228
+ system: z.string().optional()
229
+ });
230
+ const defaultSubAgentToolName = (agent) => {
231
+ const id = agent.id ?? `${agent.model.provider}_${agent.model.modelId}`;
232
+ return `subagent_${id.replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "agent"}`;
233
+ };
234
+ const countToolCallsInSteps = (steps) => steps.reduce((total, step) => total + countToolCalls(step.response?.messages ?? []), 0);
235
+ const countToolErrors = (toolResults) => toolResults.filter((result) => result.isError).length;
236
+ export const createSubAgentTool = (options) => {
237
+ const toolName = options.toolName ?? options.name ?? defaultSubAgentToolName(options.agent);
238
+ const metadata = {
239
+ type: "subagent"
240
+ };
241
+ if (options.agent.id) {
242
+ metadata.childAgentId = options.agent.id;
243
+ }
244
+ if (options.parentRunId) {
245
+ metadata.parentRunId = options.parentRunId;
246
+ }
247
+ if (options.parentAgentId) {
248
+ metadata.parentAgentId = options.parentAgentId;
249
+ }
250
+ return {
251
+ name: toolName,
252
+ description: options.description ??
253
+ `Delegate the task to ${options.agent.id ? `subagent "${options.agent.id}"` : "a subagent"} and return its result.`,
254
+ schema: subAgentToolInputSchema,
255
+ requiresApproval: options.requiresApproval,
256
+ metadata: cloneMetadata(metadata, options.metadata),
257
+ execute: async (input) => {
258
+ await options.onStart?.({
259
+ toolName,
260
+ childAgentId: options.agent.id,
261
+ parentRunId: options.parentRunId
262
+ });
263
+ const childMetadata = {
264
+ subagentToolName: toolName
265
+ };
266
+ if (options.parentRunId) {
267
+ childMetadata.parentRunId = options.parentRunId;
268
+ }
269
+ if (options.parentAgentId) {
270
+ childMetadata.parentAgentId = options.parentAgentId;
271
+ }
272
+ const output = await runAgent(options.agent, {
273
+ prompt: input.prompt,
274
+ system: joinInstructions(options.system, input.system),
275
+ parentRunId: options.parentRunId,
276
+ maxSteps: options.maxSteps,
277
+ metadata: cloneMetadata(options.metadata, childMetadata)
278
+ });
279
+ const childRun = {
280
+ runId: output.state.runId,
281
+ status: output.status,
282
+ outputText: output.outputText,
283
+ steps: output.state.currentStep,
284
+ toolCalls: countToolCallsInSteps(output.steps),
285
+ toolErrors: countToolErrors(output.toolResults)
286
+ };
287
+ if (output.state.agentId) {
288
+ childRun.agentId = output.state.agentId;
289
+ }
290
+ if (options.parentRunId) {
291
+ childRun.parentRunId = options.parentRunId;
292
+ }
293
+ childRun.toolName = toolName;
294
+ if (output.usage) {
295
+ childRun.usage = output.usage;
296
+ }
297
+ if (output.state.startedAt !== undefined) {
298
+ childRun.startedAt = output.state.startedAt;
299
+ }
300
+ if (output.state.updatedAt !== undefined) {
301
+ childRun.updatedAt = output.state.updatedAt;
302
+ }
303
+ if (output.error) {
304
+ childRun.error = output.error;
305
+ }
306
+ if (output.state.metadata) {
307
+ childRun.metadata = output.state.metadata;
308
+ }
309
+ await options.onFinish?.(childRun);
310
+ return serializeJsonValue(childRun);
311
+ }
312
+ };
313
+ };
197
314
  const persistState = async (agent, state) => {
198
315
  state.updatedAt = Date.now();
199
316
  await agent.store?.save(cloneState(state));
@@ -270,11 +387,27 @@ const runGuardrails = async (agent, state, stage, guardrails, requestFactory) =>
270
387
  return undefined;
271
388
  };
272
389
  const resolveContext = async (agent, input) => {
273
- let loadedState = input.state;
390
+ ensureValidIdempotencyInput(input, agent.store);
391
+ let loadedState = input.state ? normalizeRunState(input.state) : undefined;
392
+ let loadedByIdempotencyKey = false;
393
+ if (!loadedState && input.idempotencyKey) {
394
+ loadedState = await agent.store?.findByIdempotencyKey?.(input.idempotencyKey);
395
+ if (loadedState) {
396
+ loadedState = normalizeRunState(loadedState);
397
+ loadedByIdempotencyKey = true;
398
+ }
399
+ }
274
400
  if (!loadedState && input.runId && agent.store) {
275
401
  loadedState = await agent.store.load(input.runId);
402
+ if (loadedState) {
403
+ loadedState = normalizeRunState(loadedState);
404
+ }
276
405
  }
277
- const normalizedInput = loadedState ? { ...input, state: loadedState } : input;
406
+ const normalizedInput = loadedState && loadedByIdempotencyKey
407
+ ? { ...input, prompt: undefined, messages: undefined, system: undefined, handoff: undefined, state: loadedState }
408
+ : loadedState
409
+ ? { ...input, state: loadedState }
410
+ : input;
278
411
  ensureValidStateInput(normalizedInput);
279
412
  const metadata = cloneMetadata(agent.metadata, loadedState?.metadata, input.metadata, input.handoff?.metadata);
280
413
  if (loadedState) {
@@ -283,7 +416,10 @@ const resolveContext = async (agent, input) => {
283
416
  return {
284
417
  state: {
285
418
  ...loadedState,
419
+ schemaVersion: AGENT_RUN_STATE_SCHEMA_VERSION,
420
+ idempotencyKey: loadedState.idempotencyKey ?? input.idempotencyKey,
286
421
  agentId: loadedState.agentId ?? agent.id,
422
+ parentRunId: loadedState.parentRunId ?? input.parentRunId,
287
423
  provider: agent.model.provider,
288
424
  modelId: agent.model.modelId,
289
425
  maxSteps,
@@ -301,32 +437,65 @@ const resolveContext = async (agent, input) => {
301
437
  const maxSteps = Math.max(1, input.maxSteps ?? agent.maxSteps ?? 1);
302
438
  const prepared = await prepareFreshMessages(agent, input, runId);
303
439
  return {
304
- state: createBaseState(agent.model.provider, agent.model.modelId, prepared.messages, maxSteps, metadata, agent.id, runId, input.handoff),
440
+ state: createBaseState(agent.model.provider, agent.model.modelId, prepared.messages, maxSteps, metadata, agent.id, runId, input.handoff, input.parentRunId, input.idempotencyKey),
305
441
  messages: prepared.messages,
306
442
  remainingSteps: maxSteps,
307
443
  memoryMessages: prepared.memoryMessages
308
444
  };
309
445
  };
310
- const createGenerateOptions = (agent, state, input, messages, maxSteps) => ({
311
- model: agent.model,
312
- messages,
313
- tools: toToolSet(input.tools ?? agent.tools),
314
- toolChoice: input.toolChoice,
315
- toolExecution: input.toolExecution ?? agent.toolExecution,
316
- toolApprovalPolicy: input.toolApprovalPolicy ?? agent.toolApprovalPolicy,
317
- onToolApprovalDecision: async (event) => {
318
- await emitToolApprovalTelemetry(agent, state, event);
319
- },
320
- maxSteps,
321
- temperature: input.temperature ?? agent.temperature,
322
- maxTokens: input.maxTokens ?? agent.maxTokens,
323
- reasoning: input.reasoning ?? agent.reasoning,
324
- providerOptions: input.providerOptions ?? agent.providerOptions,
325
- abortSignal: input.abortSignal,
326
- timeoutMs: input.timeoutMs,
327
- maxRetries: input.maxRetries,
328
- retryBackoffMs: input.retryBackoffMs
329
- });
446
+ const createGenerateOptions = (agent, state, input, messages, maxSteps, abortSignal = input.abortSignal) => {
447
+ const tools = { ...(toToolSet(input.tools ?? agent.tools) ?? {}) };
448
+ for (const subagent of agent.subagents ?? []) {
449
+ const subagentTool = createSubAgentTool({
450
+ ...subagent,
451
+ parentRunId: state.runId,
452
+ parentAgentId: state.agentId,
453
+ onStart: async ({ toolName, childAgentId }) => {
454
+ await emitTelemetryEvent(agent, {
455
+ type: "subagent-start",
456
+ runId: state.runId,
457
+ agentId: state.agentId,
458
+ childAgentId,
459
+ toolName
460
+ });
461
+ },
462
+ onFinish: async (childRun) => {
463
+ state.childRuns = [...(state.childRuns ?? []), childRun];
464
+ await emitTelemetryEvent(agent, {
465
+ type: "subagent-finish",
466
+ runId: state.runId,
467
+ agentId: state.agentId,
468
+ childRun
469
+ });
470
+ }
471
+ });
472
+ if (tools[subagentTool.name]) {
473
+ throw new ValidationError(`Subagent tool "${subagentTool.name}" conflicts with an existing tool.`);
474
+ }
475
+ tools[subagentTool.name] = subagentTool;
476
+ }
477
+ const finalTools = Object.keys(tools).length ? tools : undefined;
478
+ return {
479
+ model: agent.model,
480
+ messages,
481
+ tools: finalTools,
482
+ toolChoice: input.toolChoice,
483
+ toolExecution: input.toolExecution ?? agent.toolExecution,
484
+ toolApprovalPolicy: input.toolApprovalPolicy ?? agent.toolApprovalPolicy,
485
+ onToolApprovalDecision: async (event) => {
486
+ await emitToolApprovalTelemetry(agent, state, event);
487
+ },
488
+ maxSteps,
489
+ temperature: input.temperature ?? agent.temperature,
490
+ maxTokens: input.maxTokens ?? agent.maxTokens,
491
+ reasoning: input.reasoning ?? agent.reasoning,
492
+ providerOptions: input.providerOptions ?? agent.providerOptions,
493
+ abortSignal,
494
+ timeoutMs: input.timeoutMs,
495
+ maxRetries: input.maxRetries,
496
+ retryBackoffMs: input.retryBackoffMs
497
+ };
498
+ };
330
499
  const emptyAsyncIterable = async function* () {
331
500
  return;
332
501
  };
@@ -338,6 +507,64 @@ const createFailedState = (state, message) => ({
338
507
  },
339
508
  updatedAt: Date.now()
340
509
  });
510
+ const createTerminalState = (state, status, message) => ({
511
+ ...state,
512
+ status,
513
+ error: {
514
+ message
515
+ },
516
+ cancellationReason: status === "cancel_requested" ? message : state.cancellationReason,
517
+ cancelledAt: status === "cancel_requested" ? Date.now() : state.cancelledAt,
518
+ updatedAt: Date.now()
519
+ });
520
+ const resolveRunPolicy = (agent, input) => {
521
+ const policy = {
522
+ ...(agent.policy ?? {}),
523
+ ...(input.policy ?? {})
524
+ };
525
+ return Object.keys(policy).length ? policy : undefined;
526
+ };
527
+ const withAgentPolicyTimeout = async (operation, timeout) => {
528
+ if (!timeout.timeoutPromise) {
529
+ return operation;
530
+ }
531
+ try {
532
+ return await Promise.race([operation, timeout.timeoutPromise]);
533
+ }
534
+ finally {
535
+ timeout.cleanup();
536
+ }
537
+ };
538
+ const createAgentAbortContext = (inputAbortSignal, policy) => {
539
+ if (!policy?.timeoutMs) {
540
+ return {
541
+ signal: inputAbortSignal,
542
+ timeoutPromise: undefined,
543
+ cleanup: () => undefined,
544
+ isTimedOut: () => false
545
+ };
546
+ }
547
+ const controller = new AbortController();
548
+ let timedOut = false;
549
+ let timeout;
550
+ const timeoutPromise = new Promise((_, reject) => {
551
+ timeout = setTimeout(() => {
552
+ timedOut = true;
553
+ controller.abort();
554
+ reject(new AgentPolicyTimeoutError(policy.timeoutMs));
555
+ }, policy.timeoutMs);
556
+ });
557
+ return {
558
+ signal: mergeAbortSignals(inputAbortSignal, controller.signal),
559
+ timeoutPromise,
560
+ cleanup: () => {
561
+ if (timeout) {
562
+ clearTimeout(timeout);
563
+ }
564
+ },
565
+ isTimedOut: () => timedOut
566
+ };
567
+ };
341
568
  const emitRunStartTelemetry = async (agent, state, memoryMessages, approvals) => {
342
569
  await emitTelemetryEvent(agent, {
343
570
  type: "run-start",
@@ -385,13 +612,178 @@ export const createAgent = (definition) => ({
385
612
  ...definition,
386
613
  metadata: cloneMetadata(definition.metadata)
387
614
  });
615
+ export const prepareSubagentsForAgent = (agent, options = {}) => {
616
+ const store = options.store ?? agent.store;
617
+ const memory = options.memory ?? agent.memory;
618
+ const onTelemetryEvent = options.onTelemetryEvent ?? agent.onTelemetryEvent;
619
+ const toolApprovalPolicy = options.toolApprovalPolicy ?? agent.toolApprovalPolicy;
620
+ const toolExecution = options.toolExecution ?? agent.toolExecution;
621
+ const defaultMetadata = cloneMetadata(agent.metadata, options.metadata);
622
+ return {
623
+ ...agent,
624
+ metadata: cloneMetadata(agent.metadata),
625
+ subagents: (agent.subagents ?? []).map((subagent) => ({
626
+ ...subagent,
627
+ metadata: cloneMetadata(defaultMetadata, subagent.metadata),
628
+ agent: {
629
+ ...subagent.agent,
630
+ store: subagent.agent.store ?? store,
631
+ memory: subagent.agent.memory ?? memory,
632
+ onTelemetryEvent: subagent.agent.onTelemetryEvent ?? onTelemetryEvent,
633
+ toolApprovalPolicy: subagent.agent.toolApprovalPolicy ?? toolApprovalPolicy,
634
+ toolExecution: subagent.agent.toolExecution ?? toolExecution,
635
+ metadata: cloneMetadata(defaultMetadata, subagent.agent.metadata)
636
+ }
637
+ }))
638
+ };
639
+ };
640
+ export const runAgentGroup = async (agents, input = {}) => {
641
+ const { stopOnError, runId: _runId, state: _state, approvals: _approvals, handoff: _handoff, ...sharedInput } = input;
642
+ const parentRunId = input.parentRunId;
643
+ const controllers = agents.map(() => new AbortController());
644
+ let failFastTriggered = false;
645
+ const isFailingOutput = (output) => output.status === "failed" || output.status === "timed_out";
646
+ const abortPending = (currentIndex) => {
647
+ if (!stopOnError || failFastTriggered) {
648
+ return;
649
+ }
650
+ failFastTriggered = true;
651
+ controllers.forEach((controller, index) => {
652
+ if (index !== currentIndex) {
653
+ controller.abort();
654
+ }
655
+ });
656
+ };
657
+ const runs = agents.map(async (member, index) => {
658
+ const runInput = {
659
+ ...sharedInput,
660
+ ...(member.input ?? {}),
661
+ parentRunId: member.input?.parentRunId ?? parentRunId,
662
+ abortSignal: mergeAbortSignals(input.abortSignal, member.input?.abortSignal, controllers[index].signal),
663
+ metadata: cloneMetadata(input.metadata, member.input?.metadata, {
664
+ ...(member.name ? { agentGroupMember: member.name } : {})
665
+ })
666
+ };
667
+ try {
668
+ const output = await runAgent(member.agent, runInput);
669
+ if (isFailingOutput(output)) {
670
+ abortPending(index);
671
+ }
672
+ return output;
673
+ }
674
+ catch (error) {
675
+ abortPending(index);
676
+ throw error;
677
+ }
678
+ });
679
+ const settled = await Promise.allSettled(runs);
680
+ const outputs = settled.map((result, index) => {
681
+ const member = agents[index];
682
+ if (result.status === "fulfilled") {
683
+ return {
684
+ name: member.name,
685
+ agentId: result.value.state.agentId ?? member.agent.id,
686
+ status: "fulfilled",
687
+ output: result.value
688
+ };
689
+ }
690
+ return {
691
+ name: member.name,
692
+ agentId: member.agent.id,
693
+ status: "rejected",
694
+ error: {
695
+ message: stopOnError && failFastTriggered && controllers[index].signal.aborted
696
+ ? AGENT_GROUP_FAIL_FAST_ABORT_MESSAGE
697
+ : result.reason instanceof Error
698
+ ? result.reason.message
699
+ : String(result.reason)
700
+ }
701
+ };
702
+ });
703
+ const failed = outputs.some((output) => output.status === "rejected" || output.output?.status === "failed" || output.output?.status === "timed_out");
704
+ return {
705
+ status: stopOnError && failed ? "failed" : failed ? "failed" : "completed",
706
+ parentRunId,
707
+ outputs
708
+ };
709
+ };
710
+ export const cancelAgentRun = async (store, runId, options = {}) => {
711
+ const loadedState = await store.load(runId);
712
+ if (!loadedState) {
713
+ return undefined;
714
+ }
715
+ const cancelledAt = Date.now();
716
+ const status = options.mode === "final" ? "cancelled" : "cancel_requested";
717
+ const state = normalizeRunState({
718
+ ...loadedState,
719
+ status,
720
+ cancelledAt,
721
+ cancellationReason: options.reason,
722
+ updatedAt: cancelledAt,
723
+ error: undefined
724
+ });
725
+ await store.save(cloneState(state));
726
+ return cloneState(state);
727
+ };
728
+ export const cancelAgentRunTree = async (store, runId, options = {}) => {
729
+ if (!store.findByParentRunId) {
730
+ throw new ValidationError('The agent run "store" must implement "findByParentRunId()" to cancel an agent run tree.');
731
+ }
732
+ const cancelledAt = Date.now();
733
+ const status = options.mode === "final" ? "cancelled" : "cancel_requested";
734
+ const cancelState = (state) => normalizeRunState({
735
+ ...state,
736
+ status,
737
+ cancelledAt,
738
+ cancellationReason: options.reason,
739
+ updatedAt: cancelledAt,
740
+ error: undefined
741
+ });
742
+ const parent = await store.load(runId);
743
+ if (!parent) {
744
+ return {
745
+ parent: undefined,
746
+ children: []
747
+ };
748
+ }
749
+ const visited = new Set([runId]);
750
+ const children = [];
751
+ const collectChildren = async (parentRunId) => {
752
+ const directChildren = await store.findByParentRunId?.(parentRunId);
753
+ for (const child of directChildren ?? []) {
754
+ if (visited.has(child.runId)) {
755
+ continue;
756
+ }
757
+ visited.add(child.runId);
758
+ children.push(child);
759
+ await collectChildren(child.runId);
760
+ }
761
+ };
762
+ await collectChildren(runId);
763
+ const cancelledParent = cancelState(parent);
764
+ const cancelledChildren = children.map(cancelState);
765
+ await store.save(cloneState(cancelledParent));
766
+ for (const child of cancelledChildren) {
767
+ await store.save(cloneState(child));
768
+ }
769
+ return {
770
+ parent: cloneState(cancelledParent),
771
+ children: cancelledChildren.map(cloneState)
772
+ };
773
+ };
388
774
  export const runAgent = async (agent, input = {}) => {
389
775
  const context = await resolveContext(agent, input);
390
776
  await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals);
391
- if (context.state.status === "completed" || context.state.status === "cancelled") {
777
+ const currentStatus = normalizeApprovalStatus(context.state.status);
778
+ if (currentStatus === "completed" ||
779
+ currentStatus === "cancelled" ||
780
+ currentStatus === "cancel_requested" ||
781
+ currentStatus === "timed_out") {
782
+ context.state.status = currentStatus;
392
783
  return toOutput(context.state);
393
784
  }
394
- if (context.state.status === "suspended" && context.state.pendingApprovals.length > 0 && !input.approvals?.length) {
785
+ if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0 && !input.approvals?.length) {
786
+ context.state.status = currentStatus;
395
787
  return toOutput(context.state);
396
788
  }
397
789
  if (context.remainingSteps === 0) {
@@ -418,8 +810,10 @@ export const runAgent = async (agent, input = {}) => {
418
810
  agentId: context.state.agentId,
419
811
  stepIndex: context.state.currentStep + 1
420
812
  });
813
+ const policy = resolveRunPolicy(agent, input);
814
+ const abortContext = createAgentAbortContext(input.abortSignal, policy);
421
815
  try {
422
- const result = await generateText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps));
816
+ const result = await withAgentPolicyTimeout(generateText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, abortContext.signal)), abortContext);
423
817
  const newSteps = mapSteps(result.steps, context.state.currentStep, result.toolResults);
424
818
  let output = finalizeState(context.state, result, newSteps, result.toolResults);
425
819
  const outputGuardrail = await runGuardrails(agent, output.state, "output", agent.outputGuardrails, () => ({
@@ -439,6 +833,14 @@ export const runAgent = async (agent, input = {}) => {
439
833
  return output;
440
834
  }
441
835
  catch (error) {
836
+ if (error instanceof AgentPolicyTimeoutError || abortContext.isTimedOut()) {
837
+ const status = policy?.onTimeout === "cancel-requested" ? "cancel_requested" : "timed_out";
838
+ const message = error instanceof Error ? error.message : `Agent run timed out after ${policy?.timeoutMs}ms.`;
839
+ const timedOutState = createTerminalState(context.state, status, message);
840
+ await persistState(agent, timedOutState);
841
+ await emitRunFinishTelemetry(agent, timedOutState);
842
+ return toOutput(timedOutState);
843
+ }
442
844
  const failedState = createFailedState(context.state, error instanceof Error ? error.message : String(error));
443
845
  await persistState(agent, failedState);
444
846
  await emitRunFinishTelemetry(agent, failedState);
@@ -484,14 +886,20 @@ export const streamAgent = (agent, input = {}) => {
484
886
  const runner = (async () => {
485
887
  const context = await resolveContext(agent, input);
486
888
  await emitRunStartTelemetry(agent, context.state, context.memoryMessages, input.approvals);
487
- if (context.state.status === "completed" || context.state.status === "cancelled") {
889
+ const currentStatus = normalizeApprovalStatus(context.state.status);
890
+ if (currentStatus === "completed" ||
891
+ currentStatus === "cancelled" ||
892
+ currentStatus === "cancel_requested" ||
893
+ currentStatus === "timed_out") {
894
+ context.state.status = currentStatus;
488
895
  publish({ done: true, value: undefined });
489
896
  return {
490
897
  output: toOutput(context.state),
491
898
  textStream: emptyAsyncIterable()
492
899
  };
493
900
  }
494
- if (context.state.status === "suspended" && context.state.pendingApprovals.length > 0 && !input.approvals?.length) {
901
+ if (currentStatus === "waiting_approval" && context.state.pendingApprovals.length > 0 && !input.approvals?.length) {
902
+ context.state.status = currentStatus;
495
903
  publish({ done: true, value: undefined });
496
904
  return {
497
905
  output: toOutput(context.state),
@@ -571,7 +979,9 @@ export const streamAgent = (agent, input = {}) => {
571
979
  agentId: context.state.agentId,
572
980
  stepIndex: context.state.currentStep + 1
573
981
  });
574
- const streamResult = streamText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps));
982
+ const policy = resolveRunPolicy(agent, input);
983
+ const abortContext = createAgentAbortContext(input.abortSignal, policy);
984
+ const streamResult = streamText(createGenerateOptions(agent, context.state, input, context.messages, context.remainingSteps, abortContext.signal));
575
985
  const approvalRequests = [];
576
986
  const eventRelay = (async () => {
577
987
  for await (const event of streamResult.eventStream) {
@@ -611,8 +1021,7 @@ export const streamAgent = (agent, input = {}) => {
611
1021
  })();
612
1022
  const output = (async () => {
613
1023
  try {
614
- await eventRelay;
615
- const final = await streamResult.collect();
1024
+ const final = await withAgentPolicyTimeout(eventRelay.then(() => streamResult.collect()), abortContext);
616
1025
  const newSteps = mapSteps(final.steps, context.state.currentStep, final.toolResults);
617
1026
  let result = finalizeState(context.state, final, newSteps, final.toolResults);
618
1027
  const outputGuardrail = await runGuardrails(agent, result.state, "output", agent.outputGuardrails, () => ({
@@ -659,6 +1068,30 @@ export const streamAgent = (agent, input = {}) => {
659
1068
  return result;
660
1069
  }
661
1070
  catch (error) {
1071
+ if (error instanceof AgentPolicyTimeoutError || abortContext.isTimedOut()) {
1072
+ const status = policy?.onTimeout === "cancel-requested" ? "cancel_requested" : "timed_out";
1073
+ const message = error instanceof Error ? error.message : `Agent run timed out after ${policy?.timeoutMs}ms.`;
1074
+ const timedOutState = createTerminalState(context.state, status, message);
1075
+ await persistState(agent, timedOutState);
1076
+ await emitRunFinishTelemetry(agent, timedOutState);
1077
+ publish({
1078
+ done: false,
1079
+ value: {
1080
+ type: "error",
1081
+ error: new AgentPolicyTimeoutError(policy?.timeoutMs ?? 0)
1082
+ }
1083
+ });
1084
+ publish({
1085
+ done: false,
1086
+ value: {
1087
+ type: "agent-run-finish",
1088
+ status: timedOutState.status,
1089
+ state: timedOutState
1090
+ }
1091
+ });
1092
+ publish({ done: true, value: undefined });
1093
+ return toOutput(timedOutState);
1094
+ }
662
1095
  const failedState = createFailedState(context.state, error instanceof Error ? error.message : String(error));
663
1096
  await persistState(agent, failedState);
664
1097
  await emitRunFinishTelemetry(agent, failedState);