theorum 0.1.15 → 1.1.3

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 (305) hide show
  1. package/README.md +241 -98
  2. package/esm/mod.d.ts +57 -28
  3. package/esm/mod.js +43 -23
  4. package/esm/src/cli/commands/bench.js +18 -18
  5. package/esm/src/cli/commands/fuzz-canary.d.ts +13 -0
  6. package/esm/src/cli/commands/fuzz-canary.js +191 -0
  7. package/esm/src/cli/commands/fuzz-guardrails.d.ts +3 -5
  8. package/esm/src/cli/commands/fuzz-guardrails.js +4 -581
  9. package/esm/src/cli/commands/guardrails-eval.d.ts +14 -0
  10. package/esm/src/cli/commands/guardrails-eval.js +15 -0
  11. package/esm/src/cli/commands/profile.js +35 -15
  12. package/esm/src/cli/commands/run.d.ts +3 -0
  13. package/esm/src/cli/commands/run.js +23 -32
  14. package/esm/src/cli/commands/test.d.ts +10 -1
  15. package/esm/src/cli/commands/test.js +34 -34
  16. package/esm/src/cli/event-log.d.ts +19 -0
  17. package/esm/src/cli/event-log.js +147 -0
  18. package/esm/src/cli/index.js +57 -11
  19. package/esm/src/cli/matrix/synthesizer.d.ts +10 -12
  20. package/esm/src/cli/matrix/synthesizer.js +45 -118
  21. package/esm/src/guardrails/canary-gate.d.ts +21 -0
  22. package/esm/src/guardrails/canary-gate.js +32 -0
  23. package/esm/src/guardrails/canary.d.ts +34 -0
  24. package/esm/src/guardrails/canary.js +150 -0
  25. package/esm/src/guardrails/corpus/canary-egress-attacks.d.ts +17 -0
  26. package/esm/src/guardrails/corpus/canary-egress-attacks.js +151 -0
  27. package/esm/src/guardrails/corpus/fuzz-inbound.d.ts +11 -0
  28. package/esm/src/guardrails/corpus/fuzz-inbound.js +213 -0
  29. package/esm/src/guardrails/corpus/inbound-payloads.d.ts +10 -0
  30. package/esm/src/guardrails/corpus/inbound-payloads.js +125 -0
  31. package/esm/src/guardrails/corpus/live-attacks.d.ts +20 -0
  32. package/esm/src/guardrails/corpus/live-attacks.js +231 -0
  33. package/esm/src/guardrails/corpus/mod.d.ts +14 -0
  34. package/esm/src/guardrails/corpus/mod.js +11 -0
  35. package/esm/src/guardrails/corpus/secrets.d.ts +17 -0
  36. package/esm/src/guardrails/corpus/secrets.js +17 -0
  37. package/esm/src/guardrails/corpus/strings.d.ts +28 -0
  38. package/esm/src/guardrails/corpus/strings.js +34 -0
  39. package/esm/src/guardrails/corpus/types.d.ts +38 -0
  40. package/esm/src/guardrails/corpus/types.js +6 -0
  41. package/esm/src/guardrails/egress.d.ts +32 -0
  42. package/esm/src/guardrails/egress.js +87 -0
  43. package/esm/src/guardrails/error.d.ts +14 -23
  44. package/esm/src/guardrails/error.js +87 -76
  45. package/esm/src/guardrails/eval/corpus.d.ts +108 -0
  46. package/esm/src/guardrails/eval/corpus.js +978 -0
  47. package/esm/src/guardrails/eval/mod.d.ts +51 -0
  48. package/esm/src/guardrails/eval/mod.js +133 -0
  49. package/esm/src/guardrails/eval/score.d.ts +66 -0
  50. package/esm/src/guardrails/eval/score.js +114 -0
  51. package/esm/src/guardrails/events.d.ts +25 -0
  52. package/esm/src/guardrails/events.js +56 -0
  53. package/esm/src/guardrails/hits.d.ts +24 -0
  54. package/esm/src/guardrails/hits.js +45 -0
  55. package/esm/src/guardrails/injection.js +28 -5
  56. package/esm/src/guardrails/lexicon.d.ts +39 -0
  57. package/esm/src/guardrails/lexicon.js +200 -0
  58. package/esm/src/guardrails/live-outbound-gate.d.ts +41 -0
  59. package/esm/src/guardrails/live-outbound-gate.js +222 -0
  60. package/esm/src/guardrails/mod.d.ts +30 -6
  61. package/esm/src/guardrails/mod.js +20 -5
  62. package/esm/src/guardrails/network.d.ts +19 -0
  63. package/esm/src/guardrails/network.js +234 -0
  64. package/esm/src/guardrails/policy.d.ts +35 -0
  65. package/esm/src/guardrails/policy.js +50 -0
  66. package/esm/src/guardrails/progressive-yield.d.ts +51 -0
  67. package/esm/src/guardrails/progressive-yield.js +98 -0
  68. package/esm/src/guardrails/quota.d.ts +17 -3
  69. package/esm/src/guardrails/quota.js +18 -4
  70. package/esm/src/guardrails/sanitize.d.ts +45 -19
  71. package/esm/src/guardrails/sanitize.js +177 -94
  72. package/esm/src/guardrails/sensitive.js +2 -1
  73. package/esm/src/guardrails/serialize.d.ts +35 -0
  74. package/esm/src/guardrails/serialize.js +58 -0
  75. package/esm/src/guardrails/testing.d.ts +17 -0
  76. package/esm/src/guardrails/testing.js +13 -0
  77. package/esm/src/guardrails/theorum-error.d.ts +12 -0
  78. package/esm/src/guardrails/theorum-error.js +15 -0
  79. package/esm/src/guardrails/tool-directives.d.ts +48 -0
  80. package/esm/src/guardrails/tool-directives.js +124 -0
  81. package/esm/src/guardrails/tool-result.d.ts +93 -0
  82. package/esm/src/guardrails/tool-result.js +276 -0
  83. package/esm/src/guardrails/types.d.ts +291 -0
  84. package/esm/src/guardrails/types.js +72 -0
  85. package/esm/src/host/client-turn.d.ts +19 -0
  86. package/esm/src/host/client-turn.js +36 -0
  87. package/esm/src/host/mint-trace.d.ts +1 -1
  88. package/esm/src/host/mod.d.ts +5 -3
  89. package/esm/src/host/mod.js +4 -3
  90. package/esm/src/kernel/auth/crypto.d.ts +42 -0
  91. package/esm/src/kernel/auth/crypto.js +106 -0
  92. package/esm/src/kernel/auth/mod.d.ts +11 -0
  93. package/esm/src/kernel/auth/mod.js +11 -0
  94. package/esm/src/kernel/auth/oauth.d.ts +47 -0
  95. package/esm/src/kernel/auth/oauth.js +278 -0
  96. package/esm/src/kernel/auth/types.d.ts +133 -0
  97. package/esm/src/kernel/auth/types.js +13 -0
  98. package/esm/src/kernel/engine/delta.d.ts +24 -2
  99. package/esm/src/kernel/engine/delta.js +478 -39
  100. package/esm/src/kernel/engine/live-inbound.d.ts +21 -0
  101. package/esm/src/kernel/engine/live-inbound.js +31 -0
  102. package/esm/src/kernel/engine/live-ingress.d.ts +19 -0
  103. package/esm/src/kernel/engine/live-ingress.js +47 -0
  104. package/esm/src/kernel/engine/repair.js +13 -12
  105. package/esm/src/kernel/engine/runner/gates.d.ts +1 -1
  106. package/esm/src/kernel/engine/runner/gates.js +130 -43
  107. package/esm/src/kernel/engine/runner/mod.d.ts +6 -4
  108. package/esm/src/kernel/engine/runner/mod.js +192 -53
  109. package/esm/src/kernel/engine/runner/schema-validation.js +3 -3
  110. package/esm/src/kernel/engine/runner/stages.d.ts +39 -0
  111. package/esm/src/kernel/engine/runner/stages.js +89 -0
  112. package/esm/src/kernel/engine/runner/state.d.ts +31 -0
  113. package/esm/src/kernel/engine/runner/steps.d.ts +1 -1
  114. package/esm/src/kernel/engine/runner/steps.js +244 -43
  115. package/esm/src/kernel/engine/runner/stream.d.ts +9 -3
  116. package/esm/src/kernel/engine/runner/stream.js +140 -44
  117. package/esm/src/kernel/engine/session/mod.d.ts +25 -0
  118. package/esm/src/kernel/engine/session/mod.js +557 -0
  119. package/esm/src/kernel/interaction-parts.d.ts +14 -0
  120. package/esm/src/kernel/interaction-parts.js +23 -0
  121. package/esm/src/kernel/mod.d.ts +21 -10
  122. package/esm/src/kernel/mod.js +11 -8
  123. package/esm/src/kernel/profile-graph.d.ts +159 -0
  124. package/esm/src/kernel/profile-graph.js +156 -0
  125. package/esm/src/kernel/registry/attachments.d.ts +12 -10
  126. package/esm/src/kernel/registry/attachments.js +33 -27
  127. package/esm/src/kernel/registry/catalog.d.ts +25 -24
  128. package/esm/src/kernel/registry/catalog.js +60 -101
  129. package/esm/src/kernel/registry/ingress.d.ts +9 -4
  130. package/esm/src/kernel/registry/ingress.js +97 -75
  131. package/esm/src/kernel/registry/profile-outputs.d.ts +4 -0
  132. package/esm/src/kernel/registry/profile-outputs.js +8 -0
  133. package/esm/src/kernel/registry/profiles.d.ts +55 -12
  134. package/esm/src/kernel/registry/profiles.js +413 -73
  135. package/esm/src/kernel/registry/provider-request.js +13 -7
  136. package/esm/src/kernel/registry/resolve.d.ts +8 -8
  137. package/esm/src/kernel/registry/resolve.js +169 -154
  138. package/esm/src/kernel/registry/schemas.js +1 -1
  139. package/esm/src/kernel/registry/sole-model.d.ts +8 -0
  140. package/esm/src/kernel/registry/sole-model.js +10 -0
  141. package/esm/src/kernel/registry/system-prompt.d.ts +10 -0
  142. package/esm/src/kernel/registry/system-prompt.js +40 -0
  143. package/esm/src/kernel/registry/system-role.d.ts +8 -0
  144. package/esm/src/kernel/registry/system-role.js +14 -0
  145. package/esm/src/kernel/registry/vault.d.ts +12 -7
  146. package/esm/src/kernel/registry/vault.js +32 -10
  147. package/esm/src/kernel/schema.d.ts +231 -0
  148. package/esm/src/kernel/schema.js +607 -0
  149. package/esm/src/kernel/stages.d.ts +175 -0
  150. package/esm/src/kernel/stages.js +476 -0
  151. package/esm/src/kernel/stop.d.ts +78 -19
  152. package/esm/src/kernel/stop.js +51 -16
  153. package/esm/src/kernel/tools/events.d.ts +41 -0
  154. package/esm/src/kernel/tools/events.js +71 -0
  155. package/esm/src/kernel/tools/execute.d.ts +84 -0
  156. package/esm/src/kernel/tools/execute.js +614 -0
  157. package/esm/src/kernel/tools/harness.d.ts +8 -0
  158. package/esm/src/kernel/tools/harness.js +46 -0
  159. package/esm/src/kernel/tools/invoke.d.ts +10 -0
  160. package/esm/src/kernel/tools/invoke.js +101 -0
  161. package/esm/src/kernel/tools/mod.d.ts +13 -0
  162. package/esm/src/kernel/tools/mod.js +11 -0
  163. package/esm/src/kernel/tools/permission.d.ts +15 -0
  164. package/esm/src/kernel/tools/permission.js +47 -0
  165. package/esm/src/kernel/tools/project.d.ts +12 -0
  166. package/esm/src/kernel/tools/project.js +36 -0
  167. package/esm/src/kernel/tools/registry.d.ts +23 -0
  168. package/esm/src/kernel/tools/registry.js +81 -0
  169. package/esm/src/kernel/tools/remote.d.ts +94 -0
  170. package/esm/src/kernel/tools/remote.js +577 -0
  171. package/esm/src/kernel/tools/resolve.d.ts +39 -0
  172. package/esm/src/kernel/tools/resolve.js +283 -0
  173. package/esm/src/kernel/tools/schema.d.ts +15 -0
  174. package/esm/src/kernel/tools/schema.js +176 -0
  175. package/esm/src/kernel/tools/stage-run.d.ts +105 -0
  176. package/esm/src/kernel/tools/stage-run.js +155 -0
  177. package/esm/src/kernel/tools/types.d.ts +394 -0
  178. package/esm/src/kernel/tools/types.js +9 -0
  179. package/esm/src/kernel/types.d.ts +540 -256
  180. package/esm/src/kernel/util/find-last.d.ts +2 -0
  181. package/esm/src/kernel/util/find-last.js +10 -0
  182. package/esm/src/observability/destinations.d.ts +31 -0
  183. package/esm/src/observability/destinations.js +67 -0
  184. package/esm/src/observability/mod.d.ts +10 -3
  185. package/esm/src/observability/mod.js +6 -2
  186. package/esm/src/observability/policy.d.ts +27 -0
  187. package/esm/src/observability/policy.js +80 -0
  188. package/esm/src/observability/resolve-policy.d.ts +16 -0
  189. package/esm/src/observability/resolve-policy.js +64 -0
  190. package/esm/src/observability/trace-attach.d.ts +8 -4
  191. package/esm/src/observability/trace-attach.js +50 -29
  192. package/esm/src/observability/trace-record.d.ts +23 -13
  193. package/esm/src/observability/trace-record.js +96 -39
  194. package/esm/src/observability/trace-sink.d.ts +19 -0
  195. package/esm/src/observability/trace-sink.js +10 -0
  196. package/esm/src/observability/trace-usage.d.ts +10 -3
  197. package/esm/src/observability/trace-usage.js +70 -17
  198. package/esm/src/observability/trace.d.ts +18 -7
  199. package/esm/src/observability/trace.js +34 -17
  200. package/esm/src/observability/types.d.ts +113 -0
  201. package/esm/src/observability/types.js +11 -0
  202. package/esm/src/presets/google/speech-voices.d.ts +11 -0
  203. package/esm/src/presets/google/speech-voices.js +41 -0
  204. package/esm/src/presets/google.d.ts +36 -24
  205. package/esm/src/presets/google.js +50 -63
  206. package/esm/src/presets/mod.d.ts +2 -2
  207. package/esm/src/presets/mod.js +1 -1
  208. package/esm/src/providers/create-provider.d.ts +20 -17
  209. package/esm/src/providers/create-provider.js +72 -26
  210. package/esm/src/providers/google/interactions/framing.d.ts +23 -0
  211. package/esm/src/providers/google/interactions/framing.js +269 -0
  212. package/esm/src/providers/google/interactions/mod.d.ts +7 -0
  213. package/esm/src/providers/google/interactions/mod.js +7 -0
  214. package/esm/src/providers/google/interactions/stream.d.ts +83 -0
  215. package/esm/src/providers/google/interactions/stream.js +588 -0
  216. package/esm/src/providers/google/keys.d.ts +26 -0
  217. package/esm/src/providers/{keys.js → google/keys.js} +19 -31
  218. package/esm/src/providers/google/live/framing.d.ts +49 -0
  219. package/esm/src/providers/google/live/framing.js +552 -0
  220. package/esm/src/providers/google/live/openapi-schema.d.ts +6 -0
  221. package/esm/src/providers/google/live/openapi-schema.js +46 -0
  222. package/esm/src/providers/google/live/session.d.ts +25 -0
  223. package/esm/src/providers/google/live/session.js +134 -0
  224. package/esm/src/providers/google/live/stream.d.ts +45 -0
  225. package/esm/src/providers/google/live/stream.js +214 -0
  226. package/esm/src/providers/google/urls.d.ts +6 -0
  227. package/esm/src/providers/google/urls.js +6 -0
  228. package/esm/src/providers/local/local.d.ts +30 -0
  229. package/esm/src/providers/{local.js → local/local.js} +66 -126
  230. package/esm/src/providers/local/mod.d.ts +9 -0
  231. package/esm/src/providers/local/mod.js +9 -0
  232. package/esm/src/providers/mod.d.ts +6 -3
  233. package/esm/src/providers/mod.js +3 -1
  234. package/esm/src/providers/openrouter/cache-control.d.ts +24 -0
  235. package/esm/src/providers/openrouter/cache-control.js +23 -0
  236. package/esm/src/providers/openrouter/chat.d.ts +107 -0
  237. package/esm/src/providers/{openrouter.js → openrouter/chat.js} +117 -231
  238. package/esm/src/providers/openrouter/image.d.ts +34 -0
  239. package/esm/src/providers/openrouter/image.js +275 -0
  240. package/esm/src/providers/openrouter/openai/chat-payload.d.ts +24 -0
  241. package/esm/src/providers/openrouter/openai/chat-payload.js +82 -0
  242. package/esm/src/providers/openrouter/openai/compat.d.ts +53 -0
  243. package/esm/src/providers/openrouter/openai/compat.js +213 -0
  244. package/esm/src/providers/openrouter/openai/image-payload.d.ts +18 -0
  245. package/esm/src/providers/openrouter/openai/image-payload.js +90 -0
  246. package/esm/src/providers/openrouter/openai/sdk-messages.d.ts +22 -0
  247. package/esm/src/providers/openrouter/openai/sdk-messages.js +122 -0
  248. package/esm/src/providers/openrouter/resolve-api-key.d.ts +9 -0
  249. package/esm/src/providers/openrouter/resolve-api-key.js +24 -0
  250. package/esm/src/providers/openrouter/speech.d.ts +23 -0
  251. package/esm/src/providers/{speech.js → openrouter/speech.js} +32 -55
  252. package/esm/src/providers/probe.d.ts +1 -0
  253. package/esm/src/providers/probe.js +22 -0
  254. package/esm/src/providers/shared/pcm.d.ts +12 -0
  255. package/esm/src/providers/{pcm.js → shared/pcm.js} +16 -3
  256. package/esm/src/providers/shared/sse.d.ts +18 -0
  257. package/esm/src/providers/shared/sse.js +87 -0
  258. package/esm/src/providers/shared/tool-args.d.ts +17 -0
  259. package/esm/src/providers/shared/tool-args.js +45 -0
  260. package/esm/src/providers/shared/upstream-tap.d.ts +5 -0
  261. package/esm/src/providers/{google-tap.js → shared/upstream-tap.js} +4 -7
  262. package/esm/src/providers/shared/upstream-tape.d.ts +6 -0
  263. package/esm/src/providers/{gemini-tape.js → shared/upstream-tape.js} +12 -22
  264. package/esm/src/providers/types.d.ts +27 -0
  265. package/esm/src/providers/types.js +1 -0
  266. package/package.json +11 -7
  267. package/docs/cli.md +0 -97
  268. package/docs/guardrails.md +0 -178
  269. package/docs/host.md +0 -97
  270. package/docs/kernel.md +0 -404
  271. package/docs/observability.md +0 -105
  272. package/docs/openrouter.md +0 -125
  273. package/docs/presets-google.md +0 -91
  274. package/docs/presets.md +0 -88
  275. package/docs/providers.md +0 -202
  276. package/docs/streaming.md +0 -96
  277. package/esm/src/kernel/engine/boundary.d.ts +0 -10
  278. package/esm/src/kernel/engine/boundary.js +0 -55
  279. package/esm/src/kernel/engine/runner/tools.d.ts +0 -13
  280. package/esm/src/kernel/engine/runner/tools.js +0 -198
  281. package/esm/src/kernel/registry/tools.d.ts +0 -12
  282. package/esm/src/kernel/registry/tools.js +0 -36
  283. package/esm/src/providers/expose-for-tests.d.ts +0 -1
  284. package/esm/src/providers/expose-for-tests.js +0 -25
  285. package/esm/src/providers/gemini-tape.d.ts +0 -2
  286. package/esm/src/providers/google-tap.d.ts +0 -3
  287. package/esm/src/providers/interactions.d.ts +0 -5
  288. package/esm/src/providers/interactions.js +0 -169
  289. package/esm/src/providers/keys.d.ts +0 -19
  290. package/esm/src/providers/local.d.ts +0 -29
  291. package/esm/src/providers/openrouter-mod.d.ts +0 -13
  292. package/esm/src/providers/openrouter-mod.js +0 -12
  293. package/esm/src/providers/openrouter-payload.d.ts +0 -39
  294. package/esm/src/providers/openrouter-payload.js +0 -195
  295. package/esm/src/providers/openrouter.d.ts +0 -15
  296. package/esm/src/providers/pcm.d.ts +0 -7
  297. package/esm/src/providers/provider.d.ts +0 -15
  298. package/esm/src/providers/provider.js +0 -202
  299. package/esm/src/providers/speech.d.ts +0 -23
  300. package/esm/src/providers/sse.d.ts +0 -7
  301. package/esm/src/providers/sse.js +0 -55
  302. package/esm/src/streaming/mod.d.ts +0 -9
  303. package/esm/src/streaming/mod.js +0 -8
  304. /package/esm/src/{streaming → host}/readStreamingJsonStringField.d.ts +0 -0
  305. /package/esm/src/{streaming → host}/readStreamingJsonStringField.js +0 -0
@@ -0,0 +1,614 @@
1
+ /**
2
+ * Shared tool execution core for model-initiated and host-initiated calls.
3
+ *
4
+ * @module
5
+ */
6
+ import { throwIfAborted } from '../../guardrails/error.js';
7
+ import { lexiconText } from '../../guardrails/lexicon.js';
8
+ import { resolveGuardrailPolicy } from '../../guardrails/policy.js';
9
+ import { sanitizeText } from '../../guardrails/sanitize.js';
10
+ import { checkTaintGate, composeToolText, guardToolFailureText, guardToolResult, inspectToolArguments, toolCallEvent, } from '../../guardrails/tool-result.js';
11
+ import { isAwaitingUserInput } from '../stages.js';
12
+ import { failureEvent, messageOf, startToolExecution, toolEvent } from './events.js';
13
+ import { checkPermission, isGateResumeDenied, isResumeContinuation } from './permission.js';
14
+ import { getTool } from './registry.js';
15
+ import { executeHttpTool, executeMcpTool, modelResultFromOutput, parseToolOutput, } from './remote.js';
16
+ import { promoteLoadedTools } from './resolve.js';
17
+ import { plainToolInput } from './schema.js';
18
+ import { emitGateSettlement, runPostToolStages, runPreToolPipeline, } from './stage-run.js';
19
+ export { checkPermission, isGateResumeGranted, isResumeContinuation, permissionGranted, } from './permission.js';
20
+ /** Map a registered tool's type onto the origin its bytes carry. */
21
+ function originOfTool(type) {
22
+ if (type === 'http')
23
+ return 'http';
24
+ if (type === 'mcp')
25
+ return 'mcp';
26
+ if (type === 'builtin')
27
+ return 'builtin';
28
+ return 'local';
29
+ }
30
+ function provenanceFor(tool, depth = 1) {
31
+ return { origin: originOfTool(tool.type), tool: tool.name, depth };
32
+ }
33
+ function isStreamHandler(handler) {
34
+ return (typeof handler === 'function' &&
35
+ Object.prototype.toString.call(handler) === '[object AsyncGeneratorFunction]');
36
+ }
37
+ const MEDIA_PART_TYPES = new Set(['image', 'audio', 'video', 'document']);
38
+ function isRecord(value) {
39
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
40
+ }
41
+ /** Validate host-emitted InteractionPart shapes; drop invalid entries. */
42
+ export function coerceToolResultParts(raw) {
43
+ if (!Array.isArray(raw) || raw.length === 0)
44
+ return undefined;
45
+ const parts = [];
46
+ for (const item of raw) {
47
+ if (!isRecord(item) || typeof item.type !== 'string')
48
+ continue;
49
+ if (item.type === 'text' && typeof item.text === 'string') {
50
+ parts.push({ type: 'text', text: item.text });
51
+ continue;
52
+ }
53
+ if (MEDIA_PART_TYPES.has(item.type) &&
54
+ typeof item.mimeType === 'string' &&
55
+ item.mimeType.trim() &&
56
+ typeof item.data === 'string' &&
57
+ item.data.length > 0) {
58
+ parts.push({
59
+ type: item.type,
60
+ mimeType: item.mimeType,
61
+ data: item.data,
62
+ });
63
+ }
64
+ }
65
+ return parts.length > 0 ? parts : undefined;
66
+ }
67
+ /** Copy tool output for model `data`, omitting media `parts`. */
68
+ export function leanToolResultData(output) {
69
+ if (!isRecord(output))
70
+ return output;
71
+ const { parts: _parts, ...rest } = output;
72
+ return rest;
73
+ }
74
+ /** @deprecated Gates use `ToolGate` / `phase: 'gate'`. Kept for type narrowing during migration. */
75
+ export function isToolPause(value) {
76
+ return ('kind' in value &&
77
+ (value.kind === 'interactive' ||
78
+ value.kind === 'confirmation' ||
79
+ value.kind === 'permission' ||
80
+ value.kind === 'auth'));
81
+ }
82
+ export function* yieldHandlerSideEvent(base, event) {
83
+ if (event.kind === 'progress') {
84
+ yield toolEvent(base, { phase: 'progress', data: event.data });
85
+ }
86
+ else if (event.kind === 'trace') {
87
+ yield toolEvent(base, { phase: 'trace', step: event.step });
88
+ }
89
+ else if (event.kind === 'artifact') {
90
+ yield toolEvent(base, { phase: 'artifact', artifact: event.artifact });
91
+ }
92
+ else if (event.kind === 'warning') {
93
+ yield toolEvent(base, { phase: 'warning', warning: event.warning });
94
+ }
95
+ }
96
+ /** Run the handler, yielding stream side-events live; returns terminal output. */
97
+ async function* runHandler(handler, input, ctx, base) {
98
+ if (isStreamHandler(handler)) {
99
+ let output;
100
+ const gen = handler(input, ctx);
101
+ for await (const event of gen) {
102
+ throwIfAborted(ctx.signal);
103
+ if (event.kind === 'complete') {
104
+ output = event.output;
105
+ continue;
106
+ }
107
+ yield* yieldHandlerSideEvent(base, event);
108
+ }
109
+ return output;
110
+ }
111
+ const output = await handler(input, ctx);
112
+ return output;
113
+ }
114
+ export function projectForModel(tool, output) {
115
+ if (tool.exposeToModel === false) {
116
+ return { finding: 'Completed.' };
117
+ }
118
+ if (isAwaitingUserInput(output)) {
119
+ return {
120
+ finding: lexiconText('tool.awaiting_user', { kind: output.kind, prompt: output.prompt }),
121
+ data: leanToolResultData(output),
122
+ };
123
+ }
124
+ const finding = typeof output === 'object' && output !== null && 'finding' in output
125
+ ? String(output.finding)
126
+ : JSON.stringify(output);
127
+ const parts = isRecord(output) && 'parts' in output ? coerceToolResultParts(output.parts) : undefined;
128
+ return {
129
+ finding: sanitizeText(finding),
130
+ data: leanToolResultData(output),
131
+ ...(parts ? { parts } : {}),
132
+ };
133
+ }
134
+ /**
135
+ * Format model-facing tool output for provider history continuation.
136
+ *
137
+ * Text projection only — never embeds `parts[].data`; media travels on
138
+ * `TurnHistoryMessage.parts` and adapters wire it from there.
139
+ *
140
+ * `executeRegisteredTool` guards at the boundary and leaves `modelText` behind, so
141
+ * the common path returns already-fenced text. A result recorded elsewhere — a
142
+ * host replaying a transcript — is guarded here instead, under full detection.
143
+ */
144
+ export function formatToolResult(result) {
145
+ if (result.modelText !== undefined) {
146
+ return result.modelText;
147
+ }
148
+ return sanitizeText(composeToolText(result.finding, result.data));
149
+ }
150
+ /**
151
+ * Format a tool failure for provider history — structured so the model (or host)
152
+ * sees the code.
153
+ *
154
+ * The message is remote-authored on HTTP and MCP tools, so it is redacted before
155
+ * the kernel frames it as a system report.
156
+ */
157
+ export function formatToolFailureForModel(failure, provenance, policy = resolveGuardrailPolicy(undefined)) {
158
+ const safe = provenance
159
+ ? guardToolFailureText(failure.message, provenance, policy).text
160
+ : sanitizeText(failure.message);
161
+ return {
162
+ finding: `Tool error (${failure.code}): ${safe}`,
163
+ data: {
164
+ ok: false,
165
+ code: failure.code,
166
+ message: safe,
167
+ ...(failure.details !== undefined ? { details: failure.details } : {}),
168
+ },
169
+ };
170
+ }
171
+ /** True when this tool is the profile's T2 loader: its output drives the snapshot. */
172
+ function loadsT2(tool, ctx) {
173
+ if (ctx.profile.type === 'speech' || ctx.profile.type === 'live' || ctx.profile.type === 'host') {
174
+ return false;
175
+ }
176
+ return ctx.profile.tools.t2Loader === tool.name;
177
+ }
178
+ function applyT2LoaderPromotion(tool, checkedData, ctx, snapshot) {
179
+ if (!loadsT2(tool, ctx)) {
180
+ return { ok: true, output: checkedData };
181
+ }
182
+ if (!snapshot) {
183
+ return {
184
+ ok: false,
185
+ failure: {
186
+ code: 'invalid_output',
187
+ message: lexiconText('tool.t2_loader_needs_snapshot', { tool: tool.name }),
188
+ },
189
+ };
190
+ }
191
+ const loaded = extractLoadedIds(checkedData);
192
+ if (!loaded) {
193
+ return {
194
+ ok: false,
195
+ failure: {
196
+ code: 'invalid_output',
197
+ message: lexiconText('tool.t2_loader_shape', { tool: tool.name }),
198
+ },
199
+ };
200
+ }
201
+ const { promoted, failure: promoteFailure } = promoteLoadedTools(snapshot, loaded, ctx.profile);
202
+ if (promoteFailure) {
203
+ return { ok: false, failure: promoteFailure };
204
+ }
205
+ const finalOutput = { ...checkedData, loaded: promoted };
206
+ const rechecked = tool.output.safeParse(finalOutput);
207
+ if (!rechecked.success) {
208
+ return {
209
+ ok: false,
210
+ failure: {
211
+ code: 'invalid_output',
212
+ message: lexiconText('tool.t2_loader_output_invalid'),
213
+ details: rechecked.error.flatten(),
214
+ },
215
+ };
216
+ }
217
+ return { ok: true, output: rechecked.data };
218
+ }
219
+ /**
220
+ * Settle a tool call, once, for every transport: guard the provisional result,
221
+ * run `post_tool`, apply a host `deny` or `mutate` (re-projected and re-guarded),
222
+ * then emit the single terminal `tool` event with what the model actually gets.
223
+ * `reproject` is absent when the kernel owns the output (the T2 loader), which
224
+ * makes `mutate` a warning instead of a replacement.
225
+ */
226
+ async function* settleToolCall(args) {
227
+ const { base, toolName, callId, input, stages, provisional, guard, reproject } = args;
228
+ let modelResult = yield* guard(provisional.modelResult);
229
+ let failure = 'failure' in provisional ? provisional.failure : undefined;
230
+ let outputRaw = 'outputRaw' in provisional ? provisional.outputRaw : undefined;
231
+ const callNotStarted = 'callNotStarted' in provisional ? provisional.callNotStarted : undefined;
232
+ let awaiting = !failure && isAwaitingUserInput(outputRaw);
233
+ let post;
234
+ if (stages) {
235
+ // Do not applyInject here — the text runner records the tool result first.
236
+ const { applyInject: _apply, ...rest } = stages;
237
+ post = yield* runPostToolStages({
238
+ stages: rest,
239
+ toolName,
240
+ callId,
241
+ input,
242
+ callNotStarted,
243
+ outputRaw,
244
+ outputModel: modelResult,
245
+ failure,
246
+ awaiting: awaiting || undefined,
247
+ mutable: !failure && reproject !== undefined,
248
+ });
249
+ if (post.deny) {
250
+ failure = { code: post.deny.code, message: post.deny.message };
251
+ modelResult = yield* guard(formatToolFailureForModel(failure));
252
+ outputRaw = undefined;
253
+ awaiting = false;
254
+ }
255
+ else if (post.mutate && reproject) {
256
+ const next = reproject(plainToolInput(post.mutate.output));
257
+ if (next.ok) {
258
+ failure = undefined;
259
+ outputRaw = next.outputRaw;
260
+ modelResult = yield* guard(next.modelResult);
261
+ awaiting = isAwaitingUserInput(outputRaw);
262
+ }
263
+ else {
264
+ failure = next.failure;
265
+ modelResult = yield* guard(formatToolFailureForModel(next.failure));
266
+ outputRaw = undefined;
267
+ awaiting = false;
268
+ }
269
+ }
270
+ }
271
+ yield failure
272
+ ? failureEvent(base, failure)
273
+ : toolEvent(base, { phase: 'complete', output: outputRaw });
274
+ return {
275
+ modelResult,
276
+ ...(failure ? { failure } : { outputRaw }),
277
+ ...(callNotStarted ? { callNotStarted: true } : {}),
278
+ ...(awaiting ? { awaiting: true } : {}),
279
+ ...(post?.abort !== undefined ? { aborted: post.abort } : {}),
280
+ ...(post?.inject?.length ? { pendingInject: post.inject } : {}),
281
+ };
282
+ }
283
+ /** Settle a call that failed before or during its body. */
284
+ function settleToolFailure(guard, base, failure, stages, args) {
285
+ return settleToolCall({
286
+ base,
287
+ toolName: args.toolName,
288
+ callId: args.callId,
289
+ input: args.input,
290
+ stages,
291
+ guard,
292
+ provisional: {
293
+ failure,
294
+ modelResult: formatToolFailureForModel(failure),
295
+ ...(args.callNotStarted ? { callNotStarted: true } : {}),
296
+ },
297
+ });
298
+ }
299
+ /** The one guard every model-facing tool result passes through, bound to this call. */
300
+ function resultGuard(tool, ctx, snapshot) {
301
+ const provenance = provenanceFor(tool);
302
+ const policy = resolveGuardrailPolicy(ctx.profile.guardrails);
303
+ const callableTools = snapshot?.executable ?? [];
304
+ return (result) => guardResult(result, provenance, policy, callableTools);
305
+ }
306
+ /** Tool `preTool` + host `pre_tool` + mutate re-parse, mapped onto a function-tool settlement. */
307
+ async function* runFunctionPreBodyStages(args) {
308
+ const { tool, base, stages, guard } = args;
309
+ const pre = yield* runPreToolPipeline(args);
310
+ if (pre.ok)
311
+ return pre;
312
+ if (pre.kind === 'aborted') {
313
+ return { aborted: pre.aborted, callNotStarted: true };
314
+ }
315
+ if (pre.kind === 'gated') {
316
+ return { gated: pre.gate, callNotStarted: true };
317
+ }
318
+ return yield* settleToolFailure(guard, base, pre.failure, stages, {
319
+ toolName: tool.name,
320
+ callId: base.callId ?? '',
321
+ input: args.input,
322
+ callNotStarted: true,
323
+ });
324
+ }
325
+ function makeReproject(parse, project) {
326
+ return (output) => {
327
+ const checked = parse(output);
328
+ if (!checked.success) {
329
+ return {
330
+ ok: false,
331
+ failure: {
332
+ code: 'invalid_output',
333
+ message: lexiconText('tool.output_invalid_after_mutate'),
334
+ details: checked.error.flatten(),
335
+ },
336
+ };
337
+ }
338
+ return { ok: true, outputRaw: checked.data, modelResult: project(checked.data) };
339
+ };
340
+ }
341
+ export async function* executeFunction(tool, rawInput, ctx, base, snapshot, stages) {
342
+ const guard = resultGuard(tool, ctx, snapshot);
343
+ const callId = base.callId ?? '';
344
+ const parsed = yield* startToolExecution(tool, rawInput, ctx, base);
345
+ if (!parsed.ok) {
346
+ return yield* settleToolFailure(guard, base, { code: 'invalid_input', message: lexiconText('tool.input_invalid') }, stages, { toolName: tool.name, callId, callNotStarted: true });
347
+ }
348
+ let input = parsed.data;
349
+ const permissionGate = checkPermission(tool.name, tool.permission, ctx.sessionPermissions, ctx.resume);
350
+ if (permissionGate) {
351
+ yield* emitGateSettlement({ base, gate: permissionGate, callId, toolName: tool.name });
352
+ return { gated: permissionGate, callNotStarted: true };
353
+ }
354
+ throwIfAborted(ctx.signal);
355
+ const preBody = yield* runFunctionPreBodyStages({ tool, input, ctx, base, stages, guard });
356
+ if (!('ok' in preBody)) {
357
+ return preBody;
358
+ }
359
+ input = preBody.input;
360
+ throwIfAborted(ctx.signal);
361
+ const fail = (failure) => settleToolFailure(guard, base, failure, stages, { toolName: tool.name, callId, input });
362
+ let output;
363
+ try {
364
+ output = yield* runHandler(tool.handler, input, ctx, base);
365
+ }
366
+ catch (err) {
367
+ return yield* fail({ code: 'handler_error', message: messageOf(err) });
368
+ }
369
+ if (output === undefined) {
370
+ return yield* fail({ code: 'invalid_output', message: lexiconText('tool.handler_no_output') });
371
+ }
372
+ const checked = tool.output.safeParse(output);
373
+ if (!checked.success) {
374
+ return yield* fail({
375
+ code: 'invalid_output',
376
+ message: lexiconText('tool.output_invalid'),
377
+ details: checked.error.flatten(),
378
+ });
379
+ }
380
+ const promoted = applyT2LoaderPromotion(tool, checked.data, ctx, snapshot);
381
+ if (!promoted.ok) {
382
+ return yield* fail(promoted.failure);
383
+ }
384
+ // The T2 loader's output drives the snapshot; the kernel owns it, so no mutate.
385
+ const ownsOutput = loadsT2(tool, ctx);
386
+ return yield* settleToolCall({
387
+ base,
388
+ toolName: tool.name,
389
+ callId,
390
+ input,
391
+ stages,
392
+ guard,
393
+ provisional: {
394
+ outputRaw: promoted.output,
395
+ modelResult: projectForModel(tool, promoted.output),
396
+ },
397
+ ...(ownsOutput
398
+ ? {}
399
+ : {
400
+ reproject: makeReproject((v) => tool.output.safeParse(v), (data) => projectForModel(tool, data)),
401
+ }),
402
+ });
403
+ }
404
+ export function notLoadedMessage(tool) {
405
+ if (tool.loadTier === 'T1') {
406
+ return lexiconText('tool.not_wired_t1', { tool: tool.name });
407
+ }
408
+ if (tool.loadTier === 'T2') {
409
+ return lexiconText('tool.not_loaded_t2', { tool: tool.name });
410
+ }
411
+ return lexiconText('tool.not_visible', { tool: tool.name });
412
+ }
413
+ export function extractLoadedIds(output) {
414
+ if (!output || typeof output !== 'object' || Array.isArray(output)) {
415
+ return undefined;
416
+ }
417
+ const loaded = output.loaded;
418
+ if (!Array.isArray(loaded) || !loaded.every((id) => typeof id === 'string')) {
419
+ return undefined;
420
+ }
421
+ return loaded;
422
+ }
423
+ function earlyFailure(failure) {
424
+ return { failure, callNotStarted: true, modelResult: formatToolFailureForModel(failure) };
425
+ }
426
+ export async function* executeBuiltin(tool, ctx, base, snapshot) {
427
+ yield toolEvent(base, { phase: 'running' });
428
+ throwIfAborted(ctx.signal);
429
+ if (!snapshot.builtins.includes(tool.name)) {
430
+ const failure = {
431
+ code: 'not_loaded',
432
+ message: lexiconText('tool.builtin_not_enabled', { tool: tool.name }),
433
+ };
434
+ yield failureEvent(base, failure);
435
+ return earlyFailure(failure);
436
+ }
437
+ const failure = {
438
+ code: 'provider_native',
439
+ message: lexiconText('tool.provider_native', { tool: tool.name }),
440
+ };
441
+ yield failureEvent(base, failure);
442
+ return earlyFailure(failure);
443
+ }
444
+ /** Snapshot / allowlist / load-tier checks before body execution. */
445
+ function registeredEligibilityFailure(args) {
446
+ const { tool, profile, name, resume, snapshot } = args;
447
+ if (profile.type === 'speech' || !profile.tools.allow.includes(name)) {
448
+ return {
449
+ code: 'not_allowed',
450
+ message: lexiconText('tool.not_allowed', { tool: name, profile: profile.id }),
451
+ };
452
+ }
453
+ if (!snapshot)
454
+ return undefined;
455
+ const continuing = isResumeContinuation(resume);
456
+ if (!continuing && !snapshot.gated.includes(name)) {
457
+ return { code: 'not_gated', message: lexiconText('tool.not_eligible', { tool: name }) };
458
+ }
459
+ if (!snapshot.visible.includes(name)) {
460
+ const skipLoadCheck = continuing && tool.loadTier === 'T0';
461
+ if (!skipLoadCheck) {
462
+ return { code: 'not_loaded', message: notLoadedMessage(tool) };
463
+ }
464
+ }
465
+ return undefined;
466
+ }
467
+ async function* settleRemoteOutcome(args) {
468
+ const { outcome, tool, base, callId, safeInput, stages, guard } = args;
469
+ const name = tool.name;
470
+ if (outcome.kind === 'gated') {
471
+ if (outcome.gate.kind === 'confirmation') {
472
+ return { gated: outcome.gate, callNotStarted: true };
473
+ }
474
+ yield* emitGateSettlement({ base, gate: outcome.gate, callId, toolName: name });
475
+ return { gated: outcome.gate, callNotStarted: true };
476
+ }
477
+ if (outcome.kind === 'aborted') {
478
+ return { aborted: outcome.aborted, callNotStarted: true };
479
+ }
480
+ if (outcome.kind === 'failed') {
481
+ return yield* settleToolFailure(guard, base, outcome.failure, stages, {
482
+ toolName: name,
483
+ callId,
484
+ input: safeInput,
485
+ callNotStarted: outcome.callNotStarted,
486
+ });
487
+ }
488
+ return yield* settleToolCall({
489
+ base,
490
+ toolName: name,
491
+ callId,
492
+ input: safeInput,
493
+ stages,
494
+ guard,
495
+ provisional: { outputRaw: outcome.outputRaw, modelResult: outcome.modelResult },
496
+ reproject: makeReproject((v) => parseToolOutput(tool.output, v), modelResultFromOutput),
497
+ });
498
+ }
499
+ export async function* executeRegisteredTool(args) {
500
+ const { profile, name, input, callId, ctx, snapshot, stages } = args;
501
+ const tool = getTool(name);
502
+ const safeInput = plainToolInput(input);
503
+ const base = {
504
+ name,
505
+ callId,
506
+ arguments: typeof safeInput === 'object' && safeInput !== null && !Array.isArray(safeInput)
507
+ ? safeInput
508
+ : { value: safeInput },
509
+ };
510
+ if (!tool) {
511
+ const failure = {
512
+ code: 'unknown_tool',
513
+ message: lexiconText('tool.not_registered', { tool: name }),
514
+ };
515
+ yield failureEvent(base, failure);
516
+ return earlyFailure(failure);
517
+ }
518
+ if (tool.type === 'builtin') {
519
+ if (!snapshot) {
520
+ const failure = {
521
+ code: 'provider_native',
522
+ message: lexiconText('tool.builtin_needs_snapshot', { tool: name }),
523
+ };
524
+ yield failureEvent(base, failure);
525
+ return earlyFailure(failure);
526
+ }
527
+ const fullCtx = { ...ctx, callId, profile };
528
+ return yield* executeBuiltin(tool, fullCtx, base, snapshot);
529
+ }
530
+ const eligibility = registeredEligibilityFailure({
531
+ tool,
532
+ profile,
533
+ name,
534
+ resume: ctx.resume,
535
+ snapshot,
536
+ });
537
+ if (eligibility) {
538
+ yield failureEvent(base, eligibility);
539
+ return earlyFailure(eligibility);
540
+ }
541
+ // Host denied after a gate — synthetic failure + post_tool, no body.
542
+ if (isGateResumeDenied(ctx.resume)) {
543
+ return yield* settleToolFailure(resultGuard(tool, { ...ctx, callId, profile }, snapshot), base, { code: 'denied', message: lexiconText('session.tool_denied', { tool: name }) }, stages, { toolName: name, callId, input: safeInput, callNotStarted: true });
544
+ }
545
+ const fullCtx = { ...ctx, callId, profile };
546
+ const policy = resolveGuardrailPolicy(profile.guardrails);
547
+ const provenance = provenanceFor(tool);
548
+ const argEvent = toolCallEvent(inspectToolArguments(safeInput, policy), provenance);
549
+ if (argEvent) {
550
+ yield { type: 'guardrail', guardrail: argEvent };
551
+ }
552
+ const taintVerdict = checkTaintGate(ctx.turn?.taint, tool.access, policy);
553
+ const taintEvent = toolCallEvent(taintVerdict, provenance);
554
+ if (taintEvent) {
555
+ yield { type: 'guardrail', guardrail: taintEvent };
556
+ }
557
+ if (taintVerdict.action === 'block') {
558
+ const failure = {
559
+ code: 'tainted_turn',
560
+ message: taintVerdict.rejection,
561
+ };
562
+ yield failureEvent(base, failure);
563
+ return earlyFailure(failure);
564
+ }
565
+ return yield* settleByType(tool, safeInput, fullCtx, base, snapshot, stages);
566
+ }
567
+ /** Run the body for the tool's transport and settle it through `settleToolCall`. */
568
+ async function* settleByType(tool, safeInput, fullCtx, base, snapshot, stages) {
569
+ const { name } = tool;
570
+ if (tool.type === 'function') {
571
+ return yield* executeFunction(tool, safeInput, fullCtx, base, snapshot, stages);
572
+ }
573
+ if (tool.type !== 'http' && tool.type !== 'mcp') {
574
+ return earlyFailure({
575
+ code: 'unknown_tool',
576
+ message: lexiconText('tool.unsupported_type', { tool: name }),
577
+ });
578
+ }
579
+ // HTTP / MCP: schema → permission → auth → preTool → body (inside remote).
580
+ const remoteOutcome = tool.type === 'http'
581
+ ? yield* executeHttpTool(tool, safeInput, fullCtx, base, stages)
582
+ : yield* executeMcpTool(tool, safeInput, fullCtx, base, stages);
583
+ return yield* settleRemoteOutcome({
584
+ outcome: remoteOutcome,
585
+ tool,
586
+ base,
587
+ callId: base.callId ?? '',
588
+ safeInput,
589
+ stages,
590
+ guard: resultGuard(tool, fullCtx, snapshot),
591
+ });
592
+ }
593
+ /**
594
+ * Guard a tool result before it becomes model context.
595
+ *
596
+ * Every tool returns through here, so the fence, the redaction, and the
597
+ * provenance label are applied once and cannot be skipped by adding a tool type.
598
+ */
599
+ function* guardResult(result, provenance, policy, callableTools) {
600
+ const guarded = guardToolResult(result.finding, result.data, provenance, policy, callableTools);
601
+ if (guarded.event) {
602
+ yield { type: 'guardrail', guardrail: guarded.event };
603
+ }
604
+ return {
605
+ ...result,
606
+ modelText: guarded.text,
607
+ provenance,
608
+ ...(guarded.suspicious ? { suspicious: guarded.suspicious } : {}),
609
+ };
610
+ }
611
+ export { startToolExecution };
612
+ export function newCallId(name) {
613
+ return `call_${name}_${Date.now()}`;
614
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Built-in harness tools registered with THEORUM.
3
+ *
4
+ * @module
5
+ */
6
+ /** Register harness tools shipped with THEORUM. */
7
+ declare function registerHarnessTools(): void;
8
+ export { registerHarnessTools };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Built-in harness tools registered with THEORUM.
3
+ *
4
+ * @module
5
+ */
6
+ import { z } from 'zod';
7
+ import { AWAITING_USER_INPUT_STATUS } from '../schema.js';
8
+ import { registerTool } from './registry.js';
9
+ const AskInputSchema = z.object({
10
+ kind: z.enum(['confirm', 'choice', 'text']),
11
+ prompt: z.string().trim().min(1),
12
+ options: z.array(z.string()).optional(),
13
+ });
14
+ const AskOutputSchema = z.object({
15
+ status: z.literal(AWAITING_USER_INPUT_STATUS),
16
+ kind: z.enum(['confirm', 'choice', 'text']),
17
+ prompt: z.string().trim().min(1),
18
+ options: z.array(z.string()).optional(),
19
+ });
20
+ /** Register harness tools shipped with THEORUM. */
21
+ function registerHarnessTools() {
22
+ registerTool({
23
+ type: 'function',
24
+ name: 'ask_user',
25
+ description: 'Ask the user a question (completes with awaiting_user_input)', // lexicon-exempt: developer contract / internal diagnostic — not end-user or model copy (P2)
26
+ category: 'conversation',
27
+ access: 'read-only',
28
+ paths: ['*'],
29
+ loadTier: 'T0',
30
+ permission: 'auto',
31
+ input: AskInputSchema,
32
+ output: AskOutputSchema,
33
+ handler: (input) => {
34
+ const out = {
35
+ status: AWAITING_USER_INPUT_STATUS,
36
+ kind: input.kind,
37
+ prompt: input.prompt,
38
+ };
39
+ if (input.kind === 'choice' && input.options?.length) {
40
+ out.options = input.options;
41
+ }
42
+ return out;
43
+ },
44
+ });
45
+ }
46
+ export { registerHarnessTools };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Host-initiated tool execution entrypoint.
3
+ *
4
+ * @module
5
+ */
6
+ import type { TurnEvent } from '../types.js';
7
+ import type { InvokeToolRequest } from './types.js';
8
+ /** Execute a registered tool without calling a model provider. */
9
+ declare function invokeTool(request: InvokeToolRequest): AsyncGenerator<TurnEvent>;
10
+ export { invokeTool };