@openrouter/agent 0.8.0 → 0.9.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 (87) hide show
  1. package/README.md +483 -5
  2. package/esm/index.d.ts +19 -4
  3. package/esm/index.d.ts.map +1 -1
  4. package/esm/index.js +9 -2
  5. package/esm/index.js.map +1 -1
  6. package/esm/inner-loop/call-model.d.ts.map +1 -1
  7. package/esm/inner-loop/call-model.js +40 -41
  8. package/esm/inner-loop/call-model.js.map +1 -1
  9. package/esm/inner-loop/resume-tool-results.d.ts +128 -0
  10. package/esm/inner-loop/resume-tool-results.d.ts.map +1 -0
  11. package/esm/inner-loop/resume-tool-results.js +244 -0
  12. package/esm/inner-loop/resume-tool-results.js.map +1 -0
  13. package/esm/lib/agent-tool.d.ts +92 -0
  14. package/esm/lib/agent-tool.d.ts.map +1 -0
  15. package/esm/lib/agent-tool.js +211 -0
  16. package/esm/lib/agent-tool.js.map +1 -0
  17. package/esm/lib/async-params.d.ts +82 -0
  18. package/esm/lib/async-params.d.ts.map +1 -1
  19. package/esm/lib/async-params.js +5 -0
  20. package/esm/lib/async-params.js.map +1 -1
  21. package/esm/lib/async-tool-registry.d.ts +134 -0
  22. package/esm/lib/async-tool-registry.d.ts.map +1 -0
  23. package/esm/lib/async-tool-registry.js +352 -0
  24. package/esm/lib/async-tool-registry.js.map +1 -0
  25. package/esm/lib/async-tools.d.ts +23 -0
  26. package/esm/lib/async-tools.d.ts.map +1 -0
  27. package/esm/lib/async-tools.js +19 -0
  28. package/esm/lib/async-tools.js.map +1 -0
  29. package/esm/lib/conversation-state.d.ts +1 -0
  30. package/esm/lib/conversation-state.d.ts.map +1 -1
  31. package/esm/lib/conversation-state.js +8 -0
  32. package/esm/lib/conversation-state.js.map +1 -1
  33. package/esm/lib/doom-loop.d.ts +533 -0
  34. package/esm/lib/doom-loop.d.ts.map +1 -0
  35. package/esm/lib/doom-loop.js +1221 -0
  36. package/esm/lib/doom-loop.js.map +1 -0
  37. package/esm/lib/hooks-schemas.d.ts +33 -0
  38. package/esm/lib/hooks-schemas.d.ts.map +1 -1
  39. package/esm/lib/hooks-schemas.js +54 -0
  40. package/esm/lib/hooks-schemas.js.map +1 -1
  41. package/esm/lib/hooks-types.d.ts +6 -2
  42. package/esm/lib/hooks-types.d.ts.map +1 -1
  43. package/esm/lib/hooks-types.js.map +1 -1
  44. package/esm/lib/model-result.d.ts +646 -10
  45. package/esm/lib/model-result.d.ts.map +1 -1
  46. package/esm/lib/model-result.js +2749 -153
  47. package/esm/lib/model-result.js.map +1 -1
  48. package/esm/lib/reusable-stream.d.ts +9 -0
  49. package/esm/lib/reusable-stream.d.ts.map +1 -1
  50. package/esm/lib/reusable-stream.js +17 -0
  51. package/esm/lib/reusable-stream.js.map +1 -1
  52. package/esm/lib/stream-transformers.d.ts +20 -0
  53. package/esm/lib/stream-transformers.d.ts.map +1 -1
  54. package/esm/lib/stream-transformers.js +45 -0
  55. package/esm/lib/stream-transformers.js.map +1 -1
  56. package/esm/lib/tool-check.d.ts +76 -0
  57. package/esm/lib/tool-check.d.ts.map +1 -0
  58. package/esm/lib/tool-check.js +241 -0
  59. package/esm/lib/tool-check.js.map +1 -0
  60. package/esm/lib/tool-concurrency.d.ts +42 -0
  61. package/esm/lib/tool-concurrency.d.ts.map +1 -0
  62. package/esm/lib/tool-concurrency.js +80 -0
  63. package/esm/lib/tool-concurrency.js.map +1 -0
  64. package/esm/lib/tool-context.d.ts +45 -1
  65. package/esm/lib/tool-context.d.ts.map +1 -1
  66. package/esm/lib/tool-context.js +60 -1
  67. package/esm/lib/tool-context.js.map +1 -1
  68. package/esm/lib/tool-executor.d.ts +52 -9
  69. package/esm/lib/tool-executor.d.ts.map +1 -1
  70. package/esm/lib/tool-executor.js +253 -21
  71. package/esm/lib/tool-executor.js.map +1 -1
  72. package/esm/lib/tool-orchestrator.d.ts.map +1 -1
  73. package/esm/lib/tool-orchestrator.js +87 -50
  74. package/esm/lib/tool-orchestrator.js.map +1 -1
  75. package/esm/lib/tool-task.d.ts +157 -0
  76. package/esm/lib/tool-task.d.ts.map +1 -0
  77. package/esm/lib/tool-task.js +206 -0
  78. package/esm/lib/tool-task.js.map +1 -0
  79. package/esm/lib/tool-types.d.ts +473 -14
  80. package/esm/lib/tool-types.d.ts.map +1 -1
  81. package/esm/lib/tool-types.js +59 -10
  82. package/esm/lib/tool-types.js.map +1 -1
  83. package/esm/lib/tool.d.ts +184 -2
  84. package/esm/lib/tool.d.ts.map +1 -1
  85. package/esm/lib/tool.js +193 -3
  86. package/esm/lib/tool.js.map +1 -1
  87. package/package.json +30 -2
package/esm/index.js CHANGED
@@ -5,14 +5,18 @@ export { SDKHooks } from '@openrouter/sdk/hooks/hooks';
5
5
  // Message format compatibility helpers
6
6
  // High-level model calling
7
7
  export { callModel } from './inner-loop/call-model.js';
8
+ export { resumeToolResults, ToolTaskAlreadySettledError, } from './inner-loop/resume-tool-results.js';
9
+ export { AgentTranscriptSource } from './lib/agent-tool.js';
8
10
  export { fromClaudeMessages, toClaudeMessage } from './lib/anthropic-compat.js';
9
11
  export { hasAsyncFunctions, resolveAsyncFunctions } from './lib/async-params.js';
12
+ export { AsyncToolRegistry } from './lib/async-tool-registry.js';
10
13
  export { fromChatMessages, toChatMessage } from './lib/chat-compat.js';
11
14
  // Claude constants and type guards
12
15
  export { ClaudeContentBlockType, NonClaudeMessageRole } from './lib/claude-constants.js';
13
16
  export { isClaudeStyleMessages } from './lib/claude-type-guards.js';
14
17
  // Conversation state helpers
15
18
  export { appendToMessages, CONVERSATION_STATE_VERSION, createInitialState, createRejectedResult, createUnsentResult, deserializeConversationState, generateConversationId, InvalidStateError, partitionToolCalls, serializeConversationState, toolRequiresApproval, UnsupportedStateVersionError, updateState, } from './lib/conversation-state.js';
19
+ export { canonicalizeKeyMaterial, DEFAULT_DOOM_LOOP_LADDER, DEFAULT_MAX_ESCALATIONS, DoomLoopMonitor, detectTextRepetition, fingerprintKeyMaterial, fingerprintToolCall, MAX_CANONICALIZE_DEPTH, resolveDoomLoopOption, resolveLoopKeyMaterial, } from './lib/doom-loop.js';
16
20
  // Lifecycle hooks system (PreToolUse, PostToolUse, Stop, SessionStart, ...).
17
21
  // Distinct from the SDK transport hooks re-exported above (SDKHooks,
18
22
  // BeforeRequestHook, HookContext, ...), which intercept HTTP requests.
@@ -27,13 +31,16 @@ export { applyNextTurnParamsToRequest, buildNextTurnParamsContext, executeNextTu
27
31
  // Stop condition helpers
28
32
  export { finishReasonIs, hasToolCall, isStopConditionMet, maxCost, maxTokensUsed, stepCountIs, } from './lib/stop-conditions.js';
29
33
  export { extractUnsupportedContent, getUnsupportedContentSummary, hasUnsupportedContent, } from './lib/stream-transformers.js';
30
- // Tool creation helpers
34
+ // Tool creation helpers (tool also carries tool.background / tool.deferred)
31
35
  export { markMcp, serverTool, tool } from './lib/tool.js';
36
+ export { TASK_TOOL_NAME, TaskToolInputSchema } from './lib/tool-check.js';
37
+ export { acquireAll, Semaphore } from './lib/tool-concurrency.js';
32
38
  // Tool context helpers
33
39
  export { buildToolExecuteContext, ToolContextStore } from './lib/tool-context.js';
34
40
  // Real-time tool event broadcasting
35
41
  export { ToolEventBroadcaster } from './lib/tool-event-broadcaster.js';
36
- export { hasApprovalRequiredTools, hasExecuteFunction, isAutoResolvableTool, isClientTool, isGeneratorTool, isHITLTool, isManualTool, isMcpTool, isRegularExecuteTool, isServerTool, isToolCallOutputEvent, isToolPreliminaryResultEvent, isToolResultEvent, isTurnEndEvent, isTurnStartEvent, ToolType, toolHasApprovalConfigured, } from './lib/tool-types.js';
42
+ export { DEFAULT_TASK_LOG_LIMITS, ToolTask } from './lib/tool-task.js';
43
+ export { hasApprovalRequiredTools, hasExecuteFunction, isAgentTool, isAutoResolvableTool, isClientTool, isDeferredHandle, isGeneratorTool, isHITLTool, isLongRunningTool, isManualTool, isMcpTool, isRegularExecuteTool, isServerTool, isToolAsyncSettledEvent, isToolAsyncStartedEvent, isToolCallOutputEvent, isToolPreliminaryResultEvent, isToolResultEvent, isTurnEndEvent, isTurnStartEvent, isUnifiedTool, ToolType, toolHasApprovalConfigured, } from './lib/tool-types.js';
37
44
  // Turn context helpers
38
45
  export { buildTurnContext, normalizeInputToArray } from './lib/turn-context.js';
39
46
  export { OpenRouter } from './openrouter.js';
package/esm/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,iEAAiE;AAEjE,yDAAyD;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AA2EvD,uCAAuC;AAEvC,2BAA2B;AAC3B,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAMhF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACjF,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACvE,mCAAmC;AACnC,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,6BAA6B;AAC7B,OAAO,EACL,gBAAgB,EAChB,0BAA0B,EAC1B,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,4BAA4B,EAC5B,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,EAC1B,oBAAoB,EACpB,4BAA4B,EAC5B,WAAW,GACZ,MAAM,6BAA6B,CAAC;AACrC,6EAA6E;AAC7E,qEAAqE;AACrE,uEAAuE;AACvE,6EAA6E;AAC7E,4EAA4E;AAC5E,8BAA8B;AAC9B,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA8BtD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,EAAE,gCAAgC,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACtF,2BAA2B;AAC3B,OAAO,EACL,4BAA4B,EAC5B,0BAA0B,EAC1B,8BAA8B,GAC/B,MAAM,2BAA2B,CAAC;AACnC,yBAAyB;AACzB,OAAO,EACL,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,OAAO,EACP,aAAa,EACb,WAAW,GACZ,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,yBAAyB,EACzB,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,wBAAwB;AACxB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1D,uBAAuB;AACvB,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAClF,oCAAoC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAqDvE,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EACZ,eAAe,EACf,UAAU,EACV,YAAY,EACZ,SAAS,EACT,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,4BAA4B,EAC5B,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,QAAQ,EACR,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,uBAAuB;AACvB,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEhF,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"call-model.d.ts","sourceRoot":"","sources":["../../src/inner-loop/call-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,IAAI,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAErD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,wBAAgB,SAAS,CACvB,MAAM,SAAS,SAAS,IAAI,EAAE,EAC9B,aAAa,SAAS,UAAU,CAAC,SAAS,CAAC,GAAG,SAAS,GAAG,SAAS,EACnE,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,SAAS,UAAU,CAAC,SAAS,CAAC,GACjF,QAAQ,CAAC,aAAa,CAAC,GACvB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAEzB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACzC,mBAAmB,CAAC,EAAE,aAAa,CAAC;CACrC,EACD,OAAO,CAAC,EAAE,cAAc,GACvB,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAuF9B"}
1
+ {"version":3,"file":"call-model.d.ts","sourceRoot":"","sources":["../../src/inner-loop/call-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,IAAI,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGrD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,wBAAgB,SAAS,CACvB,MAAM,SAAS,SAAS,IAAI,EAAE,EAC9B,aAAa,SAAS,UAAU,CAAC,SAAS,CAAC,GAAG,SAAS,GAAG,SAAS,EACnE,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,SAAS,UAAU,CAAC,SAAS,CAAC,GACjF,QAAQ,CAAC,aAAa,CAAC,GACvB,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAEzB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACzC,mBAAmB,CAAC,EAAE,aAAa,CAAC;CACrC,EACD,OAAO,CAAC,EAAE,cAAc,GACvB,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CA8F9B"}
@@ -1,6 +1,7 @@
1
1
  import { resolveHooks } from '../lib/hooks-resolve.js';
2
2
  import { ModelResult } from '../lib/model-result.js';
3
- import { convertToolsToAPIFormat } from '../lib/tool-executor.js';
3
+ import { buildTaskToolApiDefinition, needsTaskTool } from '../lib/tool-check.js';
4
+ import { convertToolsToAPIFormat, convertZodToJsonSchema } from '../lib/tool-executor.js';
4
5
  /**
5
6
  * Get a response with multiple consumption patterns
6
7
  *
@@ -71,9 +72,18 @@ import { convertToolsToAPIFormat } from '../lib/tool-executor.js';
71
72
  */
72
73
  export function callModel(client, request, options) {
73
74
  // Destructure state management options along with tools and stopWhen
74
- const { tools, stopWhen, state, requireApproval, approveToolCalls, rejectToolCalls, context, sharedContextSchema, onTurnStart, onTurnEnd, allowFinalResponse, strictFinalResponse, hooks, ...apiRequest } = request;
75
+ const { tools, stopWhen, state, requireApproval, approveToolCalls, rejectToolCalls, context, sharedContextSchema, onTurnStart, onTurnEnd, allowFinalResponse, strictFinalResponse, hooks, doomLoop, signal, toolTimeoutMs, toolConcurrency, asyncTools, ...apiRequest } = request;
75
76
  // Convert tools to API format - no cast needed now that convertToolsToAPIFormat accepts readonly
76
77
  const apiTools = tools ? convertToolsToAPIFormat(tools) : undefined;
78
+ // Append the single universal `task` tool when any long-running tool is
79
+ // registered (and check-ins aren't disabled): ONE static wire definition
80
+ // for check/steer/result/cancel across every async tool — per-tool
81
+ // context cost stays constant regardless of the tool count. Appended
82
+ // here (not per-request in ModelResult) so `resolvedRequest.tools` stays
83
+ // stable across turns. Calls to it are engine-intercepted.
84
+ if (apiTools && tools && asyncTools?.checkins !== false && needsTaskTool(tools)) {
85
+ apiTools.push(buildTaskToolApiDefinition(convertZodToJsonSchema));
86
+ }
77
87
  // Build the request with converted tools
78
88
  // Note: async functions are resolved later in ModelResult.executeToolsIfNeeded()
79
89
  // The request can have async fields (functions) or sync fields, and the tools are converted to API format
@@ -91,48 +101,37 @@ export function callModel(client, request, options) {
91
101
  'x-openrouter-callmodel': 'true',
92
102
  },
93
103
  };
94
- return new ModelResult({
104
+ // Assemble engine options, then drop undefined entries so optional keys
105
+ // are ABSENT (not present-as-undefined) — same semantics as the previous
106
+ // per-key conditional spreads without a branch per option.
107
+ const engineOptions = {
95
108
  client,
96
109
  request: finalRequest,
97
110
  options: callModelOptions,
98
111
  tools,
99
- ...(stopWhen !== undefined && {
100
- stopWhen,
101
- }),
102
- // Pass state management options
103
- ...(state !== undefined && {
104
- state,
105
- }),
106
- ...(requireApproval !== undefined && {
107
- requireApproval,
108
- }),
109
- ...(approveToolCalls !== undefined && {
110
- approveToolCalls,
111
- }),
112
- ...(rejectToolCalls !== undefined && {
113
- rejectToolCalls,
114
- }),
115
- ...(context !== undefined && {
116
- context,
117
- }),
118
- ...(sharedContextSchema !== undefined && {
119
- sharedContextSchema,
120
- }),
121
- ...(onTurnStart !== undefined && {
122
- onTurnStart,
123
- }),
124
- ...(onTurnEnd !== undefined && {
125
- onTurnEnd,
126
- }),
127
- ...(allowFinalResponse !== undefined && {
128
- allowFinalResponse,
129
- }),
130
- ...(strictFinalResponse !== undefined && {
131
- strictFinalResponse,
132
- }),
133
- ...(hooks !== undefined && {
134
- hooks: resolveHooks(hooks),
135
- }),
136
- });
112
+ stopWhen,
113
+ state,
114
+ requireApproval,
115
+ approveToolCalls,
116
+ rejectToolCalls,
117
+ context,
118
+ sharedContextSchema,
119
+ onTurnStart,
120
+ onTurnEnd,
121
+ allowFinalResponse,
122
+ strictFinalResponse,
123
+ hooks: hooks !== undefined ? resolveHooks(hooks) : undefined,
124
+ doomLoop,
125
+ signal,
126
+ toolTimeoutMs,
127
+ toolConcurrency,
128
+ asyncTools,
129
+ };
130
+ for (const key of Object.keys(engineOptions)) {
131
+ if (engineOptions[key] === undefined) {
132
+ delete engineOptions[key];
133
+ }
134
+ }
135
+ return new ModelResult(engineOptions);
137
136
  }
138
137
  //# sourceMappingURL=call-model.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"call-model.js","sourceRoot":"","sources":["../../src/inner-loop/call-model.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAMlE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,MAAM,UAAU,SAAS,CAOvB,MAAsB,EACtB,OAEC,EACD,OAAwB;IAExB,qEAAqE;IACrE,MAAM,EACJ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,OAAO,EACP,mBAAmB,EACnB,WAAW,EACX,SAAS,EACT,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,EACL,GAAG,UAAU,EACd,GAAG,OAAO,CAAC;IAEZ,iGAAiG;IACjG,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEpE,yCAAyC;IACzC,iFAAiF;IACjF,0GAA0G;IAC1G,MAAM,YAAY,GAA4B;QAC5C,GAAG,UAAU;KACd,CAAC;IAEF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,YAAY,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC;IACnC,CAAC;IAED,oEAAoE;IACpE,MAAM,gBAAgB,GAAmB;QACvC,GAAG,OAAO;QACV,OAAO,EAAE;YACP,GAAG,MAAM,CAAC,WAAW,CACnB,IAAI,OAAO,CAAC,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,YAAY,EAAE,OAAO,IAAI,SAAS,CAAC,CAC7E;YACD,wBAAwB,EAAE,MAAM;SACjC;KACF,CAAC;IAEF,OAAO,IAAI,WAAW,CAAkB;QACtC,MAAM;QACN,OAAO,EAAE,YAAY;QACrB,OAAO,EAAE,gBAAgB;QACzB,KAAK;QACL,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI;YAC5B,QAAQ;SACT,CAAC;QACF,gCAAgC;QAChC,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI;YACzB,KAAK;SACN,CAAC;QACF,GAAG,CAAC,eAAe,KAAK,SAAS,IAAI;YACnC,eAAe;SAChB,CAAC;QACF,GAAG,CAAC,gBAAgB,KAAK,SAAS,IAAI;YACpC,gBAAgB;SACjB,CAAC;QACF,GAAG,CAAC,eAAe,KAAK,SAAS,IAAI;YACnC,eAAe;SAChB,CAAC;QACF,GAAG,CAAC,OAAO,KAAK,SAAS,IAAI;YAC3B,OAAO;SACR,CAAC;QACF,GAAG,CAAC,mBAAmB,KAAK,SAAS,IAAI;YACvC,mBAAmB;SACpB,CAAC;QACF,GAAG,CAAC,WAAW,KAAK,SAAS,IAAI;YAC/B,WAAW;SACZ,CAAC;QACF,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI;YAC7B,SAAS;SACV,CAAC;QACF,GAAG,CAAC,kBAAkB,KAAK,SAAS,IAAI;YACtC,kBAAkB;SACnB,CAAC;QACF,GAAG,CAAC,mBAAmB,KAAK,SAAS,IAAI;YACvC,mBAAmB;SACpB,CAAC;QACF,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI;YACzB,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC;SAC3B,CAAC;KACoC,CAAC,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"call-model.js","sourceRoot":"","sources":["../../src/inner-loop/call-model.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAM1F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,MAAM,UAAU,SAAS,CAOvB,MAAsB,EACtB,OAEC,EACD,OAAwB;IAExB,qEAAqE;IACrE,MAAM,EACJ,KAAK,EACL,QAAQ,EACR,KAAK,EACL,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,OAAO,EACP,mBAAmB,EACnB,WAAW,EACX,SAAS,EACT,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,EACL,QAAQ,EACR,MAAM,EACN,aAAa,EACb,eAAe,EACf,UAAU,EACV,GAAG,UAAU,EACd,GAAG,OAAO,CAAC;IAEZ,iGAAiG;IACjG,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEpE,wEAAwE;IACxE,yEAAyE;IACzE,mEAAmE;IACnE,qEAAqE;IACrE,yEAAyE;IACzE,2DAA2D;IAC3D,IAAI,QAAQ,IAAI,KAAK,IAAI,UAAU,EAAE,QAAQ,KAAK,KAAK,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAChF,QAAQ,CAAC,IAAI,CAAC,0BAA0B,CAAC,sBAAsB,CAAC,CAAC,CAAC;IACpE,CAAC;IAED,yCAAyC;IACzC,iFAAiF;IACjF,0GAA0G;IAC1G,MAAM,YAAY,GAA4B;QAC5C,GAAG,UAAU;KACd,CAAC;IAEF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,YAAY,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC;IACnC,CAAC;IAED,oEAAoE;IACpE,MAAM,gBAAgB,GAAmB;QACvC,GAAG,OAAO;QACV,OAAO,EAAE;YACP,GAAG,MAAM,CAAC,WAAW,CACnB,IAAI,OAAO,CAAC,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,YAAY,EAAE,OAAO,IAAI,SAAS,CAAC,CAC7E;YACD,wBAAwB,EAAE,MAAM;SACjC;KACF,CAAC;IAEF,wEAAwE;IACxE,yEAAyE;IACzE,2DAA2D;IAC3D,MAAM,aAAa,GAA4B;QAC7C,MAAM;QACN,OAAO,EAAE,YAAY;QACrB,OAAO,EAAE,gBAAgB;QACzB,KAAK;QACL,QAAQ;QACR,KAAK;QACL,eAAe;QACf,gBAAgB;QAChB,eAAe;QACf,OAAO;QACP,mBAAmB;QACnB,WAAW;QACX,SAAS;QACT,kBAAkB;QAClB,mBAAmB;QACnB,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;QAC5D,QAAQ;QACR,MAAM;QACN,aAAa;QACb,eAAe;QACf,UAAU;KACX,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;QAC7C,IAAI,aAAa,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YACrC,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,IAAI,WAAW,CACpB,aAA+D,CAChE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,128 @@
1
+ import type { OpenRouterCore } from '@openrouter/sdk/core';
2
+ import type { RequestOptions } from '@openrouter/sdk/lib/sdks';
3
+ import type * as models from '@openrouter/sdk/models';
4
+ import type { CallModelInput } from '../lib/async-params.js';
5
+ import type { ModelResult } from '../lib/model-result.js';
6
+ import type { StateAccessor, Tool } from '../lib/tool-types.js';
7
+ /**
8
+ * Thrown by {@link resumeToolResults} (and the deferred tool `.resolve()` /
9
+ * `.fail()` / `.cancel()` methods) when the target task was already settled —
10
+ * the at-most-once guard against double resolution and replayed webhooks.
11
+ * Opt out per call with `ifSettled: 'ignore'`.
12
+ */
13
+ export declare class ToolTaskAlreadySettledError extends Error {
14
+ readonly taskId: string;
15
+ readonly callId: string;
16
+ constructor(taskId: string, callId: string);
17
+ }
18
+ /**
19
+ * One task resolution: identified by `taskId` (typical — it's what the
20
+ * external system knows) or `callId`, carrying either a successful `output`
21
+ * or an `error`. `status` refines error deliveries (default `'failed'`).
22
+ */
23
+ export type ResumeToolResultEntry = ({
24
+ taskId: string;
25
+ callId?: never;
26
+ } | {
27
+ callId: string;
28
+ taskId?: never;
29
+ }) & ({
30
+ output: unknown;
31
+ error?: never;
32
+ } | {
33
+ error: string;
34
+ output?: never;
35
+ status?: 'failed' | 'cancelled' | 'expired';
36
+ });
37
+ /**
38
+ * Run configuration for continuing the conversation immediately after
39
+ * recording the results. Any `CallModelInput` field except `state` / `input`
40
+ * (the SDK supplies both — the resumed history already carries everything).
41
+ */
42
+ export type ResumeRunConfig<TTools extends readonly Tool[]> = Omit<CallModelInput<TTools>, 'state' | 'input' | 'approveToolCalls' | 'rejectToolCalls'>;
43
+ /**
44
+ * The `tool_task_result` envelope injected as a user-role message when an
45
+ * async tool's result arrives. Never a second `function_call_output` — the
46
+ * pending placeholder already paired the original `function_call` in
47
+ * history, and providers reject duplicate outputs for one callId.
48
+ */
49
+ export interface ToolTaskResultEnvelope {
50
+ type: 'tool_task_result';
51
+ tool: string;
52
+ taskId: string;
53
+ callId: string;
54
+ status: 'completed' | 'failed' | 'cancelled' | 'timed_out' | 'expired';
55
+ result?: unknown;
56
+ error?: string;
57
+ }
58
+ /** Build the user-role message carrying a task-result envelope. */
59
+ export declare function buildTaskResultMessage(envelope: ToolTaskResultEnvelope): models.BaseInputsUnion;
60
+ /**
61
+ * Deliver results for pending async tool tasks (started by `tool.deferred`,
62
+ * or background tasks orphaned across a run boundary) into a persisted
63
+ * conversation — typically from a different process than the one that
64
+ * started them (a webhook handler, a queue worker).
65
+ *
66
+ * This is the low-level, untyped entry point; prefer the typed `.resolve()`
67
+ * / `.fail()` / `.cancel()` methods on the deferred tool itself, which
68
+ * check `output` against the tool's `outputSchema` at compile time.
69
+ *
70
+ * Behavior:
71
+ * 1. Loads the conversation via `state`, resolves each entry to a pending
72
+ * task (by `taskId` or `callId`).
73
+ * 2. Enforces at-most-once settlement: an already-settled task throws
74
+ * {@link ToolTaskAlreadySettledError} (or is skipped with
75
+ * `ifSettled: 'ignore'`).
76
+ * 3. Validates each `output` against the named tool's `outputSchema` when
77
+ * the tool is present in `tools` — an invalid payload throws before
78
+ * anything is persisted or dispatched.
79
+ * 4. Appends a `tool_task_result` envelope (user-role message) per entry,
80
+ * marks the tasks settled, and persists the state. The conversation
81
+ * status becomes `'in_progress'` (or stays `'awaiting_async_tools'` when
82
+ * other tasks are still pending).
83
+ * 5. With `run` config: continues the conversation immediately and returns
84
+ * the `ModelResult`. Without: returns `null` — the recorded results ride
85
+ * along on the next `callModel({ state })`. When every entry was skipped
86
+ * under `ifSettled: 'ignore'` there is nothing new to deliver, so the
87
+ * function returns `null` WITHOUT running — a replayed webhook never
88
+ * triggers a duplicate continuation.
89
+ *
90
+ * SECURITY: this call injects a value the model will treat as a tool
91
+ * result. Authenticate the webhook/caller BEFORE invoking it — the SDK
92
+ * cannot do that for you. And PASS `tools`: without it the output is
93
+ * appended UNVALIDATED (no outputSchema to check against) — prefer the
94
+ * typed `.resolve()` methods, which always carry the tool's schema.
95
+ *
96
+ * CONCURRENCY: this is a read-modify-write over the StateAccessor. Two
97
+ * concurrent calls for the SAME conversation (e.g. two webhooks resolving
98
+ * two different tasks) can interleave load/save and the later save wins,
99
+ * silently dropping the earlier envelope. Serialize calls per conversation
100
+ * — a per-conversation lock or queue in your StateAccessor, or batch
101
+ * concurrent completions into one call via `results: [...]` (which settles
102
+ * any number of tasks atomically).
103
+ *
104
+ * HOOKS: PostToolUse / PostToolUseFailure do NOT fire here — hooks are
105
+ * run-scoped (a HooksManager passed to `callModel`), and this entry point
106
+ * runs outside any run, typically in another process. Deployments that
107
+ * audit tool outputs via hooks should scan webhook payloads in the webhook
108
+ * handler itself. In-process settlements (background tasks) DO fire these
109
+ * hooks at delivery.
110
+ */
111
+ export declare function resumeToolResults<TTools extends readonly Tool[]>(client: OpenRouterCore, request: {
112
+ state: StateAccessor<TTools>;
113
+ /** Tools list — used to validate outputs against each tool's outputSchema. */
114
+ tools?: TTools;
115
+ results: ReadonlyArray<ResumeToolResultEntry>;
116
+ /** Behavior when a task is already settled. Default 'throw'. */
117
+ ifSettled?: 'throw' | 'ignore';
118
+ /** Continue the conversation immediately with this run configuration. */
119
+ run?: ResumeRunConfig<TTools>;
120
+ /**
121
+ * Ownership guard: every resolved task must belong to this tool (by
122
+ * name) or the call throws BEFORE anything is persisted. Set by the
123
+ * typed `.resolve()`/`.fail()`/`.cancel()` methods — a taskId from a
124
+ * webhook cannot settle a different tool's task through them.
125
+ */
126
+ expectToolName?: string;
127
+ }, options?: RequestOptions): Promise<ModelResult<TTools> | null>;
128
+ //# sourceMappingURL=resume-tool-results.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume-tool-results.d.ts","sourceRoot":"","sources":["../../src/inner-loop/resume-tool-results.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,KAAK,MAAM,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAG1D,OAAO,KAAK,EAAoB,aAAa,EAAE,IAAI,EAAkB,MAAM,sBAAsB,CAAC;AAIlG;;;;;GAKG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAM3C;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAChC;IACE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,KAAK,CAAC;CAChB,GACD;IACE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,KAAK,CAAC;CAChB,CACJ,GACC,CACI;IACE,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,GACD;IACE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,MAAM,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;CAC7C,CACJ,CAAC;AAEJ;;;;GAIG;AACH,MAAM,MAAM,eAAe,CAAC,MAAM,SAAS,SAAS,IAAI,EAAE,IAAI,IAAI,CAChE,cAAc,CAAC,MAAM,CAAC,EACtB,OAAO,GAAG,OAAO,GAAG,kBAAkB,GAAG,iBAAiB,CAC3D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;IACvE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,mEAAmE;AACnE,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,CAAC,eAAe,CAK/F;AAqDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,SAAS,SAAS,IAAI,EAAE,EACpE,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE;IACP,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC7B,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IAC9C,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC/B,yEAAyE;IACzE,GAAG,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAC9B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,EACD,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAkHrC"}
@@ -0,0 +1,244 @@
1
+ import { appendToMessages, updateState } from '../lib/conversation-state.js';
2
+ import { validateToolOutput } from '../lib/tool-executor.js';
3
+ import { TASK_RESULT_BOUNDARY } from '../lib/tool-task.js';
4
+ import { isClientTool, isUnifiedTool } from '../lib/tool-types.js';
5
+ import { callModel } from './call-model.js';
6
+ /**
7
+ * Thrown by {@link resumeToolResults} (and the deferred tool `.resolve()` /
8
+ * `.fail()` / `.cancel()` methods) when the target task was already settled —
9
+ * the at-most-once guard against double resolution and replayed webhooks.
10
+ * Opt out per call with `ifSettled: 'ignore'`.
11
+ */
12
+ export class ToolTaskAlreadySettledError extends Error {
13
+ constructor(taskId, callId) {
14
+ super(`Tool task "${taskId}" (call ${callId}) has already been settled`);
15
+ this.name = 'ToolTaskAlreadySettledError';
16
+ this.taskId = taskId;
17
+ this.callId = callId;
18
+ }
19
+ }
20
+ /** Build the user-role message carrying a task-result envelope. */
21
+ export function buildTaskResultMessage(envelope) {
22
+ return {
23
+ role: 'user',
24
+ content: `${TASK_RESULT_BOUNDARY}\n${JSON.stringify(envelope)}`,
25
+ };
26
+ }
27
+ /**
28
+ * Find the pending task an incoming result belongs to, or `null` when the entry
29
+ * is already settled and the caller asked to ignore that.
30
+ *
31
+ * Settled tasks deliberately STAY in `pendingAsyncTools` with a terminal
32
+ * status, so a replayed webhook resolves to `ToolTaskAlreadySettledError`
33
+ * rather than "not found".
34
+ *
35
+ * @throws ToolTaskAlreadySettledError when the task already settled and
36
+ * `ifSettled` is `'throw'` (the default).
37
+ * @throws Error when no pending task matches the entry at all.
38
+ */
39
+ function resolvePendingTask({ entry, pending, settledIds, ifSettled, }) {
40
+ const task = pending.find((t) => 'taskId' in entry && entry.taskId !== undefined
41
+ ? t.taskId === entry.taskId
42
+ : t.callId === entry.callId);
43
+ if (!task) {
44
+ // The callId fallback covers states persisted by older SDK versions whose
45
+ // in-process delivery REMOVED the entry, leaving only the callId on
46
+ // settledAsyncCallIds.
47
+ if (entry.callId !== undefined && settledIds.has(entry.callId)) {
48
+ if (ifSettled === 'ignore') {
49
+ return null;
50
+ }
51
+ throw new ToolTaskAlreadySettledError(entry.taskId ?? entry.callId, entry.callId);
52
+ }
53
+ throw new Error(`resumeToolResults: no pending async tool task found for "${entry.taskId ?? entry.callId}"`);
54
+ }
55
+ if (settledIds.has(task.callId) || task.status !== 'working') {
56
+ if (ifSettled === 'ignore') {
57
+ return null;
58
+ }
59
+ throw new ToolTaskAlreadySettledError(task.taskId, task.callId);
60
+ }
61
+ return task;
62
+ }
63
+ /**
64
+ * Deliver results for pending async tool tasks (started by `tool.deferred`,
65
+ * or background tasks orphaned across a run boundary) into a persisted
66
+ * conversation — typically from a different process than the one that
67
+ * started them (a webhook handler, a queue worker).
68
+ *
69
+ * This is the low-level, untyped entry point; prefer the typed `.resolve()`
70
+ * / `.fail()` / `.cancel()` methods on the deferred tool itself, which
71
+ * check `output` against the tool's `outputSchema` at compile time.
72
+ *
73
+ * Behavior:
74
+ * 1. Loads the conversation via `state`, resolves each entry to a pending
75
+ * task (by `taskId` or `callId`).
76
+ * 2. Enforces at-most-once settlement: an already-settled task throws
77
+ * {@link ToolTaskAlreadySettledError} (or is skipped with
78
+ * `ifSettled: 'ignore'`).
79
+ * 3. Validates each `output` against the named tool's `outputSchema` when
80
+ * the tool is present in `tools` — an invalid payload throws before
81
+ * anything is persisted or dispatched.
82
+ * 4. Appends a `tool_task_result` envelope (user-role message) per entry,
83
+ * marks the tasks settled, and persists the state. The conversation
84
+ * status becomes `'in_progress'` (or stays `'awaiting_async_tools'` when
85
+ * other tasks are still pending).
86
+ * 5. With `run` config: continues the conversation immediately and returns
87
+ * the `ModelResult`. Without: returns `null` — the recorded results ride
88
+ * along on the next `callModel({ state })`. When every entry was skipped
89
+ * under `ifSettled: 'ignore'` there is nothing new to deliver, so the
90
+ * function returns `null` WITHOUT running — a replayed webhook never
91
+ * triggers a duplicate continuation.
92
+ *
93
+ * SECURITY: this call injects a value the model will treat as a tool
94
+ * result. Authenticate the webhook/caller BEFORE invoking it — the SDK
95
+ * cannot do that for you. And PASS `tools`: without it the output is
96
+ * appended UNVALIDATED (no outputSchema to check against) — prefer the
97
+ * typed `.resolve()` methods, which always carry the tool's schema.
98
+ *
99
+ * CONCURRENCY: this is a read-modify-write over the StateAccessor. Two
100
+ * concurrent calls for the SAME conversation (e.g. two webhooks resolving
101
+ * two different tasks) can interleave load/save and the later save wins,
102
+ * silently dropping the earlier envelope. Serialize calls per conversation
103
+ * — a per-conversation lock or queue in your StateAccessor, or batch
104
+ * concurrent completions into one call via `results: [...]` (which settles
105
+ * any number of tasks atomically).
106
+ *
107
+ * HOOKS: PostToolUse / PostToolUseFailure do NOT fire here — hooks are
108
+ * run-scoped (a HooksManager passed to `callModel`), and this entry point
109
+ * runs outside any run, typically in another process. Deployments that
110
+ * audit tool outputs via hooks should scan webhook payloads in the webhook
111
+ * handler itself. In-process settlements (background tasks) DO fire these
112
+ * hooks at delivery.
113
+ */
114
+ export async function resumeToolResults(client, request, options) {
115
+ const state = await request.state.load();
116
+ if (!state) {
117
+ throw new Error('resumeToolResults: no conversation state found for the given StateAccessor');
118
+ }
119
+ const pending = state.pendingAsyncTools ?? [];
120
+ const settledIds = new Set(state.settledAsyncCallIds ?? []);
121
+ const envelopes = [];
122
+ /** callId → the terminal lifecycle status persisted for that entry. */
123
+ const settledNow = new Map();
124
+ for (const entry of request.results) {
125
+ const task = resolvePendingTask({
126
+ entry,
127
+ pending,
128
+ settledIds,
129
+ ifSettled: request.ifSettled,
130
+ });
131
+ if (!task) {
132
+ continue; // already settled and the caller asked to ignore it
133
+ }
134
+ // Ownership guard: a taskId is an external identifier handed to
135
+ // third-party systems — a lower-trust webhook must not be able to
136
+ // settle a DIFFERENT tool's task through a tool-bound completion
137
+ // method (confused deputy; it would also skip that tool's
138
+ // outputSchema validation).
139
+ if (request.expectToolName !== undefined && task.name !== request.expectToolName) {
140
+ throw new Error(`resumeToolResults: task "${task.taskId}" belongs to tool "${task.name}", not "${request.expectToolName}" — use the owning tool's completion methods (or the untyped resumeToolResults entry point).`);
141
+ }
142
+ const envelope = buildResumeEnvelope(entry, task, request.tools);
143
+ envelopes.push(buildTaskResultMessage(envelope));
144
+ // Persist the entry's real terminal status. 'expired' / 'timed_out'
145
+ // have no ToolTaskStatus member — they persist as 'failed'.
146
+ settledNow.set(task.callId, envelope.status === 'completed' || envelope.status === 'cancelled'
147
+ ? envelope.status
148
+ : 'failed');
149
+ settledIds.add(task.callId);
150
+ }
151
+ if (envelopes.length === 0) {
152
+ // Every entry was already settled and ignored — nothing to record.
153
+ return null;
154
+ }
155
+ // Keep settled entries with a terminal status (rather than dropping them)
156
+ // so a replayed resolution surfaces as ToolTaskAlreadySettledError.
157
+ const nextPending = pending.map((t) => {
158
+ const terminalStatus = settledNow.get(t.callId);
159
+ return terminalStatus !== undefined
160
+ ? {
161
+ ...t,
162
+ status: terminalStatus,
163
+ }
164
+ : t;
165
+ });
166
+ // Status transition rules:
167
+ // - Orphaned (detached) tasks never settle — counting them as working
168
+ // would leave the conversation reporting 'awaiting_async_tools' forever.
169
+ // - Only advance a status this delivery OWNS. A conversation can hold a
170
+ // non-async pause (awaiting_approval / awaiting_hitl /
171
+ // awaiting_client_tools) while async tasks are outstanding; clobbering
172
+ // it to 'in_progress' would make the engine skip the decision-resume
173
+ // path and drop the caller's approvals.
174
+ // - A 'complete' conversation is only reopened when the caller asked to
175
+ // continue (`run` config). Record-only delivery on a finished
176
+ // conversation keeps it finished — the envelope rides along if the
177
+ // caller ever resumes it.
178
+ const stillWorking = nextPending.some((t) => t.status === 'working' && t.orphaned !== true);
179
+ const statusOwnedByAsyncDelivery = state.status === 'awaiting_async_tools' ||
180
+ state.status === 'in_progress' ||
181
+ (state.status === 'complete' && request.run !== undefined) ||
182
+ state.status === undefined;
183
+ const updated = updateState(state, {
184
+ messages: appendToMessages(state.messages, envelopes),
185
+ settledAsyncCallIds: [
186
+ ...(state.settledAsyncCallIds ?? []),
187
+ ...settledNow.keys(),
188
+ ],
189
+ ...(statusOwnedByAsyncDelivery && {
190
+ status: stillWorking ? 'awaiting_async_tools' : 'in_progress',
191
+ }),
192
+ pendingAsyncTools: nextPending,
193
+ });
194
+ await request.state.save(updated);
195
+ if (!request.run) {
196
+ return null;
197
+ }
198
+ // Continue the conversation: the envelopes are already in persisted
199
+ // history, so no fresh input is supplied.
200
+ return callModel(client, {
201
+ ...request.run,
202
+ input: [],
203
+ tools: request.run.tools ?? request.tools,
204
+ state: request.state,
205
+ }, options);
206
+ }
207
+ /**
208
+ * Build the `tool_task_result` envelope for one resume entry. Successful
209
+ * outputs are validated against the owning tool's `outputSchema` when the
210
+ * tool is available; error entries carry the caller's refined status
211
+ * (default `'failed'`).
212
+ */
213
+ function buildResumeEnvelope(entry, task, tools) {
214
+ if ('output' in entry && entry.error === undefined) {
215
+ const tool = tools?.find((t) => isClientTool(t) && t.function.name === task.name);
216
+ // Fail closed: when a tools list was supplied but the owning tool is
217
+ // not in it, the caller expected validation — appending the output
218
+ // unvalidated would silently void that expectation.
219
+ if (tools !== undefined && tool === undefined) {
220
+ throw new Error(`resumeToolResults: task "${task.taskId}" belongs to tool "${task.name}", which is not in the supplied tools list — its output cannot be validated. Include the tool, or omit \`tools\` to skip validation explicitly.`);
221
+ }
222
+ let output = entry.output;
223
+ if (tool && isUnifiedTool(tool) && tool.function.outputSchema !== undefined) {
224
+ output = validateToolOutput(tool.function.outputSchema, output);
225
+ }
226
+ return {
227
+ type: 'tool_task_result',
228
+ tool: task.name,
229
+ taskId: task.taskId,
230
+ callId: task.callId,
231
+ status: 'completed',
232
+ result: output,
233
+ };
234
+ }
235
+ return {
236
+ type: 'tool_task_result',
237
+ tool: task.name,
238
+ taskId: task.taskId,
239
+ callId: task.callId,
240
+ status: entry.status ?? 'failed',
241
+ error: entry.error ?? 'Task failed',
242
+ };
243
+ }
244
+ //# sourceMappingURL=resume-tool-results.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume-tool-results.js","sourceRoot":"","sources":["../../src/inner-loop/resume-tool-results.ts"],"names":[],"mappings":"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"}