@jangts/agent-core 0.3.5 → 0.5.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 (288) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +33 -31
  3. package/development/docs/README.en.md +2 -2
  4. package/development/docs/acceptance.en.md +2 -2
  5. package/development/docs/agent-development.en.md +1 -1
  6. package/development/docs/api/README.en.md +6 -2
  7. package/development/docs/api/README.md +4 -0
  8. package/development/docs/api/configuration-in-workflow.en.md +403 -0
  9. package/development/docs/api/configuration-in-workflow.md +403 -0
  10. package/development/docs/api/core.en.md +7 -7
  11. package/development/docs/api/core.md +6 -6
  12. package/development/docs/api/decorators.en.md +20 -0
  13. package/development/docs/api/decorators.md +20 -0
  14. package/development/docs/api/examples.en.md +1 -1
  15. package/development/docs/api/examples.md +1 -1
  16. package/development/docs/api/kits.en.md +22 -4
  17. package/development/docs/api/kits.md +22 -4
  18. package/development/docs/api/knowledge-answering.en.md +4 -1
  19. package/development/docs/api/knowledge-answering.md +4 -1
  20. package/development/docs/api/mock.en.md +2 -0
  21. package/development/docs/api/mock.md +2 -0
  22. package/development/docs/api/observability.en.md +37 -11
  23. package/development/docs/api/observability.md +64 -11
  24. package/development/docs/api/reference.en.md +876 -657
  25. package/development/docs/api/reference.md +662 -508
  26. package/development/docs/api/support.en.md +55 -9
  27. package/development/docs/api/support.md +53 -7
  28. package/development/docs/api/types.en.md +36 -1
  29. package/development/docs/api/types.md +44 -0
  30. package/development/docs/api/workflow-views.en.md +64 -0
  31. package/development/docs/api/workflow-views.md +78 -0
  32. package/development/docs/api/workflow.en.md +6 -2
  33. package/development/docs/api/workflow.md +6 -2
  34. package/development/docs/basic-usage.en.md +1 -1
  35. package/development/docs/basic-usage.md +1 -1
  36. package/development/docs/create-app.en.md +1 -1
  37. package/development/docs/examples/registration/associated.mjs +8 -0
  38. package/development/docs/examples/registration/independent.mjs +20 -0
  39. package/development/docs/examples/registration/protocol.mjs +48 -0
  40. package/development/docs/examples/registration/run.mjs +47 -0
  41. package/development/docs/examples/registration/tools.mjs +75 -0
  42. package/development/docs/examples/registration/workflow.mjs +109 -0
  43. package/development/docs/flows/knowledge-maintenance.en.md +1 -1
  44. package/development/docs/materials.en.md +1 -1
  45. package/development/docs/mechanisms.en.md +2 -2
  46. package/development/docs/mechanisms.md +1 -1
  47. package/development/docs/operations.en.md +1 -1
  48. package/development/docs/records.en.md +1 -1
  49. package/development/docs/registration.en.md +4 -4
  50. package/development/docs/registration.md +2 -2
  51. package/development/skills/agent-core-app/SKILL.md +16 -4
  52. package/development/skills/agent-core-app/templates/node-ts/README.md.tpl +1 -1
  53. package/development/skills/agent-core-app/templates/node-ts/src/catalog.ts.tpl +2 -2
  54. package/development/skills/agent-core-app/templates/node-ts/test/app.test.mjs.tpl +1 -1
  55. package/dist/cjs/capabilities/index.js +5 -12
  56. package/dist/cjs/capabilities/invocation.js +21 -1
  57. package/dist/cjs/capabilities/model.js +56 -12
  58. package/dist/cjs/capabilities/tool-conditions.js +65 -0
  59. package/dist/cjs/capabilities/tool-runtime.js +18 -4
  60. package/dist/cjs/capabilities/tools.js +12 -17
  61. package/dist/cjs/context/audit.js +5 -8
  62. package/dist/cjs/context/index.js +3 -4
  63. package/dist/cjs/diagnostics/index.js +51 -3
  64. package/dist/cjs/diagnostics/native-sub-flow.js +106 -0
  65. package/dist/cjs/diagnostics/sub-flow.js +278 -32
  66. package/dist/cjs/execution/engine.js +3 -2
  67. package/dist/cjs/execution/events.js +5 -5
  68. package/dist/cjs/execution/reporting.js +18 -28
  69. package/dist/cjs/execution/workflow-chain.js +211 -0
  70. package/dist/cjs/execution/workflow-graph.js +72 -0
  71. package/dist/cjs/execution/workflow.js +34 -6
  72. package/dist/cjs/index.js +21 -17
  73. package/dist/cjs/kits/aggregation.js +17 -14
  74. package/dist/cjs/kits/answering/checks.js +2 -1
  75. package/dist/cjs/kits/answering/flow.js +24 -21
  76. package/dist/cjs/kits/answering/tasks.js +30 -25
  77. package/dist/cjs/kits/delivery.js +36 -24
  78. package/dist/cjs/kits/domains/catalog.js +113 -0
  79. package/dist/cjs/kits/domains/index.js +20 -0
  80. package/dist/cjs/kits/{intents → domains}/registry.js +77 -31
  81. package/dist/cjs/kits/{intents → domains}/scopes.js +21 -21
  82. package/dist/cjs/kits/index.js +14 -13
  83. package/dist/cjs/kits/knowledge/catalog.js +33 -30
  84. package/dist/cjs/kits/policies.js +3 -2
  85. package/dist/cjs/kits/presentation/formats.js +110 -0
  86. package/dist/cjs/kits/presentation/index.js +10 -5
  87. package/dist/cjs/kits/presentation/outputs.js +2 -1
  88. package/dist/cjs/kits/reflection.js +2 -2
  89. package/dist/cjs/kits/retrieval.js +7 -1
  90. package/dist/cjs/kits/text-blocks.js +101 -0
  91. package/dist/cjs/kits/tools.js +247 -158
  92. package/dist/cjs/model/decision-retry.js +2 -1
  93. package/dist/cjs/model/index.js +2 -6
  94. package/dist/cjs/model/interactions/calls.js +61 -0
  95. package/dist/cjs/model/interactions/json-object.js +11 -11
  96. package/dist/cjs/model/interactions/native-functions.js +17 -23
  97. package/dist/cjs/model/message-diagnostics.js +13 -3
  98. package/dist/cjs/model/options.js +2 -0
  99. package/dist/cjs/model/preparation.js +39 -6
  100. package/dist/cjs/model/runner.js +34 -5
  101. package/dist/cjs/model/selection-transport.js +4 -4
  102. package/dist/cjs/model/token-budget.js +3 -0
  103. package/dist/cjs/model/transports.js +3 -2
  104. package/dist/cjs/structs/index.js +7 -6
  105. package/dist/cjs/structs/kits/domains/scopes.js +2 -0
  106. package/dist/cjs/structs/kits/presentation/formats.js +2 -0
  107. package/dist/cjs/structs/kits/text-blocks.js +11 -0
  108. package/dist/cjs/structs/model/session.js +11 -0
  109. package/dist/cjs/structs/support/associations.js +2 -0
  110. package/dist/cjs/support/associations.js +79 -0
  111. package/dist/cjs/support/context/nodes.js +48 -45
  112. package/dist/cjs/support/context/processing.js +6 -3
  113. package/dist/cjs/support/context/snapshot.js +11 -1
  114. package/dist/cjs/support/decorators/index.js +45 -3
  115. package/dist/cjs/support/index.js +1 -0
  116. package/dist/cjs/support/model/capabilities.js +35 -5
  117. package/dist/cjs/support/model/chat-completions.js +11 -2
  118. package/dist/cjs/support/model/completion/calls.js +10 -56
  119. package/dist/cjs/support/model/preview-rules.js +11 -11
  120. package/dist/cjs/support/model/reply-preview.js +3 -3
  121. package/dist/cjs/support/model/session.js +152 -0
  122. package/dist/cjs/support/model/stage-transport.js +8 -8
  123. package/dist/cjs/support/model/transports.js +5 -2
  124. package/dist/cjs/support/tools.js +42 -17
  125. package/dist/cjs/utils/invocation.js +1 -0
  126. package/dist/esm/capabilities/index.d.ts +5 -10
  127. package/dist/esm/capabilities/index.js +5 -9
  128. package/dist/esm/capabilities/invocation.js +21 -1
  129. package/dist/esm/capabilities/model.d.ts +17 -13
  130. package/dist/esm/capabilities/model.js +54 -10
  131. package/dist/esm/capabilities/tool-conditions.d.ts +9 -0
  132. package/dist/esm/capabilities/tool-conditions.js +59 -0
  133. package/dist/esm/capabilities/tool-runtime.js +18 -4
  134. package/dist/esm/capabilities/tools.d.ts +3 -3
  135. package/dist/esm/capabilities/tools.js +12 -17
  136. package/dist/esm/context/audit.js +5 -8
  137. package/dist/esm/context/index.d.ts +3 -4
  138. package/dist/esm/context/index.js +3 -4
  139. package/dist/esm/diagnostics/index.d.ts +1 -0
  140. package/dist/esm/diagnostics/index.js +48 -2
  141. package/dist/esm/diagnostics/native-sub-flow.d.ts +94 -0
  142. package/dist/esm/diagnostics/native-sub-flow.js +101 -0
  143. package/dist/esm/diagnostics/sub-flow.d.ts +56 -4
  144. package/dist/esm/diagnostics/sub-flow.js +271 -32
  145. package/dist/esm/execution/engine.d.ts +1 -0
  146. package/dist/esm/execution/engine.js +3 -2
  147. package/dist/esm/execution/events.js +5 -5
  148. package/dist/esm/execution/reporting.d.ts +6 -12
  149. package/dist/esm/execution/reporting.js +19 -29
  150. package/dist/esm/execution/workflow-chain.d.ts +3 -0
  151. package/dist/esm/execution/workflow-chain.js +208 -0
  152. package/dist/esm/execution/workflow-graph.d.ts +7 -0
  153. package/dist/esm/execution/workflow-graph.js +68 -0
  154. package/dist/esm/execution/workflow.d.ts +6 -0
  155. package/dist/esm/execution/workflow.js +34 -6
  156. package/dist/esm/index.d.ts +19 -13
  157. package/dist/esm/index.js +18 -15
  158. package/dist/esm/kits/aggregation.d.ts +1 -1
  159. package/dist/esm/kits/aggregation.js +17 -14
  160. package/dist/esm/kits/answering/checks.js +2 -1
  161. package/dist/esm/kits/answering/flow.js +24 -21
  162. package/dist/esm/kits/answering/tasks.d.ts +6 -6
  163. package/dist/esm/kits/answering/tasks.js +30 -25
  164. package/dist/esm/kits/delivery.d.ts +0 -9
  165. package/dist/esm/kits/delivery.js +36 -24
  166. package/dist/esm/kits/domains/catalog.d.ts +42 -0
  167. package/dist/esm/kits/domains/catalog.js +110 -0
  168. package/dist/esm/kits/domains/index.d.ts +4 -0
  169. package/dist/esm/kits/domains/index.js +4 -0
  170. package/dist/esm/kits/domains/registry.d.ts +100 -0
  171. package/dist/esm/kits/{intents → domains}/registry.js +77 -31
  172. package/dist/esm/kits/{intents → domains}/scopes.d.ts +18 -18
  173. package/dist/esm/kits/{intents → domains}/scopes.js +21 -21
  174. package/dist/esm/kits/index.d.ts +6 -5
  175. package/dist/esm/kits/index.js +6 -5
  176. package/dist/esm/kits/knowledge/catalog.js +33 -30
  177. package/dist/esm/kits/policies.d.ts +1 -1
  178. package/dist/esm/kits/policies.js +3 -2
  179. package/dist/esm/kits/presentation/formats.d.ts +42 -0
  180. package/dist/esm/kits/presentation/formats.js +105 -0
  181. package/dist/esm/kits/presentation/index.d.ts +14 -5
  182. package/dist/esm/kits/presentation/index.js +10 -5
  183. package/dist/esm/kits/presentation/outputs.js +2 -1
  184. package/dist/esm/kits/reflection.js +3 -3
  185. package/dist/esm/kits/retrieval.js +7 -1
  186. package/dist/esm/kits/text-blocks.d.ts +24 -0
  187. package/dist/esm/kits/text-blocks.js +97 -0
  188. package/dist/esm/kits/tools.d.ts +69 -52
  189. package/dist/esm/kits/tools.js +247 -158
  190. package/dist/esm/model/decision-retry.d.ts +2 -0
  191. package/dist/esm/model/decision-retry.js +2 -1
  192. package/dist/esm/model/index.d.ts +2 -6
  193. package/dist/esm/model/index.js +2 -6
  194. package/dist/esm/model/interactions/calls.d.ts +40 -0
  195. package/dist/esm/model/interactions/calls.js +58 -0
  196. package/dist/esm/model/interactions/json-object.js +11 -11
  197. package/dist/esm/model/interactions/native-functions.js +17 -23
  198. package/dist/esm/model/message-diagnostics.d.ts +6 -5
  199. package/dist/esm/model/message-diagnostics.js +13 -3
  200. package/dist/esm/model/options.js +2 -0
  201. package/dist/esm/model/preparation.js +39 -6
  202. package/dist/esm/model/runner.d.ts +2 -0
  203. package/dist/esm/model/runner.js +34 -5
  204. package/dist/esm/model/selection-transport.js +4 -4
  205. package/dist/esm/model/token-budget.js +3 -0
  206. package/dist/esm/model/transports.d.ts +2 -1
  207. package/dist/esm/model/transports.js +3 -2
  208. package/dist/esm/structs/capabilities/invocation.d.ts +3 -1
  209. package/dist/esm/structs/capabilities/model.d.ts +42 -11
  210. package/dist/esm/structs/capabilities/tools.d.ts +16 -7
  211. package/dist/esm/structs/common.d.ts +3 -1
  212. package/dist/esm/structs/debug.d.ts +36 -20
  213. package/dist/esm/structs/execution/workflow-views.d.ts +123 -0
  214. package/dist/esm/structs/execution/workflow.d.ts +4 -3
  215. package/dist/esm/structs/index.d.ts +8 -6
  216. package/dist/esm/structs/index.js +7 -6
  217. package/dist/esm/structs/kits/answering/tasks.d.ts +2 -2
  218. package/dist/esm/structs/kits/{intents → domains}/catalog.d.ts +20 -20
  219. package/dist/esm/structs/kits/{intents → domains}/registry.d.ts +24 -21
  220. package/dist/esm/structs/kits/{intents → domains}/scopes.d.ts +9 -9
  221. package/dist/esm/structs/kits/domains/scopes.js +1 -0
  222. package/dist/esm/structs/kits/presentation/formats.d.ts +42 -0
  223. package/dist/esm/structs/kits/presentation/formats.js +1 -0
  224. package/dist/esm/structs/kits/text-blocks.d.ts +25 -0
  225. package/dist/esm/structs/kits/text-blocks.js +10 -0
  226. package/dist/esm/structs/kits/tools.d.ts +82 -83
  227. package/dist/esm/structs/model/interactions.d.ts +18 -11
  228. package/dist/esm/structs/model/messages.d.ts +34 -2
  229. package/dist/esm/structs/model/runtime.d.ts +9 -0
  230. package/dist/esm/structs/model/session.d.ts +29 -0
  231. package/dist/esm/structs/model/session.js +10 -0
  232. package/dist/esm/structs/support/associations.d.ts +22 -0
  233. package/dist/esm/structs/support/associations.js +1 -0
  234. package/dist/esm/structs/support/model/interactions.d.ts +2 -2
  235. package/dist/esm/structs/support/model/messages.d.ts +6 -13
  236. package/dist/esm/structs/support/tools.d.ts +23 -23
  237. package/dist/esm/support/associations.d.ts +26 -0
  238. package/dist/esm/support/associations.js +76 -0
  239. package/dist/esm/support/context/nodes.js +48 -45
  240. package/dist/esm/support/context/processing.js +6 -3
  241. package/dist/esm/support/context/snapshot.js +11 -1
  242. package/dist/esm/support/decorators/index.d.ts +13 -2
  243. package/dist/esm/support/decorators/index.js +40 -3
  244. package/dist/esm/support/decorators/pool.d.ts +6 -5
  245. package/dist/esm/support/decorators/types.d.ts +3 -1
  246. package/dist/esm/support/index.d.ts +1 -0
  247. package/dist/esm/support/index.js +1 -0
  248. package/dist/esm/support/model/capabilities.d.ts +8 -3
  249. package/dist/esm/support/model/capabilities.js +35 -5
  250. package/dist/esm/support/model/chat-completions.js +11 -2
  251. package/dist/esm/support/model/completion/calls.d.ts +6 -34
  252. package/dist/esm/support/model/completion/calls.js +8 -55
  253. package/dist/esm/support/model/index.d.ts +1 -1
  254. package/dist/esm/support/model/preview-rules.d.ts +13 -8
  255. package/dist/esm/support/model/preview-rules.js +11 -11
  256. package/dist/esm/support/model/reply-preview.d.ts +2 -2
  257. package/dist/esm/support/model/reply-preview.js +3 -3
  258. package/dist/esm/support/model/session.d.ts +61 -0
  259. package/dist/esm/support/model/session.js +148 -0
  260. package/dist/esm/support/model/stage-transport.d.ts +1 -1
  261. package/dist/esm/support/model/stage-transport.js +9 -9
  262. package/dist/esm/support/model/transports.d.ts +2 -0
  263. package/dist/esm/support/model/transports.js +2 -1
  264. package/dist/esm/support/tools.d.ts +11 -3
  265. package/dist/esm/support/tools.js +43 -18
  266. package/dist/esm/utils/catalog.d.ts +1 -1
  267. package/dist/esm/utils/invocation.d.ts +1 -0
  268. package/dist/esm/utils/invocation.js +1 -0
  269. package/package.json +20 -6
  270. package/dist/cjs/execution/index.js +0 -30
  271. package/dist/cjs/kits/intents/catalog.js +0 -82
  272. package/dist/cjs/kits/intents/index.js +0 -35
  273. package/dist/cjs/utils/debug.js +0 -30
  274. package/dist/esm/execution/index.d.ts +0 -11
  275. package/dist/esm/execution/index.js +0 -11
  276. package/dist/esm/kits/intents/catalog.d.ts +0 -39
  277. package/dist/esm/kits/intents/catalog.js +0 -79
  278. package/dist/esm/kits/intents/index.d.ts +0 -22
  279. package/dist/esm/kits/intents/index.js +0 -18
  280. package/dist/esm/kits/intents/registry.d.ts +0 -88
  281. package/dist/esm/utils/debug.d.ts +0 -27
  282. package/dist/esm/utils/debug.js +0 -27
  283. /package/dist/cjs/structs/{kits/intents/catalog.js → execution/workflow-views.js} +0 -0
  284. /package/dist/cjs/structs/kits/{intents/registry.js → domains/catalog.js} +0 -0
  285. /package/dist/cjs/structs/kits/{intents/scopes.js → domains/registry.js} +0 -0
  286. /package/dist/esm/structs/{kits/intents/catalog.js → execution/workflow-views.js} +0 -0
  287. /package/dist/esm/structs/kits/{intents/registry.js → domains/catalog.js} +0 -0
  288. /package/dist/esm/structs/kits/{intents/scopes.js → domains/registry.js} +0 -0
@@ -9,6 +9,8 @@
9
9
  * @throws The original error when it is not a multi-call rejection, or the fallback error.
10
10
  */
11
11
  export declare function retryMultipleModelCalls<Value>(request: {
12
+ readonly logger?: import('../structs/debug.js').SubFlowLogger;
13
+ readonly signal?: AbortSignal;
12
14
  readonly invoke: () => Promise<Value>;
13
15
  readonly retry: (error: unknown) => Promise<Value>;
14
16
  }): Promise<Value>;
@@ -7,6 +7,7 @@
7
7
  *
8
8
  * Exports: `retryMultipleModelCalls`.
9
9
  */
10
+ import { nativeSubFlowAsync } from '../diagnostics/native-sub-flow.js';
10
11
  import { getErrorInfo } from '../errors.js';
11
12
  /**
12
13
  * Retry once when the failure is a rejected multi-call proposal.
@@ -25,6 +26,6 @@ export async function retryMultipleModelCalls(request) {
25
26
  catch (error) {
26
27
  if (!getErrorInfo(error).codes.includes('MODEL_MULTIPLE_CALLS'))
27
28
  throw error;
28
- return request.retry(error);
29
+ return nativeSubFlowAsync(request, 'retry', 'MODEL_MULTIPLE_CALLS', () => request.retry(error));
29
30
  }
30
31
  }
@@ -1,13 +1,9 @@
1
1
  /**
2
2
  * Public surface of the model protocol layer.
3
3
  *
4
- * Re-exports the runner, the provider and transport contracts, the interaction
5
- * adapters and the defaults. Everything that interprets model output passes through
6
- * this module, so consumers should not import the individual files.
4
+ * Re-exports the runner and the provider/transport contracts. Support implementations are
5
+ * exported from the package root or `/support`; this Core barrel stays free of Support code.
7
6
  */
8
7
  export * from './runner.js';
9
8
  export * from '../structs/model/chat-provider.js';
10
- export * from '../support/model/compatible-provider.js';
11
- export * from '../support/model/transports.js';
12
- export * from '../support/model/interactions/index.js';
13
9
  export { modelRunnerDefaults } from './defaults.js';
@@ -1,13 +1,9 @@
1
1
  /**
2
2
  * Public surface of the model protocol layer.
3
3
  *
4
- * Re-exports the runner, the provider and transport contracts, the interaction
5
- * adapters and the defaults. Everything that interprets model output passes through
6
- * this module, so consumers should not import the individual files.
4
+ * Re-exports the runner and the provider/transport contracts. Support implementations are
5
+ * exported from the package root or `/support`; this Core barrel stays free of Support code.
7
6
  */
8
7
  export * from './runner.js';
9
8
  export * from '../structs/model/chat-provider.js';
10
- export * from '../support/model/compatible-provider.js';
11
- export * from '../support/model/transports.js';
12
- export * from '../support/model/interactions/index.js';
13
9
  export { modelRunnerDefaults } from './defaults.js';
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Tool-call delta aggregation.
3
+ *
4
+ * One algorithm is shared by completion policies, session transports and direct callers:
5
+ * fragments are keyed by their provider index, IDs may not conflict, and a complete call
6
+ * requires both an ID and a name. A complete call never hides another started but incomplete one.
7
+ *
8
+ * Exports: `AggregatedToolCall`, `ToolCallCollector`, `ToolCallFailure` and
9
+ * `createToolCallCollector`.
10
+ */
11
+ import type { ModelPart } from '../../structs/model/messages.js';
12
+ /** One completed native tool call in provider index order. */
13
+ export interface AggregatedToolCall {
14
+ readonly index: number;
15
+ readonly id: string;
16
+ readonly name: string;
17
+ readonly arguments: string;
18
+ }
19
+ /** Report a protocol violation and stop; callers decide how the failure is classified. */
20
+ export type ToolCallFailure = (message: string) => never;
21
+ /** Aggregation entry point for streamed function-call deltas. */
22
+ export interface ToolCallCollector {
23
+ /** Feed one part; text parts are ignored and cannot satisfy or break a call. */
24
+ push(part: ModelPart): void;
25
+ /** Calls started so far, including incomplete ones, in index order. */
26
+ readonly calls: readonly AggregatedToolCall[];
27
+ /**
28
+ * Finalize the calls.
29
+ *
30
+ * @throws When any started call is still missing its ID or name.
31
+ */
32
+ complete(): readonly AggregatedToolCall[];
33
+ }
34
+ /**
35
+ * Create a collector for one response.
36
+ *
37
+ * @param fail - Protocol failure factory; defaults to the Core model protocol error.
38
+ * @returns A collector that keeps its own state and never shares it with another response.
39
+ */
40
+ export declare function createToolCallCollector(fail?: ToolCallFailure): ToolCallCollector;
@@ -0,0 +1,58 @@
1
+ import { modelProtocolError } from '../../utils/model-protocol.js';
2
+ /**
3
+ * Create a collector for one response.
4
+ *
5
+ * @param fail - Protocol failure factory; defaults to the Core model protocol error.
6
+ * @returns A collector that keeps its own state and never shares it with another response.
7
+ */
8
+ export function createToolCallCollector(fail = modelProtocolError) {
9
+ const pending = new Map();
10
+ const ids = new Set();
11
+ const calls = () => [...pending.entries()]
12
+ .sort(([left], [right]) => left - right)
13
+ .map(([index, call]) => ({ index, ...call }));
14
+ return {
15
+ push(part) {
16
+ if (typeof part === 'string')
17
+ return;
18
+ if (!part || typeof part !== 'object' || Array.isArray(part))
19
+ fail('Expected a native model call');
20
+ const { type, index, id, name, arguments: args } = part;
21
+ if (type !== 'function-call-delta')
22
+ fail('Expected a native model call');
23
+ if (!Number.isInteger(index) || index < 0)
24
+ fail('Invalid model call index');
25
+ if ((id !== undefined && (typeof id !== 'string' || !id)) ||
26
+ (name !== undefined && typeof name !== 'string') ||
27
+ (args !== undefined && typeof args !== 'string'))
28
+ fail('Invalid model call fields');
29
+ let call = pending.get(index);
30
+ if (!call) {
31
+ call = { id: '', name: '', arguments: '' };
32
+ pending.set(index, call);
33
+ }
34
+ if (id !== undefined) {
35
+ if (call.id && call.id !== id)
36
+ fail('Conflicting model call IDs');
37
+ if (!call.id) {
38
+ if (ids.has(id))
39
+ fail('Duplicate model call ID');
40
+ ids.add(id);
41
+ call.id = id;
42
+ }
43
+ }
44
+ call.name += name ?? '';
45
+ call.arguments += args ?? '';
46
+ },
47
+ get calls() {
48
+ return calls();
49
+ },
50
+ complete() {
51
+ const completed = calls();
52
+ for (const call of completed)
53
+ if (!call.id || !call.name)
54
+ fail('Incomplete model call');
55
+ return completed;
56
+ },
57
+ };
58
+ }
@@ -22,24 +22,24 @@ export const jsonObjectInteraction = Object.freeze({
22
22
  /**
23
23
  * Build the instruction and response collector for one stage.
24
24
  *
25
- * @param capabilities - Capabilities registered for this stage; must be non-empty.
25
+ * @param tools - Capabilities registered for this stage; must be non-empty.
26
26
  * @returns JSON mode options, the measured instruction and the collector factory.
27
27
  */
28
- prepare(capabilities) {
29
- const declarations = capabilities.map(({ id, description, inputSchema }) => ({
28
+ prepare(tools) {
29
+ const declarations = tools.map(({ id, description, inputSchema }) => ({
30
30
  id,
31
31
  description,
32
32
  input: inputSchema.jsonSchema,
33
33
  }));
34
34
  // Keep the wire shape after potentially long schemas so it cannot be mistaken
35
35
  // for a schema definition or a bare capability argument object.
36
- const instruction = 'Registered capabilities: ' +
36
+ const instruction = 'Registered tools: ' +
37
37
  JSON.stringify(declarations) +
38
- '\nReturn exactly one JSON object with exactly these two fields: {"capability":"registered id","input":{...}}. ' +
39
- 'Choose capability from ' +
40
- JSON.stringify(capabilities.map((capability) => capability.id)) +
41
- "; input is the chosen capability's actual argument object matching its input schema. " +
42
- 'Do not return the schema, response_format, an array, multiple calls, prose or fences. The outer object must contain capability and input.';
38
+ '\nReturn exactly one JSON object with exactly these two fields: {"name":"registered id","input":{...}}. ' +
39
+ 'Choose tool name from ' +
40
+ JSON.stringify(tools.map((capability) => capability.id)) +
41
+ "; input is the chosen tool's actual argument object matching its input schema. " +
42
+ 'Do not return the schema, response_format, an array, multiple calls, prose or fences. The outer object must contain name and input.';
43
43
  return {
44
44
  options: { json: true },
45
45
  schemaChars: instruction.length,
@@ -58,10 +58,10 @@ export const jsonObjectInteraction = Object.freeze({
58
58
  finish() {
59
59
  const value = parse(raw);
60
60
  if (Object.keys(value).length !== 2 ||
61
- typeof value.capability !== 'string' ||
61
+ typeof value.name !== 'string' ||
62
62
  !Object.hasOwn(value, 'input'))
63
63
  modelProtocolError('Invalid model selection envelope');
64
- return { capabilityId: value.capability, input: object(value.input) };
64
+ return { toolId: value.name, input: object(value.input) };
65
65
  },
66
66
  };
67
67
  },
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import { modelProtocolError } from '../../utils/model-protocol.js';
11
11
  import { parse, wireName } from './arguments.js';
12
+ import { createToolCallCollector } from './calls.js';
12
13
  /**
13
14
  * Adapter that decodes a streamed native function call.
14
15
  *
@@ -24,15 +25,15 @@ export const nativeFunctionInteraction = Object.freeze({
24
25
  * Wire names are derived from capability ids and must stay unique after mapping;
25
26
  * a collision would make it impossible to resolve the proposed name back to an id.
26
27
  *
27
- * @param capabilities - Capabilities registered for this stage; must be non-empty.
28
+ * @param tools - Capabilities registered for this stage; must be non-empty.
28
29
  * @returns Function declarations, the measured instruction and the collector factory.
29
30
  * @throws TypeError When two capability ids map to the same wire name.
30
31
  */
31
- prepare(capabilities) {
32
- const names = new Map(capabilities.map((capability) => [wireName(capability.id), capability.id]));
33
- if (names.size !== capabilities.length)
32
+ prepare(tools) {
33
+ const names = new Map(tools.map((capability) => [wireName(capability.id), capability.id]));
34
+ if (names.size !== tools.length)
34
35
  throw new TypeError('Model wire name collision');
35
- const functions = capabilities.map((capability) => ({
36
+ const functions = tools.map((capability) => ({
36
37
  name: wireName(capability.id),
37
38
  description: capability.description,
38
39
  parameters: capability.inputSchema.jsonSchema,
@@ -44,11 +45,12 @@ export const nativeFunctionInteraction = Object.freeze({
44
45
  schemaChars: JSON.stringify(functions).length + instruction.length,
45
46
  instruction,
46
47
  createResponse() {
48
+ const collector = createToolCallCollector();
47
49
  let raw = '';
48
- let call;
49
50
  return {
50
51
  get outputChars() {
51
- return (raw.length + (call ? call.id.length + call.name.length + call.arguments.length : 0));
52
+ return (raw.length +
53
+ collector.calls.reduce((sum, call) => sum + call.id.length + call.name.length + call.arguments.length, 0));
52
54
  },
53
55
  push(part) {
54
56
  if (typeof part === 'string') {
@@ -57,32 +59,24 @@ export const nativeFunctionInteraction = Object.freeze({
57
59
  }
58
60
  if (!part || typeof part !== 'object' || Array.isArray(part))
59
61
  modelProtocolError('Expected a native model call');
60
- const { type, index, id, name, arguments: args } = part;
62
+ const { type, index } = part;
61
63
  if (type !== 'function-call-delta')
62
64
  modelProtocolError('Expected a native model call');
63
65
  if (index !== 0)
64
66
  modelProtocolError('Expected exactly one model call', 'MODEL_MULTIPLE_CALLS');
65
- if ((id !== undefined && (typeof id !== 'string' || !id)) ||
66
- (name !== undefined && typeof name !== 'string') ||
67
- (args !== undefined && typeof args !== 'string'))
68
- modelProtocolError('Invalid native model call fields');
69
- call ??= { id: '', name: '', arguments: '' };
70
- if (id !== undefined) {
71
- if (call.id && call.id !== id)
72
- modelProtocolError('Conflicting model call IDs');
73
- call.id = id;
74
- }
75
- call.name += name ?? '';
76
- call.arguments += args ?? '';
67
+ collector.push(part);
77
68
  },
78
69
  finish() {
79
- if (!call?.id || !call.name || raw.trim())
70
+ const calls = collector.complete();
71
+ if (calls.length !== 1)
72
+ modelProtocolError('Expected exactly one model call', 'MODEL_MULTIPLE_CALLS');
73
+ if (raw.trim())
80
74
  modelProtocolError('Expected one complete model call without prose');
81
- const completed = call;
75
+ const completed = calls[0];
82
76
  const capability = names.get(completed.name) ??
83
77
  modelProtocolError('Model call is not registered for this stage');
84
78
  return {
85
- capabilityId: capability,
79
+ toolId: capability,
86
80
  input: parse(completed.arguments),
87
81
  call: {
88
82
  id: completed.id,
@@ -1,19 +1,20 @@
1
1
  /**
2
2
  * Redaction of model messages before they are observed or persisted.
3
3
  *
4
- * Diagnostics may keep message text, but image bytes must never leave the process,
5
- * even when full content logging is enabled.
4
+ * Diagnostics may keep message text, but image bytes and raw tool arguments must never leave
5
+ * the process, even when full content logging is enabled.
6
6
  *
7
7
  * Exports: `modelMessageDiagnostics`.
8
8
  */
9
9
  import type { ModelMessage } from '../structs/model/messages.js';
10
10
  /**
11
- * Replace every image payload with a placeholder.
11
+ * Replace every image payload and raw tool argument with a placeholder.
12
12
  *
13
13
  * Only the `url` is substituted; the `detail` hint is kept because it is small and
14
- * explains how the image was interpreted. The input array is not mutated.
14
+ * explains how the image was interpreted. Tool call ids and names remain because they
15
+ * identify the proposal; raw arguments are redacted. The input array is not mutated.
15
16
  *
16
17
  * @param messages - Messages about to be reported.
17
- * @returns New message objects whose images carry a placeholder URL.
18
+ * @returns New message objects with redacted images and tool arguments.
18
19
  */
19
20
  export declare function modelMessageDiagnostics(messages: readonly ModelMessage[]): ModelMessage[];
@@ -1,11 +1,12 @@
1
1
  /**
2
- * Replace every image payload with a placeholder.
2
+ * Replace every image payload and raw tool argument with a placeholder.
3
3
  *
4
4
  * Only the `url` is substituted; the `detail` hint is kept because it is small and
5
- * explains how the image was interpreted. The input array is not mutated.
5
+ * explains how the image was interpreted. Tool call ids and names remain because they
6
+ * identify the proposal; raw arguments are redacted. The input array is not mutated.
6
7
  *
7
8
  * @param messages - Messages about to be reported.
8
- * @returns New message objects whose images carry a placeholder URL.
9
+ * @returns New message objects with redacted images and tool arguments.
9
10
  */
10
11
  export function modelMessageDiagnostics(messages) {
11
12
  return messages.map((message) => ({
@@ -15,5 +16,14 @@ export function modelMessageDiagnostics(messages) {
15
16
  images: message.images.map((image) => ({ ...image, url: '[image omitted]' })),
16
17
  }
17
18
  : {}),
19
+ ...(message.toolCalls
20
+ ? {
21
+ toolCalls: message.toolCalls.map((call) => ({
22
+ id: call.id,
23
+ name: call.name,
24
+ arguments: '[tool arguments omitted]',
25
+ })),
26
+ }
27
+ : {}),
18
28
  }));
19
29
  }
@@ -15,6 +15,8 @@ export function snapshotModelOptions(options = {}) {
15
15
  includeUsage: options.includeUsage,
16
16
  onUsage: options.onUsage?.bind(options),
17
17
  functions: options.functions,
18
+ toolChoice: options.toolChoice,
19
+ parallelToolCalls: options.parallelToolCalls,
18
20
  };
19
21
  for (const key of Object.keys(snapshot)) {
20
22
  if (!(key in options))
@@ -35,6 +35,23 @@ function snapshotImage(image) {
35
35
  (detail !== undefined && !['auto', 'low', 'high', 'original'].includes(detail)), () => new TypeError('Invalid model image'));
36
36
  return Object.freeze({ url, ...(detail === undefined ? {} : { detail }) });
37
37
  }
38
+ /**
39
+ * Validate and freeze one assistant tool declaration.
40
+ *
41
+ * @param call - Candidate tool call.
42
+ * @returns A frozen tool call with only the declared fields.
43
+ * @throws TypeError When the id, name or raw arguments are invalid.
44
+ */
45
+ function snapshotToolCall(call) {
46
+ breakWhile(!call || typeof call !== 'object' || Array.isArray(call), () => new TypeError('Invalid model tool call'));
47
+ const { id, name, arguments: args } = call;
48
+ breakWhile(typeof id !== 'string' ||
49
+ !id ||
50
+ typeof name !== 'string' ||
51
+ !name ||
52
+ typeof args !== 'string', () => new TypeError('Invalid model tool call'));
53
+ return Object.freeze({ id, name, arguments: args });
54
+ }
38
55
  /**
39
56
  * Snapshot a dynamic transport once, preserving its collector factory receiver.
40
57
  *
@@ -95,19 +112,35 @@ export function preparedInputSize(base, prepared) {
95
112
  breakWhile(!Array.isArray(base), () => new TypeError('Model messages must be an array'));
96
113
  const snapshot = Array.from(base, (message) => {
97
114
  breakWhile(!message || typeof message !== 'object' || Array.isArray(message), () => new TypeError('Invalid model message'));
98
- const { role, content, images } = message;
99
- breakWhile(!['system', 'user', 'assistant'].includes(role) ||
115
+ const { role, content, images, toolCalls, toolCallId } = message;
116
+ const assistant = role === 'assistant';
117
+ const tool = role === 'tool';
118
+ breakWhile(!['system', 'user', 'assistant', 'tool'].includes(role) ||
100
119
  typeof content !== 'string' ||
101
- (images !== undefined && !Array.isArray(images)), () => new TypeError('Invalid model message'));
120
+ (images !== undefined && (!Array.isArray(images) || role !== 'user')) ||
121
+ (toolCalls !== undefined && (!Array.isArray(toolCalls) || !assistant)) ||
122
+ (toolCallId !== undefined &&
123
+ (!tool || typeof toolCallId !== 'string' || toolCallId.length === 0)) ||
124
+ (tool && (typeof toolCallId !== 'string' || toolCallId.length === 0)), () => new TypeError('Invalid model message'));
102
125
  return Object.freeze({
103
126
  role,
104
127
  content,
105
- ...(images === undefined ? {} : { images: Object.freeze(Array.from(images, snapshotImage)) }),
128
+ ...(images === undefined
129
+ ? {}
130
+ : { images: Object.freeze(Array.from(images, snapshotImage)) }),
131
+ ...(toolCalls === undefined
132
+ ? {}
133
+ : { toolCalls: Object.freeze(Array.from(toolCalls, snapshotToolCall)) }),
134
+ ...(toolCallId === undefined ? {} : { toolCallId }),
106
135
  });
107
136
  });
108
137
  const messages = Object.freeze(modelInputMessages(snapshot, prepared.instruction).map((message) => Object.freeze(message)));
109
- const messageChars = snapshot.reduce((sum, message) => sum + message.content.length, 0);
110
- const instructionChars = messages.reduce((sum, message) => sum + message.content.length, 0) - messageChars;
138
+ const contentChars = snapshot.reduce((sum, message) => sum + message.content.length, 0);
139
+ const messageChars = snapshot.reduce((sum, message) => sum +
140
+ message.content.length +
141
+ (message.toolCalls?.reduce((callSum, call) => callSum + call.id.length + call.name.length + call.arguments.length, 0) ?? 0) +
142
+ (message.toolCallId?.length ?? 0), 0);
143
+ const instructionChars = messages.reduce((sum, message) => sum + message.content.length, 0) - contentChars;
111
144
  const functions = prepared.options.functions;
112
145
  const schemaChars = functions
113
146
  ? (functionSizes.get(functions) ?? JSON.stringify(functions).length)
@@ -145,6 +145,8 @@ export declare class ModelRunner<TransportId extends string = string, const Tran
145
145
  }): Promise<ModelResult<import('../structs/model/runtime.js').RegisteredTransportOutput<Transports, Id>>>;
146
146
  /** Invoke without narrowing the transport type. */
147
147
  invoke(request: ModelCallRequest<TransportId | ModelTransportDefinition>): Promise<ModelResult<unknown>>;
148
+ /** Validate and execute a single request; optional recording is owned by invoke. */
149
+ private invokeRequest;
148
150
  /**
149
151
  * Acquire the per-runner call slot and execute one call.
150
152
  *
@@ -10,6 +10,8 @@
10
10
  * Exports: `ModelRunner`, the re-exported `BudgetError`, and the runtime/config type
11
11
  * re-exports at the bottom of the file.
12
12
  */
13
+ import { requireSubFlowLogger } from '../diagnostics/sub-flow.js';
14
+ import { nativeSubFlowAsync } from '../diagnostics/native-sub-flow.js';
13
15
  import { executionEvents } from '../execution/events.js';
14
16
  import { ModelCallBudget, parentModelBudget } from './call-budget.js';
15
17
  import { synchronous } from '../utils/synchronous.js';
@@ -317,6 +319,19 @@ export class ModelRunner {
317
319
  return snapshot;
318
320
  }
319
321
  async invoke(request) {
322
+ const context = typeof request === 'string' ? { logger: undefined, signal: undefined } : request;
323
+ requireSubFlowLogger(context.logger);
324
+ const callId = invocationId();
325
+ return nativeSubFlowAsync({
326
+ logger: context.logger,
327
+ signal: combineSignals(this.signal, context.signal),
328
+ invocationId: callId,
329
+ }, 'model', typeof request === 'string'
330
+ ? modelRunnerDefaults.stage
331
+ : (request.stageId ?? request.prepared?.stageId ?? modelRunnerDefaults.stage), () => this.invokeRequest(request, callId));
332
+ }
333
+ /** Validate and execute a single request; optional recording is owned by invoke. */
334
+ async invokeRequest(request, callId) {
320
335
  const call = typeof request === 'string' ? { input: request } : request;
321
336
  const state = call.prepared === undefined ? undefined : this.preparedCalls.get(call.prepared);
322
337
  if (call.prepared !== undefined && (!state || state.used))
@@ -380,7 +395,7 @@ export class ModelRunner {
380
395
  throw invalid('Prepared model request settings cannot be replaced');
381
396
  if (state)
382
397
  state.used = true;
383
- return this.invokePrepared(stage, input, tokens, transportId, call.diagnostics, prepared, call.signal);
398
+ return this.invokePrepared(stage, input, tokens, transportId, call.diagnostics, prepared, call.signal, callId);
384
399
  }
385
400
  /**
386
401
  * Acquire the per-runner call slot and execute one call.
@@ -399,7 +414,7 @@ export class ModelRunner {
399
414
  * @returns The parsed result with invocation metadata.
400
415
  * @throws Error When a call is already in flight or the request capacity is exhausted.
401
416
  */
402
- async invokePrepared(stage, input, tokens, transportId, diagnostics = this.ports.diagnostics, prepared, requestSignal) {
417
+ async invokePrepared(stage, input, tokens, transportId, diagnostics = this.ports.diagnostics, prepared, requestSignal, callId = invocationId()) {
403
418
  const configuredRun = this.ports.diagnostics?.identity?.runId, selectedRun = diagnostics?.identity?.runId;
404
419
  if (configuredRun && selectedRun && configuredRun !== selectedRun)
405
420
  throw recordErrorContext(new TypeError('Model observation parent belongs to a different run'), { stageId: stage, codes: ['MODEL_CONFIG'], isRetryable: false });
@@ -409,7 +424,7 @@ export class ModelRunner {
409
424
  this.requestGate.active++;
410
425
  let failure;
411
426
  try {
412
- return await this.execute(stage, input, tokens, transportId, combineSignals(this.signal, requestSignal), diagnostics, prepared);
427
+ return await this.execute(stage, input, tokens, transportId, combineSignals(this.signal, requestSignal), diagnostics, prepared, callId);
413
428
  }
414
429
  catch (error) {
415
430
  failure = recordErrorContext(error, { stageId: stage });
@@ -440,8 +455,8 @@ export class ModelRunner {
440
455
  * @returns The parsed result with invocation metadata.
441
456
  * @throws Error or ValidationError from any phase, decorated with stage context.
442
457
  */
443
- async execute(stage, input, tokens, transportId, signal, parentDiagnostics = this.ports.diagnostics, prepared) {
444
- const id = invocationId(), startedAtMs = Date.now();
458
+ async execute(stage, input, tokens, transportId, signal, parentDiagnostics = this.ports.diagnostics, prepared, callId = invocationId()) {
459
+ const id = callId, startedAtMs = Date.now();
445
460
  this.activeCall = undefined;
446
461
  const invocationDiagnostics = parentDiagnostics?.child(id, {
447
462
  invocationId: id,
@@ -613,6 +628,20 @@ export class ModelRunner {
613
628
  catch {
614
629
  /* best-effort metadata */
615
630
  }
631
+ try {
632
+ const calls = response.calls;
633
+ if (calls?.length) {
634
+ step.calls = calls.map((call) => ({
635
+ id: call.id,
636
+ name: call.name,
637
+ argumentChars: call.argumentChars,
638
+ ...(call.toolId === undefined ? {} : { toolId: call.toolId }),
639
+ }));
640
+ }
641
+ }
642
+ catch {
643
+ /* best-effort metadata */
644
+ }
616
645
  };
617
646
  const accountOutput = () => {
618
647
  const outputChars = response.outputChars;
@@ -30,7 +30,7 @@ export function createModelTransport(registration) {
30
30
  const adapter = registration.adapter ?? nativeFunctionInteraction;
31
31
  if (registration.requireStrictArguments && !adapter.features.strictArguments)
32
32
  throw new TypeError(`Model interaction does not support strict arguments: ${adapter.id}`);
33
- const source = registration.capabilities, createSource = typeof source === 'function' ? source.bind(registration) : undefined;
33
+ const source = registration.tools, createSource = typeof source === 'function' ? source.bind(registration) : undefined;
34
34
  const prepare = adapter.prepare.bind(adapter), options = Object.freeze(snapshotModelOptions(registration.options));
35
35
  return Object.freeze({
36
36
  id: registration.id,
@@ -40,8 +40,8 @@ export function createModelTransport(registration) {
40
40
  : source;
41
41
  const definitions = [...catalog.values()];
42
42
  if (!definitions.length)
43
- throw new TypeError('Model stage requires registered capabilities');
44
- const capabilityIds = new Set(definitions.map((capability) => capability.id));
43
+ throw new TypeError('Model stage requires registered tools');
44
+ const toolIds = new Set(definitions.map((capability) => capability.id));
45
45
  const prepared = synchronous(prepare(definitions), 'Model interaction preparation must be synchronous');
46
46
  const createResponse = prepared.createResponse.bind(prepared), resolve = catalog.resolve.bind(catalog);
47
47
  return {
@@ -62,7 +62,7 @@ export function createModelTransport(registration) {
62
62
  finish() {
63
63
  const selection = synchronous(buffer.finish(), 'Model selection parsers must be synchronous');
64
64
  call = selection.call;
65
- if (!capabilityIds.has(selection.capabilityId))
65
+ if (!toolIds.has(selection.toolId))
66
66
  modelProtocolError('Model capability is unavailable in this prepared call');
67
67
  return synchronous(resolve(selection, { stageId }), 'Model result parsers must be synchronous');
68
68
  },
@@ -49,6 +49,9 @@ export function countModelInput(budget, messages, options) {
49
49
  const snapshot = messages.map((message) => ({
50
50
  ...message,
51
51
  ...(message.images ? { images: message.images.map((image) => ({ ...image })) } : {}),
52
+ ...(message.toolCalls
53
+ ? { toolCalls: message.toolCalls.map((call) => ({ ...call })) }
54
+ : {}),
52
55
  }));
53
56
  const count = budget.countTokens({
54
57
  messages: snapshot,
@@ -8,9 +8,10 @@ import type { ModelTransportDefinition } from '../structs/model/runtime.js';
8
8
  *
9
9
  * @param id - Unique transport id, also used as the typed handle in `ModelRunner`.
10
10
  * @param options - Optional request options; only `temperature` is honored.
11
+ * @param trim - Trim surrounding whitespace from the final text; defaults to true.
11
12
  * @returns A frozen transport definition.
12
13
  */
13
- export declare function createPlainTextTransport<const Id extends string>(id: Id, options?: Pick<ModelOptions, 'temperature'>): ModelTransportDefinition<string> & {
14
+ export declare function createPlainTextTransport<const Id extends string>(id: Id, options?: Pick<ModelOptions, 'temperature'>, trim?: boolean): ModelTransportDefinition<string> & {
14
15
  readonly id: Id;
15
16
  };
16
17
  /**
@@ -45,16 +45,17 @@ function textResponse(decode) {
45
45
  *
46
46
  * @param id - Unique transport id, also used as the typed handle in `ModelRunner`.
47
47
  * @param options - Optional request options; only `temperature` is honored.
48
+ * @param trim - Trim surrounding whitespace from the final text; defaults to true.
48
49
  * @returns A frozen transport definition.
49
50
  */
50
- export function createPlainTextTransport(id, options = {}) {
51
+ export function createPlainTextTransport(id, options = {}, trim = true) {
51
52
  const settings = Object.freeze({ ...snapshotModelOptions(options), json: false });
52
53
  return Object.freeze({
53
54
  id,
54
55
  prepare: (_stageId) => ({
55
56
  options: settings,
56
57
  schemaChars: 0,
57
- createResponse: () => textResponse((raw) => raw.trim()),
58
+ createResponse: () => textResponse((raw) => (trim ? raw.trim() : raw)),
58
59
  }),
59
60
  });
60
61
  }
@@ -47,8 +47,10 @@ export interface InvocationHandler<Input, Context> {
47
47
  */
48
48
  call(input: Input, context: Context, execution: import('./tools.js').ToolExecutionContext): void;
49
49
  }
50
- /** Optional capabilities the lifecycle resolves from the host context for one invocation. */
50
+ /** Optional tools the lifecycle resolves from the host context for one invocation. */
51
51
  export interface ToolInvocationPorts<Context> {
52
+ /** Explicit recorder selected for this invocation only. */
53
+ readonly logger?: (context: Context) => import('../debug.js').SubFlowLogger | undefined;
52
54
  /** Diagnostic scope; omitted disables recording for this tool. */
53
55
  readonly diagnostics?: (context: Context) => import('../diagnostics.js').DiagnosticContext | undefined;
54
56
  /** Extra cancellation; must not replace the tool execution signal. */