@nhtio/adk 0.1.0-master-2a4e1bd2 → 0.1.0-master-cd595e6f

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 (413) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/artifact_methods-CJDQ5Lv-.mjs +1 -0
  3. package/artifact_methods-D6k_82r3.js +1 -0
  4. package/batteries/artifacts/ecmascript.cjs +1 -1
  5. package/batteries/artifacts/ecmascript.mjs +1 -1
  6. package/batteries/artifacts/toon.cjs +1 -1
  7. package/batteries/artifacts/toon.mjs +1 -1
  8. package/batteries/artifacts/xml.cjs +1 -1
  9. package/batteries/artifacts/xml.mjs +1 -1
  10. package/batteries/artifacts/yaml.cjs +1 -1
  11. package/batteries/artifacts/yaml.mjs +1 -1
  12. package/batteries/artifacts.cjs +4 -4
  13. package/batteries/artifacts.mjs +4 -4
  14. package/batteries/dev-tools/forge.cjs +3 -3
  15. package/batteries/dev-tools/forge.mjs +3 -3
  16. package/batteries/dev-tools.cjs +2 -2
  17. package/batteries/dev-tools.mjs +2 -2
  18. package/batteries/encoding.cjs +7 -7
  19. package/batteries/encoding.mjs +7 -7
  20. package/batteries/generation/gemini/adapter.cjs +1 -1
  21. package/batteries/generation/gemini/adapter.mjs +1 -1
  22. package/batteries/generation/local_diffusion/adapter.cjs +1 -1
  23. package/batteries/generation/local_diffusion/adapter.mjs +1 -1
  24. package/batteries/generation/openai/adapter.cjs +1 -1
  25. package/batteries/generation/openai/adapter.mjs +1 -1
  26. package/batteries/isolation/browser.d.ts +1 -1
  27. package/batteries/isolation/child_process/transport.d.ts +2 -2
  28. package/batteries/isolation/child_process.cjs +4 -4
  29. package/batteries/isolation/child_process.cjs.map +1 -1
  30. package/batteries/isolation/child_process.mjs +4 -4
  31. package/batteries/isolation/child_process.mjs.map +1 -1
  32. package/batteries/isolation/codec.d.ts +2 -2
  33. package/batteries/isolation/crash_policy.d.ts +1 -1
  34. package/batteries/isolation/isolate_function.d.ts +2 -2
  35. package/batteries/isolation/protocol.d.ts +4 -4
  36. package/batteries/isolation/serve.d.ts +2 -2
  37. package/batteries/isolation/types.d.ts +4 -4
  38. package/batteries/isolation.cjs +1 -1
  39. package/batteries/isolation.mjs +1 -1
  40. package/batteries/llm/anthropic_messages/adapter.cjs +6 -6
  41. package/batteries/llm/anthropic_messages/adapter.cjs.map +1 -1
  42. package/batteries/llm/anthropic_messages/adapter.mjs +6 -6
  43. package/batteries/llm/anthropic_messages/adapter.mjs.map +1 -1
  44. package/batteries/llm/anthropic_messages/count_tokens.cjs +1 -1
  45. package/batteries/llm/anthropic_messages/count_tokens.mjs +1 -1
  46. package/batteries/llm/anthropic_messages/helpers.cjs +3 -3
  47. package/batteries/llm/anthropic_messages/helpers.mjs +3 -3
  48. package/batteries/llm/anthropic_messages/types.d.ts +2 -2
  49. package/batteries/llm/anthropic_messages/validation.cjs +3 -3
  50. package/batteries/llm/anthropic_messages/validation.cjs.map +1 -1
  51. package/batteries/llm/anthropic_messages/validation.mjs +3 -3
  52. package/batteries/llm/anthropic_messages/validation.mjs.map +1 -1
  53. package/batteries/llm/anthropic_messages.cjs +2 -2
  54. package/batteries/llm/anthropic_messages.mjs +2 -2
  55. package/batteries/llm/bedrock_converse/adapter.cjs +29 -7
  56. package/batteries/llm/bedrock_converse/adapter.cjs.map +1 -1
  57. package/batteries/llm/bedrock_converse/adapter.mjs +30 -8
  58. package/batteries/llm/bedrock_converse/adapter.mjs.map +1 -1
  59. package/batteries/llm/bedrock_converse/exceptions.cjs +3 -0
  60. package/batteries/llm/bedrock_converse/exceptions.cjs.map +1 -1
  61. package/batteries/llm/bedrock_converse/exceptions.d.ts +6 -0
  62. package/batteries/llm/bedrock_converse/exceptions.mjs +3 -1
  63. package/batteries/llm/bedrock_converse/exceptions.mjs.map +1 -1
  64. package/batteries/llm/bedrock_converse/helpers.cjs +1 -1
  65. package/batteries/llm/bedrock_converse/helpers.mjs +1 -1
  66. package/batteries/llm/bedrock_converse/types.d.ts +6 -2
  67. package/batteries/llm/bedrock_converse/validation.cjs +7 -1
  68. package/batteries/llm/bedrock_converse/validation.cjs.map +1 -1
  69. package/batteries/llm/bedrock_converse/validation.mjs +7 -1
  70. package/batteries/llm/bedrock_converse/validation.mjs.map +1 -1
  71. package/batteries/llm/bedrock_converse.cjs +2 -2
  72. package/batteries/llm/bedrock_converse.mjs +2 -2
  73. package/batteries/llm/claude_code_cli/adapter.cjs +6 -6
  74. package/batteries/llm/claude_code_cli/adapter.cjs.map +1 -1
  75. package/batteries/llm/claude_code_cli/adapter.mjs +6 -6
  76. package/batteries/llm/claude_code_cli/adapter.mjs.map +1 -1
  77. package/batteries/llm/claude_code_cli/helpers.cjs +3 -3
  78. package/batteries/llm/claude_code_cli/helpers.mjs +3 -3
  79. package/batteries/llm/claude_code_cli/types.d.ts +3 -2
  80. package/batteries/llm/claude_code_cli/validation.cjs +3 -3
  81. package/batteries/llm/claude_code_cli/validation.cjs.map +1 -1
  82. package/batteries/llm/claude_code_cli/validation.mjs +3 -3
  83. package/batteries/llm/claude_code_cli/validation.mjs.map +1 -1
  84. package/batteries/llm/claude_code_cli.cjs +1 -1
  85. package/batteries/llm/claude_code_cli.mjs +1 -1
  86. package/batteries/llm/gemini_generate_content/adapter.cjs +29 -7
  87. package/batteries/llm/gemini_generate_content/adapter.cjs.map +1 -1
  88. package/batteries/llm/gemini_generate_content/adapter.mjs +30 -8
  89. package/batteries/llm/gemini_generate_content/adapter.mjs.map +1 -1
  90. package/batteries/llm/gemini_generate_content/exceptions.cjs +3 -0
  91. package/batteries/llm/gemini_generate_content/exceptions.cjs.map +1 -1
  92. package/batteries/llm/gemini_generate_content/exceptions.d.ts +6 -0
  93. package/batteries/llm/gemini_generate_content/exceptions.mjs +3 -1
  94. package/batteries/llm/gemini_generate_content/exceptions.mjs.map +1 -1
  95. package/batteries/llm/gemini_generate_content/helpers.cjs +1 -1
  96. package/batteries/llm/gemini_generate_content/helpers.mjs +1 -1
  97. package/batteries/llm/gemini_generate_content/types.d.ts +7 -3
  98. package/batteries/llm/gemini_generate_content/validation.cjs +11 -2
  99. package/batteries/llm/gemini_generate_content/validation.cjs.map +1 -1
  100. package/batteries/llm/gemini_generate_content/validation.mjs +11 -2
  101. package/batteries/llm/gemini_generate_content/validation.mjs.map +1 -1
  102. package/batteries/llm/gemini_generate_content.cjs +1 -1
  103. package/batteries/llm/gemini_generate_content.mjs +1 -1
  104. package/batteries/llm/litert_lm/adapter.cjs +6 -6
  105. package/batteries/llm/litert_lm/adapter.cjs.map +1 -1
  106. package/batteries/llm/litert_lm/adapter.mjs +6 -6
  107. package/batteries/llm/litert_lm/adapter.mjs.map +1 -1
  108. package/batteries/llm/litert_lm/helpers.cjs +3 -3
  109. package/batteries/llm/litert_lm/helpers.mjs +3 -3
  110. package/batteries/llm/litert_lm/types.d.ts +2 -2
  111. package/batteries/llm/litert_lm/validation.cjs +3 -3
  112. package/batteries/llm/litert_lm/validation.cjs.map +1 -1
  113. package/batteries/llm/litert_lm/validation.mjs +3 -3
  114. package/batteries/llm/litert_lm/validation.mjs.map +1 -1
  115. package/batteries/llm/litert_lm.cjs +1 -1
  116. package/batteries/llm/litert_lm.mjs +1 -1
  117. package/batteries/llm/ollama/adapter.cjs +6 -6
  118. package/batteries/llm/ollama/adapter.cjs.map +1 -1
  119. package/batteries/llm/ollama/adapter.mjs +6 -6
  120. package/batteries/llm/ollama/adapter.mjs.map +1 -1
  121. package/batteries/llm/ollama/helpers.cjs +4 -4
  122. package/batteries/llm/ollama/helpers.mjs +4 -4
  123. package/batteries/llm/ollama/types.d.ts +2 -2
  124. package/batteries/llm/ollama/validation.cjs +3 -3
  125. package/batteries/llm/ollama/validation.cjs.map +1 -1
  126. package/batteries/llm/ollama/validation.mjs +3 -3
  127. package/batteries/llm/ollama/validation.mjs.map +1 -1
  128. package/batteries/llm/ollama.cjs +2 -2
  129. package/batteries/llm/ollama.mjs +2 -2
  130. package/batteries/llm/openai_chat_completions/adapter.cjs +6 -6
  131. package/batteries/llm/openai_chat_completions/adapter.cjs.map +1 -1
  132. package/batteries/llm/openai_chat_completions/adapter.mjs +6 -6
  133. package/batteries/llm/openai_chat_completions/adapter.mjs.map +1 -1
  134. package/batteries/llm/openai_chat_completions/helpers.cjs +4 -4
  135. package/batteries/llm/openai_chat_completions/helpers.mjs +4 -4
  136. package/batteries/llm/openai_chat_completions/types.d.ts +2 -2
  137. package/batteries/llm/openai_chat_completions/validation.cjs +3 -3
  138. package/batteries/llm/openai_chat_completions/validation.cjs.map +1 -1
  139. package/batteries/llm/openai_chat_completions/validation.mjs +3 -3
  140. package/batteries/llm/openai_chat_completions/validation.mjs.map +1 -1
  141. package/batteries/llm/openai_chat_completions.cjs +2 -2
  142. package/batteries/llm/openai_chat_completions.mjs +2 -2
  143. package/batteries/llm/openai_responses/adapter.cjs +6 -6
  144. package/batteries/llm/openai_responses/adapter.cjs.map +1 -1
  145. package/batteries/llm/openai_responses/adapter.mjs +6 -6
  146. package/batteries/llm/openai_responses/adapter.mjs.map +1 -1
  147. package/batteries/llm/openai_responses/helpers.cjs +3 -3
  148. package/batteries/llm/openai_responses/helpers.mjs +3 -3
  149. package/batteries/llm/openai_responses/types.d.ts +2 -2
  150. package/batteries/llm/openai_responses/validation.cjs +3 -3
  151. package/batteries/llm/openai_responses/validation.cjs.map +1 -1
  152. package/batteries/llm/openai_responses/validation.mjs +3 -3
  153. package/batteries/llm/openai_responses/validation.mjs.map +1 -1
  154. package/batteries/llm/openai_responses.cjs +2 -2
  155. package/batteries/llm/openai_responses.mjs +2 -2
  156. package/batteries/llm/transformers_js/adapter.cjs +6 -6
  157. package/batteries/llm/transformers_js/adapter.cjs.map +1 -1
  158. package/batteries/llm/transformers_js/adapter.mjs +6 -6
  159. package/batteries/llm/transformers_js/adapter.mjs.map +1 -1
  160. package/batteries/llm/transformers_js/helpers.cjs +3 -3
  161. package/batteries/llm/transformers_js/helpers.mjs +3 -3
  162. package/batteries/llm/transformers_js/types.d.ts +2 -2
  163. package/batteries/llm/transformers_js/validation.cjs +3 -3
  164. package/batteries/llm/transformers_js/validation.cjs.map +1 -1
  165. package/batteries/llm/transformers_js/validation.mjs +3 -3
  166. package/batteries/llm/transformers_js/validation.mjs.map +1 -1
  167. package/batteries/llm/transformers_js.cjs +1 -1
  168. package/batteries/llm/transformers_js.mjs +1 -1
  169. package/batteries/llm/webllm_chat_completions/adapter.cjs +6 -6
  170. package/batteries/llm/webllm_chat_completions/adapter.cjs.map +1 -1
  171. package/batteries/llm/webllm_chat_completions/adapter.mjs +6 -6
  172. package/batteries/llm/webllm_chat_completions/adapter.mjs.map +1 -1
  173. package/batteries/llm/webllm_chat_completions/helpers.cjs +1 -1
  174. package/batteries/llm/webllm_chat_completions/helpers.mjs +1 -1
  175. package/batteries/llm/webllm_chat_completions/validation.cjs +3 -3
  176. package/batteries/llm/webllm_chat_completions/validation.cjs.map +1 -1
  177. package/batteries/llm/webllm_chat_completions/validation.mjs +3 -3
  178. package/batteries/llm/webllm_chat_completions/validation.mjs.map +1 -1
  179. package/batteries/llm/webllm_chat_completions.cjs +2 -2
  180. package/batteries/llm/webllm_chat_completions.mjs +2 -2
  181. package/batteries/llm.cjs +2 -2
  182. package/batteries/llm.mjs +2 -2
  183. package/batteries/media/builder.d.ts +3 -0
  184. package/batteries/media/contracts.cjs.map +1 -1
  185. package/batteries/media/contracts.d.ts +40 -1
  186. package/batteries/media/contracts.mjs.map +1 -1
  187. package/batteries/media/engines/sharp.cjs +36 -2
  188. package/batteries/media/engines/sharp.cjs.map +1 -1
  189. package/batteries/media/engines/sharp.mjs +36 -2
  190. package/batteries/media/engines/sharp.mjs.map +1 -1
  191. package/batteries/media/forge.cjs +5 -5
  192. package/batteries/media/forge.mjs +5 -5
  193. package/batteries/media/index.d.ts +1 -1
  194. package/batteries/media/lint.cjs +3 -2
  195. package/batteries/media/lint.cjs.map +1 -1
  196. package/batteries/media/lint.mjs +3 -2
  197. package/batteries/media/lint.mjs.map +1 -1
  198. package/batteries/media.cjs +66 -2
  199. package/batteries/media.cjs.map +1 -1
  200. package/batteries/media.mjs +66 -2
  201. package/batteries/media.mjs.map +1 -1
  202. package/batteries/orchestration/conformance.cjs +123 -74
  203. package/batteries/orchestration/conformance.cjs.map +1 -1
  204. package/batteries/orchestration/conformance.d.ts +10 -8
  205. package/batteries/orchestration/conformance.mjs +54 -5
  206. package/batteries/orchestration/conformance.mjs.map +1 -1
  207. package/batteries/orchestration/dispatch_reasoner.cjs +4 -4
  208. package/batteries/orchestration/dispatch_reasoner.mjs +4 -4
  209. package/batteries/orchestration/executor.cjs +2 -2
  210. package/batteries/orchestration/executor.mjs +2 -2
  211. package/batteries/orchestration/forge.cjs +4 -4
  212. package/batteries/orchestration/forge.mjs +4 -4
  213. package/batteries/orchestration/validation.cjs +2 -2
  214. package/batteries/orchestration/validation.mjs +2 -2
  215. package/batteries/orchestration.cjs +4 -4
  216. package/batteries/orchestration.mjs +4 -4
  217. package/batteries/sandbox/contracts/policy_enforcer.d.ts +13 -1
  218. package/batteries/sandbox/js/compartment.d.ts +7 -2
  219. package/batteries/sandbox/js/exceptions.d.ts +4 -0
  220. package/batteries/sandbox/js/runner.d.ts +1 -1
  221. package/batteries/sandbox/js/service.d.ts +1 -1
  222. package/batteries/sandbox/js/ses_contracts.d.ts +4 -3
  223. package/batteries/sandbox/js.cjs +17 -8
  224. package/batteries/sandbox/js.cjs.map +1 -1
  225. package/batteries/sandbox/js.mjs +17 -9
  226. package/batteries/sandbox/js.mjs.map +1 -1
  227. package/batteries/sandbox/manager.d.ts +4 -3
  228. package/batteries/sandbox/node/srt_enforcer.d.ts +1 -1
  229. package/batteries/sandbox/node.cjs +36 -12
  230. package/batteries/sandbox/node.cjs.map +1 -1
  231. package/batteries/sandbox/node.mjs +36 -12
  232. package/batteries/sandbox/node.mjs.map +1 -1
  233. package/batteries/sandbox/tools.cjs +1 -1
  234. package/batteries/sandbox/tools.mjs +1 -1
  235. package/batteries/sandbox/types.d.ts +10 -1
  236. package/batteries/sandbox.cjs +27 -9
  237. package/batteries/sandbox.cjs.map +1 -1
  238. package/batteries/sandbox.mjs +28 -11
  239. package/batteries/sandbox.mjs.map +1 -1
  240. package/batteries/skills.cjs +9 -9
  241. package/batteries/skills.mjs +9 -9
  242. package/batteries/tools/_shared.cjs +1 -1
  243. package/batteries/tools/_shared.mjs +1 -1
  244. package/batteries/tools/color.cjs +2 -2
  245. package/batteries/tools/color.mjs +2 -2
  246. package/batteries/tools/comparison.cjs +2 -2
  247. package/batteries/tools/comparison.mjs +2 -2
  248. package/batteries/tools/data_structure.cjs +2 -2
  249. package/batteries/tools/data_structure.mjs +2 -2
  250. package/batteries/tools/datetime_extended.cjs +3 -3
  251. package/batteries/tools/datetime_extended.mjs +3 -3
  252. package/batteries/tools/datetime_math.cjs +3 -3
  253. package/batteries/tools/datetime_math.mjs +3 -3
  254. package/batteries/tools/encoding.cjs +2 -2
  255. package/batteries/tools/encoding.mjs +2 -2
  256. package/batteries/tools/formatting.cjs +2 -2
  257. package/batteries/tools/formatting.mjs +2 -2
  258. package/batteries/tools/geo_basics.cjs +2 -2
  259. package/batteries/tools/geo_basics.mjs +2 -2
  260. package/batteries/tools/math.cjs +2 -2
  261. package/batteries/tools/math.mjs +2 -2
  262. package/batteries/tools/memory.cjs +5 -5
  263. package/batteries/tools/memory.mjs +5 -5
  264. package/batteries/tools/parsing.cjs +3 -3
  265. package/batteries/tools/parsing.mjs +3 -3
  266. package/batteries/tools/retrievables.cjs +5 -5
  267. package/batteries/tools/retrievables.mjs +5 -5
  268. package/batteries/tools/scrapper.cjs +1 -1
  269. package/batteries/tools/scrapper.mjs +1 -1
  270. package/batteries/tools/searxng.cjs +1 -1
  271. package/batteries/tools/searxng.mjs +1 -1
  272. package/batteries/tools/standing_instructions.cjs +3 -3
  273. package/batteries/tools/standing_instructions.mjs +3 -3
  274. package/batteries/tools/statistics.cjs +3 -3
  275. package/batteries/tools/statistics.mjs +3 -3
  276. package/batteries/tools/string_processing.cjs +2 -2
  277. package/batteries/tools/string_processing.mjs +2 -2
  278. package/batteries/tools/structured_data.cjs +2 -2
  279. package/batteries/tools/structured_data.mjs +2 -2
  280. package/batteries/tools/text_analysis.cjs +3 -3
  281. package/batteries/tools/text_analysis.mjs +3 -3
  282. package/batteries/tools/text_comparison.cjs +2 -2
  283. package/batteries/tools/text_comparison.mjs +2 -2
  284. package/batteries/tools/time.cjs +3 -3
  285. package/batteries/tools/time.mjs +3 -3
  286. package/batteries/tools/unit_conversion.cjs +2 -2
  287. package/batteries/tools/unit_conversion.mjs +2 -2
  288. package/batteries/tools.cjs +2 -2
  289. package/batteries/tools.mjs +2 -2
  290. package/batteries/validation.cjs +5 -5
  291. package/batteries/validation.mjs +5 -5
  292. package/batteries/vector/conformance/index.d.ts +11 -16
  293. package/batteries/vector/conformance.cjs +107 -36
  294. package/batteries/vector/conformance.cjs.map +1 -1
  295. package/batteries/vector/conformance.mjs +84 -13
  296. package/batteries/vector/conformance.mjs.map +1 -1
  297. package/batteries/vector/retrievable.cjs +1 -1
  298. package/batteries/vector/retrievable.mjs +1 -1
  299. package/batteries.cjs +5 -5
  300. package/batteries.mjs +5 -5
  301. package/{chat_common-DbbIESdo.mjs → chat_common-CmKV5Dz4.mjs} +2 -2
  302. package/{chat_common-DbbIESdo.mjs.map → chat_common-CmKV5Dz4.mjs.map} +1 -1
  303. package/{chat_common-B3O3zz-i.js → chat_common-Dpan9moo.js} +2 -2
  304. package/{chat_common-B3O3zz-i.js.map → chat_common-Dpan9moo.js.map} +1 -1
  305. package/{common-v9Jo9M5k.mjs → common-BpdHIpQE.mjs} +7 -7
  306. package/{common-v9Jo9M5k.mjs.map → common-BpdHIpQE.mjs.map} +1 -1
  307. package/{common-BV3HGeLj.js → common-H44Ik9ou.js} +7 -7
  308. package/{common-BV3HGeLj.js.map → common-H44Ik9ou.js.map} +1 -1
  309. package/common.cjs +7 -7
  310. package/common.mjs +7 -7
  311. package/{dispatch_runner-CbNyqSMy.mjs → dispatch_runner-BZDXUe0G.mjs} +5 -5
  312. package/{dispatch_runner-CbNyqSMy.mjs.map → dispatch_runner-BZDXUe0G.mjs.map} +1 -1
  313. package/{dispatch_runner-Bl3Xwebu.js → dispatch_runner-CKhm-scw.js} +5 -5
  314. package/{dispatch_runner-Bl3Xwebu.js.map → dispatch_runner-CKhm-scw.js.map} +1 -1
  315. package/dispatch_runner.cjs +1 -1
  316. package/dispatch_runner.mjs +1 -1
  317. package/{ecmascript-BSnvyY8T.js → ecmascript-CCtgamDh.js} +3 -3
  318. package/{ecmascript-BSnvyY8T.js.map → ecmascript-CCtgamDh.js.map} +1 -1
  319. package/{ecmascript-CZ7_YKfZ.mjs → ecmascript-DcmUQ9Jx.mjs} +3 -3
  320. package/{ecmascript-CZ7_YKfZ.mjs.map → ecmascript-DcmUQ9Jx.mjs.map} +1 -1
  321. package/eslint.cjs +1 -1
  322. package/eslint.mjs +1 -1
  323. package/exceptions.cjs +1 -1
  324. package/exceptions.mjs +1 -1
  325. package/forge.cjs +2 -2
  326. package/forge.mjs +2 -2
  327. package/guards.cjs +7 -7
  328. package/guards.mjs +7 -7
  329. package/{helpers-CDmLDBq5.mjs → helpers-BH7HTtkD.mjs} +3 -3
  330. package/{helpers-CDmLDBq5.mjs.map → helpers-BH7HTtkD.mjs.map} +1 -1
  331. package/{helpers-Tvvwpx1M.js → helpers-CEjjkXso.js} +3 -3
  332. package/{helpers-Tvvwpx1M.js.map → helpers-CEjjkXso.js.map} +1 -1
  333. package/index.cjs +10 -10
  334. package/index.mjs +10 -10
  335. package/{isolation-BQCTQYZ_.js → isolation-7Jrcucgc.js} +11 -11
  336. package/isolation-7Jrcucgc.js.map +1 -0
  337. package/{isolation-7xbkc12r.mjs → isolation-CO0NiK8R.mjs} +11 -11
  338. package/isolation-CO0NiK8R.mjs.map +1 -0
  339. package/lib/classes/tokenizable.d.ts +2 -3
  340. package/mcp/adk-docs-corpus.json +2 -2
  341. package/package.json +469 -469
  342. package/{retrievable-C5dXuDhD.js → retrievable-BTwjmPnl.js} +3 -3
  343. package/{retrievable-C5dXuDhD.js.map → retrievable-BTwjmPnl.js.map} +1 -1
  344. package/{retrievable-gl_TIHnX.mjs → retrievable-CXTAUuuv.mjs} +3 -3
  345. package/{retrievable-gl_TIHnX.mjs.map → retrievable-CXTAUuuv.mjs.map} +1 -1
  346. package/{scrapper-DD6-_2u-.js → scrapper-CJexXXK-.js} +4 -4
  347. package/{scrapper-DD6-_2u-.js.map → scrapper-CJexXXK-.js.map} +1 -1
  348. package/{scrapper-u_orpxIB.mjs → scrapper-DiQ5FfDL.mjs} +4 -4
  349. package/{scrapper-u_orpxIB.mjs.map → scrapper-DiQ5FfDL.mjs.map} +1 -1
  350. package/{searxng-fVdJUCK2.mjs → searxng-BFTJ6rTE.mjs} +4 -4
  351. package/{searxng-fVdJUCK2.mjs.map → searxng-BFTJ6rTE.mjs.map} +1 -1
  352. package/{searxng-C7GK0Nrz.js → searxng-DzlGZG4V.js} +4 -4
  353. package/{searxng-C7GK0Nrz.js.map → searxng-DzlGZG4V.js.map} +1 -1
  354. package/server.json +2 -2
  355. package/skills/adk-assembly/SKILL.md +2 -2
  356. package/{spooled_artifact-DriNQRj7.js → spooled_artifact-BpcesRBv.js} +384 -384
  357. package/spooled_artifact-BpcesRBv.js.map +1 -0
  358. package/{spooled_artifact-BkueYJhR.mjs → spooled_artifact-Bw_7swIp.mjs} +385 -385
  359. package/spooled_artifact-Bw_7swIp.mjs.map +1 -0
  360. package/spooled_artifact.cjs +2 -2
  361. package/spooled_artifact.mjs +2 -2
  362. package/{spooled_markdown_artifact-CfHRZUkl.mjs → spooled_markdown_artifact-vd6eNtIP.mjs} +3 -3
  363. package/{spooled_markdown_artifact-CfHRZUkl.mjs.map → spooled_markdown_artifact-vd6eNtIP.mjs.map} +1 -1
  364. package/{spooled_markdown_artifact-C_VkCDhF.js → spooled_markdown_artifact-wJPnE8Ij.js} +3 -3
  365. package/{spooled_markdown_artifact-C_VkCDhF.js.map → spooled_markdown_artifact-wJPnE8Ij.js.map} +1 -1
  366. package/{thought-B8JAi3-W.mjs → thought-BE1KGEhV.mjs} +3 -3
  367. package/{thought-B8JAi3-W.mjs.map → thought-BE1KGEhV.mjs.map} +1 -1
  368. package/{thought--fRgOwTP.js → thought-bS7O9O5H.js} +3 -3
  369. package/{thought--fRgOwTP.js.map → thought-bS7O9O5H.js.map} +1 -1
  370. package/{tokenizable-CWW7iSrD.js → tokenizable-BMZuGWRd.js} +35 -29
  371. package/tokenizable-BMZuGWRd.js.map +1 -0
  372. package/{tokenizable-DLMP68ZV.mjs → tokenizable-VB4Av8GF.mjs} +36 -30
  373. package/tokenizable-VB4Av8GF.mjs.map +1 -0
  374. package/{tool_call-CPjyvkm4.mjs → tool_call-Cfaj-6zK.mjs} +4 -4
  375. package/{tool_call-CPjyvkm4.mjs.map → tool_call-Cfaj-6zK.mjs.map} +1 -1
  376. package/{tool_call-DgqVwL5n.js → tool_call-Fs_Uh5wQ.js} +4 -4
  377. package/{tool_call-DgqVwL5n.js.map → tool_call-Fs_Uh5wQ.js.map} +1 -1
  378. package/{tools-BmLzkvNr.mjs → tools-B3_8C78Q.mjs} +6 -6
  379. package/{tools-BmLzkvNr.mjs.map → tools-B3_8C78Q.mjs.map} +1 -1
  380. package/{tools-DmcIe2Cb.js → tools-CqOcmHR8.js} +6 -6
  381. package/{tools-DmcIe2Cb.js.map → tools-CqOcmHR8.js.map} +1 -1
  382. package/{toon-BvTQsKdN.mjs → toon-BSlr88px.mjs} +5 -5
  383. package/{toon-BvTQsKdN.mjs.map → toon-BSlr88px.mjs.map} +1 -1
  384. package/{toon-B05eKx-q.js → toon-D71tkyI3.js} +5 -5
  385. package/{toon-B05eKx-q.js.map → toon-D71tkyI3.js.map} +1 -1
  386. package/{turn_runner-DEGCvfVZ.mjs → turn_runner-BHHD6h77.mjs} +5 -5
  387. package/{turn_runner-DEGCvfVZ.mjs.map → turn_runner-BHHD6h77.mjs.map} +1 -1
  388. package/{turn_runner-DT8TseR4.js → turn_runner-DhLI7OTH.js} +5 -5
  389. package/{turn_runner-DT8TseR4.js.map → turn_runner-DhLI7OTH.js.map} +1 -1
  390. package/turn_runner.cjs +1 -1
  391. package/turn_runner.mjs +1 -1
  392. package/types-D6X_aUIb.mjs.map +1 -1
  393. package/types-DrSosVaM.js.map +1 -1
  394. package/{validate-lzy9nG_E.mjs → validate-Bi7FGjoq.mjs} +13 -1
  395. package/{validate-lzy9nG_E.mjs.map → validate-Bi7FGjoq.mjs.map} +1 -1
  396. package/{validate-CdOS7S2u.js → validate-CkQI-48O.js} +13 -1
  397. package/{validate-CdOS7S2u.js.map → validate-CkQI-48O.js.map} +1 -1
  398. package/{xml-DDTIMpY3.js → xml-BeNvlifb.js} +5 -5
  399. package/{xml-DDTIMpY3.js.map → xml-BeNvlifb.js.map} +1 -1
  400. package/{xml-VhJ4NrMB.mjs → xml-vyCZZiF_.mjs} +5 -5
  401. package/{xml-VhJ4NrMB.mjs.map → xml-vyCZZiF_.mjs.map} +1 -1
  402. package/{yaml-Cx16IYJq.js → yaml-DhLLa2cj.js} +5 -5
  403. package/{yaml-Cx16IYJq.js.map → yaml-DhLLa2cj.js.map} +1 -1
  404. package/{yaml-Du4wFGzL.mjs → yaml-Dtz0Bd7J.mjs} +5 -5
  405. package/{yaml-Du4wFGzL.mjs.map → yaml-Dtz0Bd7J.mjs.map} +1 -1
  406. package/artifact_methods-BMCahBLn.mjs +0 -1
  407. package/artifact_methods-f_bSy4kX.js +0 -1
  408. package/isolation-7xbkc12r.mjs.map +0 -1
  409. package/isolation-BQCTQYZ_.js.map +0 -1
  410. package/spooled_artifact-BkueYJhR.mjs.map +0 -1
  411. package/spooled_artifact-DriNQRj7.js.map +0 -1
  412. package/tokenizable-CWW7iSrD.js.map +0 -1
  413. package/tokenizable-DLMP68ZV.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"turn_runner-DT8TseR4.js","names":["#id","#turnId","#reason","#payload","#createdAt","#settled","#schema","#controller","#promise","#resolve","#reject","#config","#inputPipeline","#outputPipeline","#functionalEmitter","#observabilityEmitter"],"sources":["../src/lib/classes/turn_gate.ts","../src/lib/contracts/turn_runner_config.ts","../src/lib/turn_runner.ts"],"sourcesContent":["import { DateTime } from 'luxon'\nimport { validator } from '@nhtio/validation'\nimport { validateOrThrow } from '../utils/validation'\nimport { isInstanceOf, isError, isObject } from '../utils/guards'\nimport {\n E_TURN_GATE_ABORTED,\n E_TURN_GATE_TIMEOUT,\n E_INVALID_TURN_GATE_RESOLUTION,\n E_INVALID_INITIAL_TURN_GATE_VALUE,\n} from '../exceptions/runtime'\nimport type { Schema } from '@nhtio/validation'\n\n/**\n * Plain input object supplied to {@link TurnGate} at construction time.\n *\n * @remarks\n * `turnId` and `abortSignal` are injected by the runner — callers constructing a gate via\n * `ctx.waitFor()` never supply them directly.\n *\n * `abortSignal` is `AbortSignal` (not `AbortController`) because the gate reacts to turn-level\n * cancellation but cannot trigger it. The gate owns its own internal `AbortController` for\n * `gate.abort()`.\n */\nexport interface RawTurnGate {\n /** Stable unique identifier for this gate. */\n id: string\n /** The ID of the turn that opened this gate. */\n turnId: string\n /** Human-readable label describing why this gate was opened (e.g. `'tool_approval'`). */\n reason: string\n /** Arbitrary data supplied to the gate opener; passed through to `turnGateOpen` listeners. */\n payload: unknown\n /** Optional validator schema for the resolution value. When present, `resolve()` validates before settling. */\n schema?: Schema\n /** Optional timeout in milliseconds. When elapsed the gate self-rejects with {@link @nhtio/adk!E_TURN_GATE_TIMEOUT}. */\n timeout?: number\n /** The turn's abort signal. When fired the gate self-rejects with {@link @nhtio/adk!E_TURN_GATE_ABORTED}. */\n abortSignal?: AbortSignal\n /** When this gate was created. */\n createdAt: string | number | Date | DateTime\n}\n\n/**\n * Fully-resolved {@link RawTurnGate} after schema validation.\n *\n * @internal\n */\ninterface ResolvedTurnGate {\n id: string\n turnId: string\n reason: string\n payload: unknown\n schema?: Schema\n timeout?: number\n abortSignal?: AbortSignal\n createdAt: DateTime\n}\n\n/**\n * Validator schema used to validate a {@link RawTurnGate} before constructing a {@link TurnGate}.\n *\n * @remarks\n * - `schema` and `abortSignal` are validated as opaque passthrough values.\n * - `timeout` must be a positive integer when provided.\n */\nconst rawTurnGateSchema = validator.object<RawTurnGate>({\n id: validator.string().required(),\n turnId: validator.string().required(),\n reason: validator.string().required(),\n payload: validator.any().required(),\n schema: validator\n .any()\n .custom((value, helpers) => {\n if (value === undefined || value === null) return undefined\n if (typeof (value as any).validate === 'function') return value\n return helpers.error('any.invalid')\n })\n .optional(),\n timeout: validator.number().integer().min(1).optional(),\n abortSignal: validator\n .any()\n .custom((value, helpers) => {\n if (value === undefined || value === null) return undefined\n // eslint-disable-next-line adk/use-is-instance-of -- native built-in; AbortSignal cross-realm is handled by the duck-type fallback below\n if (typeof AbortSignal !== 'undefined' && value instanceof AbortSignal) return value\n if (\n isObject(value) &&\n typeof (value as any).aborted === 'boolean' &&\n typeof (value as any).addEventListener === 'function'\n ) {\n return value\n }\n return helpers.error('any.invalid')\n })\n .optional(),\n createdAt: validator.datetime().required(),\n})\n\n/**\n * A cooperative suspension gate that blocks a turn's middleware pipeline until resolved, rejected,\n * aborted, or timed out.\n *\n * @typeParam T - The expected type of the resolution value.\n *\n * @remarks\n * Created exclusively via `ctx.waitFor()` — middleware never constructs a gate directly.\n * The gate emits `turnGateOpen` on the runner's observability bus at creation time and\n * `turnGateClosed` when it settles.\n *\n * Resolution is validated against an optional schema before the internal promise is settled.\n * A validation failure throws {@link @nhtio/adk!E_INVALID_TURN_GATE_RESOLUTION} **synchronously in the\n * caller's context** — the promise is NOT settled and the gate remains open.\n */\nexport class TurnGate<T = unknown> {\n /**\n * Validator schema that accepts a {@link RawTurnGate} object.\n *\n * @remarks\n * Reusable fragment for any schema that needs to validate or nest a gate entry.\n */\n public static schema = rawTurnGateSchema\n\n /**\n * Returns `true` if `value` is a {@link TurnGate} instance.\n *\n * @remarks\n * Uses {@link @nhtio/adk!isInstanceOf} for cross-realm safety.\n *\n * @param value - The value to test.\n * @returns `true` when `value` is a {@link TurnGate} instance.\n */\n public static isTurnGate(value: unknown): value is TurnGate {\n return isInstanceOf(value, 'TurnGate', TurnGate)\n }\n\n /** Unique identifier for this gate instance. */\n declare readonly id: string\n /** Id of the turn this gate belongs to. */\n declare readonly turnId: string\n /** Human-readable reason the gate was opened. */\n declare readonly reason: string\n /** Optional caller-supplied payload describing what the gate is waiting on. */\n declare readonly payload: unknown\n /** When the gate was created. */\n declare readonly createdAt: DateTime\n /** Whether the gate has been settled (resolved or rejected) and no longer blocks the turn. */\n declare readonly isSettled: boolean\n\n #id: string\n #turnId: string\n #reason: string\n #payload: unknown\n #createdAt: DateTime\n #settled: boolean\n #schema: Schema | undefined\n #controller: AbortController\n #resolve!: (value: T) => void\n #reject!: (reason: unknown) => void\n #promise: Promise<T>\n\n /**\n * @param raw - The raw gate input validated against `rawTurnGateSchema`.\n * @throws {@link @nhtio/adk!E_INVALID_INITIAL_TURN_GATE_VALUE} when `raw` does not satisfy the schema.\n */\n constructor(raw: RawTurnGate) {\n let resolved: ResolvedTurnGate\n try {\n resolved = validateOrThrow<ResolvedTurnGate>(rawTurnGateSchema, raw, true)\n } catch (err) {\n throw new E_INVALID_INITIAL_TURN_GATE_VALUE({ cause: isError(err) ? err : undefined })\n }\n\n this.#id = resolved.id\n this.#turnId = resolved.turnId\n this.#reason = resolved.reason\n this.#payload = resolved.payload\n this.#createdAt = resolved.createdAt\n this.#settled = false\n this.#schema = resolved.schema\n this.#controller = new AbortController()\n\n this.#promise = new Promise<T>((resolve, reject) => {\n this.#resolve = resolve\n this.#reject = reject\n })\n\n // Wire the internal abort controller\n const onAbort = () => {\n if (!this.#settled) {\n this.#settled = true\n this.#reject(new E_TURN_GATE_ABORTED())\n }\n }\n\n this.#controller.signal.addEventListener('abort', onAbort, { once: true })\n\n // Wire the external turn abort signal\n if (resolved.abortSignal) {\n if (resolved.abortSignal.aborted) {\n // Already aborted — reject immediately after construction\n queueMicrotask(() => onAbort())\n } else {\n resolved.abortSignal.addEventListener('abort', onAbort, { once: true })\n // Clean up the external listener once the gate settles via another path\n this.#promise.then(\n () => resolved.abortSignal!.removeEventListener('abort', onAbort),\n () => resolved.abortSignal!.removeEventListener('abort', onAbort)\n )\n }\n }\n\n // Wire the timeout\n if (resolved.timeout !== undefined) {\n const timer = setTimeout(() => {\n if (!this.#settled) {\n this.#settled = true\n this.#reject(new E_TURN_GATE_TIMEOUT())\n }\n }, resolved.timeout)\n\n this.#promise.then(\n () => clearTimeout(timer),\n () => clearTimeout(timer)\n )\n }\n\n Object.defineProperties(this, {\n id: {\n get: () => this.#id,\n enumerable: true,\n configurable: false,\n },\n turnId: {\n get: () => this.#turnId,\n enumerable: true,\n configurable: false,\n },\n reason: {\n get: () => this.#reason,\n enumerable: true,\n configurable: false,\n },\n payload: {\n get: () => this.#payload,\n enumerable: true,\n configurable: false,\n },\n createdAt: {\n get: () => this.#createdAt,\n enumerable: true,\n configurable: false,\n },\n isSettled: {\n get: () => this.#settled,\n enumerable: true,\n configurable: false,\n },\n })\n }\n\n /**\n * Resolves the gate with `value`, unblocking the awaiting middleware.\n *\n * @remarks\n * If a schema was provided at construction, `value` is validated synchronously before the\n * promise is settled. A validation failure throws {@link @nhtio/adk!E_INVALID_TURN_GATE_RESOLUTION}\n * in the caller's context — the promise is NOT settled and the gate remains open.\n *\n * No-ops if the gate is already settled.\n *\n * @param value - The resolution value. Must satisfy the gate's schema when one was provided.\n * @throws {@link @nhtio/adk!E_INVALID_TURN_GATE_RESOLUTION} when `value` fails schema validation.\n */\n resolve(value: unknown): void {\n if (this.#settled) return\n if (this.#schema !== undefined) {\n try {\n value = validateOrThrow(this.#schema, value, true)\n } catch (err) {\n throw new E_INVALID_TURN_GATE_RESOLUTION({\n cause: isError(err) ? err : undefined,\n })\n }\n }\n this.#settled = true\n this.#resolve(value as T)\n }\n\n /**\n * Rejects the gate with `error`, unblocking the awaiting middleware with a rejection.\n *\n * @remarks\n * No-ops if the gate is already settled.\n *\n * @param error - The rejection reason.\n */\n reject(error: Error): void {\n if (this.#settled) return\n this.#settled = true\n this.#reject(error)\n }\n\n /**\n * Aborts the gate by firing the internal `AbortController`, which rejects the promise with\n * {@link @nhtio/adk!E_TURN_GATE_ABORTED}.\n *\n * @remarks\n * No-ops if the gate is already settled. Distinct from the turn-level abort signal — this\n * allows callers to cancel a specific gate without aborting the whole turn.\n */\n abort(): void {\n if (this.#settled) return\n this.#controller.abort()\n }\n\n /**\n * Returns the internal promise. Called by `ctx.waitFor()` to block the middleware pipeline.\n *\n * @internal\n */\n _promise(): Promise<T> {\n return this.#promise\n }\n}\n","import { Tool } from '../classes/tool'\nimport { validator } from '@nhtio/validation'\nimport type { TurnPipelineMiddlewareFn } from '../types/turn_runner'\nimport type { DispatchPipelineMiddlewareFn, DispatchExecutorFn } from '../types/dispatch_runner'\nimport type {\n MemoryRetrievalFn,\n MessageRetrievalFn,\n ThoughtRetrievalFn,\n ToolCallRetrievalFn,\n ToolsRetrievalFn,\n StandingInstructionsRefreshFn,\n StandingInstructionStoreFn,\n StandingInstructionMutateFn,\n StandingInstructionDeleteFn,\n MemoryStoreFn,\n MemoryMutateFn,\n MemoryDeleteFn,\n RetrievableRetrievalFn,\n RetrievableStoreFn,\n RetrievableMutateFn,\n RetrievableDeleteFn,\n MessageStoreFn,\n MessageMutateFn,\n MessageDeleteFn,\n ThoughtStoreFn,\n ThoughtMutateFn,\n ThoughtDeleteFn,\n ToolCallStoreFn,\n ToolCallMutateFn,\n ToolCallDeleteFn,\n ToolCallGroupReplaceFn,\n MediaBytesStoreFn,\n RetrievableBytesStoreFn,\n} from './turn_runner_context'\n\n/**\n * Configuration supplied to {@link @nhtio/adk!TurnRunner} at construction time.\n *\n * @remarks\n * Validated against `turnRunnerConfigSchema` at construction — a misconfigured runner throws\n * immediately rather than failing on the first turn.\n *\n * All fetch and mutation callbacks are required: they are injected into each {@link @nhtio/adk!TurnContext}\n * so middleware can call fetch, refresh, and persistence methods directly on the context without\n * coupling to the runner.\n *\n * `tools` is optional at the caller level and defaults to `[]` after schema resolution — a runner\n * with no baseline tools is valid.\n */\nexport interface TurnRunnerConfig {\n /** Performs the LLM API/SDK call for each iteration of the dispatch loop; receives the active {@link @nhtio/adk!DispatchContext} and an {@link @nhtio/adk!DispatchExecutorHelpers} object for managing per-id stream state. */\n executorCallback: DispatchExecutorFn\n /** Called once per turn to supply memories; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchMemoriesCallback: MemoryRetrievalFn\n /** Called once per turn to supply conversation history; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchMessagesCallback: MessageRetrievalFn\n /** Called once per turn to supply thought traces; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchThoughtsCallback: ThoughtRetrievalFn\n /** Called once per turn to supply tool call records; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchToolCallsCallback: ToolCallRetrievalFn\n /** Called to supply available tools; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchToolsCallback: ToolsRetrievalFn\n /** Called to refresh and return standing instructions; receives the active {@link @nhtio/adk!TurnContext}. */\n refreshStandingInstructionsCallback: StandingInstructionsRefreshFn\n /** Persists a new standing instruction. */\n storeStandingInstructionCallback: StandingInstructionStoreFn\n /** Updates an existing standing instruction in the persistence layer. */\n mutateStandingInstructionCallback: StandingInstructionMutateFn\n /** Removes a standing instruction from the persistence layer. */\n deleteStandingInstructionCallback: StandingInstructionDeleteFn\n /** Persists a new memory. */\n storeMemoryCallback: MemoryStoreFn\n /** Updates an existing memory in the persistence layer. */\n mutateMemoryCallback: MemoryMutateFn\n /** Removes a memory from the persistence layer by ID. */\n deleteMemoryCallback: MemoryDeleteFn\n /** Called once per turn to supply retrievable (RAG) records; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchRetrievablesCallback: RetrievableRetrievalFn\n /** Persists a new retrievable record. */\n storeRetrievableCallback: RetrievableStoreFn\n /** Updates an existing retrievable record in the persistence layer. */\n mutateRetrievableCallback: RetrievableMutateFn\n /** Removes a retrievable record from the persistence layer by ID. */\n deleteRetrievableCallback: RetrievableDeleteFn\n /** Persists a new message. */\n storeMessageCallback: MessageStoreFn\n /** Updates an existing message in the persistence layer. */\n mutateMessageCallback: MessageMutateFn\n /** Removes a message from the persistence layer by ID. */\n deleteMessageCallback: MessageDeleteFn\n /** Persists a new thought. */\n storeThoughtCallback: ThoughtStoreFn\n /** Updates an existing thought in the persistence layer. */\n mutateThoughtCallback: ThoughtMutateFn\n /** Removes a thought from the persistence layer by ID. */\n deleteThoughtCallback: ThoughtDeleteFn\n /** Persists a new tool call. */\n storeToolCallCallback: ToolCallStoreFn\n /** Updates an existing tool call in the persistence layer. */\n mutateToolCallCallback: ToolCallMutateFn\n /** Removes a tool call from the persistence layer by ID. */\n deleteToolCallCallback: ToolCallDeleteFn\n /** Optionally replaces a complete tool-call group atomically. */\n replaceToolCallGroupCallback?: ToolCallGroupReplaceFn\n /** Persists tool-generated media bytes into consumer storage; returns a `MediaReader`. */\n storeMediaBytesCallback: MediaBytesStoreFn\n /** Persists extracted retrievable text bytes into consumer storage; returns a `SpoolReader`. */\n storeRetrievableBytesCallback: RetrievableBytesStoreFn\n /** Baseline tools available on every turn. Middleware may trim or extend this per-turn via `ctx.tools`. Defaults to `[]`. */\n tools?: Tool[]\n /** Turn-level input middleware, executed in order against the {@link @nhtio/adk!TurnContext} before the LLM dispatch. Defaults to `[]`. */\n turnInputPipeline?: TurnPipelineMiddlewareFn[]\n /** Turn-level output middleware, executed in order against the {@link @nhtio/adk!TurnContext} after the LLM dispatch resolves successfully. Defaults to `[]`. */\n turnOutputPipeline?: TurnPipelineMiddlewareFn[]\n /** LLM-iteration input middleware, executed in order against the {@link @nhtio/adk!DispatchContext} before the executor on each iteration. Defaults to `[]`. */\n dispatchInputPipeline?: DispatchPipelineMiddlewareFn[]\n /** LLM-iteration output middleware, executed in order against the {@link @nhtio/adk!DispatchContext} after the executor on each iteration. Defaults to `[]`. */\n dispatchOutputPipeline?: DispatchPipelineMiddlewareFn[]\n}\n\n/**\n * Fully-resolved {@link TurnRunnerConfig} after schema validation.\n *\n * @remarks\n * All optional fields are guaranteed present (e.g. `tools` defaults to `[]`). The runner stores\n * this type internally so field access never needs to guard for undefined.\n */\nexport type ResolvedTurnRunnerConfig = Required<TurnRunnerConfig>\n\n/**\n * Validator schema used to validate a {@link TurnRunnerConfig} at {@link @nhtio/adk!TurnRunner} construction time.\n *\n * @remarks\n * Validates that all callbacks are functions of the correct arity, and that `tools` — when\n * provided — is an array of valid {@link @nhtio/adk!Tool} instances. Defaults `tools` to `[]`.\n *\n * Throws {@link @nhtio/adk!E_INVALID_TURN_RUNNER_CONFIG} (via the {@link @nhtio/adk!TurnRunner} constructor) when\n * validation fails.\n */\nexport const turnRunnerConfigSchema = validator.object<TurnRunnerConfig>({\n executorCallback: validator.function().required(),\n fetchMemoriesCallback: validator.function().arity(1).required(),\n fetchMessagesCallback: validator.function().arity(1).required(),\n fetchThoughtsCallback: validator.function().arity(1).required(),\n fetchToolCallsCallback: validator.function().arity(1).required(),\n fetchToolsCallback: validator.function().arity(1).required(),\n refreshStandingInstructionsCallback: validator.function().arity(1).required(),\n storeStandingInstructionCallback: validator.function().arity(2).required(),\n mutateStandingInstructionCallback: validator.function().arity(2).required(),\n deleteStandingInstructionCallback: validator.function().arity(2).required(),\n storeMemoryCallback: validator.function().arity(2).required(),\n mutateMemoryCallback: validator.function().arity(2).required(),\n deleteMemoryCallback: validator.function().arity(2).required(),\n fetchRetrievablesCallback: validator.function().arity(1).required(),\n storeRetrievableCallback: validator.function().arity(2).required(),\n mutateRetrievableCallback: validator.function().arity(2).required(),\n deleteRetrievableCallback: validator.function().arity(2).required(),\n storeMessageCallback: validator.function().arity(2).required(),\n mutateMessageCallback: validator.function().arity(2).required(),\n deleteMessageCallback: validator.function().arity(2).required(),\n storeThoughtCallback: validator.function().arity(2).required(),\n mutateThoughtCallback: validator.function().arity(2).required(),\n deleteThoughtCallback: validator.function().arity(2).required(),\n storeToolCallCallback: validator.function().arity(2).required(),\n mutateToolCallCallback: validator.function().arity(2).required(),\n deleteToolCallCallback: validator.function().arity(2).required(),\n replaceToolCallGroupCallback: validator.function().arity(3).optional(),\n storeMediaBytesCallback: validator.function().arity(3).required(),\n storeRetrievableBytesCallback: validator.function().arity(3).required(),\n tools: validator\n .array()\n .items(\n // `.optional()`, not `.required()`: this `.any()` is a type-argument inside `items(...)`,\n // describing what a tool element looks like — it must NOT impose array cardinality. A\n // `.required()` item turns the array into `array.includesRequiredUnknowns` (\"must contain\n // ≥1 tool\"), which contradicts the contract above (`tools` is optional, defaults to `[]`).\n // `.optional()` satisfies adk/require-validator-any-required by declaring disposition\n // explicitly while leaving the empty-array default valid.\n validator\n .any()\n .optional()\n .custom((value: unknown, helpers: { error: (code: string) => unknown }) => {\n if (Tool.isTool(value)) return value\n return helpers.error('any.invalid')\n })\n )\n .default([]),\n turnInputPipeline: validator.array().items(validator.function()).default([]),\n turnOutputPipeline: validator.array().items(validator.function()).default([]),\n dispatchInputPipeline: validator.array().items(validator.function()).default([]),\n dispatchOutputPipeline: validator.array().items(validator.function()).default([]),\n})\n","import { DateTime } from 'luxon'\nimport { Middleware } from '@nhtio/middleware'\nimport { TurnGate } from './classes/turn_gate'\nimport { DispatchRunner } from './dispatch_runner'\nimport { validateOrThrow } from './utils/validation'\nimport { ToolRegistry } from './classes/tool_registry'\nimport { isInstanceOf, isError } from './utils/guards'\nimport { TypedEventEmitter } from '@nhtio/tiny-typed-emitter'\nimport { runWithEstimationWarnings } from './utils/estimation_context'\nimport { turnRunnerConfigSchema } from './contracts/turn_runner_config'\nimport { TurnContext, RawTurnContext } from './contracts/turn_runner_context'\nimport {\n E_INVALID_TURN_RUNNER_CONFIG,\n E_INPUT_PIPELINE_ERROR,\n E_OUTPUT_PIPELINE_ERROR,\n E_PIPELINE_SHORT_CIRCUITED,\n} from './exceptions/runtime'\nimport type { RawTurnGate } from './classes/turn_gate'\nimport type { WarningEvent } from './types/dispatch_runner'\nimport type { EstimationWarning } from './utils/estimation_context'\nimport type { ResolvedTurnRunnerConfig, TurnRunnerConfig } from './contracts/turn_runner_config'\nimport type {\n OpenGateFn,\n TurnEvents,\n TurnEvent,\n TurnEventListener,\n TurnPipelineMiddlewareFn,\n TurnObservabilityEvents,\n TurnObservabilityEvent,\n TurnObservabilityEventListener,\n} from './types/turn_runner'\n\nexport type {\n TurnPipelineMiddlewareFn,\n TurnStreamableContent,\n TurnToolCallContent,\n TurnStartEvent,\n TurnEndEvent,\n TurnGateClosedEvent,\n ToolExecutionStartEvent,\n ToolExecutionEndEvent,\n EmitMessageFn,\n EmitThoughtFn,\n EmitToolCallFn,\n EmitToolExecutionStartFn,\n EmitToolExecutionEndFn,\n OpenGateFn,\n TurnEvents,\n TurnEvent,\n TurnEventListener,\n TurnObservabilityEvents,\n TurnObservabilityEvent,\n TurnObservabilityEventListener,\n} from './types/turn_runner'\n\n/**\n * Executes a single agent turn through paired input and output middleware pipelines.\n *\n * @remarks\n * Construction validates `config` eagerly and throws {@link @nhtio/adk!E_INVALID_TURN_RUNNER_CONFIG} if it\n * does not satisfy the schema — fail-fast so misconfiguration surfaces before any turn runs.\n *\n * Each call to {@link TurnRunner.run} threads a {@link @nhtio/adk!TurnContext} through the input pipeline,\n * invokes the model, then threads the result through the output pipeline. Middleware on each side\n * can read and mutate the context for pre- and post-processing (e.g. message normalisation, tool\n * call dispatch, response filtering).\n *\n * **Two event buses:**\n * - Functional bus (`on` / `off` / `once`): `message`, `thought`, `toolCall` — pipeline-affecting\n * events that middleware raises throughout turn execution.\n * - Observability bus (`observe` / `unobserve` / `observeOnce`): `turnStart`, `turnEnd`,\n * `turnGateOpen`, `turnGateClosed`, `error` — instrumentation-only events that monitor execution\n * without participating in it.\n *\n * Streaming content is surfaced via `message` and `thought` events; tool call lifecycle via\n * `toolCall`; non-fatal pipeline errors via the observability `error` event; gate lifecycle via\n * `turnGateOpen` and `turnGateClosed` — all throughout execution.\n *\n * @example\n * ```ts\n * const runner = new TurnRunner({\n * fetchMemoriesCallback: async (ctx) => memoryStore.query(ctx),\n * fetchMessagesCallback: async (ctx) => messageStore.history(ctx),\n * fetchThoughtsCallback: async (ctx) => thoughtStore.history(ctx),\n * fetchToolCallsCallback: async (ctx) => toolCallStore.history(ctx),\n * })\n * // Functional bus — pipeline events\n * runner.on('message', (chunk) => process.stdout.write(chunk.aDelta))\n * // Observability bus — instrumentation\n * runner.observe('error', (err) => console.error(err.toString()))\n * runner.observe('turnStart', ({ turnId }) => console.log('turn started', turnId))\n * runner.observe('turnGateOpen', (gate) => {\n * if (gate.reason === 'tool_approval') {\n * gate.resolve(true) // approve immediately for this example\n * }\n * })\n * await runner.run({\n * turnAbortController: new AbortController(),\n * systemPrompt: 'You are a helpful assistant.',\n * standingInstructions: [],\n * })\n * ```\n */\nexport class TurnRunner {\n /**\n * Returns `true` if `value` is a {@link TurnRunner} instance.\n *\n * @remarks\n * Uses {@link @nhtio/adk!isInstanceOf} for cross-realm safety.\n *\n * @param value - The value to test.\n * @returns `true` when `value` is a {@link TurnRunner} instance.\n */\n public static isTurnRunner(value: unknown): value is TurnRunner {\n return isInstanceOf(value, 'TurnRunner', TurnRunner)\n }\n\n #config: ResolvedTurnRunnerConfig\n #inputPipeline: Middleware<TurnPipelineMiddlewareFn>\n #outputPipeline: Middleware<TurnPipelineMiddlewareFn>\n #functionalEmitter: TypedEventEmitter<TurnEvents>\n #observabilityEmitter: TypedEventEmitter<TurnObservabilityEvents>\n\n /**\n * @param config - Construction-time configuration validated against the turn-runner config schema.\n * @throws {@link @nhtio/adk!E_INVALID_TURN_RUNNER_CONFIG} when `config` does not satisfy the schema.\n */\n constructor(config: TurnRunnerConfig) {\n // Validate once, capturing the field-level error so the thrown exception can name the\n // offending field(s) (e.g. a missing required callback) instead of failing opaquely.\n const { error } = turnRunnerConfigSchema.validate(config, {\n abortEarly: false,\n })\n if (error) {\n const detail = error.details.map((d) => d.message).join('; ')\n throw new E_INVALID_TURN_RUNNER_CONFIG([detail], { cause: error })\n }\n // Store the resolved config so optional fields (e.g. tools) are always present.\n this.#config = validateOrThrow<ResolvedTurnRunnerConfig>(turnRunnerConfigSchema, config, true)\n const turnInputPipeline = new Middleware<TurnPipelineMiddlewareFn>()\n const turnOutputPipeline = new Middleware<TurnPipelineMiddlewareFn>()\n const wrap =\n (fn: TurnPipelineMiddlewareFn): TurnPipelineMiddlewareFn =>\n (ctx, next) => {\n // Skip downstream user middlewares once an abort has been signalled. The\n // wrapper still calls next() so the pipeline reaches its terminal resolver\n // (keeping the short-circuit detector quiet); the original middleware body\n // does not run, so it has nothing to clean up.\n if (ctx.aborted) return next()\n return fn(ctx, next)\n }\n for (const fn of this.#config.turnInputPipeline) turnInputPipeline.add(wrap(fn))\n for (const fn of this.#config.turnOutputPipeline) turnOutputPipeline.add(wrap(fn))\n // Hold the Middleware; derive a fresh Runner per turn (a Runner's internal cursor never\n // resets across .run() calls, so caching one here would only execute the pipeline once).\n this.#inputPipeline = turnInputPipeline\n this.#outputPipeline = turnOutputPipeline\n this.#functionalEmitter = new TypedEventEmitter<TurnEvents>()\n this.#observabilityEmitter = new TypedEventEmitter<TurnObservabilityEvents>()\n }\n\n // ── Functional bus ───────────────────────────────────────────────────────\n\n /**\n * Removes a previously registered functional listener for `event`.\n *\n * @param event - The event to stop listening to.\n * @param listener - The listener function to remove.\n * @returns `this` for chaining.\n */\n off<K>(event: TurnEvent<K>, listener: TurnEventListener<K>): this {\n this.#functionalEmitter.off(event, listener)\n return this\n }\n\n /**\n * Registers a persistent functional listener for `event`.\n *\n * @param event - The event to listen to.\n * @param listener - The function to call on each emission.\n * @returns `this` for chaining.\n */\n on<K>(event: TurnEvent<K>, listener: TurnEventListener<K>): this {\n this.#functionalEmitter.on(event, listener)\n return this\n }\n\n /**\n * Registers a one-time functional listener for `event` that is automatically removed after the\n * first emission.\n *\n * @param event - The event to listen to.\n * @param listener - The function to call on the next emission.\n * @returns `this` for chaining.\n */\n once<K>(event: TurnEvent<K>, listener: TurnEventListener<K>): this {\n this.#functionalEmitter.once(event, listener)\n return this\n }\n\n // ── Observability bus ────────────────────────────────────────────────────\n\n /**\n * Removes a previously registered observability listener for `event`.\n *\n * @param event - The event to stop observing.\n * @param listener - The listener function to remove.\n * @returns `this` for chaining.\n */\n unobserve<K>(\n event: TurnObservabilityEvent<K>,\n listener: TurnObservabilityEventListener<K>\n ): this {\n this.#observabilityEmitter.off(event, listener)\n return this\n }\n\n /**\n * Registers a persistent observability listener for `event`.\n *\n * @remarks\n * Use the observability bus (`observe` / `unobserve` / `observeOnce`) for instrumentation:\n * turn lifecycle, gate lifecycle, and non-fatal errors. Use the functional bus (`on` / `off` /\n * `once`) for pipeline-affecting events: `message`, `thought`, `toolCall`.\n *\n * @param event - The event to observe.\n * @param listener - The function to call on each emission.\n * @returns `this` for chaining.\n */\n observe<K>(event: TurnObservabilityEvent<K>, listener: TurnObservabilityEventListener<K>): this {\n this.#observabilityEmitter.on(event, listener)\n return this\n }\n\n /**\n * Registers a one-time observability listener for `event` that is automatically removed after\n * the first emission.\n *\n * @param event - The event to observe once.\n * @param listener - The function to call on the next emission.\n * @returns `this` for chaining.\n */\n observeOnce<K>(\n event: TurnObservabilityEvent<K>,\n listener: TurnObservabilityEventListener<K>\n ): this {\n this.#observabilityEmitter.once(event, listener)\n return this\n }\n\n // ── Turn execution ───────────────────────────────────────────────────────\n\n /**\n * Executes a single agent turn against the provided raw context.\n *\n * @remarks\n * Returns `Promise<void>` intentionally — all meaningful output surfaces via events, not return\n * values. Register listeners before calling `run`: observability events (`turnStart`, `turnEnd`)\n * bracket execution; functional events (`message`, `thought`, `toolCall`) fire throughout;\n * observability `error` carries non-fatal pipeline failures; `turnGateOpen` and `turnGateClosed`\n * fire when middleware suspends via `ctx.waitFor()`. Awaiting this method only tells you the\n * pipeline has finished, not what it produced.\n *\n * Constructs a validated {@link @nhtio/adk!TurnContext} from `context` (throwing\n * {@link @nhtio/adk!E_INVALID_TURN_CONTEXT} on failure), then runs the input middleware pipeline.\n * Abort signals are silently swallowed.\n *\n * @param context - Raw input validated and wrapped into a {@link @nhtio/adk!TurnContext} before execution.\n * @throws {@link @nhtio/adk!E_INVALID_TURN_CONTEXT} when `context` does not satisfy the schema.\n */\n async run(context: RawTurnContext): Promise<void> {\n const abortController = context.turnAbortController ?? new AbortController()\n\n // Forward declaration so openGate can reference turnContext.id before it is assigned.\n let turnContextId: string\n\n const openGate: OpenGateFn = <T>(raw: Omit<RawTurnGate, 'turnId' | 'abortSignal'>) => {\n const gate = new TurnGate<T>({\n ...raw,\n turnId: turnContextId,\n abortSignal: abortController.signal,\n })\n this.#observabilityEmitter.emit('turnGateOpen', gate)\n const promise = gate._promise()\n promise.then(\n () => {\n this.#observabilityEmitter.emit('turnGateClosed', {\n gateId: gate.id,\n turnId: gate.turnId,\n result: 'resolved',\n settledAt: DateTime.now(),\n })\n },\n (err: unknown) => {\n let result: 'rejected' | 'aborted' | 'timeout' = 'rejected'\n if (isInstanceOf(err, 'E_TURN_GATE_ABORTED')) result = 'aborted'\n else if (isInstanceOf(err, 'E_TURN_GATE_TIMEOUT')) result = 'timeout'\n this.#observabilityEmitter.emit('turnGateClosed', {\n gateId: gate.id,\n turnId: gate.turnId,\n result,\n settledAt: DateTime.now(),\n })\n }\n )\n return promise\n }\n\n const tools = new ToolRegistry(this.#config.tools)\n\n const turnContext = new TurnContext(\n { ...context, turnAbortController: abortController },\n {\n fetchMemories: this.#config.fetchMemoriesCallback,\n fetchMessages: this.#config.fetchMessagesCallback,\n fetchThoughts: this.#config.fetchThoughtsCallback,\n fetchToolCalls: this.#config.fetchToolCallsCallback,\n fetchTools: this.#config.fetchToolsCallback,\n refreshStandingInstructions: this.#config.refreshStandingInstructionsCallback,\n storeStandingInstruction: this.#config.storeStandingInstructionCallback,\n mutateStandingInstruction: this.#config.mutateStandingInstructionCallback,\n deleteStandingInstruction: this.#config.deleteStandingInstructionCallback,\n storeMemory: this.#config.storeMemoryCallback,\n mutateMemory: this.#config.mutateMemoryCallback,\n deleteMemory: this.#config.deleteMemoryCallback,\n fetchRetrievables: this.#config.fetchRetrievablesCallback,\n storeRetrievable: this.#config.storeRetrievableCallback,\n mutateRetrievable: this.#config.mutateRetrievableCallback,\n deleteRetrievable: this.#config.deleteRetrievableCallback,\n storeMessage: this.#config.storeMessageCallback,\n mutateMessage: this.#config.mutateMessageCallback,\n deleteMessage: this.#config.deleteMessageCallback,\n storeThought: this.#config.storeThoughtCallback,\n mutateThought: this.#config.mutateThoughtCallback,\n deleteThought: this.#config.deleteThoughtCallback,\n storeToolCall: this.#config.storeToolCallCallback,\n mutateToolCall: this.#config.mutateToolCallCallback,\n deleteToolCall: this.#config.deleteToolCallCallback,\n replaceToolCallGroup: this.#config.replaceToolCallGroupCallback,\n storeMediaBytes: this.#config.storeMediaBytesCallback,\n storeRetrievableBytes: this.#config.storeRetrievableBytesCallback,\n emitMessage: (content) => this.#functionalEmitter.emit('message', content),\n emitThought: (content) => this.#functionalEmitter.emit('thought', content),\n emitToolCall: (content) => this.#functionalEmitter.emit('toolCall', content),\n emitToolExecutionStart: (event) =>\n this.#observabilityEmitter.emit('toolExecutionStart', event),\n emitToolExecutionEnd: (event) => this.#observabilityEmitter.emit('toolExecutionEnd', event),\n openGate,\n tools,\n }\n )\n\n turnContextId = turnContext.id\n\n // Publish a turn-level warn-sink for the duration of the run so a token estimation done directly in a\n // TURN pipeline (outside any dispatch) can degrade-and-warn instead of throwing. A nested\n // DispatchRunner.dispatch() pushes its own sink on top (see estimation_context), so estimations inside\n // a dispatch route to the richer dispatch-scoped sink and this turn-level one is the fallback. No\n // dispatchId/iteration here — those belong to a dispatch scope, not the turn.\n const emitEstimationWarning = (w: EstimationWarning): void => {\n const event: WarningEvent = {\n emittedAt: DateTime.now(),\n source: 'turn-runner',\n kind: 'token-estimation-degraded',\n message: `token estimation for encoding \"${String(w.encoding)}\" failed; fell back to a char-based estimate`,\n error: w.error,\n payload: { encoding: w.encoding, textPreview: w.textPreview },\n }\n this.#observabilityEmitter.emit('warning', event)\n }\n\n await runWithEstimationWarnings(emitEstimationWarning, async () => {\n const startedAt = DateTime.now()\n this.#observabilityEmitter.emit('turnStart', {\n turnId: turnContext.id,\n startedAt,\n })\n\n const emitTurnEnd = () => {\n const endedAt = DateTime.now()\n this.#observabilityEmitter.emit('turnEnd', {\n turnId: turnContext.id,\n startedAt,\n endedAt,\n durationMs: endedAt.diff(startedAt).milliseconds,\n })\n }\n\n // 1. Input pipeline\n let inputFailed = false\n let inputReached = false\n await this.#inputPipeline\n .runner()\n .errorHandler(async (error) => {\n if (!isError(error) || !isInstanceOf(error, 'AbortError')) {\n inputFailed = true\n const err = new E_INPUT_PIPELINE_ERROR({\n cause: isError(error) ? error : undefined,\n })\n this.#observabilityEmitter.emit('error', err)\n }\n })\n .finalHandler(async () => {\n inputReached = true\n })\n .run((fn, next) => Promise.resolve(fn(turnContext, next)))\n\n if (!inputReached && !inputFailed && !turnContext.aborted) {\n inputFailed = true\n const err = new E_PIPELINE_SHORT_CIRCUITED(['turn-input'])\n this.#observabilityEmitter.emit('error', err)\n }\n\n if (inputFailed || turnContext.aborted) {\n emitTurnEnd()\n return\n }\n\n // 2. LLM execution dispatch\n let dispatchFailed = false\n try {\n await DispatchRunner.dispatch({\n source: turnContext,\n executor: this.#config.executorCallback,\n turnInputPipeline: this.#config.dispatchInputPipeline,\n turnOutputPipeline: this.#config.dispatchOutputPipeline,\n observers: {\n dispatchStart: [\n (e) => {\n this.#observabilityEmitter.emit('dispatchStart', e)\n },\n ],\n dispatchEnd: [\n (e) => {\n this.#observabilityEmitter.emit('dispatchEnd', e)\n },\n ],\n iterationStart: [\n (e) => {\n this.#observabilityEmitter.emit('iterationStart', e)\n },\n ],\n iterationEnd: [\n (e) => {\n this.#observabilityEmitter.emit('iterationEnd', e)\n },\n ],\n log: [\n (e) => {\n this.#observabilityEmitter.emit('log', e)\n },\n ],\n // Re-forward a DispatchRunner-emitted `warning` (e.g. token-estimation degraded) up to the turn\n // bus, so a consumer subscribed only to the TurnRunner still sees it — mirrors the log/dispatch*\n // bridges above. Non-fatal; the dispatch continues.\n warning: [\n (e) => {\n this.#observabilityEmitter.emit('warning', e)\n },\n ],\n },\n })\n } catch (err) {\n dispatchFailed = true\n const wrapped = isInstanceOf(err, 'BaseException')\n ? (err as InstanceType<typeof Error>)\n : err\n this.#observabilityEmitter.emit('error', wrapped as any)\n }\n\n if (dispatchFailed || turnContext.aborted) {\n emitTurnEnd()\n return\n }\n\n // 3. Output pipeline\n let outputFailed = false\n let outputReached = false\n await this.#outputPipeline\n .runner()\n .errorHandler(async (error) => {\n if (!isError(error) || !isInstanceOf(error, 'AbortError')) {\n outputFailed = true\n const err = new E_OUTPUT_PIPELINE_ERROR({\n cause: isError(error) ? error : undefined,\n })\n this.#observabilityEmitter.emit('error', err)\n }\n })\n .finalHandler(async () => {\n outputReached = true\n })\n .run((fn, next) => Promise.resolve(fn(turnContext, next)))\n\n if (!outputReached && !outputFailed && !turnContext.aborted) {\n const err = new E_PIPELINE_SHORT_CIRCUITED(['turn-output'])\n this.#observabilityEmitter.emit('error', err)\n }\n\n emitTurnEnd()\n })\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"turn_runner-DhLI7OTH.js","names":["#id","#turnId","#reason","#payload","#createdAt","#settled","#schema","#controller","#promise","#resolve","#reject","#config","#inputPipeline","#outputPipeline","#functionalEmitter","#observabilityEmitter"],"sources":["../src/lib/classes/turn_gate.ts","../src/lib/contracts/turn_runner_config.ts","../src/lib/turn_runner.ts"],"sourcesContent":["import { DateTime } from 'luxon'\nimport { validator } from '@nhtio/validation'\nimport { validateOrThrow } from '../utils/validation'\nimport { isInstanceOf, isError, isObject } from '../utils/guards'\nimport {\n E_TURN_GATE_ABORTED,\n E_TURN_GATE_TIMEOUT,\n E_INVALID_TURN_GATE_RESOLUTION,\n E_INVALID_INITIAL_TURN_GATE_VALUE,\n} from '../exceptions/runtime'\nimport type { Schema } from '@nhtio/validation'\n\n/**\n * Plain input object supplied to {@link TurnGate} at construction time.\n *\n * @remarks\n * `turnId` and `abortSignal` are injected by the runner — callers constructing a gate via\n * `ctx.waitFor()` never supply them directly.\n *\n * `abortSignal` is `AbortSignal` (not `AbortController`) because the gate reacts to turn-level\n * cancellation but cannot trigger it. The gate owns its own internal `AbortController` for\n * `gate.abort()`.\n */\nexport interface RawTurnGate {\n /** Stable unique identifier for this gate. */\n id: string\n /** The ID of the turn that opened this gate. */\n turnId: string\n /** Human-readable label describing why this gate was opened (e.g. `'tool_approval'`). */\n reason: string\n /** Arbitrary data supplied to the gate opener; passed through to `turnGateOpen` listeners. */\n payload: unknown\n /** Optional validator schema for the resolution value. When present, `resolve()` validates before settling. */\n schema?: Schema\n /** Optional timeout in milliseconds. When elapsed the gate self-rejects with {@link @nhtio/adk!E_TURN_GATE_TIMEOUT}. */\n timeout?: number\n /** The turn's abort signal. When fired the gate self-rejects with {@link @nhtio/adk!E_TURN_GATE_ABORTED}. */\n abortSignal?: AbortSignal\n /** When this gate was created. */\n createdAt: string | number | Date | DateTime\n}\n\n/**\n * Fully-resolved {@link RawTurnGate} after schema validation.\n *\n * @internal\n */\ninterface ResolvedTurnGate {\n id: string\n turnId: string\n reason: string\n payload: unknown\n schema?: Schema\n timeout?: number\n abortSignal?: AbortSignal\n createdAt: DateTime\n}\n\n/**\n * Validator schema used to validate a {@link RawTurnGate} before constructing a {@link TurnGate}.\n *\n * @remarks\n * - `schema` and `abortSignal` are validated as opaque passthrough values.\n * - `timeout` must be a positive integer when provided.\n */\nconst rawTurnGateSchema = validator.object<RawTurnGate>({\n id: validator.string().required(),\n turnId: validator.string().required(),\n reason: validator.string().required(),\n payload: validator.any().required(),\n schema: validator\n .any()\n .custom((value, helpers) => {\n if (value === undefined || value === null) return undefined\n if (typeof (value as any).validate === 'function') return value\n return helpers.error('any.invalid')\n })\n .optional(),\n timeout: validator.number().integer().min(1).optional(),\n abortSignal: validator\n .any()\n .custom((value, helpers) => {\n if (value === undefined || value === null) return undefined\n // eslint-disable-next-line adk/use-is-instance-of -- native built-in; AbortSignal cross-realm is handled by the duck-type fallback below\n if (typeof AbortSignal !== 'undefined' && value instanceof AbortSignal) return value\n if (\n isObject(value) &&\n typeof (value as any).aborted === 'boolean' &&\n typeof (value as any).addEventListener === 'function'\n ) {\n return value\n }\n return helpers.error('any.invalid')\n })\n .optional(),\n createdAt: validator.datetime().required(),\n})\n\n/**\n * A cooperative suspension gate that blocks a turn's middleware pipeline until resolved, rejected,\n * aborted, or timed out.\n *\n * @typeParam T - The expected type of the resolution value.\n *\n * @remarks\n * Created exclusively via `ctx.waitFor()` — middleware never constructs a gate directly.\n * The gate emits `turnGateOpen` on the runner's observability bus at creation time and\n * `turnGateClosed` when it settles.\n *\n * Resolution is validated against an optional schema before the internal promise is settled.\n * A validation failure throws {@link @nhtio/adk!E_INVALID_TURN_GATE_RESOLUTION} **synchronously in the\n * caller's context** — the promise is NOT settled and the gate remains open.\n */\nexport class TurnGate<T = unknown> {\n /**\n * Validator schema that accepts a {@link RawTurnGate} object.\n *\n * @remarks\n * Reusable fragment for any schema that needs to validate or nest a gate entry.\n */\n public static schema = rawTurnGateSchema\n\n /**\n * Returns `true` if `value` is a {@link TurnGate} instance.\n *\n * @remarks\n * Uses {@link @nhtio/adk!isInstanceOf} for cross-realm safety.\n *\n * @param value - The value to test.\n * @returns `true` when `value` is a {@link TurnGate} instance.\n */\n public static isTurnGate(value: unknown): value is TurnGate {\n return isInstanceOf(value, 'TurnGate', TurnGate)\n }\n\n /** Unique identifier for this gate instance. */\n declare readonly id: string\n /** Id of the turn this gate belongs to. */\n declare readonly turnId: string\n /** Human-readable reason the gate was opened. */\n declare readonly reason: string\n /** Optional caller-supplied payload describing what the gate is waiting on. */\n declare readonly payload: unknown\n /** When the gate was created. */\n declare readonly createdAt: DateTime\n /** Whether the gate has been settled (resolved or rejected) and no longer blocks the turn. */\n declare readonly isSettled: boolean\n\n #id: string\n #turnId: string\n #reason: string\n #payload: unknown\n #createdAt: DateTime\n #settled: boolean\n #schema: Schema | undefined\n #controller: AbortController\n #resolve!: (value: T) => void\n #reject!: (reason: unknown) => void\n #promise: Promise<T>\n\n /**\n * @param raw - The raw gate input validated against `rawTurnGateSchema`.\n * @throws {@link @nhtio/adk!E_INVALID_INITIAL_TURN_GATE_VALUE} when `raw` does not satisfy the schema.\n */\n constructor(raw: RawTurnGate) {\n let resolved: ResolvedTurnGate\n try {\n resolved = validateOrThrow<ResolvedTurnGate>(rawTurnGateSchema, raw, true)\n } catch (err) {\n throw new E_INVALID_INITIAL_TURN_GATE_VALUE({ cause: isError(err) ? err : undefined })\n }\n\n this.#id = resolved.id\n this.#turnId = resolved.turnId\n this.#reason = resolved.reason\n this.#payload = resolved.payload\n this.#createdAt = resolved.createdAt\n this.#settled = false\n this.#schema = resolved.schema\n this.#controller = new AbortController()\n\n this.#promise = new Promise<T>((resolve, reject) => {\n this.#resolve = resolve\n this.#reject = reject\n })\n\n // Wire the internal abort controller\n const onAbort = () => {\n if (!this.#settled) {\n this.#settled = true\n this.#reject(new E_TURN_GATE_ABORTED())\n }\n }\n\n this.#controller.signal.addEventListener('abort', onAbort, { once: true })\n\n // Wire the external turn abort signal\n if (resolved.abortSignal) {\n if (resolved.abortSignal.aborted) {\n // Already aborted — reject immediately after construction\n queueMicrotask(() => onAbort())\n } else {\n resolved.abortSignal.addEventListener('abort', onAbort, { once: true })\n // Clean up the external listener once the gate settles via another path\n this.#promise.then(\n () => resolved.abortSignal!.removeEventListener('abort', onAbort),\n () => resolved.abortSignal!.removeEventListener('abort', onAbort)\n )\n }\n }\n\n // Wire the timeout\n if (resolved.timeout !== undefined) {\n const timer = setTimeout(() => {\n if (!this.#settled) {\n this.#settled = true\n this.#reject(new E_TURN_GATE_TIMEOUT())\n }\n }, resolved.timeout)\n\n this.#promise.then(\n () => clearTimeout(timer),\n () => clearTimeout(timer)\n )\n }\n\n Object.defineProperties(this, {\n id: {\n get: () => this.#id,\n enumerable: true,\n configurable: false,\n },\n turnId: {\n get: () => this.#turnId,\n enumerable: true,\n configurable: false,\n },\n reason: {\n get: () => this.#reason,\n enumerable: true,\n configurable: false,\n },\n payload: {\n get: () => this.#payload,\n enumerable: true,\n configurable: false,\n },\n createdAt: {\n get: () => this.#createdAt,\n enumerable: true,\n configurable: false,\n },\n isSettled: {\n get: () => this.#settled,\n enumerable: true,\n configurable: false,\n },\n })\n }\n\n /**\n * Resolves the gate with `value`, unblocking the awaiting middleware.\n *\n * @remarks\n * If a schema was provided at construction, `value` is validated synchronously before the\n * promise is settled. A validation failure throws {@link @nhtio/adk!E_INVALID_TURN_GATE_RESOLUTION}\n * in the caller's context — the promise is NOT settled and the gate remains open.\n *\n * No-ops if the gate is already settled.\n *\n * @param value - The resolution value. Must satisfy the gate's schema when one was provided.\n * @throws {@link @nhtio/adk!E_INVALID_TURN_GATE_RESOLUTION} when `value` fails schema validation.\n */\n resolve(value: unknown): void {\n if (this.#settled) return\n if (this.#schema !== undefined) {\n try {\n value = validateOrThrow(this.#schema, value, true)\n } catch (err) {\n throw new E_INVALID_TURN_GATE_RESOLUTION({\n cause: isError(err) ? err : undefined,\n })\n }\n }\n this.#settled = true\n this.#resolve(value as T)\n }\n\n /**\n * Rejects the gate with `error`, unblocking the awaiting middleware with a rejection.\n *\n * @remarks\n * No-ops if the gate is already settled.\n *\n * @param error - The rejection reason.\n */\n reject(error: Error): void {\n if (this.#settled) return\n this.#settled = true\n this.#reject(error)\n }\n\n /**\n * Aborts the gate by firing the internal `AbortController`, which rejects the promise with\n * {@link @nhtio/adk!E_TURN_GATE_ABORTED}.\n *\n * @remarks\n * No-ops if the gate is already settled. Distinct from the turn-level abort signal — this\n * allows callers to cancel a specific gate without aborting the whole turn.\n */\n abort(): void {\n if (this.#settled) return\n this.#controller.abort()\n }\n\n /**\n * Returns the internal promise. Called by `ctx.waitFor()` to block the middleware pipeline.\n *\n * @internal\n */\n _promise(): Promise<T> {\n return this.#promise\n }\n}\n","import { Tool } from '../classes/tool'\nimport { validator } from '@nhtio/validation'\nimport type { TurnPipelineMiddlewareFn } from '../types/turn_runner'\nimport type { DispatchPipelineMiddlewareFn, DispatchExecutorFn } from '../types/dispatch_runner'\nimport type {\n MemoryRetrievalFn,\n MessageRetrievalFn,\n ThoughtRetrievalFn,\n ToolCallRetrievalFn,\n ToolsRetrievalFn,\n StandingInstructionsRefreshFn,\n StandingInstructionStoreFn,\n StandingInstructionMutateFn,\n StandingInstructionDeleteFn,\n MemoryStoreFn,\n MemoryMutateFn,\n MemoryDeleteFn,\n RetrievableRetrievalFn,\n RetrievableStoreFn,\n RetrievableMutateFn,\n RetrievableDeleteFn,\n MessageStoreFn,\n MessageMutateFn,\n MessageDeleteFn,\n ThoughtStoreFn,\n ThoughtMutateFn,\n ThoughtDeleteFn,\n ToolCallStoreFn,\n ToolCallMutateFn,\n ToolCallDeleteFn,\n ToolCallGroupReplaceFn,\n MediaBytesStoreFn,\n RetrievableBytesStoreFn,\n} from './turn_runner_context'\n\n/**\n * Configuration supplied to {@link @nhtio/adk!TurnRunner} at construction time.\n *\n * @remarks\n * Validated against `turnRunnerConfigSchema` at construction — a misconfigured runner throws\n * immediately rather than failing on the first turn.\n *\n * All fetch and mutation callbacks are required: they are injected into each {@link @nhtio/adk!TurnContext}\n * so middleware can call fetch, refresh, and persistence methods directly on the context without\n * coupling to the runner.\n *\n * `tools` is optional at the caller level and defaults to `[]` after schema resolution — a runner\n * with no baseline tools is valid.\n */\nexport interface TurnRunnerConfig {\n /** Performs the LLM API/SDK call for each iteration of the dispatch loop; receives the active {@link @nhtio/adk!DispatchContext} and an {@link @nhtio/adk!DispatchExecutorHelpers} object for managing per-id stream state. */\n executorCallback: DispatchExecutorFn\n /** Called once per turn to supply memories; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchMemoriesCallback: MemoryRetrievalFn\n /** Called once per turn to supply conversation history; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchMessagesCallback: MessageRetrievalFn\n /** Called once per turn to supply thought traces; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchThoughtsCallback: ThoughtRetrievalFn\n /** Called once per turn to supply tool call records; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchToolCallsCallback: ToolCallRetrievalFn\n /** Called to supply available tools; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchToolsCallback: ToolsRetrievalFn\n /** Called to refresh and return standing instructions; receives the active {@link @nhtio/adk!TurnContext}. */\n refreshStandingInstructionsCallback: StandingInstructionsRefreshFn\n /** Persists a new standing instruction. */\n storeStandingInstructionCallback: StandingInstructionStoreFn\n /** Updates an existing standing instruction in the persistence layer. */\n mutateStandingInstructionCallback: StandingInstructionMutateFn\n /** Removes a standing instruction from the persistence layer. */\n deleteStandingInstructionCallback: StandingInstructionDeleteFn\n /** Persists a new memory. */\n storeMemoryCallback: MemoryStoreFn\n /** Updates an existing memory in the persistence layer. */\n mutateMemoryCallback: MemoryMutateFn\n /** Removes a memory from the persistence layer by ID. */\n deleteMemoryCallback: MemoryDeleteFn\n /** Called once per turn to supply retrievable (RAG) records; receives the active {@link @nhtio/adk!TurnContext}. */\n fetchRetrievablesCallback: RetrievableRetrievalFn\n /** Persists a new retrievable record. */\n storeRetrievableCallback: RetrievableStoreFn\n /** Updates an existing retrievable record in the persistence layer. */\n mutateRetrievableCallback: RetrievableMutateFn\n /** Removes a retrievable record from the persistence layer by ID. */\n deleteRetrievableCallback: RetrievableDeleteFn\n /** Persists a new message. */\n storeMessageCallback: MessageStoreFn\n /** Updates an existing message in the persistence layer. */\n mutateMessageCallback: MessageMutateFn\n /** Removes a message from the persistence layer by ID. */\n deleteMessageCallback: MessageDeleteFn\n /** Persists a new thought. */\n storeThoughtCallback: ThoughtStoreFn\n /** Updates an existing thought in the persistence layer. */\n mutateThoughtCallback: ThoughtMutateFn\n /** Removes a thought from the persistence layer by ID. */\n deleteThoughtCallback: ThoughtDeleteFn\n /** Persists a new tool call. */\n storeToolCallCallback: ToolCallStoreFn\n /** Updates an existing tool call in the persistence layer. */\n mutateToolCallCallback: ToolCallMutateFn\n /** Removes a tool call from the persistence layer by ID. */\n deleteToolCallCallback: ToolCallDeleteFn\n /** Optionally replaces a complete tool-call group atomically. */\n replaceToolCallGroupCallback?: ToolCallGroupReplaceFn\n /** Persists tool-generated media bytes into consumer storage; returns a `MediaReader`. */\n storeMediaBytesCallback: MediaBytesStoreFn\n /** Persists extracted retrievable text bytes into consumer storage; returns a `SpoolReader`. */\n storeRetrievableBytesCallback: RetrievableBytesStoreFn\n /** Baseline tools available on every turn. Middleware may trim or extend this per-turn via `ctx.tools`. Defaults to `[]`. */\n tools?: Tool[]\n /** Turn-level input middleware, executed in order against the {@link @nhtio/adk!TurnContext} before the LLM dispatch. Defaults to `[]`. */\n turnInputPipeline?: TurnPipelineMiddlewareFn[]\n /** Turn-level output middleware, executed in order against the {@link @nhtio/adk!TurnContext} after the LLM dispatch resolves successfully. Defaults to `[]`. */\n turnOutputPipeline?: TurnPipelineMiddlewareFn[]\n /** LLM-iteration input middleware, executed in order against the {@link @nhtio/adk!DispatchContext} before the executor on each iteration. Defaults to `[]`. */\n dispatchInputPipeline?: DispatchPipelineMiddlewareFn[]\n /** LLM-iteration output middleware, executed in order against the {@link @nhtio/adk!DispatchContext} after the executor on each iteration. Defaults to `[]`. */\n dispatchOutputPipeline?: DispatchPipelineMiddlewareFn[]\n}\n\n/**\n * Fully-resolved {@link TurnRunnerConfig} after schema validation.\n *\n * @remarks\n * All optional fields are guaranteed present (e.g. `tools` defaults to `[]`). The runner stores\n * this type internally so field access never needs to guard for undefined.\n */\nexport type ResolvedTurnRunnerConfig = Required<TurnRunnerConfig>\n\n/**\n * Validator schema used to validate a {@link TurnRunnerConfig} at {@link @nhtio/adk!TurnRunner} construction time.\n *\n * @remarks\n * Validates that all callbacks are functions of the correct arity, and that `tools` — when\n * provided — is an array of valid {@link @nhtio/adk!Tool} instances. Defaults `tools` to `[]`.\n *\n * Throws {@link @nhtio/adk!E_INVALID_TURN_RUNNER_CONFIG} (via the {@link @nhtio/adk!TurnRunner} constructor) when\n * validation fails.\n */\nexport const turnRunnerConfigSchema = validator.object<TurnRunnerConfig>({\n executorCallback: validator.function().required(),\n fetchMemoriesCallback: validator.function().arity(1).required(),\n fetchMessagesCallback: validator.function().arity(1).required(),\n fetchThoughtsCallback: validator.function().arity(1).required(),\n fetchToolCallsCallback: validator.function().arity(1).required(),\n fetchToolsCallback: validator.function().arity(1).required(),\n refreshStandingInstructionsCallback: validator.function().arity(1).required(),\n storeStandingInstructionCallback: validator.function().arity(2).required(),\n mutateStandingInstructionCallback: validator.function().arity(2).required(),\n deleteStandingInstructionCallback: validator.function().arity(2).required(),\n storeMemoryCallback: validator.function().arity(2).required(),\n mutateMemoryCallback: validator.function().arity(2).required(),\n deleteMemoryCallback: validator.function().arity(2).required(),\n fetchRetrievablesCallback: validator.function().arity(1).required(),\n storeRetrievableCallback: validator.function().arity(2).required(),\n mutateRetrievableCallback: validator.function().arity(2).required(),\n deleteRetrievableCallback: validator.function().arity(2).required(),\n storeMessageCallback: validator.function().arity(2).required(),\n mutateMessageCallback: validator.function().arity(2).required(),\n deleteMessageCallback: validator.function().arity(2).required(),\n storeThoughtCallback: validator.function().arity(2).required(),\n mutateThoughtCallback: validator.function().arity(2).required(),\n deleteThoughtCallback: validator.function().arity(2).required(),\n storeToolCallCallback: validator.function().arity(2).required(),\n mutateToolCallCallback: validator.function().arity(2).required(),\n deleteToolCallCallback: validator.function().arity(2).required(),\n replaceToolCallGroupCallback: validator.function().arity(3).optional(),\n storeMediaBytesCallback: validator.function().arity(3).required(),\n storeRetrievableBytesCallback: validator.function().arity(3).required(),\n tools: validator\n .array()\n .items(\n // `.optional()`, not `.required()`: this `.any()` is a type-argument inside `items(...)`,\n // describing what a tool element looks like — it must NOT impose array cardinality. A\n // `.required()` item turns the array into `array.includesRequiredUnknowns` (\"must contain\n // ≥1 tool\"), which contradicts the contract above (`tools` is optional, defaults to `[]`).\n // `.optional()` satisfies adk/require-validator-any-required by declaring disposition\n // explicitly while leaving the empty-array default valid.\n validator\n .any()\n .optional()\n .custom((value: unknown, helpers: { error: (code: string) => unknown }) => {\n if (Tool.isTool(value)) return value\n return helpers.error('any.invalid')\n })\n )\n .default([]),\n turnInputPipeline: validator.array().items(validator.function()).default([]),\n turnOutputPipeline: validator.array().items(validator.function()).default([]),\n dispatchInputPipeline: validator.array().items(validator.function()).default([]),\n dispatchOutputPipeline: validator.array().items(validator.function()).default([]),\n})\n","import { DateTime } from 'luxon'\nimport { Middleware } from '@nhtio/middleware'\nimport { TurnGate } from './classes/turn_gate'\nimport { DispatchRunner } from './dispatch_runner'\nimport { validateOrThrow } from './utils/validation'\nimport { ToolRegistry } from './classes/tool_registry'\nimport { isInstanceOf, isError } from './utils/guards'\nimport { TypedEventEmitter } from '@nhtio/tiny-typed-emitter'\nimport { runWithEstimationWarnings } from './utils/estimation_context'\nimport { turnRunnerConfigSchema } from './contracts/turn_runner_config'\nimport { TurnContext, RawTurnContext } from './contracts/turn_runner_context'\nimport {\n E_INVALID_TURN_RUNNER_CONFIG,\n E_INPUT_PIPELINE_ERROR,\n E_OUTPUT_PIPELINE_ERROR,\n E_PIPELINE_SHORT_CIRCUITED,\n} from './exceptions/runtime'\nimport type { RawTurnGate } from './classes/turn_gate'\nimport type { WarningEvent } from './types/dispatch_runner'\nimport type { EstimationWarning } from './utils/estimation_context'\nimport type { ResolvedTurnRunnerConfig, TurnRunnerConfig } from './contracts/turn_runner_config'\nimport type {\n OpenGateFn,\n TurnEvents,\n TurnEvent,\n TurnEventListener,\n TurnPipelineMiddlewareFn,\n TurnObservabilityEvents,\n TurnObservabilityEvent,\n TurnObservabilityEventListener,\n} from './types/turn_runner'\n\nexport type {\n TurnPipelineMiddlewareFn,\n TurnStreamableContent,\n TurnToolCallContent,\n TurnStartEvent,\n TurnEndEvent,\n TurnGateClosedEvent,\n ToolExecutionStartEvent,\n ToolExecutionEndEvent,\n EmitMessageFn,\n EmitThoughtFn,\n EmitToolCallFn,\n EmitToolExecutionStartFn,\n EmitToolExecutionEndFn,\n OpenGateFn,\n TurnEvents,\n TurnEvent,\n TurnEventListener,\n TurnObservabilityEvents,\n TurnObservabilityEvent,\n TurnObservabilityEventListener,\n} from './types/turn_runner'\n\n/**\n * Executes a single agent turn through paired input and output middleware pipelines.\n *\n * @remarks\n * Construction validates `config` eagerly and throws {@link @nhtio/adk!E_INVALID_TURN_RUNNER_CONFIG} if it\n * does not satisfy the schema — fail-fast so misconfiguration surfaces before any turn runs.\n *\n * Each call to {@link TurnRunner.run} threads a {@link @nhtio/adk!TurnContext} through the input pipeline,\n * invokes the model, then threads the result through the output pipeline. Middleware on each side\n * can read and mutate the context for pre- and post-processing (e.g. message normalisation, tool\n * call dispatch, response filtering).\n *\n * **Two event buses:**\n * - Functional bus (`on` / `off` / `once`): `message`, `thought`, `toolCall` — pipeline-affecting\n * events that middleware raises throughout turn execution.\n * - Observability bus (`observe` / `unobserve` / `observeOnce`): `turnStart`, `turnEnd`,\n * `turnGateOpen`, `turnGateClosed`, `error` — instrumentation-only events that monitor execution\n * without participating in it.\n *\n * Streaming content is surfaced via `message` and `thought` events; tool call lifecycle via\n * `toolCall`; non-fatal pipeline errors via the observability `error` event; gate lifecycle via\n * `turnGateOpen` and `turnGateClosed` — all throughout execution.\n *\n * @example\n * ```ts\n * const runner = new TurnRunner({\n * fetchMemoriesCallback: async (ctx) => memoryStore.query(ctx),\n * fetchMessagesCallback: async (ctx) => messageStore.history(ctx),\n * fetchThoughtsCallback: async (ctx) => thoughtStore.history(ctx),\n * fetchToolCallsCallback: async (ctx) => toolCallStore.history(ctx),\n * })\n * // Functional bus — pipeline events\n * runner.on('message', (chunk) => process.stdout.write(chunk.aDelta))\n * // Observability bus — instrumentation\n * runner.observe('error', (err) => console.error(err.toString()))\n * runner.observe('turnStart', ({ turnId }) => console.log('turn started', turnId))\n * runner.observe('turnGateOpen', (gate) => {\n * if (gate.reason === 'tool_approval') {\n * gate.resolve(true) // approve immediately for this example\n * }\n * })\n * await runner.run({\n * turnAbortController: new AbortController(),\n * systemPrompt: 'You are a helpful assistant.',\n * standingInstructions: [],\n * })\n * ```\n */\nexport class TurnRunner {\n /**\n * Returns `true` if `value` is a {@link TurnRunner} instance.\n *\n * @remarks\n * Uses {@link @nhtio/adk!isInstanceOf} for cross-realm safety.\n *\n * @param value - The value to test.\n * @returns `true` when `value` is a {@link TurnRunner} instance.\n */\n public static isTurnRunner(value: unknown): value is TurnRunner {\n return isInstanceOf(value, 'TurnRunner', TurnRunner)\n }\n\n #config: ResolvedTurnRunnerConfig\n #inputPipeline: Middleware<TurnPipelineMiddlewareFn>\n #outputPipeline: Middleware<TurnPipelineMiddlewareFn>\n #functionalEmitter: TypedEventEmitter<TurnEvents>\n #observabilityEmitter: TypedEventEmitter<TurnObservabilityEvents>\n\n /**\n * @param config - Construction-time configuration validated against the turn-runner config schema.\n * @throws {@link @nhtio/adk!E_INVALID_TURN_RUNNER_CONFIG} when `config` does not satisfy the schema.\n */\n constructor(config: TurnRunnerConfig) {\n // Validate once, capturing the field-level error so the thrown exception can name the\n // offending field(s) (e.g. a missing required callback) instead of failing opaquely.\n const { error } = turnRunnerConfigSchema.validate(config, {\n abortEarly: false,\n })\n if (error) {\n const detail = error.details.map((d) => d.message).join('; ')\n throw new E_INVALID_TURN_RUNNER_CONFIG([detail], { cause: error })\n }\n // Store the resolved config so optional fields (e.g. tools) are always present.\n this.#config = validateOrThrow<ResolvedTurnRunnerConfig>(turnRunnerConfigSchema, config, true)\n const turnInputPipeline = new Middleware<TurnPipelineMiddlewareFn>()\n const turnOutputPipeline = new Middleware<TurnPipelineMiddlewareFn>()\n const wrap =\n (fn: TurnPipelineMiddlewareFn): TurnPipelineMiddlewareFn =>\n (ctx, next) => {\n // Skip downstream user middlewares once an abort has been signalled. The\n // wrapper still calls next() so the pipeline reaches its terminal resolver\n // (keeping the short-circuit detector quiet); the original middleware body\n // does not run, so it has nothing to clean up.\n if (ctx.aborted) return next()\n return fn(ctx, next)\n }\n for (const fn of this.#config.turnInputPipeline) turnInputPipeline.add(wrap(fn))\n for (const fn of this.#config.turnOutputPipeline) turnOutputPipeline.add(wrap(fn))\n // Hold the Middleware; derive a fresh Runner per turn (a Runner's internal cursor never\n // resets across .run() calls, so caching one here would only execute the pipeline once).\n this.#inputPipeline = turnInputPipeline\n this.#outputPipeline = turnOutputPipeline\n this.#functionalEmitter = new TypedEventEmitter<TurnEvents>()\n this.#observabilityEmitter = new TypedEventEmitter<TurnObservabilityEvents>()\n }\n\n // ── Functional bus ───────────────────────────────────────────────────────\n\n /**\n * Removes a previously registered functional listener for `event`.\n *\n * @param event - The event to stop listening to.\n * @param listener - The listener function to remove.\n * @returns `this` for chaining.\n */\n off<K>(event: TurnEvent<K>, listener: TurnEventListener<K>): this {\n this.#functionalEmitter.off(event, listener)\n return this\n }\n\n /**\n * Registers a persistent functional listener for `event`.\n *\n * @param event - The event to listen to.\n * @param listener - The function to call on each emission.\n * @returns `this` for chaining.\n */\n on<K>(event: TurnEvent<K>, listener: TurnEventListener<K>): this {\n this.#functionalEmitter.on(event, listener)\n return this\n }\n\n /**\n * Registers a one-time functional listener for `event` that is automatically removed after the\n * first emission.\n *\n * @param event - The event to listen to.\n * @param listener - The function to call on the next emission.\n * @returns `this` for chaining.\n */\n once<K>(event: TurnEvent<K>, listener: TurnEventListener<K>): this {\n this.#functionalEmitter.once(event, listener)\n return this\n }\n\n // ── Observability bus ────────────────────────────────────────────────────\n\n /**\n * Removes a previously registered observability listener for `event`.\n *\n * @param event - The event to stop observing.\n * @param listener - The listener function to remove.\n * @returns `this` for chaining.\n */\n unobserve<K>(\n event: TurnObservabilityEvent<K>,\n listener: TurnObservabilityEventListener<K>\n ): this {\n this.#observabilityEmitter.off(event, listener)\n return this\n }\n\n /**\n * Registers a persistent observability listener for `event`.\n *\n * @remarks\n * Use the observability bus (`observe` / `unobserve` / `observeOnce`) for instrumentation:\n * turn lifecycle, gate lifecycle, and non-fatal errors. Use the functional bus (`on` / `off` /\n * `once`) for pipeline-affecting events: `message`, `thought`, `toolCall`.\n *\n * @param event - The event to observe.\n * @param listener - The function to call on each emission.\n * @returns `this` for chaining.\n */\n observe<K>(event: TurnObservabilityEvent<K>, listener: TurnObservabilityEventListener<K>): this {\n this.#observabilityEmitter.on(event, listener)\n return this\n }\n\n /**\n * Registers a one-time observability listener for `event` that is automatically removed after\n * the first emission.\n *\n * @param event - The event to observe once.\n * @param listener - The function to call on the next emission.\n * @returns `this` for chaining.\n */\n observeOnce<K>(\n event: TurnObservabilityEvent<K>,\n listener: TurnObservabilityEventListener<K>\n ): this {\n this.#observabilityEmitter.once(event, listener)\n return this\n }\n\n // ── Turn execution ───────────────────────────────────────────────────────\n\n /**\n * Executes a single agent turn against the provided raw context.\n *\n * @remarks\n * Returns `Promise<void>` intentionally — all meaningful output surfaces via events, not return\n * values. Register listeners before calling `run`: observability events (`turnStart`, `turnEnd`)\n * bracket execution; functional events (`message`, `thought`, `toolCall`) fire throughout;\n * observability `error` carries non-fatal pipeline failures; `turnGateOpen` and `turnGateClosed`\n * fire when middleware suspends via `ctx.waitFor()`. Awaiting this method only tells you the\n * pipeline has finished, not what it produced.\n *\n * Constructs a validated {@link @nhtio/adk!TurnContext} from `context` (throwing\n * {@link @nhtio/adk!E_INVALID_TURN_CONTEXT} on failure), then runs the input middleware pipeline.\n * Abort signals are silently swallowed.\n *\n * @param context - Raw input validated and wrapped into a {@link @nhtio/adk!TurnContext} before execution.\n * @throws {@link @nhtio/adk!E_INVALID_TURN_CONTEXT} when `context` does not satisfy the schema.\n */\n async run(context: RawTurnContext): Promise<void> {\n const abortController = context.turnAbortController ?? new AbortController()\n\n // Forward declaration so openGate can reference turnContext.id before it is assigned.\n let turnContextId: string\n\n const openGate: OpenGateFn = <T>(raw: Omit<RawTurnGate, 'turnId' | 'abortSignal'>) => {\n const gate = new TurnGate<T>({\n ...raw,\n turnId: turnContextId,\n abortSignal: abortController.signal,\n })\n this.#observabilityEmitter.emit('turnGateOpen', gate)\n const promise = gate._promise()\n promise.then(\n () => {\n this.#observabilityEmitter.emit('turnGateClosed', {\n gateId: gate.id,\n turnId: gate.turnId,\n result: 'resolved',\n settledAt: DateTime.now(),\n })\n },\n (err: unknown) => {\n let result: 'rejected' | 'aborted' | 'timeout' = 'rejected'\n if (isInstanceOf(err, 'E_TURN_GATE_ABORTED')) result = 'aborted'\n else if (isInstanceOf(err, 'E_TURN_GATE_TIMEOUT')) result = 'timeout'\n this.#observabilityEmitter.emit('turnGateClosed', {\n gateId: gate.id,\n turnId: gate.turnId,\n result,\n settledAt: DateTime.now(),\n })\n }\n )\n return promise\n }\n\n const tools = new ToolRegistry(this.#config.tools)\n\n const turnContext = new TurnContext(\n { ...context, turnAbortController: abortController },\n {\n fetchMemories: this.#config.fetchMemoriesCallback,\n fetchMessages: this.#config.fetchMessagesCallback,\n fetchThoughts: this.#config.fetchThoughtsCallback,\n fetchToolCalls: this.#config.fetchToolCallsCallback,\n fetchTools: this.#config.fetchToolsCallback,\n refreshStandingInstructions: this.#config.refreshStandingInstructionsCallback,\n storeStandingInstruction: this.#config.storeStandingInstructionCallback,\n mutateStandingInstruction: this.#config.mutateStandingInstructionCallback,\n deleteStandingInstruction: this.#config.deleteStandingInstructionCallback,\n storeMemory: this.#config.storeMemoryCallback,\n mutateMemory: this.#config.mutateMemoryCallback,\n deleteMemory: this.#config.deleteMemoryCallback,\n fetchRetrievables: this.#config.fetchRetrievablesCallback,\n storeRetrievable: this.#config.storeRetrievableCallback,\n mutateRetrievable: this.#config.mutateRetrievableCallback,\n deleteRetrievable: this.#config.deleteRetrievableCallback,\n storeMessage: this.#config.storeMessageCallback,\n mutateMessage: this.#config.mutateMessageCallback,\n deleteMessage: this.#config.deleteMessageCallback,\n storeThought: this.#config.storeThoughtCallback,\n mutateThought: this.#config.mutateThoughtCallback,\n deleteThought: this.#config.deleteThoughtCallback,\n storeToolCall: this.#config.storeToolCallCallback,\n mutateToolCall: this.#config.mutateToolCallCallback,\n deleteToolCall: this.#config.deleteToolCallCallback,\n replaceToolCallGroup: this.#config.replaceToolCallGroupCallback,\n storeMediaBytes: this.#config.storeMediaBytesCallback,\n storeRetrievableBytes: this.#config.storeRetrievableBytesCallback,\n emitMessage: (content) => this.#functionalEmitter.emit('message', content),\n emitThought: (content) => this.#functionalEmitter.emit('thought', content),\n emitToolCall: (content) => this.#functionalEmitter.emit('toolCall', content),\n emitToolExecutionStart: (event) =>\n this.#observabilityEmitter.emit('toolExecutionStart', event),\n emitToolExecutionEnd: (event) => this.#observabilityEmitter.emit('toolExecutionEnd', event),\n openGate,\n tools,\n }\n )\n\n turnContextId = turnContext.id\n\n // Publish a turn-level warn-sink for the duration of the run so a token estimation done directly in a\n // TURN pipeline (outside any dispatch) can degrade-and-warn instead of throwing. A nested\n // DispatchRunner.dispatch() pushes its own sink on top (see estimation_context), so estimations inside\n // a dispatch route to the richer dispatch-scoped sink and this turn-level one is the fallback. No\n // dispatchId/iteration here — those belong to a dispatch scope, not the turn.\n const emitEstimationWarning = (w: EstimationWarning): void => {\n const event: WarningEvent = {\n emittedAt: DateTime.now(),\n source: 'turn-runner',\n kind: 'token-estimation-degraded',\n message: `token estimation for encoding \"${String(w.encoding)}\" failed; fell back to a char-based estimate`,\n error: w.error,\n payload: { encoding: w.encoding, textPreview: w.textPreview },\n }\n this.#observabilityEmitter.emit('warning', event)\n }\n\n await runWithEstimationWarnings(emitEstimationWarning, async () => {\n const startedAt = DateTime.now()\n this.#observabilityEmitter.emit('turnStart', {\n turnId: turnContext.id,\n startedAt,\n })\n\n const emitTurnEnd = () => {\n const endedAt = DateTime.now()\n this.#observabilityEmitter.emit('turnEnd', {\n turnId: turnContext.id,\n startedAt,\n endedAt,\n durationMs: endedAt.diff(startedAt).milliseconds,\n })\n }\n\n // 1. Input pipeline\n let inputFailed = false\n let inputReached = false\n await this.#inputPipeline\n .runner()\n .errorHandler(async (error) => {\n if (!isError(error) || !isInstanceOf(error, 'AbortError')) {\n inputFailed = true\n const err = new E_INPUT_PIPELINE_ERROR({\n cause: isError(error) ? error : undefined,\n })\n this.#observabilityEmitter.emit('error', err)\n }\n })\n .finalHandler(async () => {\n inputReached = true\n })\n .run((fn, next) => Promise.resolve(fn(turnContext, next)))\n\n if (!inputReached && !inputFailed && !turnContext.aborted) {\n inputFailed = true\n const err = new E_PIPELINE_SHORT_CIRCUITED(['turn-input'])\n this.#observabilityEmitter.emit('error', err)\n }\n\n if (inputFailed || turnContext.aborted) {\n emitTurnEnd()\n return\n }\n\n // 2. LLM execution dispatch\n let dispatchFailed = false\n try {\n await DispatchRunner.dispatch({\n source: turnContext,\n executor: this.#config.executorCallback,\n turnInputPipeline: this.#config.dispatchInputPipeline,\n turnOutputPipeline: this.#config.dispatchOutputPipeline,\n observers: {\n dispatchStart: [\n (e) => {\n this.#observabilityEmitter.emit('dispatchStart', e)\n },\n ],\n dispatchEnd: [\n (e) => {\n this.#observabilityEmitter.emit('dispatchEnd', e)\n },\n ],\n iterationStart: [\n (e) => {\n this.#observabilityEmitter.emit('iterationStart', e)\n },\n ],\n iterationEnd: [\n (e) => {\n this.#observabilityEmitter.emit('iterationEnd', e)\n },\n ],\n log: [\n (e) => {\n this.#observabilityEmitter.emit('log', e)\n },\n ],\n // Re-forward a DispatchRunner-emitted `warning` (e.g. token-estimation degraded) up to the turn\n // bus, so a consumer subscribed only to the TurnRunner still sees it — mirrors the log/dispatch*\n // bridges above. Non-fatal; the dispatch continues.\n warning: [\n (e) => {\n this.#observabilityEmitter.emit('warning', e)\n },\n ],\n },\n })\n } catch (err) {\n dispatchFailed = true\n const wrapped = isInstanceOf(err, 'BaseException')\n ? (err as InstanceType<typeof Error>)\n : err\n this.#observabilityEmitter.emit('error', wrapped as any)\n }\n\n if (dispatchFailed || turnContext.aborted) {\n emitTurnEnd()\n return\n }\n\n // 3. Output pipeline\n let outputFailed = false\n let outputReached = false\n await this.#outputPipeline\n .runner()\n .errorHandler(async (error) => {\n if (!isError(error) || !isInstanceOf(error, 'AbortError')) {\n outputFailed = true\n const err = new E_OUTPUT_PIPELINE_ERROR({\n cause: isError(error) ? error : undefined,\n })\n this.#observabilityEmitter.emit('error', err)\n }\n })\n .finalHandler(async () => {\n outputReached = true\n })\n .run((fn, next) => Promise.resolve(fn(turnContext, next)))\n\n if (!outputReached && !outputFailed && !turnContext.aborted) {\n const err = new E_PIPELINE_SHORT_CIRCUITED(['turn-output'])\n this.#observabilityEmitter.emit('error', err)\n }\n\n emitTurnEnd()\n })\n }\n}\n"],"mappings":"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"}
package/turn_runner.cjs CHANGED
@@ -1,4 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  require("./chunk-Cek0wNdY.js");
3
- const require_turn_runner = require("./turn_runner-DT8TseR4.js");
3
+ const require_turn_runner = require("./turn_runner-DhLI7OTH.js");
4
4
  exports.TurnRunner = require_turn_runner.TurnRunner;
package/turn_runner.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as TurnRunner } from "./turn_runner-DEGCvfVZ.mjs";
1
+ import { t as TurnRunner } from "./turn_runner-BHHD6h77.mjs";
2
2
  export { TurnRunner };
@@ -1 +1 @@
1
- {"version":3,"file":"types-D6X_aUIb.mjs","names":[],"sources":["../src/batteries/sandbox/paths.ts","../src/batteries/sandbox/types.ts"],"sourcesContent":["import { isInstanceOf } from '../../lib/utils/guards'\nimport { E_INVALID_SANDBOX_CONFIG, E_SANDBOX_PATH_ESCAPE } from './exceptions'\nimport type { SandboxFileSystem } from './contracts/file_system'\nimport type { PathTranslator } from './contracts/path_translator'\n\n/** Why a path was rejected outright, before any normalisation. */\nexport type SandboxPathRejection = 'nul' | 'home' | 'absolute-host' | 'device' | 'unc'\n\n/**\n * Classify an unambiguous host escape, or `undefined` when the path is acceptable.\n *\n * @remarks\n * The REASON is returned, not just a boolean, because the narrated outcome carries it and the model\n * acts on it: \"paths are workspace-relative\" is useless advice for a NUL byte, and a UNC form needs\n * a different correction from a `~`. Reporting every rejection as `escape` collapses five distinct\n * mistakes into one unhelpful message.\n *\n * ORDER IS LOAD-BEARING and matches the plan's step 1. Recognition runs on the CANONICAL separator\n * representation (both `/` and `\\` treated as separators) but still BEFORE any stripping, so a\n * slash-mixed form like `/\\server\\share` cannot slip past a naive prefix test and then become a\n * root-relative path once separators are collapsed. UNC is distinguished from merely-repeated\n * leading separators by having a NON-EMPTY first segment. Nothing is percent-decoded and nothing is\n * case-folded: a literal `%2e%2e` is a filename, not traversal.\n *\n * @param input - The model-supplied path, exactly as given.\n * @returns The rejection reason, or `undefined` to continue normalising.\n */\nexport const classifySandboxPathRejection = (input: string): SandboxPathRejection | undefined => {\n const canonical = input.replaceAll('\\\\', '/')\n if (canonical.includes('\\0')) return 'nul'\n if (canonical.startsWith('~')) return 'home'\n if (/^[A-Za-z]:/.test(canonical)) return 'absolute-host'\n if (/^\\/{2,}[?.]\\//.test(canonical)) return 'device'\n if (/^\\/{2,}[^/]+(?:\\/|$)/.test(canonical)) return 'unc'\n return undefined\n}\n\n/** Return whether a path is an unambiguous host escape before normalisation. */\nexport const isRejectedSandboxPath = (input: string): boolean =>\n classifySandboxPathRejection(input) !== undefined\n\nconst joinSandboxBackendPath = (root: string, relative: string): string =>\n root === '/' ? `/${relative}` : `${root}${relative ? `/${relative}` : ''}`\n\n/** Normalise a model path; leading separators denote the sandbox root. */\nexport const normalizeSandboxPath = (input: string): string => {\n const parts: string[] = []\n for (const part of input.replaceAll('\\\\', '/').split('/')) {\n if (!part || part === '.') continue\n if (part === '..') {\n if (parts.length === 0) throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${input}`])\n parts.pop()\n } else parts.push(part)\n }\n return parts.join('/')\n}\n\n/**\n * Create a symlink guard for paths whose final component may not exist yet.\n * Stat failures are treated as absence, matching the sandbox regular-file helpers.\n */\nexport const createExistingSymlinkGuard = (\n root: string,\n fileSystem: SandboxFileSystem\n): ((relative: string) => Promise<void>) => {\n const canonicalRoot = root.replaceAll('\\\\', '/').replace(/\\/+$/g, '') || '/'\n return async (relative: string): Promise<void> => {\n const parts = relative ? relative.split('/') : []\n for (let index = 0; index <= parts.length; index += 1) {\n const candidate = parts.slice(0, index).join('/')\n try {\n const metadata = await fileSystem.stat(joinSandboxBackendPath(canonicalRoot, candidate))\n if (metadata.kind === 'symlink')\n throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${relative}`])\n } catch (error) {\n if (isInstanceOf(error, 'E_SANDBOX_PATH_ESCAPE', E_SANDBOX_PATH_ESCAPE)) throw error\n break\n }\n }\n }\n}\n\n/** Create a translator that applies the five-step, workspace-relative path policy. */\nexport const createPathTranslator = (\n root: string,\n fileSystem: SandboxFileSystem\n): PathTranslator => {\n if (root.includes('\\0')) throw new E_INVALID_SANDBOX_CONFIG(['root contains NUL'])\n if (!root.startsWith('/')) throw new E_INVALID_SANDBOX_CONFIG(['root must be absolute'])\n const canonicalRoot = root.replaceAll('\\\\', '/').replace(/\\/+/g, '/').replace(/\\/$/, '') || '/'\n const containmentPrefix = canonicalRoot === '/' ? '/' : `${canonicalRoot}/`\n // The explicit annotation is load-bearing, not decoration: TypeScript only treats a call as\n // terminating control flow when the callee is a function declaration or a `const` with an\n // explicit type. Without it, every `reject(input)` below reads as a normal call and the\n // assignment analysis for `relative` fails — which is exactly the error this restores.\n const reject: (input: string) => never = (input) => {\n throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${input}`])\n }\n const toRelative = async (input: string): Promise<string> => {\n const canonical = input.replaceAll('\\\\', '/')\n if (isRejectedSandboxPath(input)) reject(input)\n let relative: string\n try {\n relative = normalizeSandboxPath(canonical)\n } catch {\n reject(input)\n }\n const resolved = `${canonicalRoot}/${relative}`.replace(/\\/+/g, '/')\n if (resolved !== canonicalRoot && !resolved.startsWith(containmentPrefix)) reject(input)\n await assertNoSymlinkComponents(relative)\n return relative\n }\n /** Refuse symlink components on the resolved path and every parent. */\n const assertNoSymlinkComponents = async (relative: string): Promise<void> => {\n const parts = relative ? relative.split('/') : []\n for (let index = 0; index <= parts.length; index += 1) {\n const candidate = parts.slice(0, index).join('/')\n const metadata = await fileSystem.stat(joinSandboxBackendPath(canonicalRoot, candidate))\n if (metadata.kind === 'symlink') reject(relative)\n }\n }\n const translator: PathTranslator = {\n toRelative,\n toBackendPath: (relative: string) => joinSandboxBackendPath(canonicalRoot, relative),\n redact: (text: string) =>\n text\n .replaceAll(canonicalRoot, '<sandbox-root>')\n .replaceAll(/\\/(?:Users|home)\\/[^\\s/]+/g, '<host-user>'),\n assertNoSymlinkComponents,\n }\n return translator\n}\n","import { passesSchema } from './validation'\nimport { validator } from '@nhtio/validation'\nimport { isObject } from '../../lib/utils/guards'\nimport { isRejectedSandboxPath, normalizeSandboxPath } from './paths'\n\n/**\n * Presentation/normalisation brand only; this is NOT a containment guarantee.\n * Every filesystem use must still pass through PathTranslator.toRelative().\n */\nexport type ModelPath = string & { readonly __sandboxModelPath: unique symbol }\n/** Opaque model-facing write root; constructed only by the path layer. */\nexport type ModelWriteRoot = ModelPath & { readonly __sandboxModelWriteRoot: unique symbol }\n/**\n * Create a presentation/normalisation path only; this is NOT a containment guarantee.\n * Every filesystem use must still pass through PathTranslator.toRelative().\n */\nexport const createModelPath = (value: string): ModelPath => {\n if (isRejectedSandboxPath(value)) throw new TypeError('path is not a model path')\n try {\n return normalizeSandboxPath(value) as ModelPath\n } catch {\n throw new TypeError('path is not a model path')\n }\n}\n/** Construct the model root representation used in model-facing outcomes. */\nexport const createModelWriteRoot = (value: string): ModelWriteRoot =>\n createModelPath(value) as ModelWriteRoot\n/** Opaque epoch issued by the sandbox manager and consumed by readers. */\nexport class SandboxEpoch {\n /**\n * Nominal-typing brand. LOAD-BEARING, and `protected` deliberately rather than `#private`:\n * TypeScript derives class nominality from private/protected MEMBERS, not from a protected\n * CONSTRUCTOR, so without a member here the class is structurally `{}` and any bare object\n * satisfies `SandboxEpoch` — which is exactly what an epoch token must not permit. A `#brand`\n * would brand it equally well but reads as an unused local; a protected member does not, since\n * a subclass could legitimately use it.\n */\n protected readonly brand: undefined = undefined\n protected constructor() {}\n\n /** Issue a fresh epoch token for a sandbox manager. */\n static issue(): SandboxEpoch {\n return new SandboxEpoch()\n }\n}\n/** Issue a fresh epoch token without requiring a type assertion. */\nexport const createSandboxEpoch = (): SandboxEpoch => SandboxEpoch.issue()\n\n/** Assembly-facing policy. Reads allow by default; writes and network deny by default. */\nexport interface SandboxPolicy {\n /** Filesystem rules; `disabled` is a kill switch and deliberately does not unify axis defaults. */\n readonly filesystem: {\n /** When true, no filesystem rules apply. */ readonly disabled?: boolean\n /** Read rules use deny-then-allow precedence. */ readonly allowRead?: readonly string[]\n readonly denyRead?: readonly string[]\n /** Write rules use allow-only semantics; deny wins inside the allow list. */ readonly allowWrite?: readonly string[]\n readonly denyWrite?: readonly string[]\n /** Whether `.git/config` is included in the mandatory deny set. */ readonly allowGitConfig?: boolean\n /** Git safe directories passed to spawned children. */ readonly gitSafeDirectories?: readonly string[]\n /** Linux mandatory-deny scan depth. */ readonly mandatoryDenySearchDepth?: number\n }\n /** Network rules. An absent allow list means deny all unless disabled. */\n readonly network: {\n readonly disabled?: boolean\n readonly allowedDomains?: readonly string[]\n readonly deniedDomains?: readonly string[]\n /** Model-readable reasons for denied domains. */ readonly deniedDomainReasons?: Readonly<\n Record<string, string>\n >\n }\n}\n\n/** Backend-derived rules used for admission and drift checks, not a model-facing policy. */\nexport interface DerivedRules {\n /** Platform matcher; there is intentionally no `win32` arm. */\n readonly matcher: {\n readonly platform: 'darwin' | 'linux'\n /** Linux mandatory-deny files are folded; ordinary policy lists are not. */\n readonly caseInsensitive: boolean\n readonly readGlobs: 'native' | 'expanded'\n readonly writeGlobs: 'native' | 'dropped'\n }\n /** Reads are deny-then-allow; allowWithinDeny wins. */\n readonly read: {\n readonly denyOnly: readonly string[]\n readonly allowWithinDeny: readonly string[]\n }\n /** Writes are allow-only; denyWithinAllow wins. */\n readonly write: {\n readonly allowOnly: readonly string[]\n readonly denyWithinAllow: readonly string[]\n }\n /** Reproduced mandatory denies, with provenance because SRT does not expose this derivation. */\n readonly mandatoryDeny: {\n readonly form: 'glob' | 'expanded-paths'\n readonly entries: readonly string[]\n readonly allowGitConfig: boolean\n readonly searchDepth: number\n readonly dotGitWasDirectory?: boolean\n }\n /**\n * Whether the live sandbox has filesystem policy switched off entirely.\n *\n * @remarks\n * A kill switch, and the drift check treats it asymmetrically: `true → false` is a NARROWING and\n * therefore permitted, while `false → true` is DRIFT — it bypasses every filesystem rule while every\n * set comparison still passes, which is precisely why it is compared on its own rather than inferred\n * from the path lists.\n */\n readonly filesystemDisabled: boolean\n /** `disabled` is an ADK provenance discriminator, not an SRT field. */\n readonly network: {\n readonly disabled: boolean\n readonly allowedDomains: readonly string[]\n readonly deniedDomains: readonly string[]\n readonly strictAllowlist: boolean\n }\n /** Unknown upstream keys are drift signals and fail closed. */\n readonly unknownKeys: readonly string[]\n /** Glob forms this derivation could not compile, distinct from pairwise undecidability. */\n readonly undecidableGlobs: readonly string[]\n}\n\n/** Terminal traversal protocol. Done is mandatory so end-of-stream cannot masquerade as completion. */\nexport type Done =\n | { kind: 'done'; complete: true }\n | { kind: 'done'; complete: false; omitted: 'unexplored'; bound: 'maxDepth'; atDepth: number }\n | { kind: 'done'; complete: false; omitted: 'over-limit'; bound: 'limit'; shown: number }\n/** List item frames followed by exactly one {@link Done}. */\nexport type ListFrame = { kind: 'item'; path: string; entryKind: 'file' | 'dir' } | Done\n/** Path-search item frames followed by exactly one {@link Done}. */\nexport type PathFrame = { kind: 'item'; path: string } | Done\n/** Content-hit frames contain the whole matched line, followed by exactly one {@link Done}. */\nexport type HitFrame = { kind: 'item'; path: string; line: number; text: string } | Done\n\n/** Limits for a hostile guest: exactly seven fields, resolved before spawning and passed to both realms. */\nexport interface GuestLimits {\n /** Per-event UTF-8 cap. Default 8192; floor 32 leaves room for the cut marker. */ maxLogEventBytes: number\n /** Retained event count. Default 1000; floor 1 because framing consumes an event. */ maxLogEvents: number\n /** Post-settlement sequence drain. Default 250ms; floor 0, where zero means do not wait. */ logDrainMs: number\n /** Codec traversal depth. Default 32; floor 4. */ codecMaxDepth: number\n /** Codec node count. Default 10_000; floor 16. */ codecMaxNodes: number\n /** Producer-side hostcall/hostresult envelope cap. Default 1_000_000; floor 4096. */ maxHostcallBytes: number\n /** Guest terminal envelope cap. Default 10_000_000; floor 4096. */ maxTerminalPayloadBytes: number\n}\n\n/** Per-evaluation host RPC quotas, separate from the seven guest limits. */\nexport interface HostcallQuotas {\n /** Per-call deadline in milliseconds; default 10_000, floor 1. */ hostcallTimeoutMs: number\n /** Accepted calls per evaluation; default 1000, floor 1. */ maxHostcallsPerEvaluation: number\n /** Simultaneous accepted calls; default 8, floor 1. */ maxConcurrentHostcalls: number\n}\n\n/** A thrown guest value, preserving whether encoder representation was complete. */\nexport type GuestThrown =\n | {\n kind: 'error'\n message?: string\n messageTruncated?: boolean\n stack?: string\n stackTruncated?: boolean\n }\n | { kind: 'value'; encoded: unknown; encoding: 'encoder' | 'partial' }\n | { kind: 'opaque' }\n/** One bounded, sequence-stamped guest log event. */\n/**\n * One log event exactly as the trusted guest bootstrap's logger posts it.\n *\n * @remarks\n * The logger — our code inside the guest, never the snippet — stamps each event with a monotonic\n * per-call sequence and UTF-8-truncates it to `maxLogEventBytes` BEFORE posting. Bounding in the\n * guest is the load-bearing half: a host-side byte count is applied after structured clone has\n * already materialised the string, so a single oversized `console.log` would be in host memory\n * before any accounting could react.\n */\nexport type GuestLogEvent = {\n /** Monotonic per-call sequence, authored by the bootstrap logger — never by the snippet. It is what lets the host prove delivery completeness against the declared `logCount`. */\n seq: number\n /** The logged text, already UTF-8-truncated in the guest when it exceeded `maxLogEventBytes`. */\n text: string\n /** `true` when this event was cut to fit `maxLogEventBytes`; the host renders it with the pinned sentinel suffix so a clipped line is never read as a complete one. */\n truncated: boolean\n}\n/** Delivery and emission framing for guest logs. */\nexport type GuestLogFraming = (\n | { logsComplete: true }\n | { logsComplete: false; logsThrough: number }\n) & {\n logs: GuestLogEvent[]\n logsCapped: boolean\n}\n/** Settled guest evaluation; partial encoding is still successful execution. */\nexport type GuestOutcome = (\n | { ok: true; result: unknown; encoding: 'encoder' | 'partial'; durationMs: number }\n | { ok: false; thrown: GuestThrown; durationMs: number }\n) &\n GuestLogFraming\n/** A guest evaluation handle; timeout kills and rejects rather than fabricating a result. */\nexport interface GuestHandle {\n /** Evaluate source with the model-visible deadline. */\n evaluate(source: string, o: { timeoutMs: number }): Promise<GuestOutcome>\n /** Terminate the guest process or worker. */\n kill(): Promise<void>\n}\n\n/** Defaults visible as model-facing tool arguments; guest limits are intentionally separate. */\nexport interface SandboxCallDefaults {\n /** Default traversal depth: 20. */ maxDepth: number\n /** Default shell timeout: 300 seconds. */ shellTimeoutSeconds: number\n /** Default JavaScript timeout: 30 seconds. */ evaluateTimeoutSeconds: number\n}\n\n/** Default guest values, each paired with its own floor. */\nexport const guestLimitsDefaults: GuestLimits = {\n maxLogEventBytes: 8192,\n maxLogEvents: 1000,\n logDrainMs: 250,\n codecMaxDepth: 32,\n codecMaxNodes: 10_000,\n maxHostcallBytes: 1_000_000,\n maxTerminalPayloadBytes: 10_000_000,\n}\n/** Minimum representable guest values. */\nexport const guestLimitFloors: GuestLimits = {\n maxLogEventBytes: 32,\n maxLogEvents: 1,\n logDrainMs: 0,\n codecMaxDepth: 4,\n codecMaxNodes: 16,\n maxHostcallBytes: 4096,\n maxTerminalPayloadBytes: 4096,\n}\n/** Default host quotas. */\nexport const hostcallQuotasDefaults: HostcallQuotas = {\n hostcallTimeoutMs: 10_000,\n maxHostcallsPerEvaluation: 1000,\n maxConcurrentHostcalls: 8,\n}\n/** Closed structural schema for {@link SandboxPolicy}. */\nexport const sandboxPolicySchema = validator\n .any()\n .required()\n .custom((value, helpers) => {\n if (value === null || typeof value !== 'object') return helpers.error('any.invalid')\n const v = value as Record<string, unknown>\n if (Object.keys(v).some((key) => !['filesystem', 'network'].includes(key)))\n return helpers.error('any.invalid')\n const isStrings = (item: unknown): item is readonly string[] =>\n Array.isArray(item) && item.every((entry) => typeof entry === 'string')\n const isReasons = (item: unknown): boolean =>\n isObject(item) && Object.values(item).every((entry) => typeof entry === 'string')\n const definitions: Record<string, Record<string, (item: unknown) => boolean>> = {\n filesystem: {\n disabled: (item) => typeof item === 'boolean',\n allowRead: isStrings,\n denyRead: isStrings,\n allowWrite: isStrings,\n denyWrite: isStrings,\n allowGitConfig: (item) => typeof item === 'boolean',\n gitSafeDirectories: isStrings,\n mandatoryDenySearchDepth: (item) =>\n typeof item === 'number' && Number.isInteger(item) && item >= 0,\n },\n network: {\n disabled: (item) => typeof item === 'boolean',\n allowedDomains: isStrings,\n deniedDomains: isStrings,\n deniedDomainReasons: isReasons,\n },\n }\n for (const [section, members] of Object.entries(definitions)) {\n const part = v[section]\n if (part === null || typeof part !== 'object' || Array.isArray(part))\n return helpers.error('any.invalid')\n const record = part as Record<string, unknown>\n if (Object.keys(record).some((key) => !members[key] || !members[key](record[key])))\n return helpers.error('any.invalid')\n }\n return value\n })\n/** Duck-type guard for {@link SandboxPolicy}. */\nexport const implementsSandboxPolicy = (value: unknown): value is SandboxPolicy =>\n passesSchema(sandboxPolicySchema, value)\nconst limitSchema = (floors: GuestLimits | HostcallQuotas) =>\n validator\n .any()\n .required()\n .custom((value, helpers) => {\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n return helpers.error('any.invalid')\n if (\n Object.keys(value).length !== Object.keys(floors).length ||\n Object.keys(value).some((key) => !Object.hasOwn(floors, key))\n )\n return helpers.error('any.invalid')\n for (const key of Object.keys(floors)) {\n const n = (value as Record<string, unknown>)[key]\n if (\n typeof n !== 'number' ||\n !Number.isFinite(n) ||\n !Number.isInteger(n) ||\n n < (floors as unknown as Record<string, number>)[key]\n )\n return helpers.error('any.invalid')\n }\n return value\n })\n/** Schema enforcing every guest field's own floor. */\nexport const guestLimitsSchema = limitSchema(guestLimitFloors)\n/** Guard for resolved guest limits. */\nexport const implementsGuestLimits = (value: unknown): value is GuestLimits =>\n passesSchema(guestLimitsSchema, value)\n/** Schema enforcing every host quota floor. */\nexport const hostcallQuotasSchema = limitSchema({\n hostcallTimeoutMs: 1,\n maxHostcallsPerEvaluation: 1,\n maxConcurrentHostcalls: 1,\n})\n/** Guard for resolved host quotas. */\nexport const implementsHostcallQuotas = (value: unknown): value is HostcallQuotas =>\n passesSchema(hostcallQuotasSchema, value)\n"],"mappings":"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"}
1
+ {"version":3,"file":"types-D6X_aUIb.mjs","names":[],"sources":["../src/batteries/sandbox/paths.ts","../src/batteries/sandbox/types.ts"],"sourcesContent":["import { isInstanceOf } from '../../lib/utils/guards'\nimport { E_INVALID_SANDBOX_CONFIG, E_SANDBOX_PATH_ESCAPE } from './exceptions'\nimport type { SandboxFileSystem } from './contracts/file_system'\nimport type { PathTranslator } from './contracts/path_translator'\n\n/** Why a path was rejected outright, before any normalisation. */\nexport type SandboxPathRejection = 'nul' | 'home' | 'absolute-host' | 'device' | 'unc'\n\n/**\n * Classify an unambiguous host escape, or `undefined` when the path is acceptable.\n *\n * @remarks\n * The REASON is returned, not just a boolean, because the narrated outcome carries it and the model\n * acts on it: \"paths are workspace-relative\" is useless advice for a NUL byte, and a UNC form needs\n * a different correction from a `~`. Reporting every rejection as `escape` collapses five distinct\n * mistakes into one unhelpful message.\n *\n * ORDER IS LOAD-BEARING and matches the plan's step 1. Recognition runs on the CANONICAL separator\n * representation (both `/` and `\\` treated as separators) but still BEFORE any stripping, so a\n * slash-mixed form like `/\\server\\share` cannot slip past a naive prefix test and then become a\n * root-relative path once separators are collapsed. UNC is distinguished from merely-repeated\n * leading separators by having a NON-EMPTY first segment. Nothing is percent-decoded and nothing is\n * case-folded: a literal `%2e%2e` is a filename, not traversal.\n *\n * @param input - The model-supplied path, exactly as given.\n * @returns The rejection reason, or `undefined` to continue normalising.\n */\nexport const classifySandboxPathRejection = (input: string): SandboxPathRejection | undefined => {\n const canonical = input.replaceAll('\\\\', '/')\n if (canonical.includes('\\0')) return 'nul'\n if (canonical.startsWith('~')) return 'home'\n if (/^[A-Za-z]:/.test(canonical)) return 'absolute-host'\n if (/^\\/{2,}[?.]\\//.test(canonical)) return 'device'\n if (/^\\/{2,}[^/]+(?:\\/|$)/.test(canonical)) return 'unc'\n return undefined\n}\n\n/** Return whether a path is an unambiguous host escape before normalisation. */\nexport const isRejectedSandboxPath = (input: string): boolean =>\n classifySandboxPathRejection(input) !== undefined\n\nconst joinSandboxBackendPath = (root: string, relative: string): string =>\n root === '/' ? `/${relative}` : `${root}${relative ? `/${relative}` : ''}`\n\n/** Normalise a model path; leading separators denote the sandbox root. */\nexport const normalizeSandboxPath = (input: string): string => {\n const parts: string[] = []\n for (const part of input.replaceAll('\\\\', '/').split('/')) {\n if (!part || part === '.') continue\n if (part === '..') {\n if (parts.length === 0) throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${input}`])\n parts.pop()\n } else parts.push(part)\n }\n return parts.join('/')\n}\n\n/**\n * Create a symlink guard for paths whose final component may not exist yet.\n * Stat failures are treated as absence, matching the sandbox regular-file helpers.\n */\nexport const createExistingSymlinkGuard = (\n root: string,\n fileSystem: SandboxFileSystem\n): ((relative: string) => Promise<void>) => {\n const canonicalRoot = root.replaceAll('\\\\', '/').replace(/\\/+$/g, '') || '/'\n return async (relative: string): Promise<void> => {\n const parts = relative ? relative.split('/') : []\n for (let index = 0; index <= parts.length; index += 1) {\n const candidate = parts.slice(0, index).join('/')\n try {\n const metadata = await fileSystem.stat(joinSandboxBackendPath(canonicalRoot, candidate))\n if (metadata.kind === 'symlink')\n throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${relative}`])\n } catch (error) {\n if (isInstanceOf(error, 'E_SANDBOX_PATH_ESCAPE', E_SANDBOX_PATH_ESCAPE)) throw error\n break\n }\n }\n }\n}\n\n/** Create a translator that applies the five-step, workspace-relative path policy. */\nexport const createPathTranslator = (\n root: string,\n fileSystem: SandboxFileSystem\n): PathTranslator => {\n if (root.includes('\\0')) throw new E_INVALID_SANDBOX_CONFIG(['root contains NUL'])\n if (!root.startsWith('/')) throw new E_INVALID_SANDBOX_CONFIG(['root must be absolute'])\n const canonicalRoot = root.replaceAll('\\\\', '/').replace(/\\/+/g, '/').replace(/\\/$/, '') || '/'\n const containmentPrefix = canonicalRoot === '/' ? '/' : `${canonicalRoot}/`\n // The explicit annotation is load-bearing, not decoration: TypeScript only treats a call as\n // terminating control flow when the callee is a function declaration or a `const` with an\n // explicit type. Without it, every `reject(input)` below reads as a normal call and the\n // assignment analysis for `relative` fails — which is exactly the error this restores.\n const reject: (input: string) => never = (input) => {\n throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${input}`])\n }\n const toRelative = async (input: string): Promise<string> => {\n const canonical = input.replaceAll('\\\\', '/')\n if (isRejectedSandboxPath(input)) reject(input)\n let relative: string\n try {\n relative = normalizeSandboxPath(canonical)\n } catch {\n reject(input)\n }\n const resolved = `${canonicalRoot}/${relative}`.replace(/\\/+/g, '/')\n if (resolved !== canonicalRoot && !resolved.startsWith(containmentPrefix)) reject(input)\n await assertNoSymlinkComponents(relative)\n return relative\n }\n /** Refuse symlink components on the resolved path and every parent. */\n const assertNoSymlinkComponents = async (relative: string): Promise<void> => {\n const parts = relative ? relative.split('/') : []\n for (let index = 0; index <= parts.length; index += 1) {\n const candidate = parts.slice(0, index).join('/')\n const metadata = await fileSystem.stat(joinSandboxBackendPath(canonicalRoot, candidate))\n if (metadata.kind === 'symlink') reject(relative)\n }\n }\n const translator: PathTranslator = {\n toRelative,\n toBackendPath: (relative: string) => joinSandboxBackendPath(canonicalRoot, relative),\n redact: (text: string) =>\n text\n .replaceAll(canonicalRoot, '<sandbox-root>')\n .replaceAll(/\\/(?:Users|home)\\/[^\\s/]+/g, '<host-user>'),\n assertNoSymlinkComponents,\n }\n return translator\n}\n","import { passesSchema } from './validation'\nimport { validator } from '@nhtio/validation'\nimport { isObject } from '../../lib/utils/guards'\nimport { isRejectedSandboxPath, normalizeSandboxPath } from './paths'\n\n/**\n * Presentation/normalisation brand only; this is NOT a containment guarantee.\n * Every filesystem use must still pass through PathTranslator.toRelative().\n */\nexport type ModelPath = string & { readonly __sandboxModelPath: unique symbol }\n/** Opaque model-facing write root; constructed only by the path layer. */\nexport type ModelWriteRoot = ModelPath & { readonly __sandboxModelWriteRoot: unique symbol }\n/**\n * Create a presentation/normalisation path only; this is NOT a containment guarantee.\n * Every filesystem use must still pass through PathTranslator.toRelative().\n */\nexport const createModelPath = (value: string): ModelPath => {\n if (isRejectedSandboxPath(value)) throw new TypeError('path is not a model path')\n try {\n return normalizeSandboxPath(value) as ModelPath\n } catch {\n throw new TypeError('path is not a model path')\n }\n}\n/** Construct the model root representation used in model-facing outcomes. */\nexport const createModelWriteRoot = (value: string): ModelWriteRoot =>\n createModelPath(value) as ModelWriteRoot\n/** Opaque epoch issued by the sandbox manager and consumed by readers. */\nexport class SandboxEpoch {\n /**\n * Nominal-typing brand. LOAD-BEARING, and `protected` deliberately rather than `#private`:\n * TypeScript derives class nominality from private/protected MEMBERS, not from a protected\n * CONSTRUCTOR, so without a member here the class is structurally `{}` and any bare object\n * satisfies `SandboxEpoch` — which is exactly what an epoch token must not permit. A `#brand`\n * would brand it equally well but reads as an unused local; a protected member does not, since\n * a subclass could legitimately use it.\n */\n protected readonly brand: undefined = undefined\n protected constructor() {}\n\n /** Issue a fresh epoch token for a sandbox manager. */\n static issue(): SandboxEpoch {\n return new SandboxEpoch()\n }\n}\n/** Issue a fresh epoch token without requiring a type assertion. */\nexport const createSandboxEpoch = (): SandboxEpoch => SandboxEpoch.issue()\n\n/** Assembly-facing policy. Reads allow by default; writes and network deny by default. */\nexport interface SandboxPolicy {\n /** Filesystem rules; `disabled` is a kill switch and deliberately does not unify axis defaults. */\n readonly filesystem: {\n /** When true, no filesystem rules apply. */ readonly disabled?: boolean\n /** Read rules use deny-then-allow precedence. */ readonly allowRead?: readonly string[]\n readonly denyRead?: readonly string[]\n /** Write rules use allow-only semantics; deny wins inside the allow list. */ readonly allowWrite?: readonly string[]\n readonly denyWrite?: readonly string[]\n /** Whether `.git/config` is included in the mandatory deny set. */ readonly allowGitConfig?: boolean\n /** Git safe directories passed to spawned children. */ readonly gitSafeDirectories?: readonly string[]\n /** Linux mandatory-deny scan depth. */ readonly mandatoryDenySearchDepth?: number\n }\n /**\n * Network rules. An absent allow list means deny all unless disabled.\n *\n * @remarks\n * `disabled` means that SRT's network configuration key is omitted entirely. SRT decides whether\n * to create a network namespace from the presence of that key; its `NetworkConfigSchema` has no\n * schema-valid allow-all domain pattern (`\"*\"` is explicitly rejected). Consequently disabled\n * means no network namespace restriction, rather than an allow-all domain entry.\n */\n readonly network: {\n /** Omit SRT's network key, avoiding `--unshare-net`, when true. */\n readonly disabled?: boolean\n readonly allowedDomains?: readonly string[]\n readonly deniedDomains?: readonly string[]\n /** Model-readable reasons for denied domains. */ readonly deniedDomainReasons?: Readonly<\n Record<string, string>\n >\n }\n}\n\n/** Backend-derived rules used for admission and drift checks, not a model-facing policy. */\nexport interface DerivedRules {\n /** Platform matcher; there is intentionally no `win32` arm. */\n readonly matcher: {\n readonly platform: 'darwin' | 'linux'\n /** Linux mandatory-deny files are folded; ordinary policy lists are not. */\n readonly caseInsensitive: boolean\n readonly readGlobs: 'native' | 'expanded'\n readonly writeGlobs: 'native' | 'dropped'\n }\n /** Reads are deny-then-allow; allowWithinDeny wins. */\n readonly read: {\n readonly denyOnly: readonly string[]\n readonly allowWithinDeny: readonly string[]\n }\n /** Writes are allow-only; denyWithinAllow wins. */\n readonly write: {\n readonly allowOnly: readonly string[]\n readonly denyWithinAllow: readonly string[]\n }\n /** Reproduced mandatory denies, with provenance because SRT does not expose this derivation. */\n readonly mandatoryDeny: {\n readonly form: 'glob' | 'expanded-paths'\n readonly entries: readonly string[]\n readonly allowGitConfig: boolean\n readonly searchDepth: number\n readonly dotGitWasDirectory?: boolean\n }\n /**\n * Whether the live sandbox has filesystem policy switched off entirely.\n *\n * @remarks\n * A kill switch, and the drift check treats it asymmetrically: `true → false` is a NARROWING and\n * therefore permitted, while `false → true` is DRIFT — it bypasses every filesystem rule while every\n * set comparison still passes, which is precisely why it is compared on its own rather than inferred\n * from the path lists.\n */\n readonly filesystemDisabled: boolean\n /** `disabled` is an ADK provenance discriminator, not an SRT field. */\n readonly network: {\n readonly disabled: boolean\n readonly allowedDomains: readonly string[]\n readonly deniedDomains: readonly string[]\n readonly strictAllowlist: boolean\n }\n /** Unknown upstream keys are drift signals and fail closed. */\n readonly unknownKeys: readonly string[]\n /** Glob forms this derivation could not compile, distinct from pairwise undecidability. */\n readonly undecidableGlobs: readonly string[]\n}\n\n/** Terminal traversal protocol. Done is mandatory so end-of-stream cannot masquerade as completion. */\nexport type Done =\n | { kind: 'done'; complete: true }\n | { kind: 'done'; complete: false; omitted: 'unexplored'; bound: 'maxDepth'; atDepth: number }\n | { kind: 'done'; complete: false; omitted: 'over-limit'; bound: 'limit'; shown: number }\n/** List item frames followed by exactly one {@link Done}. */\nexport type ListFrame = { kind: 'item'; path: string; entryKind: 'file' | 'dir' } | Done\n/** Path-search item frames followed by exactly one {@link Done}. */\nexport type PathFrame = { kind: 'item'; path: string } | Done\n/** Content-hit frames contain the whole matched line, followed by exactly one {@link Done}. */\nexport type HitFrame = { kind: 'item'; path: string; line: number; text: string } | Done\n\n/** Limits for a hostile guest: exactly seven fields, resolved before spawning and passed to both realms. */\nexport interface GuestLimits {\n /** Per-event UTF-8 cap. Default 8192; floor 32 leaves room for the cut marker. */ maxLogEventBytes: number\n /** Retained event count. Default 1000; floor 1 because framing consumes an event. */ maxLogEvents: number\n /** Post-settlement sequence drain. Default 250ms; floor 0, where zero means do not wait. */ logDrainMs: number\n /** Codec traversal depth. Default 32; floor 4. */ codecMaxDepth: number\n /** Codec node count. Default 10_000; floor 16. */ codecMaxNodes: number\n /** Producer-side hostcall/hostresult envelope cap. Default 1_000_000; floor 4096. */ maxHostcallBytes: number\n /** Guest terminal envelope cap. Default 10_000_000; floor 4096. */ maxTerminalPayloadBytes: number\n}\n\n/** Per-evaluation host RPC quotas, separate from the seven guest limits. */\nexport interface HostcallQuotas {\n /** Per-call deadline in milliseconds; default 10_000, floor 1. */ hostcallTimeoutMs: number\n /** Accepted calls per evaluation; default 1000, floor 1. */ maxHostcallsPerEvaluation: number\n /** Simultaneous accepted calls; default 8, floor 1. */ maxConcurrentHostcalls: number\n}\n\n/** A thrown guest value, preserving whether encoder representation was complete. */\nexport type GuestThrown =\n | {\n kind: 'error'\n message?: string\n messageTruncated?: boolean\n stack?: string\n stackTruncated?: boolean\n }\n | { kind: 'value'; encoded: unknown; encoding: 'encoder' | 'partial' }\n | { kind: 'opaque' }\n/** One bounded, sequence-stamped guest log event. */\n/**\n * One log event exactly as the trusted guest bootstrap's logger posts it.\n *\n * @remarks\n * The logger — our code inside the guest, never the snippet — stamps each event with a monotonic\n * per-call sequence and UTF-8-truncates it to `maxLogEventBytes` BEFORE posting. Bounding in the\n * guest is the load-bearing half: a host-side byte count is applied after structured clone has\n * already materialised the string, so a single oversized `console.log` would be in host memory\n * before any accounting could react.\n */\nexport type GuestLogEvent = {\n /** Monotonic per-call sequence, authored by the bootstrap logger — never by the snippet. It is what lets the host prove delivery completeness against the declared `logCount`. */\n seq: number\n /** The logged text, already UTF-8-truncated in the guest when it exceeded `maxLogEventBytes`. */\n text: string\n /** `true` when this event was cut to fit `maxLogEventBytes`; the host renders it with the pinned sentinel suffix so a clipped line is never read as a complete one. */\n truncated: boolean\n}\n/** Delivery and emission framing for guest logs. */\nexport type GuestLogFraming = (\n | { logsComplete: true }\n | { logsComplete: false; logsThrough: number }\n) & {\n logs: GuestLogEvent[]\n logsCapped: boolean\n}\n/** Settled guest evaluation; partial encoding is still successful execution. */\nexport type GuestOutcome = (\n | { ok: true; result: unknown; encoding: 'encoder' | 'partial'; durationMs: number }\n | { ok: false; thrown: GuestThrown; durationMs: number }\n) &\n GuestLogFraming\n/** A guest evaluation handle; timeout kills and rejects rather than fabricating a result. */\nexport interface GuestHandle {\n /** Evaluate source with the model-visible deadline. */\n evaluate(source: string, o: { timeoutMs: number }): Promise<GuestOutcome>\n /** Terminate the guest process or worker. */\n kill(): Promise<void>\n}\n\n/** Defaults visible as model-facing tool arguments; guest limits are intentionally separate. */\nexport interface SandboxCallDefaults {\n /** Default traversal depth: 20. */ maxDepth: number\n /** Default shell timeout: 300 seconds. */ shellTimeoutSeconds: number\n /** Default JavaScript timeout: 30 seconds. */ evaluateTimeoutSeconds: number\n}\n\n/** Default guest values, each paired with its own floor. */\nexport const guestLimitsDefaults: GuestLimits = {\n maxLogEventBytes: 8192,\n maxLogEvents: 1000,\n logDrainMs: 250,\n codecMaxDepth: 32,\n codecMaxNodes: 10_000,\n maxHostcallBytes: 1_000_000,\n maxTerminalPayloadBytes: 10_000_000,\n}\n/** Minimum representable guest values. */\nexport const guestLimitFloors: GuestLimits = {\n maxLogEventBytes: 32,\n maxLogEvents: 1,\n logDrainMs: 0,\n codecMaxDepth: 4,\n codecMaxNodes: 16,\n maxHostcallBytes: 4096,\n maxTerminalPayloadBytes: 4096,\n}\n/** Default host quotas. */\nexport const hostcallQuotasDefaults: HostcallQuotas = {\n hostcallTimeoutMs: 10_000,\n maxHostcallsPerEvaluation: 1000,\n maxConcurrentHostcalls: 8,\n}\n/** Closed structural schema for {@link SandboxPolicy}. */\nexport const sandboxPolicySchema = validator\n .any()\n .required()\n .custom((value, helpers) => {\n if (value === null || typeof value !== 'object') return helpers.error('any.invalid')\n const v = value as Record<string, unknown>\n if (Object.keys(v).some((key) => !['filesystem', 'network'].includes(key)))\n return helpers.error('any.invalid')\n const isStrings = (item: unknown): item is readonly string[] =>\n Array.isArray(item) && item.every((entry) => typeof entry === 'string')\n const isReasons = (item: unknown): boolean =>\n isObject(item) && Object.values(item).every((entry) => typeof entry === 'string')\n const definitions: Record<string, Record<string, (item: unknown) => boolean>> = {\n filesystem: {\n disabled: (item) => typeof item === 'boolean',\n allowRead: isStrings,\n denyRead: isStrings,\n allowWrite: isStrings,\n denyWrite: isStrings,\n allowGitConfig: (item) => typeof item === 'boolean',\n gitSafeDirectories: isStrings,\n mandatoryDenySearchDepth: (item) =>\n typeof item === 'number' && Number.isInteger(item) && item >= 0,\n },\n network: {\n disabled: (item) => typeof item === 'boolean',\n allowedDomains: isStrings,\n deniedDomains: isStrings,\n deniedDomainReasons: isReasons,\n },\n }\n for (const [section, members] of Object.entries(definitions)) {\n const part = v[section]\n if (part === null || typeof part !== 'object' || Array.isArray(part))\n return helpers.error('any.invalid')\n const record = part as Record<string, unknown>\n if (Object.keys(record).some((key) => !members[key] || !members[key](record[key])))\n return helpers.error('any.invalid')\n }\n return value\n })\n/** Duck-type guard for {@link SandboxPolicy}. */\nexport const implementsSandboxPolicy = (value: unknown): value is SandboxPolicy =>\n passesSchema(sandboxPolicySchema, value)\nconst limitSchema = (floors: GuestLimits | HostcallQuotas) =>\n validator\n .any()\n .required()\n .custom((value, helpers) => {\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n return helpers.error('any.invalid')\n if (\n Object.keys(value).length !== Object.keys(floors).length ||\n Object.keys(value).some((key) => !Object.hasOwn(floors, key))\n )\n return helpers.error('any.invalid')\n for (const key of Object.keys(floors)) {\n const n = (value as Record<string, unknown>)[key]\n if (\n typeof n !== 'number' ||\n !Number.isFinite(n) ||\n !Number.isInteger(n) ||\n n < (floors as unknown as Record<string, number>)[key]\n )\n return helpers.error('any.invalid')\n }\n return value\n })\n/** Schema enforcing every guest field's own floor. */\nexport const guestLimitsSchema = limitSchema(guestLimitFloors)\n/** Guard for resolved guest limits. */\nexport const implementsGuestLimits = (value: unknown): value is GuestLimits =>\n passesSchema(guestLimitsSchema, value)\n/** Schema enforcing every host quota floor. */\nexport const hostcallQuotasSchema = limitSchema({\n hostcallTimeoutMs: 1,\n maxHostcallsPerEvaluation: 1,\n maxConcurrentHostcalls: 1,\n})\n/** Guard for resolved host quotas. */\nexport const implementsHostcallQuotas = (value: unknown): value is HostcallQuotas =>\n passesSchema(hostcallQuotasSchema, value)\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"types-DrSosVaM.js","names":[],"sources":["../src/batteries/sandbox/paths.ts","../src/batteries/sandbox/types.ts"],"sourcesContent":["import { isInstanceOf } from '../../lib/utils/guards'\nimport { E_INVALID_SANDBOX_CONFIG, E_SANDBOX_PATH_ESCAPE } from './exceptions'\nimport type { SandboxFileSystem } from './contracts/file_system'\nimport type { PathTranslator } from './contracts/path_translator'\n\n/** Why a path was rejected outright, before any normalisation. */\nexport type SandboxPathRejection = 'nul' | 'home' | 'absolute-host' | 'device' | 'unc'\n\n/**\n * Classify an unambiguous host escape, or `undefined` when the path is acceptable.\n *\n * @remarks\n * The REASON is returned, not just a boolean, because the narrated outcome carries it and the model\n * acts on it: \"paths are workspace-relative\" is useless advice for a NUL byte, and a UNC form needs\n * a different correction from a `~`. Reporting every rejection as `escape` collapses five distinct\n * mistakes into one unhelpful message.\n *\n * ORDER IS LOAD-BEARING and matches the plan's step 1. Recognition runs on the CANONICAL separator\n * representation (both `/` and `\\` treated as separators) but still BEFORE any stripping, so a\n * slash-mixed form like `/\\server\\share` cannot slip past a naive prefix test and then become a\n * root-relative path once separators are collapsed. UNC is distinguished from merely-repeated\n * leading separators by having a NON-EMPTY first segment. Nothing is percent-decoded and nothing is\n * case-folded: a literal `%2e%2e` is a filename, not traversal.\n *\n * @param input - The model-supplied path, exactly as given.\n * @returns The rejection reason, or `undefined` to continue normalising.\n */\nexport const classifySandboxPathRejection = (input: string): SandboxPathRejection | undefined => {\n const canonical = input.replaceAll('\\\\', '/')\n if (canonical.includes('\\0')) return 'nul'\n if (canonical.startsWith('~')) return 'home'\n if (/^[A-Za-z]:/.test(canonical)) return 'absolute-host'\n if (/^\\/{2,}[?.]\\//.test(canonical)) return 'device'\n if (/^\\/{2,}[^/]+(?:\\/|$)/.test(canonical)) return 'unc'\n return undefined\n}\n\n/** Return whether a path is an unambiguous host escape before normalisation. */\nexport const isRejectedSandboxPath = (input: string): boolean =>\n classifySandboxPathRejection(input) !== undefined\n\nconst joinSandboxBackendPath = (root: string, relative: string): string =>\n root === '/' ? `/${relative}` : `${root}${relative ? `/${relative}` : ''}`\n\n/** Normalise a model path; leading separators denote the sandbox root. */\nexport const normalizeSandboxPath = (input: string): string => {\n const parts: string[] = []\n for (const part of input.replaceAll('\\\\', '/').split('/')) {\n if (!part || part === '.') continue\n if (part === '..') {\n if (parts.length === 0) throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${input}`])\n parts.pop()\n } else parts.push(part)\n }\n return parts.join('/')\n}\n\n/**\n * Create a symlink guard for paths whose final component may not exist yet.\n * Stat failures are treated as absence, matching the sandbox regular-file helpers.\n */\nexport const createExistingSymlinkGuard = (\n root: string,\n fileSystem: SandboxFileSystem\n): ((relative: string) => Promise<void>) => {\n const canonicalRoot = root.replaceAll('\\\\', '/').replace(/\\/+$/g, '') || '/'\n return async (relative: string): Promise<void> => {\n const parts = relative ? relative.split('/') : []\n for (let index = 0; index <= parts.length; index += 1) {\n const candidate = parts.slice(0, index).join('/')\n try {\n const metadata = await fileSystem.stat(joinSandboxBackendPath(canonicalRoot, candidate))\n if (metadata.kind === 'symlink')\n throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${relative}`])\n } catch (error) {\n if (isInstanceOf(error, 'E_SANDBOX_PATH_ESCAPE', E_SANDBOX_PATH_ESCAPE)) throw error\n break\n }\n }\n }\n}\n\n/** Create a translator that applies the five-step, workspace-relative path policy. */\nexport const createPathTranslator = (\n root: string,\n fileSystem: SandboxFileSystem\n): PathTranslator => {\n if (root.includes('\\0')) throw new E_INVALID_SANDBOX_CONFIG(['root contains NUL'])\n if (!root.startsWith('/')) throw new E_INVALID_SANDBOX_CONFIG(['root must be absolute'])\n const canonicalRoot = root.replaceAll('\\\\', '/').replace(/\\/+/g, '/').replace(/\\/$/, '') || '/'\n const containmentPrefix = canonicalRoot === '/' ? '/' : `${canonicalRoot}/`\n // The explicit annotation is load-bearing, not decoration: TypeScript only treats a call as\n // terminating control flow when the callee is a function declaration or a `const` with an\n // explicit type. Without it, every `reject(input)` below reads as a normal call and the\n // assignment analysis for `relative` fails — which is exactly the error this restores.\n const reject: (input: string) => never = (input) => {\n throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${input}`])\n }\n const toRelative = async (input: string): Promise<string> => {\n const canonical = input.replaceAll('\\\\', '/')\n if (isRejectedSandboxPath(input)) reject(input)\n let relative: string\n try {\n relative = normalizeSandboxPath(canonical)\n } catch {\n reject(input)\n }\n const resolved = `${canonicalRoot}/${relative}`.replace(/\\/+/g, '/')\n if (resolved !== canonicalRoot && !resolved.startsWith(containmentPrefix)) reject(input)\n await assertNoSymlinkComponents(relative)\n return relative\n }\n /** Refuse symlink components on the resolved path and every parent. */\n const assertNoSymlinkComponents = async (relative: string): Promise<void> => {\n const parts = relative ? relative.split('/') : []\n for (let index = 0; index <= parts.length; index += 1) {\n const candidate = parts.slice(0, index).join('/')\n const metadata = await fileSystem.stat(joinSandboxBackendPath(canonicalRoot, candidate))\n if (metadata.kind === 'symlink') reject(relative)\n }\n }\n const translator: PathTranslator = {\n toRelative,\n toBackendPath: (relative: string) => joinSandboxBackendPath(canonicalRoot, relative),\n redact: (text: string) =>\n text\n .replaceAll(canonicalRoot, '<sandbox-root>')\n .replaceAll(/\\/(?:Users|home)\\/[^\\s/]+/g, '<host-user>'),\n assertNoSymlinkComponents,\n }\n return translator\n}\n","import { passesSchema } from './validation'\nimport { validator } from '@nhtio/validation'\nimport { isObject } from '../../lib/utils/guards'\nimport { isRejectedSandboxPath, normalizeSandboxPath } from './paths'\n\n/**\n * Presentation/normalisation brand only; this is NOT a containment guarantee.\n * Every filesystem use must still pass through PathTranslator.toRelative().\n */\nexport type ModelPath = string & { readonly __sandboxModelPath: unique symbol }\n/** Opaque model-facing write root; constructed only by the path layer. */\nexport type ModelWriteRoot = ModelPath & { readonly __sandboxModelWriteRoot: unique symbol }\n/**\n * Create a presentation/normalisation path only; this is NOT a containment guarantee.\n * Every filesystem use must still pass through PathTranslator.toRelative().\n */\nexport const createModelPath = (value: string): ModelPath => {\n if (isRejectedSandboxPath(value)) throw new TypeError('path is not a model path')\n try {\n return normalizeSandboxPath(value) as ModelPath\n } catch {\n throw new TypeError('path is not a model path')\n }\n}\n/** Construct the model root representation used in model-facing outcomes. */\nexport const createModelWriteRoot = (value: string): ModelWriteRoot =>\n createModelPath(value) as ModelWriteRoot\n/** Opaque epoch issued by the sandbox manager and consumed by readers. */\nexport class SandboxEpoch {\n /**\n * Nominal-typing brand. LOAD-BEARING, and `protected` deliberately rather than `#private`:\n * TypeScript derives class nominality from private/protected MEMBERS, not from a protected\n * CONSTRUCTOR, so without a member here the class is structurally `{}` and any bare object\n * satisfies `SandboxEpoch` — which is exactly what an epoch token must not permit. A `#brand`\n * would brand it equally well but reads as an unused local; a protected member does not, since\n * a subclass could legitimately use it.\n */\n protected readonly brand: undefined = undefined\n protected constructor() {}\n\n /** Issue a fresh epoch token for a sandbox manager. */\n static issue(): SandboxEpoch {\n return new SandboxEpoch()\n }\n}\n/** Issue a fresh epoch token without requiring a type assertion. */\nexport const createSandboxEpoch = (): SandboxEpoch => SandboxEpoch.issue()\n\n/** Assembly-facing policy. Reads allow by default; writes and network deny by default. */\nexport interface SandboxPolicy {\n /** Filesystem rules; `disabled` is a kill switch and deliberately does not unify axis defaults. */\n readonly filesystem: {\n /** When true, no filesystem rules apply. */ readonly disabled?: boolean\n /** Read rules use deny-then-allow precedence. */ readonly allowRead?: readonly string[]\n readonly denyRead?: readonly string[]\n /** Write rules use allow-only semantics; deny wins inside the allow list. */ readonly allowWrite?: readonly string[]\n readonly denyWrite?: readonly string[]\n /** Whether `.git/config` is included in the mandatory deny set. */ readonly allowGitConfig?: boolean\n /** Git safe directories passed to spawned children. */ readonly gitSafeDirectories?: readonly string[]\n /** Linux mandatory-deny scan depth. */ readonly mandatoryDenySearchDepth?: number\n }\n /** Network rules. An absent allow list means deny all unless disabled. */\n readonly network: {\n readonly disabled?: boolean\n readonly allowedDomains?: readonly string[]\n readonly deniedDomains?: readonly string[]\n /** Model-readable reasons for denied domains. */ readonly deniedDomainReasons?: Readonly<\n Record<string, string>\n >\n }\n}\n\n/** Backend-derived rules used for admission and drift checks, not a model-facing policy. */\nexport interface DerivedRules {\n /** Platform matcher; there is intentionally no `win32` arm. */\n readonly matcher: {\n readonly platform: 'darwin' | 'linux'\n /** Linux mandatory-deny files are folded; ordinary policy lists are not. */\n readonly caseInsensitive: boolean\n readonly readGlobs: 'native' | 'expanded'\n readonly writeGlobs: 'native' | 'dropped'\n }\n /** Reads are deny-then-allow; allowWithinDeny wins. */\n readonly read: {\n readonly denyOnly: readonly string[]\n readonly allowWithinDeny: readonly string[]\n }\n /** Writes are allow-only; denyWithinAllow wins. */\n readonly write: {\n readonly allowOnly: readonly string[]\n readonly denyWithinAllow: readonly string[]\n }\n /** Reproduced mandatory denies, with provenance because SRT does not expose this derivation. */\n readonly mandatoryDeny: {\n readonly form: 'glob' | 'expanded-paths'\n readonly entries: readonly string[]\n readonly allowGitConfig: boolean\n readonly searchDepth: number\n readonly dotGitWasDirectory?: boolean\n }\n /**\n * Whether the live sandbox has filesystem policy switched off entirely.\n *\n * @remarks\n * A kill switch, and the drift check treats it asymmetrically: `true → false` is a NARROWING and\n * therefore permitted, while `false → true` is DRIFT — it bypasses every filesystem rule while every\n * set comparison still passes, which is precisely why it is compared on its own rather than inferred\n * from the path lists.\n */\n readonly filesystemDisabled: boolean\n /** `disabled` is an ADK provenance discriminator, not an SRT field. */\n readonly network: {\n readonly disabled: boolean\n readonly allowedDomains: readonly string[]\n readonly deniedDomains: readonly string[]\n readonly strictAllowlist: boolean\n }\n /** Unknown upstream keys are drift signals and fail closed. */\n readonly unknownKeys: readonly string[]\n /** Glob forms this derivation could not compile, distinct from pairwise undecidability. */\n readonly undecidableGlobs: readonly string[]\n}\n\n/** Terminal traversal protocol. Done is mandatory so end-of-stream cannot masquerade as completion. */\nexport type Done =\n | { kind: 'done'; complete: true }\n | { kind: 'done'; complete: false; omitted: 'unexplored'; bound: 'maxDepth'; atDepth: number }\n | { kind: 'done'; complete: false; omitted: 'over-limit'; bound: 'limit'; shown: number }\n/** List item frames followed by exactly one {@link Done}. */\nexport type ListFrame = { kind: 'item'; path: string; entryKind: 'file' | 'dir' } | Done\n/** Path-search item frames followed by exactly one {@link Done}. */\nexport type PathFrame = { kind: 'item'; path: string } | Done\n/** Content-hit frames contain the whole matched line, followed by exactly one {@link Done}. */\nexport type HitFrame = { kind: 'item'; path: string; line: number; text: string } | Done\n\n/** Limits for a hostile guest: exactly seven fields, resolved before spawning and passed to both realms. */\nexport interface GuestLimits {\n /** Per-event UTF-8 cap. Default 8192; floor 32 leaves room for the cut marker. */ maxLogEventBytes: number\n /** Retained event count. Default 1000; floor 1 because framing consumes an event. */ maxLogEvents: number\n /** Post-settlement sequence drain. Default 250ms; floor 0, where zero means do not wait. */ logDrainMs: number\n /** Codec traversal depth. Default 32; floor 4. */ codecMaxDepth: number\n /** Codec node count. Default 10_000; floor 16. */ codecMaxNodes: number\n /** Producer-side hostcall/hostresult envelope cap. Default 1_000_000; floor 4096. */ maxHostcallBytes: number\n /** Guest terminal envelope cap. Default 10_000_000; floor 4096. */ maxTerminalPayloadBytes: number\n}\n\n/** Per-evaluation host RPC quotas, separate from the seven guest limits. */\nexport interface HostcallQuotas {\n /** Per-call deadline in milliseconds; default 10_000, floor 1. */ hostcallTimeoutMs: number\n /** Accepted calls per evaluation; default 1000, floor 1. */ maxHostcallsPerEvaluation: number\n /** Simultaneous accepted calls; default 8, floor 1. */ maxConcurrentHostcalls: number\n}\n\n/** A thrown guest value, preserving whether encoder representation was complete. */\nexport type GuestThrown =\n | {\n kind: 'error'\n message?: string\n messageTruncated?: boolean\n stack?: string\n stackTruncated?: boolean\n }\n | { kind: 'value'; encoded: unknown; encoding: 'encoder' | 'partial' }\n | { kind: 'opaque' }\n/** One bounded, sequence-stamped guest log event. */\n/**\n * One log event exactly as the trusted guest bootstrap's logger posts it.\n *\n * @remarks\n * The logger — our code inside the guest, never the snippet — stamps each event with a monotonic\n * per-call sequence and UTF-8-truncates it to `maxLogEventBytes` BEFORE posting. Bounding in the\n * guest is the load-bearing half: a host-side byte count is applied after structured clone has\n * already materialised the string, so a single oversized `console.log` would be in host memory\n * before any accounting could react.\n */\nexport type GuestLogEvent = {\n /** Monotonic per-call sequence, authored by the bootstrap logger — never by the snippet. It is what lets the host prove delivery completeness against the declared `logCount`. */\n seq: number\n /** The logged text, already UTF-8-truncated in the guest when it exceeded `maxLogEventBytes`. */\n text: string\n /** `true` when this event was cut to fit `maxLogEventBytes`; the host renders it with the pinned sentinel suffix so a clipped line is never read as a complete one. */\n truncated: boolean\n}\n/** Delivery and emission framing for guest logs. */\nexport type GuestLogFraming = (\n | { logsComplete: true }\n | { logsComplete: false; logsThrough: number }\n) & {\n logs: GuestLogEvent[]\n logsCapped: boolean\n}\n/** Settled guest evaluation; partial encoding is still successful execution. */\nexport type GuestOutcome = (\n | { ok: true; result: unknown; encoding: 'encoder' | 'partial'; durationMs: number }\n | { ok: false; thrown: GuestThrown; durationMs: number }\n) &\n GuestLogFraming\n/** A guest evaluation handle; timeout kills and rejects rather than fabricating a result. */\nexport interface GuestHandle {\n /** Evaluate source with the model-visible deadline. */\n evaluate(source: string, o: { timeoutMs: number }): Promise<GuestOutcome>\n /** Terminate the guest process or worker. */\n kill(): Promise<void>\n}\n\n/** Defaults visible as model-facing tool arguments; guest limits are intentionally separate. */\nexport interface SandboxCallDefaults {\n /** Default traversal depth: 20. */ maxDepth: number\n /** Default shell timeout: 300 seconds. */ shellTimeoutSeconds: number\n /** Default JavaScript timeout: 30 seconds. */ evaluateTimeoutSeconds: number\n}\n\n/** Default guest values, each paired with its own floor. */\nexport const guestLimitsDefaults: GuestLimits = {\n maxLogEventBytes: 8192,\n maxLogEvents: 1000,\n logDrainMs: 250,\n codecMaxDepth: 32,\n codecMaxNodes: 10_000,\n maxHostcallBytes: 1_000_000,\n maxTerminalPayloadBytes: 10_000_000,\n}\n/** Minimum representable guest values. */\nexport const guestLimitFloors: GuestLimits = {\n maxLogEventBytes: 32,\n maxLogEvents: 1,\n logDrainMs: 0,\n codecMaxDepth: 4,\n codecMaxNodes: 16,\n maxHostcallBytes: 4096,\n maxTerminalPayloadBytes: 4096,\n}\n/** Default host quotas. */\nexport const hostcallQuotasDefaults: HostcallQuotas = {\n hostcallTimeoutMs: 10_000,\n maxHostcallsPerEvaluation: 1000,\n maxConcurrentHostcalls: 8,\n}\n/** Closed structural schema for {@link SandboxPolicy}. */\nexport const sandboxPolicySchema = validator\n .any()\n .required()\n .custom((value, helpers) => {\n if (value === null || typeof value !== 'object') return helpers.error('any.invalid')\n const v = value as Record<string, unknown>\n if (Object.keys(v).some((key) => !['filesystem', 'network'].includes(key)))\n return helpers.error('any.invalid')\n const isStrings = (item: unknown): item is readonly string[] =>\n Array.isArray(item) && item.every((entry) => typeof entry === 'string')\n const isReasons = (item: unknown): boolean =>\n isObject(item) && Object.values(item).every((entry) => typeof entry === 'string')\n const definitions: Record<string, Record<string, (item: unknown) => boolean>> = {\n filesystem: {\n disabled: (item) => typeof item === 'boolean',\n allowRead: isStrings,\n denyRead: isStrings,\n allowWrite: isStrings,\n denyWrite: isStrings,\n allowGitConfig: (item) => typeof item === 'boolean',\n gitSafeDirectories: isStrings,\n mandatoryDenySearchDepth: (item) =>\n typeof item === 'number' && Number.isInteger(item) && item >= 0,\n },\n network: {\n disabled: (item) => typeof item === 'boolean',\n allowedDomains: isStrings,\n deniedDomains: isStrings,\n deniedDomainReasons: isReasons,\n },\n }\n for (const [section, members] of Object.entries(definitions)) {\n const part = v[section]\n if (part === null || typeof part !== 'object' || Array.isArray(part))\n return helpers.error('any.invalid')\n const record = part as Record<string, unknown>\n if (Object.keys(record).some((key) => !members[key] || !members[key](record[key])))\n return helpers.error('any.invalid')\n }\n return value\n })\n/** Duck-type guard for {@link SandboxPolicy}. */\nexport const implementsSandboxPolicy = (value: unknown): value is SandboxPolicy =>\n passesSchema(sandboxPolicySchema, value)\nconst limitSchema = (floors: GuestLimits | HostcallQuotas) =>\n validator\n .any()\n .required()\n .custom((value, helpers) => {\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n return helpers.error('any.invalid')\n if (\n Object.keys(value).length !== Object.keys(floors).length ||\n Object.keys(value).some((key) => !Object.hasOwn(floors, key))\n )\n return helpers.error('any.invalid')\n for (const key of Object.keys(floors)) {\n const n = (value as Record<string, unknown>)[key]\n if (\n typeof n !== 'number' ||\n !Number.isFinite(n) ||\n !Number.isInteger(n) ||\n n < (floors as unknown as Record<string, number>)[key]\n )\n return helpers.error('any.invalid')\n }\n return value\n })\n/** Schema enforcing every guest field's own floor. */\nexport const guestLimitsSchema = limitSchema(guestLimitFloors)\n/** Guard for resolved guest limits. */\nexport const implementsGuestLimits = (value: unknown): value is GuestLimits =>\n passesSchema(guestLimitsSchema, value)\n/** Schema enforcing every host quota floor. */\nexport const hostcallQuotasSchema = limitSchema({\n hostcallTimeoutMs: 1,\n maxHostcallsPerEvaluation: 1,\n maxConcurrentHostcalls: 1,\n})\n/** Guard for resolved host quotas. */\nexport const implementsHostcallQuotas = (value: unknown): value is HostcallQuotas =>\n passesSchema(hostcallQuotasSchema, value)\n"],"mappings":"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"}
1
+ {"version":3,"file":"types-DrSosVaM.js","names":[],"sources":["../src/batteries/sandbox/paths.ts","../src/batteries/sandbox/types.ts"],"sourcesContent":["import { isInstanceOf } from '../../lib/utils/guards'\nimport { E_INVALID_SANDBOX_CONFIG, E_SANDBOX_PATH_ESCAPE } from './exceptions'\nimport type { SandboxFileSystem } from './contracts/file_system'\nimport type { PathTranslator } from './contracts/path_translator'\n\n/** Why a path was rejected outright, before any normalisation. */\nexport type SandboxPathRejection = 'nul' | 'home' | 'absolute-host' | 'device' | 'unc'\n\n/**\n * Classify an unambiguous host escape, or `undefined` when the path is acceptable.\n *\n * @remarks\n * The REASON is returned, not just a boolean, because the narrated outcome carries it and the model\n * acts on it: \"paths are workspace-relative\" is useless advice for a NUL byte, and a UNC form needs\n * a different correction from a `~`. Reporting every rejection as `escape` collapses five distinct\n * mistakes into one unhelpful message.\n *\n * ORDER IS LOAD-BEARING and matches the plan's step 1. Recognition runs on the CANONICAL separator\n * representation (both `/` and `\\` treated as separators) but still BEFORE any stripping, so a\n * slash-mixed form like `/\\server\\share` cannot slip past a naive prefix test and then become a\n * root-relative path once separators are collapsed. UNC is distinguished from merely-repeated\n * leading separators by having a NON-EMPTY first segment. Nothing is percent-decoded and nothing is\n * case-folded: a literal `%2e%2e` is a filename, not traversal.\n *\n * @param input - The model-supplied path, exactly as given.\n * @returns The rejection reason, or `undefined` to continue normalising.\n */\nexport const classifySandboxPathRejection = (input: string): SandboxPathRejection | undefined => {\n const canonical = input.replaceAll('\\\\', '/')\n if (canonical.includes('\\0')) return 'nul'\n if (canonical.startsWith('~')) return 'home'\n if (/^[A-Za-z]:/.test(canonical)) return 'absolute-host'\n if (/^\\/{2,}[?.]\\//.test(canonical)) return 'device'\n if (/^\\/{2,}[^/]+(?:\\/|$)/.test(canonical)) return 'unc'\n return undefined\n}\n\n/** Return whether a path is an unambiguous host escape before normalisation. */\nexport const isRejectedSandboxPath = (input: string): boolean =>\n classifySandboxPathRejection(input) !== undefined\n\nconst joinSandboxBackendPath = (root: string, relative: string): string =>\n root === '/' ? `/${relative}` : `${root}${relative ? `/${relative}` : ''}`\n\n/** Normalise a model path; leading separators denote the sandbox root. */\nexport const normalizeSandboxPath = (input: string): string => {\n const parts: string[] = []\n for (const part of input.replaceAll('\\\\', '/').split('/')) {\n if (!part || part === '.') continue\n if (part === '..') {\n if (parts.length === 0) throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${input}`])\n parts.pop()\n } else parts.push(part)\n }\n return parts.join('/')\n}\n\n/**\n * Create a symlink guard for paths whose final component may not exist yet.\n * Stat failures are treated as absence, matching the sandbox regular-file helpers.\n */\nexport const createExistingSymlinkGuard = (\n root: string,\n fileSystem: SandboxFileSystem\n): ((relative: string) => Promise<void>) => {\n const canonicalRoot = root.replaceAll('\\\\', '/').replace(/\\/+$/g, '') || '/'\n return async (relative: string): Promise<void> => {\n const parts = relative ? relative.split('/') : []\n for (let index = 0; index <= parts.length; index += 1) {\n const candidate = parts.slice(0, index).join('/')\n try {\n const metadata = await fileSystem.stat(joinSandboxBackendPath(canonicalRoot, candidate))\n if (metadata.kind === 'symlink')\n throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${relative}`])\n } catch (error) {\n if (isInstanceOf(error, 'E_SANDBOX_PATH_ESCAPE', E_SANDBOX_PATH_ESCAPE)) throw error\n break\n }\n }\n }\n}\n\n/** Create a translator that applies the five-step, workspace-relative path policy. */\nexport const createPathTranslator = (\n root: string,\n fileSystem: SandboxFileSystem\n): PathTranslator => {\n if (root.includes('\\0')) throw new E_INVALID_SANDBOX_CONFIG(['root contains NUL'])\n if (!root.startsWith('/')) throw new E_INVALID_SANDBOX_CONFIG(['root must be absolute'])\n const canonicalRoot = root.replaceAll('\\\\', '/').replace(/\\/+/g, '/').replace(/\\/$/, '') || '/'\n const containmentPrefix = canonicalRoot === '/' ? '/' : `${canonicalRoot}/`\n // The explicit annotation is load-bearing, not decoration: TypeScript only treats a call as\n // terminating control flow when the callee is a function declaration or a `const` with an\n // explicit type. Without it, every `reject(input)` below reads as a normal call and the\n // assignment analysis for `relative` fails — which is exactly the error this restores.\n const reject: (input: string) => never = (input) => {\n throw new E_SANDBOX_PATH_ESCAPE([`Path rejected: ${input}`])\n }\n const toRelative = async (input: string): Promise<string> => {\n const canonical = input.replaceAll('\\\\', '/')\n if (isRejectedSandboxPath(input)) reject(input)\n let relative: string\n try {\n relative = normalizeSandboxPath(canonical)\n } catch {\n reject(input)\n }\n const resolved = `${canonicalRoot}/${relative}`.replace(/\\/+/g, '/')\n if (resolved !== canonicalRoot && !resolved.startsWith(containmentPrefix)) reject(input)\n await assertNoSymlinkComponents(relative)\n return relative\n }\n /** Refuse symlink components on the resolved path and every parent. */\n const assertNoSymlinkComponents = async (relative: string): Promise<void> => {\n const parts = relative ? relative.split('/') : []\n for (let index = 0; index <= parts.length; index += 1) {\n const candidate = parts.slice(0, index).join('/')\n const metadata = await fileSystem.stat(joinSandboxBackendPath(canonicalRoot, candidate))\n if (metadata.kind === 'symlink') reject(relative)\n }\n }\n const translator: PathTranslator = {\n toRelative,\n toBackendPath: (relative: string) => joinSandboxBackendPath(canonicalRoot, relative),\n redact: (text: string) =>\n text\n .replaceAll(canonicalRoot, '<sandbox-root>')\n .replaceAll(/\\/(?:Users|home)\\/[^\\s/]+/g, '<host-user>'),\n assertNoSymlinkComponents,\n }\n return translator\n}\n","import { passesSchema } from './validation'\nimport { validator } from '@nhtio/validation'\nimport { isObject } from '../../lib/utils/guards'\nimport { isRejectedSandboxPath, normalizeSandboxPath } from './paths'\n\n/**\n * Presentation/normalisation brand only; this is NOT a containment guarantee.\n * Every filesystem use must still pass through PathTranslator.toRelative().\n */\nexport type ModelPath = string & { readonly __sandboxModelPath: unique symbol }\n/** Opaque model-facing write root; constructed only by the path layer. */\nexport type ModelWriteRoot = ModelPath & { readonly __sandboxModelWriteRoot: unique symbol }\n/**\n * Create a presentation/normalisation path only; this is NOT a containment guarantee.\n * Every filesystem use must still pass through PathTranslator.toRelative().\n */\nexport const createModelPath = (value: string): ModelPath => {\n if (isRejectedSandboxPath(value)) throw new TypeError('path is not a model path')\n try {\n return normalizeSandboxPath(value) as ModelPath\n } catch {\n throw new TypeError('path is not a model path')\n }\n}\n/** Construct the model root representation used in model-facing outcomes. */\nexport const createModelWriteRoot = (value: string): ModelWriteRoot =>\n createModelPath(value) as ModelWriteRoot\n/** Opaque epoch issued by the sandbox manager and consumed by readers. */\nexport class SandboxEpoch {\n /**\n * Nominal-typing brand. LOAD-BEARING, and `protected` deliberately rather than `#private`:\n * TypeScript derives class nominality from private/protected MEMBERS, not from a protected\n * CONSTRUCTOR, so without a member here the class is structurally `{}` and any bare object\n * satisfies `SandboxEpoch` — which is exactly what an epoch token must not permit. A `#brand`\n * would brand it equally well but reads as an unused local; a protected member does not, since\n * a subclass could legitimately use it.\n */\n protected readonly brand: undefined = undefined\n protected constructor() {}\n\n /** Issue a fresh epoch token for a sandbox manager. */\n static issue(): SandboxEpoch {\n return new SandboxEpoch()\n }\n}\n/** Issue a fresh epoch token without requiring a type assertion. */\nexport const createSandboxEpoch = (): SandboxEpoch => SandboxEpoch.issue()\n\n/** Assembly-facing policy. Reads allow by default; writes and network deny by default. */\nexport interface SandboxPolicy {\n /** Filesystem rules; `disabled` is a kill switch and deliberately does not unify axis defaults. */\n readonly filesystem: {\n /** When true, no filesystem rules apply. */ readonly disabled?: boolean\n /** Read rules use deny-then-allow precedence. */ readonly allowRead?: readonly string[]\n readonly denyRead?: readonly string[]\n /** Write rules use allow-only semantics; deny wins inside the allow list. */ readonly allowWrite?: readonly string[]\n readonly denyWrite?: readonly string[]\n /** Whether `.git/config` is included in the mandatory deny set. */ readonly allowGitConfig?: boolean\n /** Git safe directories passed to spawned children. */ readonly gitSafeDirectories?: readonly string[]\n /** Linux mandatory-deny scan depth. */ readonly mandatoryDenySearchDepth?: number\n }\n /**\n * Network rules. An absent allow list means deny all unless disabled.\n *\n * @remarks\n * `disabled` means that SRT's network configuration key is omitted entirely. SRT decides whether\n * to create a network namespace from the presence of that key; its `NetworkConfigSchema` has no\n * schema-valid allow-all domain pattern (`\"*\"` is explicitly rejected). Consequently disabled\n * means no network namespace restriction, rather than an allow-all domain entry.\n */\n readonly network: {\n /** Omit SRT's network key, avoiding `--unshare-net`, when true. */\n readonly disabled?: boolean\n readonly allowedDomains?: readonly string[]\n readonly deniedDomains?: readonly string[]\n /** Model-readable reasons for denied domains. */ readonly deniedDomainReasons?: Readonly<\n Record<string, string>\n >\n }\n}\n\n/** Backend-derived rules used for admission and drift checks, not a model-facing policy. */\nexport interface DerivedRules {\n /** Platform matcher; there is intentionally no `win32` arm. */\n readonly matcher: {\n readonly platform: 'darwin' | 'linux'\n /** Linux mandatory-deny files are folded; ordinary policy lists are not. */\n readonly caseInsensitive: boolean\n readonly readGlobs: 'native' | 'expanded'\n readonly writeGlobs: 'native' | 'dropped'\n }\n /** Reads are deny-then-allow; allowWithinDeny wins. */\n readonly read: {\n readonly denyOnly: readonly string[]\n readonly allowWithinDeny: readonly string[]\n }\n /** Writes are allow-only; denyWithinAllow wins. */\n readonly write: {\n readonly allowOnly: readonly string[]\n readonly denyWithinAllow: readonly string[]\n }\n /** Reproduced mandatory denies, with provenance because SRT does not expose this derivation. */\n readonly mandatoryDeny: {\n readonly form: 'glob' | 'expanded-paths'\n readonly entries: readonly string[]\n readonly allowGitConfig: boolean\n readonly searchDepth: number\n readonly dotGitWasDirectory?: boolean\n }\n /**\n * Whether the live sandbox has filesystem policy switched off entirely.\n *\n * @remarks\n * A kill switch, and the drift check treats it asymmetrically: `true → false` is a NARROWING and\n * therefore permitted, while `false → true` is DRIFT — it bypasses every filesystem rule while every\n * set comparison still passes, which is precisely why it is compared on its own rather than inferred\n * from the path lists.\n */\n readonly filesystemDisabled: boolean\n /** `disabled` is an ADK provenance discriminator, not an SRT field. */\n readonly network: {\n readonly disabled: boolean\n readonly allowedDomains: readonly string[]\n readonly deniedDomains: readonly string[]\n readonly strictAllowlist: boolean\n }\n /** Unknown upstream keys are drift signals and fail closed. */\n readonly unknownKeys: readonly string[]\n /** Glob forms this derivation could not compile, distinct from pairwise undecidability. */\n readonly undecidableGlobs: readonly string[]\n}\n\n/** Terminal traversal protocol. Done is mandatory so end-of-stream cannot masquerade as completion. */\nexport type Done =\n | { kind: 'done'; complete: true }\n | { kind: 'done'; complete: false; omitted: 'unexplored'; bound: 'maxDepth'; atDepth: number }\n | { kind: 'done'; complete: false; omitted: 'over-limit'; bound: 'limit'; shown: number }\n/** List item frames followed by exactly one {@link Done}. */\nexport type ListFrame = { kind: 'item'; path: string; entryKind: 'file' | 'dir' } | Done\n/** Path-search item frames followed by exactly one {@link Done}. */\nexport type PathFrame = { kind: 'item'; path: string } | Done\n/** Content-hit frames contain the whole matched line, followed by exactly one {@link Done}. */\nexport type HitFrame = { kind: 'item'; path: string; line: number; text: string } | Done\n\n/** Limits for a hostile guest: exactly seven fields, resolved before spawning and passed to both realms. */\nexport interface GuestLimits {\n /** Per-event UTF-8 cap. Default 8192; floor 32 leaves room for the cut marker. */ maxLogEventBytes: number\n /** Retained event count. Default 1000; floor 1 because framing consumes an event. */ maxLogEvents: number\n /** Post-settlement sequence drain. Default 250ms; floor 0, where zero means do not wait. */ logDrainMs: number\n /** Codec traversal depth. Default 32; floor 4. */ codecMaxDepth: number\n /** Codec node count. Default 10_000; floor 16. */ codecMaxNodes: number\n /** Producer-side hostcall/hostresult envelope cap. Default 1_000_000; floor 4096. */ maxHostcallBytes: number\n /** Guest terminal envelope cap. Default 10_000_000; floor 4096. */ maxTerminalPayloadBytes: number\n}\n\n/** Per-evaluation host RPC quotas, separate from the seven guest limits. */\nexport interface HostcallQuotas {\n /** Per-call deadline in milliseconds; default 10_000, floor 1. */ hostcallTimeoutMs: number\n /** Accepted calls per evaluation; default 1000, floor 1. */ maxHostcallsPerEvaluation: number\n /** Simultaneous accepted calls; default 8, floor 1. */ maxConcurrentHostcalls: number\n}\n\n/** A thrown guest value, preserving whether encoder representation was complete. */\nexport type GuestThrown =\n | {\n kind: 'error'\n message?: string\n messageTruncated?: boolean\n stack?: string\n stackTruncated?: boolean\n }\n | { kind: 'value'; encoded: unknown; encoding: 'encoder' | 'partial' }\n | { kind: 'opaque' }\n/** One bounded, sequence-stamped guest log event. */\n/**\n * One log event exactly as the trusted guest bootstrap's logger posts it.\n *\n * @remarks\n * The logger — our code inside the guest, never the snippet — stamps each event with a monotonic\n * per-call sequence and UTF-8-truncates it to `maxLogEventBytes` BEFORE posting. Bounding in the\n * guest is the load-bearing half: a host-side byte count is applied after structured clone has\n * already materialised the string, so a single oversized `console.log` would be in host memory\n * before any accounting could react.\n */\nexport type GuestLogEvent = {\n /** Monotonic per-call sequence, authored by the bootstrap logger — never by the snippet. It is what lets the host prove delivery completeness against the declared `logCount`. */\n seq: number\n /** The logged text, already UTF-8-truncated in the guest when it exceeded `maxLogEventBytes`. */\n text: string\n /** `true` when this event was cut to fit `maxLogEventBytes`; the host renders it with the pinned sentinel suffix so a clipped line is never read as a complete one. */\n truncated: boolean\n}\n/** Delivery and emission framing for guest logs. */\nexport type GuestLogFraming = (\n | { logsComplete: true }\n | { logsComplete: false; logsThrough: number }\n) & {\n logs: GuestLogEvent[]\n logsCapped: boolean\n}\n/** Settled guest evaluation; partial encoding is still successful execution. */\nexport type GuestOutcome = (\n | { ok: true; result: unknown; encoding: 'encoder' | 'partial'; durationMs: number }\n | { ok: false; thrown: GuestThrown; durationMs: number }\n) &\n GuestLogFraming\n/** A guest evaluation handle; timeout kills and rejects rather than fabricating a result. */\nexport interface GuestHandle {\n /** Evaluate source with the model-visible deadline. */\n evaluate(source: string, o: { timeoutMs: number }): Promise<GuestOutcome>\n /** Terminate the guest process or worker. */\n kill(): Promise<void>\n}\n\n/** Defaults visible as model-facing tool arguments; guest limits are intentionally separate. */\nexport interface SandboxCallDefaults {\n /** Default traversal depth: 20. */ maxDepth: number\n /** Default shell timeout: 300 seconds. */ shellTimeoutSeconds: number\n /** Default JavaScript timeout: 30 seconds. */ evaluateTimeoutSeconds: number\n}\n\n/** Default guest values, each paired with its own floor. */\nexport const guestLimitsDefaults: GuestLimits = {\n maxLogEventBytes: 8192,\n maxLogEvents: 1000,\n logDrainMs: 250,\n codecMaxDepth: 32,\n codecMaxNodes: 10_000,\n maxHostcallBytes: 1_000_000,\n maxTerminalPayloadBytes: 10_000_000,\n}\n/** Minimum representable guest values. */\nexport const guestLimitFloors: GuestLimits = {\n maxLogEventBytes: 32,\n maxLogEvents: 1,\n logDrainMs: 0,\n codecMaxDepth: 4,\n codecMaxNodes: 16,\n maxHostcallBytes: 4096,\n maxTerminalPayloadBytes: 4096,\n}\n/** Default host quotas. */\nexport const hostcallQuotasDefaults: HostcallQuotas = {\n hostcallTimeoutMs: 10_000,\n maxHostcallsPerEvaluation: 1000,\n maxConcurrentHostcalls: 8,\n}\n/** Closed structural schema for {@link SandboxPolicy}. */\nexport const sandboxPolicySchema = validator\n .any()\n .required()\n .custom((value, helpers) => {\n if (value === null || typeof value !== 'object') return helpers.error('any.invalid')\n const v = value as Record<string, unknown>\n if (Object.keys(v).some((key) => !['filesystem', 'network'].includes(key)))\n return helpers.error('any.invalid')\n const isStrings = (item: unknown): item is readonly string[] =>\n Array.isArray(item) && item.every((entry) => typeof entry === 'string')\n const isReasons = (item: unknown): boolean =>\n isObject(item) && Object.values(item).every((entry) => typeof entry === 'string')\n const definitions: Record<string, Record<string, (item: unknown) => boolean>> = {\n filesystem: {\n disabled: (item) => typeof item === 'boolean',\n allowRead: isStrings,\n denyRead: isStrings,\n allowWrite: isStrings,\n denyWrite: isStrings,\n allowGitConfig: (item) => typeof item === 'boolean',\n gitSafeDirectories: isStrings,\n mandatoryDenySearchDepth: (item) =>\n typeof item === 'number' && Number.isInteger(item) && item >= 0,\n },\n network: {\n disabled: (item) => typeof item === 'boolean',\n allowedDomains: isStrings,\n deniedDomains: isStrings,\n deniedDomainReasons: isReasons,\n },\n }\n for (const [section, members] of Object.entries(definitions)) {\n const part = v[section]\n if (part === null || typeof part !== 'object' || Array.isArray(part))\n return helpers.error('any.invalid')\n const record = part as Record<string, unknown>\n if (Object.keys(record).some((key) => !members[key] || !members[key](record[key])))\n return helpers.error('any.invalid')\n }\n return value\n })\n/** Duck-type guard for {@link SandboxPolicy}. */\nexport const implementsSandboxPolicy = (value: unknown): value is SandboxPolicy =>\n passesSchema(sandboxPolicySchema, value)\nconst limitSchema = (floors: GuestLimits | HostcallQuotas) =>\n validator\n .any()\n .required()\n .custom((value, helpers) => {\n if (value === null || typeof value !== 'object' || Array.isArray(value))\n return helpers.error('any.invalid')\n if (\n Object.keys(value).length !== Object.keys(floors).length ||\n Object.keys(value).some((key) => !Object.hasOwn(floors, key))\n )\n return helpers.error('any.invalid')\n for (const key of Object.keys(floors)) {\n const n = (value as Record<string, unknown>)[key]\n if (\n typeof n !== 'number' ||\n !Number.isFinite(n) ||\n !Number.isInteger(n) ||\n n < (floors as unknown as Record<string, number>)[key]\n )\n return helpers.error('any.invalid')\n }\n return value\n })\n/** Schema enforcing every guest field's own floor. */\nexport const guestLimitsSchema = limitSchema(guestLimitFloors)\n/** Guard for resolved guest limits. */\nexport const implementsGuestLimits = (value: unknown): value is GuestLimits =>\n passesSchema(guestLimitsSchema, value)\n/** Schema enforcing every host quota floor. */\nexport const hostcallQuotasSchema = limitSchema({\n hostcallTimeoutMs: 1,\n maxHostcallsPerEvaluation: 1,\n maxConcurrentHostcalls: 1,\n})\n/** Guard for resolved host quotas. */\nexport const implementsHostcallQuotas = (value: unknown): value is HostcallQuotas =>\n passesSchema(hostcallQuotasSchema, value)\n"],"mappings":"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"}
@@ -981,6 +981,18 @@ var VERBS = [
981
981
  appliesTo: ["image"],
982
982
  output: "media"
983
983
  },
984
+ {
985
+ id: "image.annotate",
986
+ description: "Draw vector shapes and text over the image.",
987
+ args: { shapes: {
988
+ type: "json",
989
+ required: true,
990
+ description: "Array of rect, line, arrow, ellipse, or text primitives."
991
+ } },
992
+ requires: { capability: "mutate" },
993
+ appliesTo: ["image"],
994
+ output: "media"
995
+ },
984
996
  {
985
997
  id: "audio.transcribe",
986
998
  description: "Transcribe speech to text.",
@@ -1299,4 +1311,4 @@ var validateOps = (ops, options) => {
1299
1311
  //#endregion
1300
1312
  export { VERBS as a, suggestVerbs as c, isMediaRef as d, isRegExpRef as f, FOLDED_VERBS as i, canonicalFlags as l, toPipe as m, validateOps as n, VERB_INDEX as o, toOps as p, validateSegments as r, foldVerb as s, availableVerbs as t, fromOps as u };
1301
1313
 
1302
- //# sourceMappingURL=validate-lzy9nG_E.mjs.map
1314
+ //# sourceMappingURL=validate-Bi7FGjoq.mjs.map