@rowan-agent/agent 0.10.0 → 0.10.2

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 (3) hide show
  1. package/dist/index.d.ts +121 -90
  2. package/dist/index.js +529 -142
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -55,78 +55,6 @@ type ToolResult = {
55
55
  error?: string;
56
56
  };
57
57
 
58
- interface BeforePhaseEvent {
59
- type: "before_phase";
60
- phaseId: string;
61
- input: PhaseContext;
62
- }
63
- interface AfterPhaseEvent {
64
- type: "after_phase";
65
- phaseId: string;
66
- output: PhaseOutput;
67
- }
68
- interface BeforePromptEvent {
69
- type: "before_prompt";
70
- phaseId: string;
71
- input: PhaseContext;
72
- }
73
- interface BeforeToolCallEvent {
74
- type: "before_tool_call";
75
- tool: Tool$1;
76
- args: unknown;
77
- }
78
- interface AfterToolCallEvent {
79
- type: "after_tool_call";
80
- tool: Tool$1;
81
- result: ToolResult;
82
- }
83
- type HookEvent = BeforePhaseEvent | AfterPhaseEvent | BeforePromptEvent | BeforeToolCallEvent | AfterToolCallEvent;
84
- interface BeforePhaseResult {
85
- abort?: Outcome$1;
86
- skip?: {
87
- route: string;
88
- message: string;
89
- };
90
- input?: PhaseContext;
91
- }
92
- interface AfterPhaseResult {
93
- abort?: Outcome$1;
94
- retry?: PhaseContext;
95
- output?: PhaseOutput;
96
- }
97
- interface BeforePromptResult {
98
- input?: PhaseContext;
99
- }
100
- interface BeforeToolCallResult {
101
- allow: boolean;
102
- reason?: string;
103
- }
104
- interface AfterToolCallResult {
105
- result?: ToolResult;
106
- }
107
- interface HookResultMap {
108
- before_phase: BeforePhaseResult | undefined;
109
- after_phase: AfterPhaseResult | undefined;
110
- before_prompt: BeforePromptResult | undefined;
111
- before_tool_call: BeforeToolCallResult | undefined;
112
- after_tool_call: AfterToolCallResult | undefined;
113
- }
114
- type HookEventType = HookEvent["type"];
115
- type HookHandler<K extends HookEventType> = (event: Extract<HookEvent, {
116
- type: K;
117
- }>) => HookResultMap[K] | Promise<HookResultMap[K]> | void | Promise<void>;
118
- declare class HooksManager {
119
- private handlers;
120
- on<K extends HookEventType>(eventType: K, handler: HookHandler<K>): void;
121
- off<K extends HookEventType>(eventType: K, handler: HookHandler<K>): void;
122
- emit<K extends HookEventType>(eventType: K, event: Extract<HookEvent, {
123
- type: K;
124
- }>): Promise<void>;
125
- emitFirst<K extends HookEventType>(eventType: K, event: Extract<HookEvent, {
126
- type: K;
127
- }>): Promise<HookResultMap[K] | undefined>;
128
- }
129
-
130
58
  type LoopMetrics = {
131
59
  /** Number of phase iterations executed. */
132
60
  iterations: number;
@@ -194,6 +122,8 @@ type PhaseInteractionDriver = Readonly<{
194
122
  }>): PhaseInteraction;
195
123
  pending(): readonly PhaseInteraction[];
196
124
  answers(): ReadonlyMap<string, JsonValue>;
125
+ checkpoint(): JsonValue | undefined;
126
+ clearCheckpoint(): void;
197
127
  suspend(input?: Readonly<{
198
128
  checkpoint?: JsonValue;
199
129
  }>): never;
@@ -1115,6 +1045,95 @@ interface PhaseRegistry {
1115
1045
  entryPhaseId: string | null;
1116
1046
  }
1117
1047
 
1048
+ type ToolCallInteractionKind = PhaseInteractionKind;
1049
+ interface ToolCallInteractionRequest$1 {
1050
+ id?: string;
1051
+ kind: ToolCallInteractionKind;
1052
+ prompt: string;
1053
+ payload?: JsonValue;
1054
+ }
1055
+ interface BeforePhaseEvent {
1056
+ type: "before_phase";
1057
+ phaseId: string;
1058
+ input: PhaseContext;
1059
+ }
1060
+ interface AfterPhaseEvent {
1061
+ type: "after_phase";
1062
+ phaseId: string;
1063
+ output: PhaseOutput;
1064
+ }
1065
+ interface BeforePromptEvent {
1066
+ type: "before_prompt";
1067
+ phaseId: string;
1068
+ input: PhaseContext;
1069
+ }
1070
+ interface BeforeToolCallEvent {
1071
+ type: "before_tool_call";
1072
+ tool: Tool$1;
1073
+ args: unknown;
1074
+ readonly runId?: string;
1075
+ readonly agentId?: string;
1076
+ readonly toolCallId?: string;
1077
+ readonly metadata?: Readonly<Record<string, unknown>>;
1078
+ readonly answer?: unknown;
1079
+ }
1080
+ interface AfterToolCallEvent {
1081
+ type: "after_tool_call";
1082
+ tool: Tool$1;
1083
+ result: ToolResult;
1084
+ readonly runId?: string;
1085
+ readonly agentId?: string;
1086
+ readonly toolCallId?: string;
1087
+ readonly metadata?: Readonly<Record<string, unknown>>;
1088
+ }
1089
+ type HookEvent = BeforePhaseEvent | AfterPhaseEvent | BeforePromptEvent | BeforeToolCallEvent | AfterToolCallEvent;
1090
+ interface BeforePhaseResult {
1091
+ abort?: Outcome$1;
1092
+ skip?: {
1093
+ route: string;
1094
+ message: string;
1095
+ };
1096
+ input?: PhaseContext;
1097
+ }
1098
+ interface AfterPhaseResult {
1099
+ abort?: Outcome$1;
1100
+ retry?: PhaseContext;
1101
+ output?: PhaseOutput;
1102
+ }
1103
+ interface BeforePromptResult {
1104
+ input?: PhaseContext;
1105
+ }
1106
+ interface BeforeToolCallResult {
1107
+ allow?: boolean;
1108
+ reason?: string;
1109
+ interaction?: ToolCallInteractionRequest$1;
1110
+ }
1111
+ interface AfterToolCallResult {
1112
+ result?: ToolResult;
1113
+ }
1114
+ interface HookResultMap {
1115
+ before_phase: BeforePhaseResult | undefined;
1116
+ after_phase: AfterPhaseResult | undefined;
1117
+ before_prompt: BeforePromptResult | undefined;
1118
+ before_tool_call: BeforeToolCallResult | undefined;
1119
+ after_tool_call: AfterToolCallResult | undefined;
1120
+ }
1121
+ type HookEventType = HookEvent["type"];
1122
+ type HookHandler<K extends HookEventType> = (event: Extract<HookEvent, {
1123
+ type: K;
1124
+ }>) => HookResultMap[K] | Promise<HookResultMap[K]> | void | Promise<void>;
1125
+ declare class HooksManager {
1126
+ private handlers;
1127
+ on<K extends HookEventType>(eventType: K, handler: HookHandler<K>): void;
1128
+ off<K extends HookEventType>(eventType: K, handler: HookHandler<K>): void;
1129
+ emit<K extends HookEventType>(eventType: K, event: Extract<HookEvent, {
1130
+ type: K;
1131
+ }>): Promise<void>;
1132
+ emitFirst<K extends HookEventType>(eventType: K, event: Extract<HookEvent, {
1133
+ type: K;
1134
+ }>): Promise<HookResultMap[K] | undefined>;
1135
+ }
1136
+
1118
1137
  type ToolContext = Pick<AgentContext, "skills"> & {
1119
1138
  toolCallId: string;
1120
1139
  };
@@ -1406,11 +1425,19 @@ declare class ExtensionRunner {
1406
1425
  output?: PhaseOutput;
1407
1426
  }>;
1408
1427
  emitBeforePrompt(phaseId: string, input: PhaseContext): Promise<PhaseContext>;
1409
- emitBeforeToolCall(tool: Tool$1, args: unknown): Promise<{
1410
- allow: boolean;
1411
- reason?: string;
1412
- }>;
1413
- emitAfterToolCall(tool: Tool$1, result: ToolResult): Promise<ToolResult>;
1428
+ emitBeforeToolCall(tool: Tool$1, args: unknown, context?: {
1429
+ runId?: string;
1430
+ agentId?: string;
1431
+ toolCallId?: string;
1432
+ metadata?: Readonly<Record<string, unknown>>;
1433
+ answer?: unknown;
1434
+ }): Promise<BeforeToolCallResult>;
1435
+ emitAfterToolCall(tool: Tool$1, result: ToolResult, context?: {
1436
+ runId?: string;
1437
+ agentId?: string;
1438
+ toolCallId?: string;
1439
+ metadata?: Readonly<Record<string, unknown>>;
1440
+ }): Promise<ToolResult>;
1414
1441
  /**
1415
1442
  * Create an ExtensionAPI for a specific extension.
1416
1443
  * Registration methods write to the extension tracking object.
@@ -1574,22 +1601,27 @@ type ContextCandidate = Readonly<{
1574
1601
  name: string;
1575
1602
  value: JsonValue;
1576
1603
  }>;
1577
- type BeforeToolCall = (input: Readonly<{
1578
- tool: Tool;
1579
- args: JsonValue;
1580
- context: ToolInvocationContext;
1581
- signal: AbortSignal;
1582
- }>) => Readonly<{
1604
+ type ToolCallInteractionRequest = Readonly<{
1605
+ id?: string;
1606
+ kind: PhaseInteractionKind;
1607
+ prompt: string;
1608
+ payload?: JsonValue;
1609
+ }>;
1610
+ type BeforeToolCallDecision = Readonly<{
1583
1611
  allow: true;
1584
1612
  }> | Readonly<{
1585
1613
  allow: false;
1586
1614
  reason: string;
1587
- }> | Promise<Readonly<{
1588
- allow: true;
1589
1615
  }> | Readonly<{
1590
- allow: false;
1591
- reason: string;
1592
- }>>;
1616
+ interaction: ToolCallInteractionRequest;
1617
+ }>;
1618
+ type BeforeToolCall = (input: Readonly<{
1619
+ tool: Tool;
1620
+ args: JsonValue;
1621
+ context: ToolInvocationContext;
1622
+ signal: AbortSignal;
1623
+ answer?: JsonValue;
1624
+ }>) => BeforeToolCallDecision | Promise<BeforeToolCallDecision>;
1593
1625
  type AfterToolCall = (input: Readonly<{
1594
1626
  tool: Tool;
1595
1627
  result: ToolExecutionResult$1;
@@ -2169,7 +2201,6 @@ declare class AgentRuntime implements AgentRuntime$1 {
2169
2201
  private resolveContextWindow;
2170
2202
  private executeTool;
2171
2203
  private executeToolBatch;
2172
- private executeReservedTool;
2173
2204
  private requireAgent;
2174
2205
  private archiveDirFor;
2175
2206
  private runConsumer;
@@ -2782,4 +2813,4 @@ declare function parseFrontmatter<T = Record<string, unknown>>(raw: string): Fro
2782
2813
 
2783
2814
  declare function createCoreTools(input?: CoreToolContext): Tool[];
2784
2815
 
2785
- export { type AfterToolCall, type AgentConfiguration, type AgentDefinition, type AgentId, type AgentListCursor, type AgentRecord, type AgentRun, AgentRuntime, type AgentRuntimeOptions, type AgentSummary, type AnyRuntimeError, type AssistantContent, type AssistantMessage, type BeforeToolCall, COMPACT_PHASE_ID, type ConfigProvider, type ConfigPutResult, type ConfigResolution, type ConfigToken, type ConfigurationSnapshot, type ContextCandidate, type ContextCompactionRecord, type ContextStatus, type CoreToolContext, DEFAULT_PHASE_ID, type DefinitionLayer, type DurableConsumer, type DurableRunEvent, type DurableStore, type DurableToolResult, type EventCursor, type EventId, type ExecutionCheckpoint, type ExecutionId, type ExecutionToken, type ExtensionAPI, type ExtensionActivationError, type ExtensionActivationResult, type ExtensionContribution, type ExtensionDisposer, type ExtensionFactory, type ExtensionFactoryResult, ExtensionLifetimeError, type ExtensionLifetimeErrorCode, type ExtensionLoadInput, type FrontmatterResult, type HistorySeed, type HookEvent, type HookEventType, type HookHandler, InMemoryConfigProvider, InMemoryStore, type InputRequest, type InputRequestId, type InputRequiredCommit, type InvocationCatalogEntry, type InvocationSource, type JsonObject, type JsonPrimitive, type JsonValue, type LoadExtensionsResult, type LoadInput, type LoadResult, type LoadedExtension, type Message, type MessageBase, type MessageCommitted, type MessageContent, type MessageDelta, type MessageId, type MessageRevised, type MessageRevisionResult, type Metadata, type OpaqueId, type Outcome, type OwnerLease, type OwnerToken, type Page, type Phase, type PhaseContext, type PhaseContribution, type PhaseExecution, type PhaseExecutionIdentity, type PhaseInput, type PhaseInteraction, PhaseInteractionBoundary, PhaseInteractionCancelledError, type PhaseInteractionDriver, type PhaseInteractionKind, type PhaseInteractionState, type PhaseInteractionStatus, type PhaseInvocation, type PhaseMessageManager, type PhaseOutput, type PhaseRegistry, type PhaseRegistrySelection, type PhaseSettingsBadge, type PhaseSettingsContext, type PhaseSettingsControl, type PhaseSettingsDefinition, type PhaseSettingsItem, type PhaseSettingsOption, type PhaseSettingsProvider, type PhaseSettingsSection, type PhaseState, type PhaseStatus, type PhaseStatusState, type ResolvedResourceView, type ResourceDiagnostic, type ResourceKind, type ResourceRef, ResourceRegistry, ResourceRegistryError, type ResourceRegistryErrorCode, type ResourceSourceId, type ResourceView, type RetentionResult, type RunBoundary, type RunClaim, type RunEvent, type RunFailure, type RunId, type RunListCursor, type RunRecord, type RunSnapshot, type RunState, type RunStateChanged, type RunSummary, RuntimeBootstrapRegistry, RuntimeError, type RuntimeErrorCode, type RuntimeErrorDetails, RuntimeExtensionLifetime, STOP_PHASE_ID, type Skill, SqliteStore, type TextContent, type ThinkingContent, type ThinkingDelta, type Tool, type ToolCallId, type ToolCallSnapshot, type ToolCallState, type ToolContribution, type ToolDefinition, type ToolExecutionResult$1 as ToolExecutionResult, type ToolInvocationContext, type ToolMessage, type ToolMessageContent, type ToolProgress, type ToolResultContent, type ToolStateChanged, type ToolUseContent, type UserContent, type UserInput, type UserMessage, brandConfigToken, createCompactPhase, createCorePhases, createCoreTools, createDefaultPhase, createStopPhase, isConfigurationSnapshot, isRuntimeError, loadExtensionsFromPath as loadExtensions, loadPhase, loadPhaseSettings, loadPhases, loadSkill, loadSkills, parseAgentDefinition, parseFrontmatter, resolveConfigurationSnapshot };
2816
+ export { type AfterToolCall, type AgentConfiguration, type AgentDefinition, type AgentId, type AgentListCursor, type AgentRecord, type AgentRun, AgentRuntime, type AgentRuntimeOptions, type AgentSummary, type AnyRuntimeError, type AssistantContent, type AssistantMessage, type BeforeToolCall, COMPACT_PHASE_ID, type ConfigProvider, type ConfigPutResult, type ConfigResolution, type ConfigToken, type ConfigurationSnapshot, type ContextCandidate, type ContextCompactionRecord, type ContextStatus, type CoreToolContext, DEFAULT_PHASE_ID, type DefinitionLayer, type DurableConsumer, type DurableRunEvent, type DurableStore, type DurableToolResult, type EventCursor, type EventId, type ExecutionCheckpoint, type ExecutionId, type ExecutionToken, type ExtensionAPI, type ExtensionActivationError, type ExtensionActivationResult, type ExtensionContribution, type ExtensionDisposer, type ExtensionFactory, type ExtensionFactoryResult, ExtensionLifetimeError, type ExtensionLifetimeErrorCode, type ExtensionLoadInput, type FrontmatterResult, type HistorySeed, type HookEvent, type HookEventType, type HookHandler, InMemoryConfigProvider, InMemoryStore, type InputRequest, type InputRequestId, type InputRequiredCommit, type InvocationCatalogEntry, type InvocationSource, type JsonObject, type JsonPrimitive, type JsonValue, type LoadExtensionsResult, type LoadInput, type LoadResult, type LoadedExtension, type Message, type MessageBase, type MessageCommitted, type MessageContent, type MessageDelta, type MessageId, type MessageRevised, type MessageRevisionResult, type Metadata, type OpaqueId, type Outcome, type OwnerLease, type OwnerToken, type Page, type Phase, type PhaseContext, type PhaseContribution, type PhaseExecution, type PhaseExecutionIdentity, type PhaseInput, type PhaseInteraction, PhaseInteractionBoundary, PhaseInteractionCancelledError, type PhaseInteractionDriver, type PhaseInteractionKind, type PhaseInteractionState, type PhaseInteractionStatus, type PhaseInvocation, type PhaseMessageManager, type PhaseOutput, type PhaseRegistry, type PhaseRegistrySelection, type PhaseSettingsBadge, type PhaseSettingsContext, type PhaseSettingsControl, type PhaseSettingsDefinition, type PhaseSettingsItem, type PhaseSettingsOption, type PhaseSettingsProvider, type PhaseSettingsSection, type PhaseState, type PhaseStatus, type PhaseStatusState, type ResolvedResourceView, type ResourceDiagnostic, type ResourceKind, type ResourceRef, ResourceRegistry, ResourceRegistryError, type ResourceRegistryErrorCode, type ResourceSourceId, type ResourceView, type RetentionResult, type RunBoundary, type RunClaim, type RunEvent, type RunFailure, type RunId, type RunListCursor, type RunRecord, type RunSnapshot, type RunState, type RunStateChanged, type RunSummary, RuntimeBootstrapRegistry, RuntimeError, type RuntimeErrorCode, type RuntimeErrorDetails, RuntimeExtensionLifetime, STOP_PHASE_ID, type Skill, SqliteStore, type TextContent, type ThinkingContent, type ThinkingDelta, type Tool, type ToolCallId, type ToolCallInteractionRequest, type ToolCallSnapshot, type ToolCallState, type ToolContribution, type ToolDefinition, type ToolExecutionResult$1 as ToolExecutionResult, type ToolInvocationContext, type ToolMessage, type ToolMessageContent, type ToolProgress, type ToolResultContent, type ToolStateChanged, type ToolUseContent, type UserContent, type UserInput, type UserMessage, brandConfigToken, createCompactPhase, createCorePhases, createCoreTools, createDefaultPhase, createStopPhase, isConfigurationSnapshot, isRuntimeError, loadExtensionsFromPath as loadExtensions, loadPhase, loadPhaseSettings, loadPhases, loadSkill, loadSkills, parseAgentDefinition, parseFrontmatter, resolveConfigurationSnapshot };
package/dist/index.js CHANGED
@@ -518,15 +518,32 @@ async function executeRuntimeToolCall(input) {
518
518
  let decision;
519
519
  if (input.beforeToolCall) {
520
520
  await input.observe?.({ type: "approval_requested", tool, args });
521
- decision = await input.beforeToolCall({ tool, args });
522
- await input.observe?.({
523
- type: "approval_result",
521
+ decision = await input.beforeToolCall({
524
522
  tool,
525
523
  args,
526
- decision: decision ?? { allow: true }
524
+ toolCallId: input.toolCall.id
527
525
  });
526
+ if (decision && "allow" in decision) {
527
+ await input.observe?.({
528
+ type: "approval_result",
529
+ tool,
530
+ args,
531
+ decision
532
+ });
533
+ }
528
534
  }
529
- if (decision && !decision.allow) {
535
+ if (decision && "interaction" in decision) {
536
+ const result = toolResult({
537
+ context: input.toolContext,
538
+ toolName: tool.name,
539
+ ok: false,
540
+ content: null,
541
+ error: `Tool interaction required: ${decision.interaction.prompt}`
542
+ });
543
+ await input.observe?.({ type: "tool_blocked", tool, reason: decision.interaction.prompt });
544
+ return result;
545
+ }
546
+ if (decision && "allow" in decision && !decision.allow) {
530
547
  const result = toolResult({
531
548
  context: input.toolContext,
532
549
  toolName: tool.name,
@@ -1377,6 +1394,13 @@ function createPhaseInteractionDriver(state, phase, signal) {
1377
1394
  answers() {
1378
1395
  return new Map(answers);
1379
1396
  },
1397
+ checkpoint() {
1398
+ return checkpoint;
1399
+ },
1400
+ clearCheckpoint() {
1401
+ checkpoint = void 0;
1402
+ sync();
1403
+ },
1380
1404
  suspend(input = {}) {
1381
1405
  assertActive();
1382
1406
  const pending = this.pending();
@@ -2488,34 +2512,48 @@ async function executePhaseWithModel(ctx) {
2488
2512
  ...ctx.context,
2489
2513
  messages: ctx.messageManager.visible()
2490
2514
  };
2491
- const collected = await ctx.execution.invokeModel(roundContext);
2492
- output = {
2493
- message: collected.text,
2494
- phase: ctx.phase.name,
2495
- // A route decision sharing a model response with an ordinary Tool is
2496
- // rejected for this round. Keep ordinary calls visible so they can run,
2497
- // but do not let the route escape the error/re-decision boundary.
2498
- toolCalls: collected.toolCalls.some((candidate) => candidate.name === PhaseRouteTool) && collected.toolCalls.some((candidate) => candidate.name !== PhaseRouteTool) ? collected.toolCalls.filter((candidate) => candidate.name !== PhaseRouteTool) : collected.toolCalls
2499
- };
2500
- for (const toolCall of collected.toolCalls) {
2501
- if (toolCall.name !== PhaseRouteTool) continue;
2502
- const messageId = ctx.messageManager.start(
2503
- "tool",
2504
- createRouteToolResultContent(
2505
- toolCall,
2506
- collected.toolCalls.some((candidate) => candidate.name !== PhaseRouteTool) ? "Route must be called separately after ordinary tools finish; this route decision was ignored." : void 0
2507
- ),
2508
- {
2509
- phase: ctx.phase.name
2510
- }
2515
+ let executableToolCalls;
2516
+ const checkpoint = ctx.execution.interaction.checkpoint();
2517
+ if (checkpoint && typeof checkpoint === "object" && checkpoint.kind === "tool_call_suspension") {
2518
+ const toolSuspension = checkpoint;
2519
+ executableToolCalls = toolSuspension.toolCalls.filter(
2520
+ (toolCall) => executableToolNames.has(toolCall.name)
2511
2521
  );
2512
- await ctx.messageManager.end(messageId);
2513
- }
2514
- const executableToolCalls = collected.toolCalls.filter(
2515
- (toolCall) => executableToolNames.has(toolCall.name)
2516
- );
2517
- if (executableToolCalls.length === 0) {
2518
- return output;
2522
+ output = {
2523
+ message: "",
2524
+ phase: ctx.phase.name,
2525
+ toolCalls: [...toolSuspension.toolCalls]
2526
+ };
2527
+ } else {
2528
+ const collected = await ctx.execution.invokeModel(roundContext);
2529
+ output = {
2530
+ message: collected.text,
2531
+ phase: ctx.phase.name,
2532
+ // A route decision sharing a model response with an ordinary Tool is
2533
+ // rejected for this round. Keep ordinary calls visible so they can run,
2534
+ // but do not let the route escape the error/re-decision boundary.
2535
+ toolCalls: collected.toolCalls.some((candidate) => candidate.name === PhaseRouteTool) && collected.toolCalls.some((candidate) => candidate.name !== PhaseRouteTool) ? collected.toolCalls.filter((candidate) => candidate.name !== PhaseRouteTool) : collected.toolCalls
2536
+ };
2537
+ for (const toolCall of collected.toolCalls) {
2538
+ if (toolCall.name !== PhaseRouteTool) continue;
2539
+ const messageId = ctx.messageManager.start(
2540
+ "tool",
2541
+ createRouteToolResultContent(
2542
+ toolCall,
2543
+ collected.toolCalls.some((candidate) => candidate.name !== PhaseRouteTool) ? "Route must be called separately after ordinary tools finish; this route decision was ignored." : void 0
2544
+ ),
2545
+ {
2546
+ phase: ctx.phase.name
2547
+ }
2548
+ );
2549
+ await ctx.messageManager.end(messageId);
2550
+ }
2551
+ executableToolCalls = collected.toolCalls.filter(
2552
+ (toolCall) => executableToolNames.has(toolCall.name)
2553
+ );
2554
+ if (executableToolCalls.length === 0) {
2555
+ return output;
2556
+ }
2519
2557
  }
2520
2558
  const results = await ctx.execution.executeTools(roundContext, executableToolCalls);
2521
2559
  for (const result of results) {
@@ -2991,7 +3029,8 @@ async function executeToolCall(input) {
2991
3029
  if (input.config.runtime?.tools) {
2992
3030
  result = await input.config.runtime.tools({
2993
3031
  config: input.config,
2994
- toolCall: input.toolCall
3032
+ toolCall: input.toolCall,
3033
+ driver: input.driver
2995
3034
  });
2996
3035
  } else {
2997
3036
  const toolContext = {
@@ -3017,7 +3056,8 @@ async function executeToolCalls(input) {
3017
3056
  if (input.config.runtime?.toolsBatch) {
3018
3057
  const results2 = await input.config.runtime.toolsBatch({
3019
3058
  config: input.config,
3020
- toolCalls: input.toolCalls
3059
+ toolCalls: input.toolCalls,
3060
+ driver: input.driver
3021
3061
  });
3022
3062
  if (results2.length !== input.toolCalls.length) throw new Error("Runtime returned an invalid Tool batch result");
3023
3063
  return results2.map((result, index) => ({
@@ -3115,7 +3155,8 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3115
3155
  }
3116
3156
  },
3117
3157
  tools,
3118
- toolCall
3158
+ toolCall,
3159
+ driver: interaction
3119
3160
  });
3120
3161
  await toolExecutionManager.end(result.toolCallId, result.toolName, result, !result.ok);
3121
3162
  return result;
@@ -3137,7 +3178,8 @@ function createPhaseExecution(config, state, phase, messageManager, toolExecutio
3137
3178
  }
3138
3179
  },
3139
3180
  tools,
3140
- toolCalls
3181
+ toolCalls,
3182
+ driver: interaction
3141
3183
  });
3142
3184
  for (const result of results) {
3143
3185
  await toolExecutionManager.end(result.toolCallId, result.toolName, result, !result.ok);
@@ -3447,9 +3489,9 @@ var THINKING_LEVELS = [
3447
3489
  ];
3448
3490
  function thinkingLevelFromMetadata(metadata) {
3449
3491
  const record = isRecord3(metadata) ? metadata : void 0;
3450
- const everyield = record?.everyield;
3451
- if (!isRecord3(everyield)) return void 0;
3452
- const level = everyield.thinkingLevel;
3492
+ const mori = record?.mori;
3493
+ if (!isRecord3(mori)) return void 0;
3494
+ const level = mori.thinkingLevel;
3453
3495
  return typeof level === "string" && THINKING_LEVELS.includes(level) ? level : void 0;
3454
3496
  }
3455
3497
  function thinkingLevelFromMessages(messages) {
@@ -4222,12 +4264,30 @@ function assembleRegisteredExtensions(snapshot, runner, options = {}) {
4222
4264
  beforePrompt: (phaseId, input) => runner.emitBeforePrompt(phaseId, input),
4223
4265
  beforeToolCall: async (input) => {
4224
4266
  const tool = projectTool(input.tool, input.context.agentId, input.context.runId);
4225
- const decision = await runner.emitBeforeToolCall(tool, input.args);
4267
+ const decision = await runner.emitBeforeToolCall(tool, input.args, {
4268
+ runId: input.context.runId,
4269
+ agentId: input.context.agentId,
4270
+ toolCallId: input.context.toolCallId,
4271
+ metadata: input.context.runMetadata,
4272
+ answer: input.answer
4273
+ });
4274
+ if (decision.interaction !== void 0) {
4275
+ return { interaction: decision.interaction };
4276
+ }
4226
4277
  return decision.allow ? { allow: true } : { allow: false, reason: decision.reason ?? "Extension hook rejected the Tool." };
4227
4278
  },
4228
4279
  afterToolCall: async (input) => {
4229
4280
  const tool = projectTool(input.tool, input.context.agentId, input.context.runId);
4230
- const result = await runner.emitAfterToolCall(tool, toLoopResult(input.result, input.context.toolCallId, input.tool.name));
4281
+ const result = await runner.emitAfterToolCall(
4282
+ tool,
4283
+ toLoopResult(input.result, input.context.toolCallId, input.tool.name),
4284
+ {
4285
+ runId: input.context.runId,
4286
+ agentId: input.context.agentId,
4287
+ toolCallId: input.context.toolCallId,
4288
+ metadata: input.context.runMetadata
4289
+ }
4290
+ );
4231
4291
  return fromLoopResult(result);
4232
4292
  },
4233
4293
  setContext: (context2) => {
@@ -4873,19 +4933,28 @@ var ExtensionRunner = class {
4873
4933
  });
4874
4934
  return result?.input ?? input;
4875
4935
  }
4876
- async emitBeforeToolCall(tool, args) {
4936
+ async emitBeforeToolCall(tool, args, context) {
4877
4937
  const result = await this.emitHook("before_tool_call", {
4878
4938
  type: "before_tool_call",
4879
4939
  tool,
4880
- args
4940
+ args,
4941
+ ...context?.runId !== void 0 ? { runId: context.runId } : {},
4942
+ ...context?.agentId !== void 0 ? { agentId: context.agentId } : {},
4943
+ ...context?.toolCallId !== void 0 ? { toolCallId: context.toolCallId } : {},
4944
+ ...context?.metadata !== void 0 ? { metadata: context.metadata } : {},
4945
+ ...context?.answer !== void 0 ? { answer: context.answer } : {}
4881
4946
  });
4882
4947
  return result ?? { allow: true };
4883
4948
  }
4884
- async emitAfterToolCall(tool, result) {
4949
+ async emitAfterToolCall(tool, result, context) {
4885
4950
  const hookResult = await this.emitHook("after_tool_call", {
4886
4951
  type: "after_tool_call",
4887
4952
  tool,
4888
- result
4953
+ result,
4954
+ ...context?.runId !== void 0 ? { runId: context.runId } : {},
4955
+ ...context?.agentId !== void 0 ? { agentId: context.agentId } : {},
4956
+ ...context?.toolCallId !== void 0 ? { toolCallId: context.toolCallId } : {},
4957
+ ...context?.metadata !== void 0 ? { metadata: context.metadata } : {}
4889
4958
  });
4890
4959
  return hookResult?.result ?? result;
4891
4960
  }
@@ -6235,7 +6304,7 @@ var AgentRuntime = class _AgentRuntime {
6235
6304
  },
6236
6305
  onContext: assembly.setContext,
6237
6306
  runtime: {
6238
- tools: ({ toolCall }) => {
6307
+ tools: ({ toolCall, driver }) => {
6239
6308
  const task = toolQueue.then(async () => {
6240
6309
  const execution = await this.executeToolBatch({
6241
6310
  run,
@@ -6244,7 +6313,11 @@ var AgentRuntime = class _AgentRuntime {
6244
6313
  expectedRevision: executionRevision,
6245
6314
  toolConfig: executionTools,
6246
6315
  toolCalls: [toolCall],
6247
- signal: controller.signal
6316
+ signal: controller.signal,
6317
+ driver,
6318
+ onRevision: (rev) => {
6319
+ executionRevision = rev;
6320
+ }
6248
6321
  });
6249
6322
  executionRevision = execution.revision;
6250
6323
  return execution.results[0];
@@ -6252,7 +6325,7 @@ var AgentRuntime = class _AgentRuntime {
6252
6325
  toolQueue = task.then(() => void 0, () => void 0);
6253
6326
  return task;
6254
6327
  },
6255
- toolsBatch: ({ toolCalls }) => {
6328
+ toolsBatch: ({ toolCalls, driver }) => {
6256
6329
  const task = toolQueue.then(async () => {
6257
6330
  const execution = await this.executeToolBatch({
6258
6331
  run,
@@ -6261,7 +6334,11 @@ var AgentRuntime = class _AgentRuntime {
6261
6334
  expectedRevision: executionRevision,
6262
6335
  toolConfig: executionTools,
6263
6336
  toolCalls,
6264
- signal: controller.signal
6337
+ signal: controller.signal,
6338
+ driver,
6339
+ onRevision: (rev) => {
6340
+ executionRevision = rev;
6341
+ }
6265
6342
  });
6266
6343
  executionRevision = execution.revision;
6267
6344
  return execution.results;
@@ -6303,7 +6380,7 @@ var AgentRuntime = class _AgentRuntime {
6303
6380
  return;
6304
6381
  }
6305
6382
  if (result.type === "input_required") {
6306
- const output = latestAssistant(
6383
+ const output = result.interactions && result.interactions.length > 0 ? void 0 : latestAssistant(
6307
6384
  run,
6308
6385
  result.messages.slice(modelMessages.length),
6309
6386
  modelMessages.length
@@ -6387,109 +6464,419 @@ var AgentRuntime = class _AgentRuntime {
6387
6464
  return { result: batch.results[0], revision: batch.revision };
6388
6465
  }
6389
6466
  async executeToolBatch(input) {
6390
- const reserved = await this.owned.reserveToolCalls({
6391
- runId: input.run.id,
6392
- execution: input.execution,
6393
- expectedRevision: input.expectedRevision,
6394
- requestMessageId: createId("msg"),
6395
- calls: input.toolCalls.map((toolCall) => ({
6396
- providerToolCallId: toolCall.id,
6397
- name: toolCall.name,
6398
- args: toJsonValue(toolCall.args)
6399
- }))
6400
- });
6401
- let revision = reserved.run.revision;
6402
- const results = [];
6403
- for (let index = 0; index < input.toolCalls.length; index += 1) {
6404
- const toolCall = input.toolCalls[index];
6405
- const tool = reserved.toolCalls[index];
6406
- const execution = await this.executeReservedTool({
6407
- ...input,
6408
- toolCall,
6409
- tool,
6410
- expectedRevision: revision
6411
- });
6412
- revision = execution.revision;
6413
- results.push(execution.result);
6414
- }
6415
- return { results, revision };
6416
- }
6417
- async executeReservedTool(input) {
6418
- const durableTool = input.toolConfig.tools.find((candidate) => candidate.name === input.toolCall.name);
6419
- const providerToolCallId = input.toolCall.id;
6420
- const toolCallId = input.tool.id;
6467
+ const checkpoint = input.driver?.checkpoint();
6468
+ const isSuspensionResume = checkpoint && typeof checkpoint === "object" && checkpoint.kind === "tool_call_suspension";
6469
+ const suspension = isSuspensionResume ? checkpoint : void 0;
6421
6470
  let revision = input.expectedRevision;
6422
- const protocolFailure = (error) => ({ toolCallId: providerToolCallId, toolName: input.toolCall.name, ok: false, content: null, error });
6423
- if (!durableTool) {
6424
- const failed = await this.owned.commitToolResult({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId, state: "failed", result: { ok: false, content: null, error: `Tool ${input.toolCall.name} is not available.` } });
6425
- return { result: protocolFailure(`Tool ${input.toolCall.name} is not available.`), revision: failed.run.revision };
6426
- }
6427
- let progressActive = false;
6428
- const context = {
6429
- agentId: input.run.agentId,
6430
- runId: input.run.id,
6431
- ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
6432
- ...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
6433
- toolCallId,
6434
- reportProgress: (progress) => {
6435
- const active = this.executions.get(input.run.id);
6436
- if (!progressActive || this.closed || input.signal.aborted || active?.executionId !== input.execution.executionId || !isJsonValue(progress)) return;
6437
- let copy;
6471
+ const setRevision = (newRevision) => {
6472
+ revision = newRevision;
6473
+ input.onRevision?.(revision);
6474
+ };
6475
+ if (!suspension && input.toolCalls.length > 1) {
6476
+ let hasInteraction = false;
6477
+ const preDecisions = [];
6478
+ const toolCallIds = input.toolCalls.map(() => createId("tool"));
6479
+ for (let i = 0; i < input.toolCalls.length; i++) {
6480
+ const toolCall = input.toolCalls[i];
6481
+ const durableTool = input.toolConfig.tools.find((c) => c.name === toolCall.name);
6482
+ if (!durableTool || !input.toolConfig.beforeToolCall) {
6483
+ preDecisions.push({ allow: true });
6484
+ continue;
6485
+ }
6486
+ const toolCallId = toolCallIds[i];
6487
+ const context = {
6488
+ agentId: input.run.agentId,
6489
+ runId: input.run.id,
6490
+ ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
6491
+ ...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
6492
+ toolCallId,
6493
+ reportProgress: () => {
6494
+ }
6495
+ };
6438
6496
  try {
6439
- if (JSON.stringify(progress).length > 64 * 1024) return;
6440
- copy = structuredClone(progress);
6497
+ const decision = await input.toolConfig.beforeToolCall({
6498
+ tool: durableTool,
6499
+ args: toJsonValue(toolCall.args),
6500
+ context,
6501
+ signal: input.signal
6502
+ });
6503
+ if ("interaction" in decision && decision.interaction !== void 0) {
6504
+ hasInteraction = true;
6505
+ break;
6506
+ }
6507
+ preDecisions.push(decision);
6441
6508
  } catch {
6442
- return;
6509
+ preDecisions.push({ allow: true });
6443
6510
  }
6444
- this.transientEvents.publish({
6445
- kind: "tool_progress",
6446
- durability: "transient",
6511
+ }
6512
+ if (!hasInteraction) {
6513
+ const reserved = await this.owned.reserveToolCalls({
6447
6514
  runId: input.run.id,
6448
- executionId: input.execution.executionId,
6449
- toolCallId,
6450
- progress: copy
6515
+ execution: input.execution,
6516
+ expectedRevision: revision,
6517
+ requestMessageId: createId("msg"),
6518
+ calls: input.toolCalls.map((tc, i) => ({
6519
+ toolCallId: toolCallIds[i],
6520
+ providerToolCallId: tc.id,
6521
+ name: tc.name,
6522
+ args: toJsonValue(tc.args)
6523
+ }))
6451
6524
  });
6452
- }
6453
- };
6454
- try {
6455
- if (input.toolConfig.beforeToolCall) {
6456
- const decision = await input.toolConfig.beforeToolCall({ tool: durableTool, args: toJsonValue(input.toolCall.args), context, signal: input.signal });
6457
- if (!decision.allow) {
6458
- const failed = await this.owned.commitToolResult({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId, state: "failed", result: { ok: false, content: null, error: decision.reason } });
6459
- return { result: protocolFailure(decision.reason), revision: failed.run.revision };
6525
+ setRevision(reserved.run.revision);
6526
+ const results2 = [];
6527
+ for (let i = 0; i < input.toolCalls.length; i++) {
6528
+ const toolCall = input.toolCalls[i];
6529
+ const durableTool = input.toolConfig.tools.find((c) => c.name === toolCall.name);
6530
+ const providerToolCallId = toolCall.id;
6531
+ const toolCallId = toolCallIds[i];
6532
+ const protocolFailure = (error) => ({
6533
+ toolCallId: providerToolCallId,
6534
+ toolName: toolCall.name,
6535
+ ok: false,
6536
+ content: null,
6537
+ error
6538
+ });
6539
+ if (!durableTool) {
6540
+ const failed = await this.owned.commitToolResult({
6541
+ runId: input.run.id,
6542
+ execution: input.execution,
6543
+ expectedRevision: revision,
6544
+ toolCallId,
6545
+ state: "failed",
6546
+ result: { ok: false, content: null, error: `Tool ${toolCall.name} is not available.` }
6547
+ });
6548
+ setRevision(failed.run.revision);
6549
+ results2.push(protocolFailure(`Tool ${toolCall.name} is not available.`));
6550
+ continue;
6551
+ }
6552
+ const decision = preDecisions[i] ?? { allow: true };
6553
+ if ("allow" in decision && !decision.allow) {
6554
+ const failed = await this.owned.commitToolResult({
6555
+ runId: input.run.id,
6556
+ execution: input.execution,
6557
+ expectedRevision: revision,
6558
+ toolCallId,
6559
+ state: "failed",
6560
+ result: { ok: false, content: null, error: decision.reason }
6561
+ });
6562
+ setRevision(failed.run.revision);
6563
+ results2.push(protocolFailure(decision.reason));
6564
+ continue;
6565
+ }
6566
+ let progressActive = false;
6567
+ const context = {
6568
+ agentId: input.run.agentId,
6569
+ runId: input.run.id,
6570
+ ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
6571
+ ...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
6572
+ toolCallId,
6573
+ reportProgress: (progress) => {
6574
+ const active = this.executions.get(input.run.id);
6575
+ if (!progressActive || this.closed || input.signal.aborted || active?.executionId !== input.execution.executionId || !isJsonValue(progress)) return;
6576
+ let copy;
6577
+ try {
6578
+ if (JSON.stringify(progress).length > 64 * 1024) return;
6579
+ copy = structuredClone(progress);
6580
+ } catch {
6581
+ return;
6582
+ }
6583
+ this.transientEvents.publish({
6584
+ kind: "tool_progress",
6585
+ durability: "transient",
6586
+ runId: input.run.id,
6587
+ executionId: input.execution.executionId,
6588
+ toolCallId,
6589
+ progress: copy
6590
+ });
6591
+ }
6592
+ };
6593
+ const started = await this.owned.startToolCall({
6594
+ runId: input.run.id,
6595
+ execution: input.execution,
6596
+ expectedRevision: revision,
6597
+ toolCallId
6598
+ });
6599
+ setRevision(started.run.revision);
6600
+ progressActive = true;
6601
+ try {
6602
+ let result = await durableTool.execute(toJsonValue(toolCall.args), context, input.signal);
6603
+ assertToolExecutionResult(result);
6604
+ if (input.toolConfig.afterToolCall) {
6605
+ result = await input.toolConfig.afterToolCall({
6606
+ tool: durableTool,
6607
+ result,
6608
+ context,
6609
+ signal: input.signal
6610
+ });
6611
+ }
6612
+ assertToolExecutionResult(result);
6613
+ result = await spillLargeToolResult(
6614
+ result,
6615
+ this.archiveDirFor(input.run.agentId),
6616
+ toolCall.name
6617
+ );
6618
+ const committed = await this.owned.commitToolResult({
6619
+ runId: input.run.id,
6620
+ execution: input.execution,
6621
+ expectedRevision: revision,
6622
+ toolCallId,
6623
+ state: result.ok ? "completed" : "failed",
6624
+ result
6625
+ });
6626
+ setRevision(committed.run.revision);
6627
+ results2.push({
6628
+ toolCallId: providerToolCallId,
6629
+ toolName: toolCall.name,
6630
+ ...result
6631
+ });
6632
+ } catch (error) {
6633
+ const reason = error instanceof Error ? error.message : "Tool execution outcome is indeterminate.";
6634
+ const indeterminate = await this.owned.commitToolResult({
6635
+ runId: input.run.id,
6636
+ execution: input.execution,
6637
+ expectedRevision: revision,
6638
+ toolCallId,
6639
+ state: "indeterminate",
6640
+ reason,
6641
+ result: { ok: false, content: null, error: reason }
6642
+ });
6643
+ setRevision(indeterminate.run.revision);
6644
+ results2.push(protocolFailure(reason));
6645
+ } finally {
6646
+ progressActive = false;
6647
+ this.transientEvents.clearTool(input.run.id, toolCallId);
6648
+ }
6460
6649
  }
6650
+ return { results: results2, revision };
6461
6651
  }
6462
- } catch (error) {
6463
- const reason = error instanceof Error ? error.message : "Tool policy rejected the call.";
6464
- const failed = await this.owned.commitToolResult({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId, state: "failed", result: { ok: false, content: null, error: reason } });
6465
- return { result: protocolFailure(reason), revision: failed.run.revision };
6466
6652
  }
6467
- const started = await this.owned.startToolCall({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId });
6468
- revision = started.run.revision;
6469
- progressActive = true;
6470
- try {
6471
- let result = await durableTool.execute(toJsonValue(input.toolCall.args), context, input.signal);
6472
- assertToolExecutionResult(result);
6473
- if (input.toolConfig.afterToolCall) result = await input.toolConfig.afterToolCall({ tool: durableTool, result, context, signal: input.signal });
6474
- assertToolExecutionResult(result);
6475
- result = await spillLargeToolResult(
6476
- result,
6477
- this.archiveDirFor(input.run.agentId),
6478
- input.toolCall.name
6479
- );
6480
- const committed = await this.owned.commitToolResult({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId, state: result.ok ? "completed" : "failed", result });
6481
- return {
6482
- result: { toolCallId: providerToolCallId, toolName: input.toolCall.name, ...result },
6483
- revision: committed.run.revision
6653
+ const results = suspension ? [...suspension.completedResults] : [];
6654
+ const startIndex = suspension ? suspension.pendingIndex : 0;
6655
+ for (let index = startIndex; index < input.toolCalls.length; index += 1) {
6656
+ const toolCall = input.toolCalls[index];
6657
+ const durableTool = input.toolConfig.tools.find((candidate) => candidate.name === toolCall.name);
6658
+ const providerToolCallId = toolCall.id;
6659
+ const toolCallId = suspension && index === suspension.pendingIndex && suspension.toolCallId ? suspension.toolCallId : createId("tool");
6660
+ const protocolFailure = (error) => ({
6661
+ toolCallId: providerToolCallId,
6662
+ toolName: toolCall.name,
6663
+ ok: false,
6664
+ content: null,
6665
+ error
6666
+ });
6667
+ if (!durableTool) {
6668
+ const reserved2 = await this.owned.reserveToolCalls({
6669
+ runId: input.run.id,
6670
+ execution: input.execution,
6671
+ expectedRevision: revision,
6672
+ requestMessageId: createId("msg"),
6673
+ calls: [{
6674
+ toolCallId,
6675
+ providerToolCallId,
6676
+ name: toolCall.name,
6677
+ args: toJsonValue(toolCall.args)
6678
+ }]
6679
+ });
6680
+ setRevision(reserved2.run.revision);
6681
+ const failed = await this.owned.commitToolResult({
6682
+ runId: input.run.id,
6683
+ execution: input.execution,
6684
+ expectedRevision: revision,
6685
+ toolCallId,
6686
+ state: "failed",
6687
+ result: { ok: false, content: null, error: `Tool ${toolCall.name} is not available.` }
6688
+ });
6689
+ setRevision(failed.run.revision);
6690
+ results.push(protocolFailure(`Tool ${toolCall.name} is not available.`));
6691
+ continue;
6692
+ }
6693
+ let progressActive = false;
6694
+ const context = {
6695
+ agentId: input.run.agentId,
6696
+ runId: input.run.id,
6697
+ ...input.agentMetadata === void 0 ? {} : { agentMetadata: input.agentMetadata },
6698
+ ...input.run.metadata === void 0 ? {} : { runMetadata: input.run.metadata },
6699
+ toolCallId,
6700
+ reportProgress: (progress) => {
6701
+ const active = this.executions.get(input.run.id);
6702
+ if (!progressActive || this.closed || input.signal.aborted || active?.executionId !== input.execution.executionId || !isJsonValue(progress)) return;
6703
+ let copy;
6704
+ try {
6705
+ if (JSON.stringify(progress).length > 64 * 1024) return;
6706
+ copy = structuredClone(progress);
6707
+ } catch {
6708
+ return;
6709
+ }
6710
+ this.transientEvents.publish({
6711
+ kind: "tool_progress",
6712
+ durability: "transient",
6713
+ runId: input.run.id,
6714
+ executionId: input.execution.executionId,
6715
+ toolCallId,
6716
+ progress: copy
6717
+ });
6718
+ }
6484
6719
  };
6485
- } catch (error) {
6486
- const reason = error instanceof Error ? error.message : "Tool execution outcome is indeterminate.";
6487
- const indeterminate = await this.owned.commitToolResult({ runId: input.run.id, execution: input.execution, expectedRevision: revision, toolCallId, state: "indeterminate", reason, result: { ok: false, content: null, error: reason } });
6488
- return { result: protocolFailure(reason), revision: indeterminate.run.revision };
6489
- } finally {
6490
- progressActive = false;
6491
- this.transientEvents.clearTool(input.run.id, toolCallId);
6720
+ const answer = suspension && index === suspension.pendingIndex && suspension.interactionId ? input.driver?.answers().get(suspension.interactionId) : void 0;
6721
+ try {
6722
+ if (input.toolConfig.beforeToolCall) {
6723
+ const decision = await input.toolConfig.beforeToolCall({
6724
+ tool: durableTool,
6725
+ args: toJsonValue(toolCall.args),
6726
+ context,
6727
+ signal: input.signal,
6728
+ answer
6729
+ });
6730
+ if ("interaction" in decision && decision.interaction !== void 0) {
6731
+ if (!input.driver) {
6732
+ throw new Error("Tool call requested interaction but no interaction driver is active.");
6733
+ }
6734
+ const interactionRequest = input.driver.request({
6735
+ id: decision.interaction.id,
6736
+ kind: decision.interaction.kind,
6737
+ prompt: decision.interaction.prompt,
6738
+ payload: decision.interaction.payload
6739
+ });
6740
+ input.driver.suspend({
6741
+ checkpoint: {
6742
+ kind: "tool_call_suspension",
6743
+ toolCalls: input.toolCalls,
6744
+ pendingIndex: index,
6745
+ toolCallId,
6746
+ interactionId: interactionRequest.id,
6747
+ completedResults: results
6748
+ }
6749
+ });
6750
+ }
6751
+ if ("allow" in decision && !decision.allow) {
6752
+ const reserved2 = await this.owned.reserveToolCalls({
6753
+ runId: input.run.id,
6754
+ execution: input.execution,
6755
+ expectedRevision: revision,
6756
+ requestMessageId: createId("msg"),
6757
+ calls: [{
6758
+ toolCallId,
6759
+ providerToolCallId,
6760
+ name: toolCall.name,
6761
+ args: toJsonValue(toolCall.args)
6762
+ }]
6763
+ });
6764
+ setRevision(reserved2.run.revision);
6765
+ const failed = await this.owned.commitToolResult({
6766
+ runId: input.run.id,
6767
+ execution: input.execution,
6768
+ expectedRevision: revision,
6769
+ toolCallId,
6770
+ state: "failed",
6771
+ result: { ok: false, content: null, error: decision.reason }
6772
+ });
6773
+ setRevision(failed.run.revision);
6774
+ results.push(protocolFailure(decision.reason));
6775
+ continue;
6776
+ }
6777
+ }
6778
+ } catch (error) {
6779
+ if (error instanceof PhaseInteractionBoundary || error instanceof PhaseInteractionCancelledError) {
6780
+ throw error;
6781
+ }
6782
+ const reason = error instanceof Error ? error.message : "Tool policy rejected the call.";
6783
+ const reserved2 = await this.owned.reserveToolCalls({
6784
+ runId: input.run.id,
6785
+ execution: input.execution,
6786
+ expectedRevision: revision,
6787
+ requestMessageId: createId("msg"),
6788
+ calls: [{
6789
+ toolCallId,
6790
+ providerToolCallId,
6791
+ name: toolCall.name,
6792
+ args: toJsonValue(toolCall.args)
6793
+ }]
6794
+ });
6795
+ setRevision(reserved2.run.revision);
6796
+ const failed = await this.owned.commitToolResult({
6797
+ runId: input.run.id,
6798
+ execution: input.execution,
6799
+ expectedRevision: revision,
6800
+ toolCallId,
6801
+ state: "failed",
6802
+ result: { ok: false, content: null, error: reason }
6803
+ });
6804
+ setRevision(failed.run.revision);
6805
+ results.push(protocolFailure(reason));
6806
+ continue;
6807
+ }
6808
+ const reserved = await this.owned.reserveToolCalls({
6809
+ runId: input.run.id,
6810
+ execution: input.execution,
6811
+ expectedRevision: revision,
6812
+ requestMessageId: createId("msg"),
6813
+ calls: [{
6814
+ toolCallId,
6815
+ providerToolCallId,
6816
+ name: toolCall.name,
6817
+ args: toJsonValue(toolCall.args)
6818
+ }]
6819
+ });
6820
+ setRevision(reserved.run.revision);
6821
+ const started = await this.owned.startToolCall({
6822
+ runId: input.run.id,
6823
+ execution: input.execution,
6824
+ expectedRevision: revision,
6825
+ toolCallId
6826
+ });
6827
+ setRevision(started.run.revision);
6828
+ progressActive = true;
6829
+ try {
6830
+ let result = await durableTool.execute(toJsonValue(toolCall.args), context, input.signal);
6831
+ assertToolExecutionResult(result);
6832
+ if (input.toolConfig.afterToolCall) {
6833
+ result = await input.toolConfig.afterToolCall({
6834
+ tool: durableTool,
6835
+ result,
6836
+ context,
6837
+ signal: input.signal
6838
+ });
6839
+ }
6840
+ assertToolExecutionResult(result);
6841
+ result = await spillLargeToolResult(
6842
+ result,
6843
+ this.archiveDirFor(input.run.agentId),
6844
+ toolCall.name
6845
+ );
6846
+ const committed = await this.owned.commitToolResult({
6847
+ runId: input.run.id,
6848
+ execution: input.execution,
6849
+ expectedRevision: revision,
6850
+ toolCallId,
6851
+ state: result.ok ? "completed" : "failed",
6852
+ result
6853
+ });
6854
+ setRevision(committed.run.revision);
6855
+ results.push({
6856
+ toolCallId: providerToolCallId,
6857
+ toolName: toolCall.name,
6858
+ ...result
6859
+ });
6860
+ } catch (error) {
6861
+ const reason = error instanceof Error ? error.message : "Tool execution outcome is indeterminate.";
6862
+ const indeterminate = await this.owned.commitToolResult({
6863
+ runId: input.run.id,
6864
+ execution: input.execution,
6865
+ expectedRevision: revision,
6866
+ toolCallId,
6867
+ state: "indeterminate",
6868
+ reason,
6869
+ result: { ok: false, content: null, error: reason }
6870
+ });
6871
+ setRevision(indeterminate.run.revision);
6872
+ results.push(protocolFailure(reason));
6873
+ } finally {
6874
+ progressActive = false;
6875
+ this.transientEvents.clearTool(input.run.id, toolCallId);
6876
+ }
6492
6877
  }
6878
+ input.driver?.clearCheckpoint();
6879
+ return { results, revision };
6493
6880
  }
6494
6881
  async requireAgent(agentId) {
6495
6882
  const agent = (await this.owned.listAgents()).find((candidate) => candidate.id === agentId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rowan-agent/agent",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",