@animalabs/agent-framework 0.22.2 → 0.22.4

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 (940) hide show
  1. package/README.md +3 -1
  2. package/dist/src/agent.d.ts +77 -12
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +143 -41
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/api/mcp-server-core.js +1 -1
  7. package/dist/src/api/mcp-server-core.js.map +1 -1
  8. package/dist/src/api/server.d.ts.map +1 -1
  9. package/dist/src/api/server.js +5 -1
  10. package/dist/src/api/server.js.map +1 -1
  11. package/dist/src/api/types.d.ts +1 -1
  12. package/dist/src/api/types.d.ts.map +1 -1
  13. package/dist/src/branch-names.d.ts +14 -0
  14. package/dist/src/branch-names.d.ts.map +1 -0
  15. package/dist/src/branch-names.js +22 -0
  16. package/dist/src/branch-names.js.map +1 -0
  17. package/dist/src/closest-names.d.ts +28 -0
  18. package/dist/src/closest-names.d.ts.map +1 -0
  19. package/dist/src/closest-names.js +98 -0
  20. package/dist/src/closest-names.js.map +1 -0
  21. package/dist/src/code-execution/py-runner.d.ts +16 -5
  22. package/dist/src/code-execution/py-runner.d.ts.map +1 -1
  23. package/dist/src/code-execution/py-runner.js +186 -74
  24. package/dist/src/code-execution/py-runner.js.map +1 -1
  25. package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
  26. package/dist/src/code-execution/runtime-py.js +49 -3
  27. package/dist/src/code-execution/runtime-py.js.map +1 -1
  28. package/dist/src/code-execution/tool-definition.d.ts +12 -1
  29. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  30. package/dist/src/code-execution/tool-definition.js +21 -3
  31. package/dist/src/code-execution/tool-definition.js.map +1 -1
  32. package/dist/src/config-checks.d.ts +28 -0
  33. package/dist/src/config-checks.d.ts.map +1 -0
  34. package/dist/src/config-checks.js +52 -0
  35. package/dist/src/config-checks.js.map +1 -0
  36. package/dist/src/deadline-timer.d.ts +21 -0
  37. package/dist/src/deadline-timer.d.ts.map +1 -0
  38. package/dist/src/deadline-timer.js +43 -0
  39. package/dist/src/deadline-timer.js.map +1 -0
  40. package/dist/src/error-message.d.ts +30 -0
  41. package/dist/src/error-message.d.ts.map +1 -0
  42. package/dist/src/error-message.js +50 -0
  43. package/dist/src/error-message.js.map +1 -0
  44. package/dist/src/framework.d.ts +828 -88
  45. package/dist/src/framework.d.ts.map +1 -1
  46. package/dist/src/framework.js +5011 -1326
  47. package/dist/src/framework.js.map +1 -1
  48. package/dist/src/gate/event-gate.d.ts +8 -0
  49. package/dist/src/gate/event-gate.d.ts.map +1 -1
  50. package/dist/src/gate/event-gate.js +27 -3
  51. package/dist/src/gate/event-gate.js.map +1 -1
  52. package/dist/src/index.d.ts +4 -2
  53. package/dist/src/index.d.ts.map +1 -1
  54. package/dist/src/index.js +4 -2
  55. package/dist/src/index.js.map +1 -1
  56. package/dist/src/mcpl/channel-registry.d.ts +144 -6
  57. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  58. package/dist/src/mcpl/channel-registry.js +424 -263
  59. package/dist/src/mcpl/channel-registry.js.map +1 -1
  60. package/dist/src/mcpl/checkpoint-manager.d.ts +21 -1
  61. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  62. package/dist/src/mcpl/checkpoint-manager.js +91 -30
  63. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  64. package/dist/src/mcpl/conversation-router.d.ts +19 -3
  65. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  66. package/dist/src/mcpl/conversation-router.js +23 -8
  67. package/dist/src/mcpl/conversation-router.js.map +1 -1
  68. package/dist/src/mcpl/convert-block.d.ts +20 -0
  69. package/dist/src/mcpl/convert-block.d.ts.map +1 -0
  70. package/dist/src/mcpl/convert-block.js +89 -0
  71. package/dist/src/mcpl/convert-block.js.map +1 -0
  72. package/dist/src/mcpl/errors.d.ts +13 -0
  73. package/dist/src/mcpl/errors.d.ts.map +1 -1
  74. package/dist/src/mcpl/errors.js +28 -0
  75. package/dist/src/mcpl/errors.js.map +1 -1
  76. package/dist/src/mcpl/hook-orchestrator.d.ts +23 -0
  77. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  78. package/dist/src/mcpl/hook-orchestrator.js +83 -72
  79. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  80. package/dist/src/mcpl/inbound-source.d.ts +114 -0
  81. package/dist/src/mcpl/inbound-source.d.ts.map +1 -0
  82. package/dist/src/mcpl/inbound-source.js +83 -0
  83. package/dist/src/mcpl/inbound-source.js.map +1 -0
  84. package/dist/src/mcpl/index.d.ts +3 -1
  85. package/dist/src/mcpl/index.d.ts.map +1 -1
  86. package/dist/src/mcpl/index.js +3 -1
  87. package/dist/src/mcpl/index.js.map +1 -1
  88. package/dist/src/mcpl/inference-router.d.ts +15 -11
  89. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  90. package/dist/src/mcpl/inference-router.js +153 -84
  91. package/dist/src/mcpl/inference-router.js.map +1 -1
  92. package/dist/src/mcpl/push-coalescer.d.ts +33 -11
  93. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  94. package/dist/src/mcpl/push-coalescer.js +52 -20
  95. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  96. package/dist/src/mcpl/push-handler.d.ts +16 -9
  97. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  98. package/dist/src/mcpl/push-handler.js +18 -83
  99. package/dist/src/mcpl/push-handler.js.map +1 -1
  100. package/dist/src/mcpl/reference-fetcher.d.ts +14 -2
  101. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  102. package/dist/src/mcpl/reference-fetcher.js +72 -30
  103. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  104. package/dist/src/mcpl/references.d.ts +47 -12
  105. package/dist/src/mcpl/references.d.ts.map +1 -1
  106. package/dist/src/mcpl/references.js +74 -21
  107. package/dist/src/mcpl/references.js.map +1 -1
  108. package/dist/src/mcpl/server-connection.d.ts +89 -10
  109. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  110. package/dist/src/mcpl/server-connection.js +405 -129
  111. package/dist/src/mcpl/server-connection.js.map +1 -1
  112. package/dist/src/mcpl/server-registry.d.ts +10 -1
  113. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  114. package/dist/src/mcpl/server-registry.js +14 -3
  115. package/dist/src/mcpl/server-registry.js.map +1 -1
  116. package/dist/src/mcpl/skip-reply-wake.d.ts +36 -0
  117. package/dist/src/mcpl/skip-reply-wake.d.ts.map +1 -0
  118. package/dist/src/mcpl/skip-reply-wake.js +55 -0
  119. package/dist/src/mcpl/skip-reply-wake.js.map +1 -0
  120. package/dist/src/mcpl/transport.d.ts +51 -2
  121. package/dist/src/mcpl/transport.d.ts.map +1 -1
  122. package/dist/src/mcpl/transport.js +114 -20
  123. package/dist/src/mcpl/transport.js.map +1 -1
  124. package/dist/src/mcpl/types.d.ts +14 -3
  125. package/dist/src/mcpl/types.d.ts.map +1 -1
  126. package/dist/src/mcpl/types.js.map +1 -1
  127. package/dist/src/mcpl/visible-content.d.ts +15 -0
  128. package/dist/src/mcpl/visible-content.d.ts.map +1 -1
  129. package/dist/src/mcpl/visible-content.js +25 -0
  130. package/dist/src/mcpl/visible-content.js.map +1 -1
  131. package/dist/src/module-registry.d.ts +19 -1
  132. package/dist/src/module-registry.d.ts.map +1 -1
  133. package/dist/src/module-registry.js +54 -25
  134. package/dist/src/module-registry.js.map +1 -1
  135. package/dist/src/modules/api/index.js +1 -1
  136. package/dist/src/modules/api/index.js.map +1 -1
  137. package/dist/src/modules/discord/discord-js-client.d.ts +1 -0
  138. package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
  139. package/dist/src/modules/discord/discord-js-client.js +25 -9
  140. package/dist/src/modules/discord/discord-js-client.js.map +1 -1
  141. package/dist/src/modules/discord/index.d.ts +29 -3
  142. package/dist/src/modules/discord/index.d.ts.map +1 -1
  143. package/dist/src/modules/discord/index.js +379 -143
  144. package/dist/src/modules/discord/index.js.map +1 -1
  145. package/dist/src/modules/discord/types.d.ts +14 -1
  146. package/dist/src/modules/discord/types.d.ts.map +1 -1
  147. package/dist/src/modules/health/index.d.ts.map +1 -1
  148. package/dist/src/modules/health/index.js +2 -1
  149. package/dist/src/modules/health/index.js.map +1 -1
  150. package/dist/src/modules/history/index.d.ts +3 -1
  151. package/dist/src/modules/history/index.d.ts.map +1 -1
  152. package/dist/src/modules/history/index.js +359 -127
  153. package/dist/src/modules/history/index.js.map +1 -1
  154. package/dist/src/modules/history/semantic.d.ts +28 -1
  155. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  156. package/dist/src/modules/history/semantic.js +177 -32
  157. package/dist/src/modules/history/semantic.js.map +1 -1
  158. package/dist/src/modules/unknown-tool.d.ts +10 -0
  159. package/dist/src/modules/unknown-tool.d.ts.map +1 -0
  160. package/dist/src/modules/unknown-tool.js +17 -0
  161. package/dist/src/modules/unknown-tool.js.map +1 -0
  162. package/dist/src/modules/workspace/disk-agreement.d.ts +32 -2
  163. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
  164. package/dist/src/modules/workspace/disk-agreement.js +30 -2
  165. package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
  166. package/dist/src/modules/workspace/effects.d.ts +64 -6
  167. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  168. package/dist/src/modules/workspace/effects.js +291 -44
  169. package/dist/src/modules/workspace/effects.js.map +1 -1
  170. package/dist/src/modules/workspace/index.d.ts +119 -23
  171. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  172. package/dist/src/modules/workspace/index.js +793 -247
  173. package/dist/src/modules/workspace/index.js.map +1 -1
  174. package/dist/src/modules/workspace/observe.d.ts +75 -8
  175. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  176. package/dist/src/modules/workspace/observe.js +174 -14
  177. package/dist/src/modules/workspace/observe.js.map +1 -1
  178. package/dist/src/modules/workspace/reconcile.d.ts +58 -7
  179. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  180. package/dist/src/modules/workspace/reconcile.js +521 -56
  181. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  182. package/dist/src/modules/workspace/types.d.ts +18 -4
  183. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  184. package/dist/src/modules/workspace/types.js.map +1 -1
  185. package/dist/src/modules/workspace/watcher.d.ts +7 -2
  186. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  187. package/dist/src/modules/workspace/watcher.js +28 -4
  188. package/dist/src/modules/workspace/watcher.js.map +1 -1
  189. package/dist/src/prose-drafts.d.ts +240 -0
  190. package/dist/src/prose-drafts.d.ts.map +1 -0
  191. package/dist/src/prose-drafts.js +362 -0
  192. package/dist/src/prose-drafts.js.map +1 -0
  193. package/dist/src/prose-segments.d.ts +21 -2
  194. package/dist/src/prose-segments.d.ts.map +1 -1
  195. package/dist/src/prose-segments.js +46 -9
  196. package/dist/src/prose-segments.js.map +1 -1
  197. package/dist/src/recovery/offline-branch.d.ts +1 -0
  198. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  199. package/dist/src/recovery/offline-branch.js +3 -1
  200. package/dist/src/recovery/offline-branch.js.map +1 -1
  201. package/dist/src/safe-slice.d.ts +5 -1
  202. package/dist/src/safe-slice.d.ts.map +1 -1
  203. package/dist/src/safe-slice.js +9 -1
  204. package/dist/src/safe-slice.js.map +1 -1
  205. package/dist/src/thinking-token-stamp.d.ts +12 -0
  206. package/dist/src/thinking-token-stamp.d.ts.map +1 -1
  207. package/dist/src/thinking-token-stamp.js +34 -3
  208. package/dist/src/thinking-token-stamp.js.map +1 -1
  209. package/dist/src/tool-image-ledger.d.ts +87 -19
  210. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  211. package/dist/src/tool-image-ledger.js +135 -32
  212. package/dist/src/tool-image-ledger.js.map +1 -1
  213. package/dist/src/tool-presentation.d.ts +9 -0
  214. package/dist/src/tool-presentation.d.ts.map +1 -1
  215. package/dist/src/tool-presentation.js +257 -60
  216. package/dist/src/tool-presentation.js.map +1 -1
  217. package/dist/src/tool-result-guard.d.ts.map +1 -1
  218. package/dist/src/tool-result-guard.js +7 -10
  219. package/dist/src/tool-result-guard.js.map +1 -1
  220. package/dist/src/tool-result-history.d.ts +32 -3
  221. package/dist/src/tool-result-history.d.ts.map +1 -1
  222. package/dist/src/tool-result-history.js +106 -19
  223. package/dist/src/tool-result-history.js.map +1 -1
  224. package/dist/src/tune-out/coordinator.d.ts +25 -1
  225. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  226. package/dist/src/tune-out/coordinator.js +260 -65
  227. package/dist/src/tune-out/coordinator.js.map +1 -1
  228. package/dist/src/types/agent.d.ts +17 -5
  229. package/dist/src/types/agent.d.ts.map +1 -1
  230. package/dist/src/types/events.d.ts +17 -0
  231. package/dist/src/types/events.d.ts.map +1 -1
  232. package/dist/src/types/framework.d.ts +21 -5
  233. package/dist/src/types/framework.d.ts.map +1 -1
  234. package/dist/src/types/inference-log.d.ts +1 -1
  235. package/dist/src/types/inference-log.d.ts.map +1 -1
  236. package/dist/src/types/module.d.ts +23 -4
  237. package/dist/src/types/module.d.ts.map +1 -1
  238. package/dist/src/types/trace.d.ts +95 -6
  239. package/dist/src/types/trace.d.ts.map +1 -1
  240. package/dist/src/usage/usage-tracker.d.ts +5 -0
  241. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  242. package/dist/src/usage/usage-tracker.js +35 -8
  243. package/dist/src/usage/usage-tracker.js.map +1 -1
  244. package/package.json +6 -4
  245. package/src/agent.ts +169 -41
  246. package/src/api/mcp-server-core.ts +1 -1
  247. package/src/api/server.ts +4 -1
  248. package/src/api/types.ts +1 -1
  249. package/src/branch-names.ts +19 -0
  250. package/src/closest-names.ts +94 -0
  251. package/src/code-execution/py-runner.ts +200 -76
  252. package/src/code-execution/runtime-py.ts +49 -3
  253. package/src/code-execution/tool-definition.ts +28 -4
  254. package/src/config-checks.ts +48 -0
  255. package/src/deadline-timer.ts +44 -0
  256. package/src/error-message.ts +48 -0
  257. package/src/framework.ts +5352 -1388
  258. package/src/gate/event-gate.ts +28 -3
  259. package/src/index.ts +11 -2
  260. package/src/mcpl/channel-registry.ts +517 -286
  261. package/src/mcpl/checkpoint-manager.ts +104 -29
  262. package/src/mcpl/conversation-router.ts +28 -8
  263. package/src/mcpl/convert-block.ts +97 -0
  264. package/src/mcpl/errors.ts +28 -0
  265. package/src/mcpl/hook-orchestrator.ts +84 -77
  266. package/src/mcpl/inbound-source.ts +156 -0
  267. package/src/mcpl/index.ts +10 -1
  268. package/src/mcpl/inference-router.ts +174 -99
  269. package/src/mcpl/push-coalescer.ts +60 -24
  270. package/src/mcpl/push-handler.ts +20 -90
  271. package/src/mcpl/reference-fetcher.ts +71 -26
  272. package/src/mcpl/references.ts +81 -22
  273. package/src/mcpl/server-connection.ts +427 -132
  274. package/src/mcpl/server-registry.ts +15 -4
  275. package/src/mcpl/skip-reply-wake.ts +73 -0
  276. package/src/mcpl/transport.ts +120 -18
  277. package/src/mcpl/types.ts +14 -3
  278. package/src/mcpl/visible-content.ts +27 -0
  279. package/src/module-registry.ts +58 -23
  280. package/src/modules/api/index.ts +1 -1
  281. package/src/modules/discord/discord-js-client.ts +26 -10
  282. package/src/modules/discord/index.ts +391 -158
  283. package/src/modules/discord/types.ts +15 -2
  284. package/src/modules/health/index.ts +2 -1
  285. package/src/modules/history/index.ts +410 -120
  286. package/src/modules/history/semantic.ts +189 -32
  287. package/src/modules/unknown-tool.ts +16 -0
  288. package/src/modules/workspace/disk-agreement.ts +51 -3
  289. package/src/modules/workspace/effects.ts +279 -35
  290. package/src/modules/workspace/index.ts +810 -260
  291. package/src/modules/workspace/observe.ts +194 -20
  292. package/src/modules/workspace/reconcile.ts +619 -64
  293. package/src/modules/workspace/types.ts +18 -4
  294. package/src/modules/workspace/watcher.ts +27 -4
  295. package/src/prose-drafts.ts +481 -0
  296. package/src/prose-segments.ts +51 -8
  297. package/src/recovery/offline-branch.ts +4 -1
  298. package/src/safe-slice.ts +9 -1
  299. package/src/thinking-token-stamp.ts +34 -3
  300. package/src/tool-image-ledger.ts +153 -34
  301. package/src/tool-presentation.ts +224 -40
  302. package/src/tool-result-guard.ts +7 -8
  303. package/src/tool-result-history.ts +124 -20
  304. package/src/tune-out/coordinator.ts +259 -65
  305. package/src/types/agent.ts +17 -7
  306. package/src/types/events.ts +17 -0
  307. package/src/types/framework.ts +21 -5
  308. package/src/types/inference-log.ts +1 -1
  309. package/src/types/module.ts +24 -4
  310. package/src/types/trace.ts +101 -6
  311. package/src/usage/usage-tracker.ts +32 -8
  312. package/dist/test/activation-request-sanitize.test.d.ts +0 -2
  313. package/dist/test/activation-request-sanitize.test.d.ts.map +0 -1
  314. package/dist/test/activation-request-sanitize.test.js +0 -89
  315. package/dist/test/activation-request-sanitize.test.js.map +0 -1
  316. package/dist/test/agent-cache-ttl.test.d.ts +0 -2
  317. package/dist/test/agent-cache-ttl.test.d.ts.map +0 -1
  318. package/dist/test/agent-cache-ttl.test.js +0 -41
  319. package/dist/test/agent-cache-ttl.test.js.map +0 -1
  320. package/dist/test/agent-runtime-settings.test.d.ts +0 -2
  321. package/dist/test/agent-runtime-settings.test.d.ts.map +0 -1
  322. package/dist/test/agent-runtime-settings.test.js +0 -1598
  323. package/dist/test/agent-runtime-settings.test.js.map +0 -1
  324. package/dist/test/api-admin-gate.test.d.ts +0 -2
  325. package/dist/test/api-admin-gate.test.d.ts.map +0 -1
  326. package/dist/test/api-admin-gate.test.js +0 -116
  327. package/dist/test/api-admin-gate.test.js.map +0 -1
  328. package/dist/test/assistant-persist-refusal.test.d.ts +0 -2
  329. package/dist/test/assistant-persist-refusal.test.d.ts.map +0 -1
  330. package/dist/test/assistant-persist-refusal.test.js +0 -63
  331. package/dist/test/assistant-persist-refusal.test.js.map +0 -1
  332. package/dist/test/batched-wake-locus.test.d.ts +0 -2
  333. package/dist/test/batched-wake-locus.test.d.ts.map +0 -1
  334. package/dist/test/batched-wake-locus.test.js +0 -166
  335. package/dist/test/batched-wake-locus.test.js.map +0 -1
  336. package/dist/test/busy-agent-scheduler.test.d.ts +0 -2
  337. package/dist/test/busy-agent-scheduler.test.d.ts.map +0 -1
  338. package/dist/test/busy-agent-scheduler.test.js +0 -92
  339. package/dist/test/busy-agent-scheduler.test.js.map +0 -1
  340. package/dist/test/channel-incoming-targeting.test.d.ts +0 -2
  341. package/dist/test/channel-incoming-targeting.test.d.ts.map +0 -1
  342. package/dist/test/channel-incoming-targeting.test.js +0 -81
  343. package/dist/test/channel-incoming-targeting.test.js.map +0 -1
  344. package/dist/test/channel-label-history.test.d.ts +0 -2
  345. package/dist/test/channel-label-history.test.d.ts.map +0 -1
  346. package/dist/test/channel-label-history.test.js +0 -424
  347. package/dist/test/channel-label-history.test.js.map +0 -1
  348. package/dist/test/channel-lifecycle-provenance.test.d.ts +0 -2
  349. package/dist/test/channel-lifecycle-provenance.test.d.ts.map +0 -1
  350. package/dist/test/channel-lifecycle-provenance.test.js +0 -47
  351. package/dist/test/channel-lifecycle-provenance.test.js.map +0 -1
  352. package/dist/test/channel-registry-routing.test.d.ts +0 -2
  353. package/dist/test/channel-registry-routing.test.d.ts.map +0 -1
  354. package/dist/test/channel-registry-routing.test.js +0 -446
  355. package/dist/test/channel-registry-routing.test.js.map +0 -1
  356. package/dist/test/channel-registry-typing.test.d.ts +0 -2
  357. package/dist/test/channel-registry-typing.test.d.ts.map +0 -1
  358. package/dist/test/channel-registry-typing.test.js +0 -91
  359. package/dist/test/channel-registry-typing.test.js.map +0 -1
  360. package/dist/test/channels-changed-respond-first.test.d.ts +0 -2
  361. package/dist/test/channels-changed-respond-first.test.d.ts.map +0 -1
  362. package/dist/test/channels-changed-respond-first.test.js +0 -74
  363. package/dist/test/channels-changed-respond-first.test.js.map +0 -1
  364. package/dist/test/checkpoint-attribution.test.d.ts +0 -2
  365. package/dist/test/checkpoint-attribution.test.d.ts.map +0 -1
  366. package/dist/test/checkpoint-attribution.test.js +0 -200
  367. package/dist/test/checkpoint-attribution.test.js.map +0 -1
  368. package/dist/test/code-execution.test.d.ts +0 -11
  369. package/dist/test/code-execution.test.d.ts.map +0 -1
  370. package/dist/test/code-execution.test.js +0 -1272
  371. package/dist/test/code-execution.test.js.map +0 -1
  372. package/dist/test/component-defaults.test.d.ts +0 -2
  373. package/dist/test/component-defaults.test.d.ts.map +0 -1
  374. package/dist/test/component-defaults.test.js +0 -54
  375. package/dist/test/component-defaults.test.js.map +0 -1
  376. package/dist/test/conversation-fork-injection-scoping.test.d.ts +0 -2
  377. package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +0 -1
  378. package/dist/test/conversation-fork-injection-scoping.test.js +0 -200
  379. package/dist/test/conversation-fork-injection-scoping.test.js.map +0 -1
  380. package/dist/test/conversation-router.test.d.ts +0 -2
  381. package/dist/test/conversation-router.test.d.ts.map +0 -1
  382. package/dist/test/conversation-router.test.js +0 -174
  383. package/dist/test/conversation-router.test.js.map +0 -1
  384. package/dist/test/conversation-routing.test.d.ts +0 -7
  385. package/dist/test/conversation-routing.test.d.ts.map +0 -1
  386. package/dist/test/conversation-routing.test.js +0 -621
  387. package/dist/test/conversation-routing.test.js.map +0 -1
  388. package/dist/test/deferred-wake-integrity.test.d.ts +0 -29
  389. package/dist/test/deferred-wake-integrity.test.d.ts.map +0 -1
  390. package/dist/test/deferred-wake-integrity.test.js +0 -320
  391. package/dist/test/deferred-wake-integrity.test.js.map +0 -1
  392. package/dist/test/discord-awareness-outbox.test.d.ts +0 -2
  393. package/dist/test/discord-awareness-outbox.test.d.ts.map +0 -1
  394. package/dist/test/discord-awareness-outbox.test.js +0 -371
  395. package/dist/test/discord-awareness-outbox.test.js.map +0 -1
  396. package/dist/test/discord-history-delete-window.test.d.ts +0 -2
  397. package/dist/test/discord-history-delete-window.test.d.ts.map +0 -1
  398. package/dist/test/discord-history-delete-window.test.js +0 -81
  399. package/dist/test/discord-history-delete-window.test.js.map +0 -1
  400. package/dist/test/discord-history-sync.test.d.ts +0 -2
  401. package/dist/test/discord-history-sync.test.d.ts.map +0 -1
  402. package/dist/test/discord-history-sync.test.js +0 -56
  403. package/dist/test/discord-history-sync.test.js.map +0 -1
  404. package/dist/test/discord-typing-keepalive.test.d.ts +0 -2
  405. package/dist/test/discord-typing-keepalive.test.d.ts.map +0 -1
  406. package/dist/test/discord-typing-keepalive.test.js +0 -77
  407. package/dist/test/discord-typing-keepalive.test.js.map +0 -1
  408. package/dist/test/ephemeral-settle.test.d.ts +0 -2
  409. package/dist/test/ephemeral-settle.test.d.ts.map +0 -1
  410. package/dist/test/ephemeral-settle.test.js +0 -354
  411. package/dist/test/ephemeral-settle.test.js.map +0 -1
  412. package/dist/test/event-gate-passthrough.test.d.ts +0 -15
  413. package/dist/test/event-gate-passthrough.test.d.ts.map +0 -1
  414. package/dist/test/event-gate-passthrough.test.js +0 -289
  415. package/dist/test/event-gate-passthrough.test.js.map +0 -1
  416. package/dist/test/event-gate-policy-mutation.test.d.ts +0 -2
  417. package/dist/test/event-gate-policy-mutation.test.d.ts.map +0 -1
  418. package/dist/test/event-gate-policy-mutation.test.js +0 -137
  419. package/dist/test/event-gate-policy-mutation.test.js.map +0 -1
  420. package/dist/test/event-gate-rate-limit.test.d.ts +0 -2
  421. package/dist/test/event-gate-rate-limit.test.d.ts.map +0 -1
  422. package/dist/test/event-gate-rate-limit.test.js +0 -627
  423. package/dist/test/event-gate-rate-limit.test.js.map +0 -1
  424. package/dist/test/event-gate-script.test.d.ts +0 -2
  425. package/dist/test/event-gate-script.test.d.ts.map +0 -1
  426. package/dist/test/event-gate-script.test.js +0 -95
  427. package/dist/test/event-gate-script.test.js.map +0 -1
  428. package/dist/test/event-gate-selfwake.test.d.ts +0 -15
  429. package/dist/test/event-gate-selfwake.test.d.ts.map +0 -1
  430. package/dist/test/event-gate-selfwake.test.js +0 -125
  431. package/dist/test/event-gate-selfwake.test.js.map +0 -1
  432. package/dist/test/event-gate-tags.test.d.ts +0 -2
  433. package/dist/test/event-gate-tags.test.d.ts.map +0 -1
  434. package/dist/test/event-gate-tags.test.js +0 -76
  435. package/dist/test/event-gate-tags.test.js.map +0 -1
  436. package/dist/test/event-gate.test.d.ts +0 -2
  437. package/dist/test/event-gate.test.d.ts.map +0 -1
  438. package/dist/test/event-gate.test.js +0 -919
  439. package/dist/test/event-gate.test.js.map +0 -1
  440. package/dist/test/explicit-prose-routing.test.d.ts +0 -11
  441. package/dist/test/explicit-prose-routing.test.d.ts.map +0 -1
  442. package/dist/test/explicit-prose-routing.test.js +0 -1715
  443. package/dist/test/explicit-prose-routing.test.js.map +0 -1
  444. package/dist/test/framework.test.d.ts +0 -5
  445. package/dist/test/framework.test.d.ts.map +0 -1
  446. package/dist/test/framework.test.js +0 -1048
  447. package/dist/test/framework.test.js.map +0 -1
  448. package/dist/test/gather-context-timeout.test.d.ts +0 -2
  449. package/dist/test/gather-context-timeout.test.d.ts.map +0 -1
  450. package/dist/test/gather-context-timeout.test.js +0 -120
  451. package/dist/test/gather-context-timeout.test.js.map +0 -1
  452. package/dist/test/health-snapshot.test.d.ts +0 -2
  453. package/dist/test/health-snapshot.test.d.ts.map +0 -1
  454. package/dist/test/health-snapshot.test.js +0 -74
  455. package/dist/test/health-snapshot.test.js.map +0 -1
  456. package/dist/test/helpers/coalescing-fixture.d.ts +0 -74
  457. package/dist/test/helpers/coalescing-fixture.d.ts.map +0 -1
  458. package/dist/test/helpers/coalescing-fixture.js +0 -152
  459. package/dist/test/helpers/coalescing-fixture.js.map +0 -1
  460. package/dist/test/helpers/coalescing-kill-child.d.ts +0 -2
  461. package/dist/test/helpers/coalescing-kill-child.d.ts.map +0 -1
  462. package/dist/test/helpers/coalescing-kill-child.js +0 -24
  463. package/dist/test/helpers/coalescing-kill-child.js.map +0 -1
  464. package/dist/test/helpers/mock-membrane.d.ts +0 -53
  465. package/dist/test/helpers/mock-membrane.d.ts.map +0 -1
  466. package/dist/test/helpers/mock-membrane.js +0 -168
  467. package/dist/test/helpers/mock-membrane.js.map +0 -1
  468. package/dist/test/helpers/pytc-stop-rows.d.ts +0 -11
  469. package/dist/test/helpers/pytc-stop-rows.d.ts.map +0 -1
  470. package/dist/test/helpers/pytc-stop-rows.js +0 -143
  471. package/dist/test/helpers/pytc-stop-rows.js.map +0 -1
  472. package/dist/test/helpers/quiesce-crash-child.d.ts +0 -2
  473. package/dist/test/helpers/quiesce-crash-child.d.ts.map +0 -1
  474. package/dist/test/helpers/quiesce-crash-child.js +0 -156
  475. package/dist/test/helpers/quiesce-crash-child.js.map +0 -1
  476. package/dist/test/hide-then-turn.test.d.ts +0 -2
  477. package/dist/test/hide-then-turn.test.d.ts.map +0 -1
  478. package/dist/test/hide-then-turn.test.js +0 -101
  479. package/dist/test/hide-then-turn.test.js.map +0 -1
  480. package/dist/test/history-module-overview.test.d.ts +0 -2
  481. package/dist/test/history-module-overview.test.d.ts.map +0 -1
  482. package/dist/test/history-module-overview.test.js +0 -711
  483. package/dist/test/history-module-overview.test.js.map +0 -1
  484. package/dist/test/history-module-realstore.test.d.ts +0 -2
  485. package/dist/test/history-module-realstore.test.d.ts.map +0 -1
  486. package/dist/test/history-module-realstore.test.js +0 -363
  487. package/dist/test/history-module-realstore.test.js.map +0 -1
  488. package/dist/test/history-module-semantic.test.d.ts +0 -2
  489. package/dist/test/history-module-semantic.test.d.ts.map +0 -1
  490. package/dist/test/history-module-semantic.test.js +0 -213
  491. package/dist/test/history-module-semantic.test.js.map +0 -1
  492. package/dist/test/history-module-ux.test.d.ts +0 -2
  493. package/dist/test/history-module-ux.test.d.ts.map +0 -1
  494. package/dist/test/history-module-ux.test.js +0 -304
  495. package/dist/test/history-module-ux.test.js.map +0 -1
  496. package/dist/test/history-module.test.d.ts +0 -2
  497. package/dist/test/history-module.test.d.ts.map +0 -1
  498. package/dist/test/history-module.test.js +0 -619
  499. package/dist/test/history-module.test.js.map +0 -1
  500. package/dist/test/history-semantic-review-findings.test.d.ts +0 -2
  501. package/dist/test/history-semantic-review-findings.test.d.ts.map +0 -1
  502. package/dist/test/history-semantic-review-findings.test.js +0 -338
  503. package/dist/test/history-semantic-review-findings.test.js.map +0 -1
  504. package/dist/test/history-semantic-sync-converges.test.d.ts +0 -2
  505. package/dist/test/history-semantic-sync-converges.test.d.ts.map +0 -1
  506. package/dist/test/history-semantic-sync-converges.test.js +0 -109
  507. package/dist/test/history-semantic-sync-converges.test.js.map +0 -1
  508. package/dist/test/host-quiesce.test.d.ts +0 -2
  509. package/dist/test/host-quiesce.test.d.ts.map +0 -1
  510. package/dist/test/host-quiesce.test.js +0 -988
  511. package/dist/test/host-quiesce.test.js.map +0 -1
  512. package/dist/test/inference-exhaustion-rewind.test.d.ts +0 -2
  513. package/dist/test/inference-exhaustion-rewind.test.d.ts.map +0 -1
  514. package/dist/test/inference-exhaustion-rewind.test.js +0 -387
  515. package/dist/test/inference-exhaustion-rewind.test.js.map +0 -1
  516. package/dist/test/inference-failure-bounds.test.d.ts +0 -2
  517. package/dist/test/inference-failure-bounds.test.d.ts.map +0 -1
  518. package/dist/test/inference-failure-bounds.test.js +0 -1202
  519. package/dist/test/inference-failure-bounds.test.js.map +0 -1
  520. package/dist/test/inference-failure-observability.test.d.ts +0 -2
  521. package/dist/test/inference-failure-observability.test.d.ts.map +0 -1
  522. package/dist/test/inference-failure-observability.test.js +0 -84
  523. package/dist/test/inference-failure-observability.test.js.map +0 -1
  524. package/dist/test/journal-tool.test.d.ts +0 -2
  525. package/dist/test/journal-tool.test.d.ts.map +0 -1
  526. package/dist/test/journal-tool.test.js +0 -33
  527. package/dist/test/journal-tool.test.js.map +0 -1
  528. package/dist/test/kv-unified-open-flight-retry.test.d.ts +0 -2
  529. package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +0 -1
  530. package/dist/test/kv-unified-open-flight-retry.test.js +0 -189
  531. package/dist/test/kv-unified-open-flight-retry.test.js.map +0 -1
  532. package/dist/test/kv-unified-wiring.test.d.ts +0 -2
  533. package/dist/test/kv-unified-wiring.test.d.ts.map +0 -1
  534. package/dist/test/kv-unified-wiring.test.js +0 -95
  535. package/dist/test/kv-unified-wiring.test.js.map +0 -1
  536. package/dist/test/late-events-after-stop.test.d.ts +0 -2
  537. package/dist/test/late-events-after-stop.test.d.ts.map +0 -1
  538. package/dist/test/late-events-after-stop.test.js +0 -122
  539. package/dist/test/late-events-after-stop.test.js.map +0 -1
  540. package/dist/test/liveness-watchdog.test.d.ts +0 -2
  541. package/dist/test/liveness-watchdog.test.d.ts.map +0 -1
  542. package/dist/test/liveness-watchdog.test.js +0 -94
  543. package/dist/test/liveness-watchdog.test.js.map +0 -1
  544. package/dist/test/machine-close-provenance.test.d.ts +0 -2
  545. package/dist/test/machine-close-provenance.test.d.ts.map +0 -1
  546. package/dist/test/machine-close-provenance.test.js +0 -116
  547. package/dist/test/machine-close-provenance.test.js.map +0 -1
  548. package/dist/test/maintenance-timer.test.d.ts +0 -2
  549. package/dist/test/maintenance-timer.test.d.ts.map +0 -1
  550. package/dist/test/maintenance-timer.test.js +0 -1827
  551. package/dist/test/maintenance-timer.test.js.map +0 -1
  552. package/dist/test/mcp-server-bin.test.d.ts +0 -2
  553. package/dist/test/mcp-server-bin.test.d.ts.map +0 -1
  554. package/dist/test/mcp-server-bin.test.js +0 -142
  555. package/dist/test/mcp-server-bin.test.js.map +0 -1
  556. package/dist/test/mcpl-access-provider.test.d.ts +0 -2
  557. package/dist/test/mcpl-access-provider.test.d.ts.map +0 -1
  558. package/dist/test/mcpl-access-provider.test.js +0 -20
  559. package/dist/test/mcpl-access-provider.test.js.map +0 -1
  560. package/dist/test/mcpl-awareness-barrier.test.d.ts +0 -2
  561. package/dist/test/mcpl-awareness-barrier.test.d.ts.map +0 -1
  562. package/dist/test/mcpl-awareness-barrier.test.js +0 -769
  563. package/dist/test/mcpl-awareness-barrier.test.js.map +0 -1
  564. package/dist/test/mcpl-capability-grant.test.d.ts +0 -10
  565. package/dist/test/mcpl-capability-grant.test.d.ts.map +0 -1
  566. package/dist/test/mcpl-capability-grant.test.js +0 -111
  567. package/dist/test/mcpl-capability-grant.test.js.map +0 -1
  568. package/dist/test/mcpl-capability-scoping.test.d.ts +0 -2
  569. package/dist/test/mcpl-capability-scoping.test.d.ts.map +0 -1
  570. package/dist/test/mcpl-capability-scoping.test.js +0 -266
  571. package/dist/test/mcpl-capability-scoping.test.js.map +0 -1
  572. package/dist/test/mcpl-child-env.test.d.ts +0 -2
  573. package/dist/test/mcpl-child-env.test.d.ts.map +0 -1
  574. package/dist/test/mcpl-child-env.test.js +0 -158
  575. package/dist/test/mcpl-child-env.test.js.map +0 -1
  576. package/dist/test/mcpl-coalesced-content-parity.test.d.ts +0 -2
  577. package/dist/test/mcpl-coalesced-content-parity.test.d.ts.map +0 -1
  578. package/dist/test/mcpl-coalesced-content-parity.test.js +0 -532
  579. package/dist/test/mcpl-coalesced-content-parity.test.js.map +0 -1
  580. package/dist/test/mcpl-coalescing.test.d.ts +0 -2
  581. package/dist/test/mcpl-coalescing.test.d.ts.map +0 -1
  582. package/dist/test/mcpl-coalescing.test.js +0 -1125
  583. package/dist/test/mcpl-coalescing.test.js.map +0 -1
  584. package/dist/test/mcpl-config-diagnostics.test.d.ts +0 -2
  585. package/dist/test/mcpl-config-diagnostics.test.d.ts.map +0 -1
  586. package/dist/test/mcpl-config-diagnostics.test.js +0 -452
  587. package/dist/test/mcpl-config-diagnostics.test.js.map +0 -1
  588. package/dist/test/mcpl-empty-content.test.d.ts +0 -2
  589. package/dist/test/mcpl-empty-content.test.d.ts.map +0 -1
  590. package/dist/test/mcpl-empty-content.test.js +0 -368
  591. package/dist/test/mcpl-empty-content.test.js.map +0 -1
  592. package/dist/test/mcpl-gate-roundtrip.test.d.ts +0 -2
  593. package/dist/test/mcpl-gate-roundtrip.test.d.ts.map +0 -1
  594. package/dist/test/mcpl-gate-roundtrip.test.js +0 -285
  595. package/dist/test/mcpl-gate-roundtrip.test.js.map +0 -1
  596. package/dist/test/mcpl-list-status.test.d.ts +0 -2
  597. package/dist/test/mcpl-list-status.test.d.ts.map +0 -1
  598. package/dist/test/mcpl-list-status.test.js +0 -82
  599. package/dist/test/mcpl-list-status.test.js.map +0 -1
  600. package/dist/test/mcpl-manifest-changed.test.d.ts +0 -2
  601. package/dist/test/mcpl-manifest-changed.test.d.ts.map +0 -1
  602. package/dist/test/mcpl-manifest-changed.test.js +0 -178
  603. package/dist/test/mcpl-manifest-changed.test.js.map +0 -1
  604. package/dist/test/mcpl-push-convert.test.d.ts +0 -2
  605. package/dist/test/mcpl-push-convert.test.d.ts.map +0 -1
  606. package/dist/test/mcpl-push-convert.test.js +0 -42
  607. package/dist/test/mcpl-push-convert.test.js.map +0 -1
  608. package/dist/test/mcpl-reconnect-events.test.d.ts +0 -2
  609. package/dist/test/mcpl-reconnect-events.test.d.ts.map +0 -1
  610. package/dist/test/mcpl-reconnect-events.test.js +0 -144
  611. package/dist/test/mcpl-reconnect-events.test.js.map +0 -1
  612. package/dist/test/mcpl-reconnect-reregistration.test.d.ts +0 -2
  613. package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +0 -1
  614. package/dist/test/mcpl-reconnect-reregistration.test.js +0 -238
  615. package/dist/test/mcpl-reconnect-reregistration.test.js.map +0 -1
  616. package/dist/test/mcpl-reference-fetcher.test.d.ts +0 -2
  617. package/dist/test/mcpl-reference-fetcher.test.d.ts.map +0 -1
  618. package/dist/test/mcpl-reference-fetcher.test.js +0 -427
  619. package/dist/test/mcpl-reference-fetcher.test.js.map +0 -1
  620. package/dist/test/mcpl-reference-fields.test.d.ts +0 -2
  621. package/dist/test/mcpl-reference-fields.test.d.ts.map +0 -1
  622. package/dist/test/mcpl-reference-fields.test.js +0 -149
  623. package/dist/test/mcpl-reference-fields.test.js.map +0 -1
  624. package/dist/test/mcpl-rejection-log-lines.test.d.ts +0 -2
  625. package/dist/test/mcpl-rejection-log-lines.test.d.ts.map +0 -1
  626. package/dist/test/mcpl-rejection-log-lines.test.js +0 -349
  627. package/dist/test/mcpl-rejection-log-lines.test.js.map +0 -1
  628. package/dist/test/mcpl-request-timeout.test.d.ts +0 -2
  629. package/dist/test/mcpl-request-timeout.test.d.ts.map +0 -1
  630. package/dist/test/mcpl-request-timeout.test.js +0 -138
  631. package/dist/test/mcpl-request-timeout.test.js.map +0 -1
  632. package/dist/test/mcpl-runtime-lifecycle.test.d.ts +0 -7
  633. package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +0 -1
  634. package/dist/test/mcpl-runtime-lifecycle.test.js +0 -142
  635. package/dist/test/mcpl-runtime-lifecycle.test.js.map +0 -1
  636. package/dist/test/mcpl-stderr-tail.test.d.ts +0 -2
  637. package/dist/test/mcpl-stderr-tail.test.d.ts.map +0 -1
  638. package/dist/test/mcpl-stderr-tail.test.js +0 -210
  639. package/dist/test/mcpl-stderr-tail.test.js.map +0 -1
  640. package/dist/test/mcpl-tool-class-listing.test.d.ts +0 -2
  641. package/dist/test/mcpl-tool-class-listing.test.d.ts.map +0 -1
  642. package/dist/test/mcpl-tool-class-listing.test.js +0 -147
  643. package/dist/test/mcpl-tool-class-listing.test.js.map +0 -1
  644. package/dist/test/mcpl-tool-classes.test.d.ts +0 -7
  645. package/dist/test/mcpl-tool-classes.test.d.ts.map +0 -1
  646. package/dist/test/mcpl-tool-classes.test.js +0 -80
  647. package/dist/test/mcpl-tool-classes.test.js.map +0 -1
  648. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts +0 -2
  649. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts.map +0 -1
  650. package/dist/test/mcpl-tool-lifecycle-e2e.test.js +0 -237
  651. package/dist/test/mcpl-tool-lifecycle-e2e.test.js.map +0 -1
  652. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts +0 -11
  653. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts.map +0 -1
  654. package/dist/test/mcpl-tool-lifecycle-emitter.test.js +0 -412
  655. package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +0 -1
  656. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +0 -2
  657. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +0 -1
  658. package/dist/test/mcpl-tool-lifecycle-script.test.js +0 -288
  659. package/dist/test/mcpl-tool-lifecycle-script.test.js.map +0 -1
  660. package/dist/test/mcpl-tool-lifecycle.test.d.ts +0 -11
  661. package/dist/test/mcpl-tool-lifecycle.test.d.ts.map +0 -1
  662. package/dist/test/mcpl-tool-lifecycle.test.js +0 -270
  663. package/dist/test/mcpl-tool-lifecycle.test.js.map +0 -1
  664. package/dist/test/mcpl-transport-close.test.d.ts +0 -2
  665. package/dist/test/mcpl-transport-close.test.d.ts.map +0 -1
  666. package/dist/test/mcpl-transport-close.test.js +0 -373
  667. package/dist/test/mcpl-transport-close.test.js.map +0 -1
  668. package/dist/test/mcpl-transport-spawn.test.d.ts +0 -2
  669. package/dist/test/mcpl-transport-spawn.test.d.ts.map +0 -1
  670. package/dist/test/mcpl-transport-spawn.test.js +0 -58
  671. package/dist/test/mcpl-transport-spawn.test.js.map +0 -1
  672. package/dist/test/mcpl-ws-transport.test.d.ts +0 -2
  673. package/dist/test/mcpl-ws-transport.test.d.ts.map +0 -1
  674. package/dist/test/mcpl-ws-transport.test.js +0 -259
  675. package/dist/test/mcpl-ws-transport.test.js.map +0 -1
  676. package/dist/test/module-context-integration.test.d.ts +0 -18
  677. package/dist/test/module-context-integration.test.d.ts.map +0 -1
  678. package/dist/test/module-context-integration.test.js +0 -181
  679. package/dist/test/module-context-integration.test.js.map +0 -1
  680. package/dist/test/offline-recovery-branch.test.d.ts +0 -2
  681. package/dist/test/offline-recovery-branch.test.d.ts.map +0 -1
  682. package/dist/test/offline-recovery-branch.test.js +0 -570
  683. package/dist/test/offline-recovery-branch.test.js.map +0 -1
  684. package/dist/test/operator-surgery.test.d.ts +0 -2
  685. package/dist/test/operator-surgery.test.d.ts.map +0 -1
  686. package/dist/test/operator-surgery.test.js +0 -595
  687. package/dist/test/operator-surgery.test.js.map +0 -1
  688. package/dist/test/ops-alert.test.d.ts +0 -2
  689. package/dist/test/ops-alert.test.d.ts.map +0 -1
  690. package/dist/test/ops-alert.test.js +0 -153
  691. package/dist/test/ops-alert.test.js.map +0 -1
  692. package/dist/test/overbudget-drain-breaker.test.d.ts +0 -2
  693. package/dist/test/overbudget-drain-breaker.test.d.ts.map +0 -1
  694. package/dist/test/overbudget-drain-breaker.test.js +0 -198
  695. package/dist/test/overbudget-drain-breaker.test.js.map +0 -1
  696. package/dist/test/pack-installs-alone.test.d.ts +0 -2
  697. package/dist/test/pack-installs-alone.test.d.ts.map +0 -1
  698. package/dist/test/pack-installs-alone.test.js +0 -284
  699. package/dist/test/pack-installs-alone.test.js.map +0 -1
  700. package/dist/test/per-agent-delivery.test.d.ts +0 -2
  701. package/dist/test/per-agent-delivery.test.d.ts.map +0 -1
  702. package/dist/test/per-agent-delivery.test.js +0 -108
  703. package/dist/test/per-agent-delivery.test.js.map +0 -1
  704. package/dist/test/physical-window-restart.test.d.ts +0 -16
  705. package/dist/test/physical-window-restart.test.d.ts.map +0 -1
  706. package/dist/test/physical-window-restart.test.js +0 -487
  707. package/dist/test/physical-window-restart.test.js.map +0 -1
  708. package/dist/test/placed-reaction-baseline.test.d.ts +0 -2
  709. package/dist/test/placed-reaction-baseline.test.d.ts.map +0 -1
  710. package/dist/test/placed-reaction-baseline.test.js +0 -312
  711. package/dist/test/placed-reaction-baseline.test.js.map +0 -1
  712. package/dist/test/present-while-acting.test.d.ts +0 -19
  713. package/dist/test/present-while-acting.test.d.ts.map +0 -1
  714. package/dist/test/present-while-acting.test.js +0 -1649
  715. package/dist/test/present-while-acting.test.js.map +0 -1
  716. package/dist/test/prose-segments.test.d.ts +0 -2
  717. package/dist/test/prose-segments.test.d.ts.map +0 -1
  718. package/dist/test/prose-segments.test.js +0 -52
  719. package/dist/test/prose-segments.test.js.map +0 -1
  720. package/dist/test/prose-stream-router.test.d.ts +0 -2
  721. package/dist/test/prose-stream-router.test.d.ts.map +0 -1
  722. package/dist/test/prose-stream-router.test.js +0 -171
  723. package/dist/test/prose-stream-router.test.js.map +0 -1
  724. package/dist/test/provider-acceleration-cooldown.test.d.ts +0 -2
  725. package/dist/test/provider-acceleration-cooldown.test.d.ts.map +0 -1
  726. package/dist/test/provider-acceleration-cooldown.test.js +0 -435
  727. package/dist/test/provider-acceleration-cooldown.test.js.map +0 -1
  728. package/dist/test/provider-waits.test.d.ts +0 -2
  729. package/dist/test/provider-waits.test.d.ts.map +0 -1
  730. package/dist/test/provider-waits.test.js +0 -2259
  731. package/dist/test/provider-waits.test.js.map +0 -1
  732. package/dist/test/puppet-tool-call.test.d.ts +0 -2
  733. package/dist/test/puppet-tool-call.test.d.ts.map +0 -1
  734. package/dist/test/puppet-tool-call.test.js +0 -226
  735. package/dist/test/puppet-tool-call.test.js.map +0 -1
  736. package/dist/test/puppet-turn-reservation.test.d.ts +0 -2
  737. package/dist/test/puppet-turn-reservation.test.d.ts.map +0 -1
  738. package/dist/test/puppet-turn-reservation.test.js +0 -139
  739. package/dist/test/puppet-turn-reservation.test.js.map +0 -1
  740. package/dist/test/reaction-baseline-platform.test.d.ts +0 -2
  741. package/dist/test/reaction-baseline-platform.test.d.ts.map +0 -1
  742. package/dist/test/reaction-baseline-platform.test.js +0 -124
  743. package/dist/test/reaction-baseline-platform.test.js.map +0 -1
  744. package/dist/test/record-journal.test.d.ts +0 -6
  745. package/dist/test/record-journal.test.d.ts.map +0 -1
  746. package/dist/test/record-journal.test.js +0 -269
  747. package/dist/test/record-journal.test.js.map +0 -1
  748. package/dist/test/refusal-reaction-baseline.test.d.ts +0 -2
  749. package/dist/test/refusal-reaction-baseline.test.d.ts.map +0 -1
  750. package/dist/test/refusal-reaction-baseline.test.js +0 -81
  751. package/dist/test/refusal-reaction-baseline.test.js.map +0 -1
  752. package/dist/test/refusal-rewind.test.d.ts +0 -2
  753. package/dist/test/refusal-rewind.test.d.ts.map +0 -1
  754. package/dist/test/refusal-rewind.test.js +0 -117
  755. package/dist/test/refusal-rewind.test.js.map +0 -1
  756. package/dist/test/refusal-single-call.test.d.ts +0 -2
  757. package/dist/test/refusal-single-call.test.d.ts.map +0 -1
  758. package/dist/test/refusal-single-call.test.js +0 -177
  759. package/dist/test/refusal-single-call.test.js.map +0 -1
  760. package/dist/test/release-bump.test.d.ts +0 -2
  761. package/dist/test/release-bump.test.d.ts.map +0 -1
  762. package/dist/test/release-bump.test.js +0 -248
  763. package/dist/test/release-bump.test.js.map +0 -1
  764. package/dist/test/release-changelog.test.d.ts +0 -2
  765. package/dist/test/release-changelog.test.d.ts.map +0 -1
  766. package/dist/test/release-changelog.test.js +0 -159
  767. package/dist/test/release-changelog.test.js.map +0 -1
  768. package/dist/test/release-holds.test.d.ts +0 -2
  769. package/dist/test/release-holds.test.d.ts.map +0 -1
  770. package/dist/test/release-holds.test.js +0 -456
  771. package/dist/test/release-holds.test.js.map +0 -1
  772. package/dist/test/same-round-think-policy.test.d.ts +0 -2
  773. package/dist/test/same-round-think-policy.test.d.ts.map +0 -1
  774. package/dist/test/same-round-think-policy.test.js +0 -853
  775. package/dist/test/same-round-think-policy.test.js.map +0 -1
  776. package/dist/test/save-recent-image.test.d.ts +0 -26
  777. package/dist/test/save-recent-image.test.d.ts.map +0 -1
  778. package/dist/test/save-recent-image.test.js +0 -974
  779. package/dist/test/save-recent-image.test.js.map +0 -1
  780. package/dist/test/shutdown-errors.test.d.ts +0 -2
  781. package/dist/test/shutdown-errors.test.d.ts.map +0 -1
  782. package/dist/test/shutdown-errors.test.js +0 -662
  783. package/dist/test/shutdown-errors.test.js.map +0 -1
  784. package/dist/test/silent-heartbeat-rows.test.d.ts +0 -2
  785. package/dist/test/silent-heartbeat-rows.test.d.ts.map +0 -1
  786. package/dist/test/silent-heartbeat-rows.test.js +0 -451
  787. package/dist/test/silent-heartbeat-rows.test.js.map +0 -1
  788. package/dist/test/silent-heartbeat.test.d.ts +0 -2
  789. package/dist/test/silent-heartbeat.test.d.ts.map +0 -1
  790. package/dist/test/silent-heartbeat.test.js +0 -165
  791. package/dist/test/silent-heartbeat.test.js.map +0 -1
  792. package/dist/test/state-scaling-e2e.test.d.ts +0 -22
  793. package/dist/test/state-scaling-e2e.test.d.ts.map +0 -1
  794. package/dist/test/state-scaling-e2e.test.js +0 -206
  795. package/dist/test/state-scaling-e2e.test.js.map +0 -1
  796. package/dist/test/state-scaling.test.d.ts +0 -12
  797. package/dist/test/state-scaling.test.d.ts.map +0 -1
  798. package/dist/test/state-scaling.test.js +0 -251
  799. package/dist/test/state-scaling.test.js.map +0 -1
  800. package/dist/test/stop-with-host-operations.test.d.ts +0 -20
  801. package/dist/test/stop-with-host-operations.test.d.ts.map +0 -1
  802. package/dist/test/stop-with-host-operations.test.js +0 -1174
  803. package/dist/test/stop-with-host-operations.test.js.map +0 -1
  804. package/dist/test/stop-with-work-in-flight.test.d.ts +0 -23
  805. package/dist/test/stop-with-work-in-flight.test.d.ts.map +0 -1
  806. package/dist/test/stop-with-work-in-flight.test.js +0 -784
  807. package/dist/test/stop-with-work-in-flight.test.js.map +0 -1
  808. package/dist/test/surgery-marks-receipt.test.d.ts +0 -2
  809. package/dist/test/surgery-marks-receipt.test.d.ts.map +0 -1
  810. package/dist/test/surgery-marks-receipt.test.js +0 -540
  811. package/dist/test/surgery-marks-receipt.test.js.map +0 -1
  812. package/dist/test/thinking-token-stamp.test.d.ts +0 -12
  813. package/dist/test/thinking-token-stamp.test.d.ts.map +0 -1
  814. package/dist/test/thinking-token-stamp.test.js +0 -129
  815. package/dist/test/thinking-token-stamp.test.js.map +0 -1
  816. package/dist/test/timezone.test.d.ts +0 -2
  817. package/dist/test/timezone.test.d.ts.map +0 -1
  818. package/dist/test/timezone.test.js +0 -18
  819. package/dist/test/timezone.test.js.map +0 -1
  820. package/dist/test/tool-batch-hook.test.d.ts +0 -8
  821. package/dist/test/tool-batch-hook.test.d.ts.map +0 -1
  822. package/dist/test/tool-batch-hook.test.js +0 -275
  823. package/dist/test/tool-batch-hook.test.js.map +0 -1
  824. package/dist/test/tool-policy.test.d.ts +0 -2
  825. package/dist/test/tool-policy.test.d.ts.map +0 -1
  826. package/dist/test/tool-policy.test.js +0 -51
  827. package/dist/test/tool-policy.test.js.map +0 -1
  828. package/dist/test/tool-presentation.test.d.ts +0 -2
  829. package/dist/test/tool-presentation.test.d.ts.map +0 -1
  830. package/dist/test/tool-presentation.test.js +0 -268
  831. package/dist/test/tool-presentation.test.js.map +0 -1
  832. package/dist/test/tool-result-content.test.d.ts +0 -2
  833. package/dist/test/tool-result-content.test.d.ts.map +0 -1
  834. package/dist/test/tool-result-content.test.js +0 -37
  835. package/dist/test/tool-result-content.test.js.map +0 -1
  836. package/dist/test/tool-result-guard.test.d.ts +0 -2
  837. package/dist/test/tool-result-guard.test.d.ts.map +0 -1
  838. package/dist/test/tool-result-guard.test.js +0 -3729
  839. package/dist/test/tool-result-guard.test.js.map +0 -1
  840. package/dist/test/tool-result-history.test.d.ts +0 -7
  841. package/dist/test/tool-result-history.test.d.ts.map +0 -1
  842. package/dist/test/tool-result-history.test.js +0 -156
  843. package/dist/test/tool-result-history.test.js.map +0 -1
  844. package/dist/test/tool-result-spill.test.d.ts +0 -13
  845. package/dist/test/tool-result-spill.test.d.ts.map +0 -1
  846. package/dist/test/tool-result-spill.test.js +0 -586
  847. package/dist/test/tool-result-spill.test.js.map +0 -1
  848. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts +0 -2
  849. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts.map +0 -1
  850. package/dist/test/tool-wrapper-prose-guard-integration.test.js +0 -485
  851. package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +0 -1
  852. package/dist/test/tool-wrapper-prose-guard.test.d.ts +0 -2
  853. package/dist/test/tool-wrapper-prose-guard.test.d.ts.map +0 -1
  854. package/dist/test/tool-wrapper-prose-guard.test.js +0 -34
  855. package/dist/test/tool-wrapper-prose-guard.test.js.map +0 -1
  856. package/dist/test/tools-observe-parser-conformance.test.d.ts +0 -2
  857. package/dist/test/tools-observe-parser-conformance.test.d.ts.map +0 -1
  858. package/dist/test/tools-observe-parser-conformance.test.js +0 -129
  859. package/dist/test/tools-observe-parser-conformance.test.js.map +0 -1
  860. package/dist/test/trunk-channel-routing.test.d.ts +0 -19
  861. package/dist/test/trunk-channel-routing.test.d.ts.map +0 -1
  862. package/dist/test/trunk-channel-routing.test.js +0 -652
  863. package/dist/test/trunk-channel-routing.test.js.map +0 -1
  864. package/dist/test/tune-out-e2e.test.d.ts +0 -2
  865. package/dist/test/tune-out-e2e.test.d.ts.map +0 -1
  866. package/dist/test/tune-out-e2e.test.js +0 -142
  867. package/dist/test/tune-out-e2e.test.js.map +0 -1
  868. package/dist/test/tune-out-gate-composition.test.d.ts +0 -2
  869. package/dist/test/tune-out-gate-composition.test.d.ts.map +0 -1
  870. package/dist/test/tune-out-gate-composition.test.js +0 -221
  871. package/dist/test/tune-out-gate-composition.test.js.map +0 -1
  872. package/dist/test/tune-out-lifecycle.test.d.ts +0 -2
  873. package/dist/test/tune-out-lifecycle.test.d.ts.map +0 -1
  874. package/dist/test/tune-out-lifecycle.test.js +0 -121
  875. package/dist/test/tune-out-lifecycle.test.js.map +0 -1
  876. package/dist/test/turn-trigger-provenance.test.d.ts +0 -2
  877. package/dist/test/turn-trigger-provenance.test.d.ts.map +0 -1
  878. package/dist/test/turn-trigger-provenance.test.js +0 -137
  879. package/dist/test/turn-trigger-provenance.test.js.map +0 -1
  880. package/dist/test/unconvertible-tool-result.test.d.ts +0 -2
  881. package/dist/test/unconvertible-tool-result.test.d.ts.map +0 -1
  882. package/dist/test/unconvertible-tool-result.test.js +0 -358
  883. package/dist/test/unconvertible-tool-result.test.js.map +0 -1
  884. package/dist/test/unsent-compiles.test.d.ts +0 -2
  885. package/dist/test/unsent-compiles.test.d.ts.map +0 -1
  886. package/dist/test/unsent-compiles.test.js +0 -139
  887. package/dist/test/unsent-compiles.test.js.map +0 -1
  888. package/dist/test/usage-accounting.test.d.ts +0 -2
  889. package/dist/test/usage-accounting.test.d.ts.map +0 -1
  890. package/dist/test/usage-accounting.test.js +0 -13
  891. package/dist/test/usage-accounting.test.js.map +0 -1
  892. package/dist/test/usage-tracker.test.d.ts +0 -2
  893. package/dist/test/usage-tracker.test.d.ts.map +0 -1
  894. package/dist/test/usage-tracker.test.js +0 -129
  895. package/dist/test/usage-tracker.test.js.map +0 -1
  896. package/dist/test/utils-surface.test.d.ts +0 -2
  897. package/dist/test/utils-surface.test.d.ts.map +0 -1
  898. package/dist/test/utils-surface.test.js +0 -145
  899. package/dist/test/utils-surface.test.js.map +0 -1
  900. package/dist/test/workspace-branch-continuation.test.d.ts +0 -15
  901. package/dist/test/workspace-branch-continuation.test.d.ts.map +0 -1
  902. package/dist/test/workspace-branch-continuation.test.js +0 -183
  903. package/dist/test/workspace-branch-continuation.test.js.map +0 -1
  904. package/dist/test/workspace-materialize-freshness.test.d.ts +0 -16
  905. package/dist/test/workspace-materialize-freshness.test.d.ts.map +0 -1
  906. package/dist/test/workspace-materialize-freshness.test.js +0 -252
  907. package/dist/test/workspace-materialize-freshness.test.js.map +0 -1
  908. package/dist/test/workspace-path-containment.test.d.ts +0 -19
  909. package/dist/test/workspace-path-containment.test.d.ts.map +0 -1
  910. package/dist/test/workspace-path-containment.test.js +0 -91
  911. package/dist/test/workspace-path-containment.test.js.map +0 -1
  912. package/dist/test/workspace-read-file-from-disk.test.d.ts +0 -17
  913. package/dist/test/workspace-read-file-from-disk.test.d.ts.map +0 -1
  914. package/dist/test/workspace-read-file-from-disk.test.js +0 -174
  915. package/dist/test/workspace-read-file-from-disk.test.js.map +0 -1
  916. package/dist/test/workspace-read-image.test.d.ts +0 -2
  917. package/dist/test/workspace-read-image.test.d.ts.map +0 -1
  918. package/dist/test/workspace-read-image.test.js +0 -597
  919. package/dist/test/workspace-read-image.test.js.map +0 -1
  920. package/dist/test/workspace-read-text.test.d.ts +0 -2
  921. package/dist/test/workspace-read-text.test.d.ts.map +0 -1
  922. package/dist/test/workspace-read-text.test.js +0 -101
  923. package/dist/test/workspace-read-text.test.js.map +0 -1
  924. package/dist/test/workspace-restore-order.test.d.ts +0 -16
  925. package/dist/test/workspace-restore-order.test.d.ts.map +0 -1
  926. package/dist/test/workspace-restore-order.test.js +0 -140
  927. package/dist/test/workspace-restore-order.test.js.map +0 -1
  928. package/dist/test/workspace-three-way.test.d.ts +0 -12
  929. package/dist/test/workspace-three-way.test.d.ts.map +0 -1
  930. package/dist/test/workspace-three-way.test.js +0 -4094
  931. package/dist/test/workspace-three-way.test.js.map +0 -1
  932. package/dist/test/workspace-watcher.test.d.ts +0 -2
  933. package/dist/test/workspace-watcher.test.d.ts.map +0 -1
  934. package/dist/test/workspace-watcher.test.js +0 -281
  935. package/dist/test/workspace-watcher.test.js.map +0 -1
  936. package/dist/test/xml-round-persist.test.d.ts +0 -19
  937. package/dist/test/xml-round-persist.test.d.ts.map +0 -1
  938. package/dist/test/xml-round-persist.test.js +0 -235
  939. package/dist/test/xml-round-persist.test.js.map +0 -1
  940. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,1272 +0,0 @@
1
- /**
2
- * Client-side programmatic tool calling (code_execution) tests.
3
- *
4
- * PyRunner tests spawn a REAL python3 subprocess — the protocol, top-level
5
- * await, tool-function injection, timeout, and deadline behavior are all
6
- * exercised end-to-end. Framework tests drive a full agent turn through the
7
- * MockMembrane and assert the load-bearing invariant: inner tool results
8
- * reach the running script but never the model-facing tool_result.
9
- */
10
- import { describe, it } from 'node:test';
11
- import assert from 'node:assert';
12
- import { execFileSync } from 'node:child_process';
13
- import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
14
- import { tmpdir } from 'node:os';
15
- import { join } from 'node:path';
16
- import { AgentFramework, PYTHON_RUNTIME_SOURCE, PyRunner, buildInjectedTools } from '../src/index.js';
17
- import { createMockResponse, MockMembrane } from './helpers/mock-membrane.js';
18
- import { PYTC_STOP_ROWS_DRIVER } from './helpers/pytc-stop-rows.js';
19
- const ECHO_TOOLS = [
20
- { pyName: 'test__echo', toolName: 'test--echo' },
21
- ];
22
- describe('buildInjectedTools', () => {
23
- it('sanitizes -- to __ and keeps valid identifiers', () => {
24
- const injected = buildInjectedTools(['mcpl--discord--send_message', 'think']);
25
- assert.deepStrictEqual(injected, [
26
- { pyName: 'mcpl__discord__send_message', toolName: 'mcpl--discord--send_message' },
27
- { pyName: 'think', toolName: 'think' },
28
- ]);
29
- });
30
- it('sanitizes single hyphens inside segments (live fleet names)', () => {
31
- // Found on the first Mica canary run: module/server ids with single
32
- // hyphens were skipped entirely. They must inject.
33
- const injected = buildInjectedTools([
34
- 'mcpl-admin--mcpl_deploy',
35
- 'mcpl--dog-events--dog_events_status',
36
- 'channel-mode--set_channel_mode',
37
- ]);
38
- assert.deepStrictEqual(injected, [
39
- { pyName: 'mcpl_admin__mcpl_deploy', toolName: 'mcpl-admin--mcpl_deploy' },
40
- { pyName: 'mcpl__dog_events__dog_events_status', toolName: 'mcpl--dog-events--dog_events_status' },
41
- { pyName: 'channel_mode__set_channel_mode', toolName: 'channel-mode--set_channel_mode' },
42
- ]);
43
- });
44
- it('prefixes a leading digit', () => {
45
- const injected = buildInjectedTools(['3d-tools--render']);
46
- assert.deepStrictEqual(injected, [{ pyName: '_3d_tools__render', toolName: '3d-tools--render' }]);
47
- });
48
- it('skips colliding sanitized names loudly (first wins)', () => {
49
- const logs = [];
50
- const injected = buildInjectedTools(['a--b', 'a__b'], (m) => logs.push(m));
51
- assert.deepStrictEqual(injected, [{ pyName: 'a__b', toolName: 'a--b' }]);
52
- assert.strictEqual(logs.length, 1);
53
- assert.match(logs[0], /collides/);
54
- });
55
- });
56
- describe('PyRunner (real python3)', () => {
57
- it('runs a script and returns stdout', async () => {
58
- const runner = new PyRunner({ onToolCall: async () => '' });
59
- try {
60
- const result = await runner.exec('print("hello from python")', []);
61
- assert.strictEqual(result.returnCode, 0);
62
- assert.match(result.stdout, /hello from python/);
63
- assert.strictEqual(result.stderr, '');
64
- }
65
- finally {
66
- runner.dispose();
67
- }
68
- });
69
- it('round-trips a tool call as an awaited async function', async () => {
70
- const calls = [];
71
- const runner = new PyRunner({
72
- onToolCall: async (name, args) => {
73
- calls.push({ name, args });
74
- return JSON.stringify({ echoed: args });
75
- },
76
- });
77
- try {
78
- const result = await runner.exec([
79
- 'import json',
80
- 'r = json.loads(await test__echo({"message": "alpha"}))',
81
- 'print("echoed:", r["echoed"]["message"])',
82
- ].join('\n'), ECHO_TOOLS);
83
- assert.strictEqual(result.returnCode, 0, result.stderr);
84
- assert.match(result.stdout, /echoed: alpha/);
85
- assert.deepStrictEqual(calls, [{ name: 'test--echo', args: { message: 'alpha' } }]);
86
- }
87
- finally {
88
- runner.dispose();
89
- }
90
- });
91
- it('supports the exact-name tools[...] escape hatch', async () => {
92
- const runner = new PyRunner({
93
- onToolCall: async (_name, args) => `got:${String(args.v)}`,
94
- });
95
- try {
96
- const result = await runner.exec('print(await tools["test--echo"]({"v": 7}))', ECHO_TOOLS);
97
- assert.strictEqual(result.returnCode, 0, result.stderr);
98
- assert.match(result.stdout, /got:7/);
99
- }
100
- finally {
101
- runner.dispose();
102
- }
103
- });
104
- it('runs parallel tool calls via asyncio.gather', async () => {
105
- let inFlight = 0;
106
- let maxInFlight = 0;
107
- const runner = new PyRunner({
108
- onToolCall: async (_name, args) => {
109
- inFlight++;
110
- maxInFlight = Math.max(maxInFlight, inFlight);
111
- await new Promise((r) => setTimeout(r, 150));
112
- inFlight--;
113
- return String(args.i);
114
- },
115
- });
116
- try {
117
- const result = await runner.exec([
118
- 'import asyncio',
119
- 'results = await asyncio.gather(*(test__echo({"i": i}) for i in range(3)))',
120
- 'print(",".join(results))',
121
- ].join('\n'), ECHO_TOOLS);
122
- assert.strictEqual(result.returnCode, 0, result.stderr);
123
- assert.match(result.stdout, /0,1,2/);
124
- assert.ok(maxInFlight >= 2, `expected parallel dispatch, max in flight was ${maxInFlight}`);
125
- }
126
- finally {
127
- runner.dispose();
128
- }
129
- });
130
- it('persists interpreter state across execs (container-reuse semantics)', async () => {
131
- const runner = new PyRunner({ onToolCall: async () => '' });
132
- try {
133
- const first = await runner.exec('x = 41', []);
134
- assert.strictEqual(first.returnCode, 0, first.stderr);
135
- const second = await runner.exec('print(x + 1)', []);
136
- assert.strictEqual(second.returnCode, 0, second.stderr);
137
- assert.match(second.stdout, /42/);
138
- }
139
- finally {
140
- runner.dispose();
141
- }
142
- });
143
- it('reports script exceptions as a traceback with return_code 1', async () => {
144
- const runner = new PyRunner({ onToolCall: async () => '' });
145
- try {
146
- const result = await runner.exec('raise ValueError("boom")', []);
147
- assert.strictEqual(result.returnCode, 1);
148
- assert.match(result.stderr, /ValueError: boom/);
149
- assert.match(result.stderr, /Traceback/);
150
- }
151
- finally {
152
- runner.dispose();
153
- }
154
- });
155
- it('delivers tool errors as plain strings the script can handle', async () => {
156
- const runner = new PyRunner({
157
- onToolCall: async () => 'Error: Query timeout - table lock exceeded',
158
- });
159
- try {
160
- const result = await runner.exec([
161
- 'r = await test__echo({})',
162
- 'if r.startswith("Error:"):',
163
- ' print("handled:", r)',
164
- ].join('\n'), ECHO_TOOLS);
165
- assert.strictEqual(result.returnCode, 0, result.stderr);
166
- assert.match(result.stdout, /handled: Error: Query timeout/);
167
- }
168
- finally {
169
- runner.dispose();
170
- }
171
- });
172
- it('raises TimeoutError inside the script when a tool call gets no response', async () => {
173
- const runner = new PyRunner({
174
- toolCallTimeoutMs: 1000,
175
- onToolCall: () => new Promise((resolve) => setTimeout(() => resolve('late'), 5000)),
176
- });
177
- try {
178
- const result = await runner.exec('await test__echo({})', ECHO_TOOLS);
179
- assert.strictEqual(result.returnCode, 1);
180
- assert.match(result.stderr, /TimeoutError: Calling tool \['test--echo'\] timed out/);
181
- }
182
- finally {
183
- runner.dispose();
184
- }
185
- });
186
- it('cancels a script that exceeds the deadline', async () => {
187
- const runner = new PyRunner({
188
- scriptTimeoutMs: 1000,
189
- onToolCall: async () => '',
190
- });
191
- try {
192
- const started = Date.now();
193
- const result = await runner.exec('import asyncio\nawait asyncio.sleep(60)', []);
194
- assert.strictEqual(result.returnCode, 1);
195
- assert.match(result.stderr, /cancelled by host/);
196
- assert.ok(Date.now() - started < 15_000, 'deadline cancel took too long');
197
- }
198
- finally {
199
- runner.dispose();
200
- }
201
- });
202
- it('abort() settles a running script and reclaims the interpreter', async () => {
203
- const runner = new PyRunner({ onToolCall: async () => '' });
204
- try {
205
- const pending = runner.exec('import asyncio\nawait asyncio.sleep(60)', []);
206
- // Let the exec actually start before aborting.
207
- await new Promise((r) => setTimeout(r, 500));
208
- assert.strictEqual(runner.busy, true);
209
- runner.abort('test abort');
210
- const result = await pending;
211
- assert.strictEqual(result.aborted, true);
212
- assert.match(result.stderr, /aborted by host: test abort/);
213
- // Runner remains usable after reclaim (fresh interpreter, state gone).
214
- const after = await runner.exec('print("alive")', []);
215
- assert.strictEqual(after.returnCode, 0, after.stderr);
216
- assert.match(after.stdout, /alive/);
217
- }
218
- finally {
219
- runner.dispose();
220
- }
221
- });
222
- it('rejects concurrent execs on one runner', async () => {
223
- const runner = new PyRunner({ onToolCall: async () => '' });
224
- try {
225
- const first = runner.exec('import asyncio\nawait asyncio.sleep(2)', []);
226
- await new Promise((r) => setTimeout(r, 300));
227
- const second = await runner.exec('print("nope")', []);
228
- assert.strictEqual(second.returnCode, 1);
229
- assert.match(second.stderr, /already running/);
230
- runner.abort('cleanup');
231
- await first;
232
- }
233
- finally {
234
- runner.dispose();
235
- }
236
- });
237
- it('rejects a second exec issued before the first has started (same tick)', async () => {
238
- // exec() awaits the interpreter before it records the running script, so
239
- // a check made only after that await let two execs in at once.
240
- const runner = new PyRunner({ onToolCall: async () => '' });
241
- try {
242
- for (const warm of [false, true]) {
243
- const first = runner.exec('import asyncio\nawait asyncio.sleep(0.2)\nprint("first ran")', []);
244
- const second = runner.exec('print("second ran")', []);
245
- const [a, b] = await Promise.race([
246
- Promise.all([first, second]),
247
- new Promise((_, reject) => setTimeout(() => reject(new Error(`warm=${warm}: an exec never settled`)), 10_000)),
248
- ]);
249
- assert.strictEqual(b.returnCode, 1, `warm=${warm}`);
250
- assert.match(b.stderr, /already running/);
251
- assert.strictEqual(a.returnCode, 0, `warm=${warm}: ${a.stderr}`);
252
- assert.match(a.stdout, /first ran/);
253
- assert.ok(!a.stdout.includes('second ran') && !b.stdout.includes('second ran'));
254
- }
255
- }
256
- finally {
257
- runner.dispose();
258
- }
259
- });
260
- it('fails gracefully when the python binary is missing', async () => {
261
- const runner = new PyRunner({
262
- pythonPath: '/definitely/not/a/python',
263
- onToolCall: async () => '',
264
- });
265
- try {
266
- const result = await runner.exec('print(1)', []);
267
- assert.strictEqual(result.returnCode, 1);
268
- assert.match(result.stderr, /Failed to start python runtime|exited before becoming ready/);
269
- }
270
- finally {
271
- runner.dispose();
272
- }
273
- });
274
- });
275
- // Inner-call ids restart in every interpreter: a result that outlives its
276
- // interpreter must not resolve a call of the one that replaced it.
277
- describe('code_execution inner-call results after the interpreter is replaced (real python3)', () => {
278
- const until = async (cond, what) => {
279
- const deadline = Date.now() + 10_000;
280
- while (!cond()) {
281
- if (Date.now() > deadline)
282
- throw new Error(`timed out waiting for ${what}`);
283
- await new Promise((r) => setTimeout(r, 20));
284
- }
285
- };
286
- const stops = {
287
- aborted: { code: 'print(await test__echo({}))', stop: (runner) => runner.abort('turn abandoned') },
288
- crashed: {
289
- code: 'import asyncio, os\nasyncio.get_running_loop().call_later(0.2, os._exit, 1)\nprint(await test__echo({}))',
290
- },
291
- };
292
- for (const [how, { code, stop }] of Object.entries(stops)) {
293
- it(`a late result from a script whose interpreter ${how} does not reach the next script`, async () => {
294
- const calls = [];
295
- const runner = new PyRunner({
296
- onToolCall: () => new Promise((resolve) => calls.push(resolve)),
297
- });
298
- const logged = [];
299
- const consoleError = console.error;
300
- console.error = (...args) => {
301
- logged.push(args.map(String).join(' '));
302
- consoleError(...args);
303
- };
304
- try {
305
- const first = runner.exec(code, ECHO_TOOLS);
306
- await until(() => calls.length === 1, 'the first script to call its tool');
307
- stop?.(runner);
308
- const stopped = await first;
309
- assert.strictEqual(stopped.aborted, true, stopped.stderr);
310
- const second = runner.exec('print(await test__echo({}))', ECHO_TOOLS);
311
- await until(() => calls.length === 2, 'the second script to call its tool');
312
- // Both calls are the first of their interpreter.
313
- calls[0]('stale result of the stopped script');
314
- await new Promise((r) => setTimeout(r, 300));
315
- calls[1]('result of this script');
316
- const result = await second;
317
- assert.strictEqual(result.returnCode, 0, result.stderr);
318
- assert.strictEqual(result.stdout, 'result of this script\n');
319
- assert.ok(logged.some((l) => /dropped late result of test--echo call t1: the interpreter that asked for it is gone/.test(l)), 'the dropped result is logged');
320
- }
321
- finally {
322
- console.error = consoleError;
323
- runner.dispose();
324
- }
325
- });
326
- }
327
- });
328
- // ---------------------------------------------------------------------------
329
- // Framework integration
330
- // ---------------------------------------------------------------------------
331
- class ScriptToolModule {
332
- name = 'test';
333
- calls = [];
334
- async start(_ctx) { }
335
- async stop() { }
336
- getTools() {
337
- return [
338
- {
339
- name: 'echo',
340
- description: 'Echo input. Returns JSON: {"echoed": <input>}.',
341
- inputSchema: { type: 'object', properties: { message: { type: 'string' } } },
342
- },
343
- {
344
- name: 'finish',
345
- description: 'End the turn.',
346
- inputSchema: { type: 'object', properties: {} },
347
- },
348
- ];
349
- }
350
- async handleToolCall(call) {
351
- this.calls.push(call);
352
- const baseName = call.name.includes('--')
353
- ? call.name.slice(call.name.lastIndexOf('--') + 2)
354
- : call.name;
355
- if (baseName === 'finish') {
356
- return { success: true, data: { finished: true }, endTurn: true };
357
- }
358
- return { success: true, data: { echoed: call.input } };
359
- }
360
- async onProcess(event, _state) {
361
- if (event.type === 'external-message') {
362
- const content = Array.isArray(event.content)
363
- ? event.content
364
- : [{ type: 'text', text: String(event.content) }];
365
- return {
366
- addMessages: [{ participant: 'User', content: content }],
367
- requestInference: true,
368
- };
369
- }
370
- return {};
371
- }
372
- }
373
- function tempStorePath(prefix) {
374
- const tempDir = mkdtempSync(join(tmpdir(), prefix));
375
- return { tempDir, storePath: join(tempDir, 'store.chronicle') };
376
- }
377
- async function createFrameworkWithCodeExecution(storePath, membrane, module) {
378
- return AgentFramework.create({
379
- storePath,
380
- membrane: membrane.asMembrane(),
381
- agents: [],
382
- modules: [module],
383
- syncIntervalMs: 0,
384
- codeExecution: { enabled: true },
385
- });
386
- }
387
- describe('framework code_execution integration (real python3)', () => {
388
- it('synthesizes the tool only when enabled', async () => {
389
- const { tempDir, storePath } = tempStorePath('pytc-tools-');
390
- const membrane = new MockMembrane();
391
- const framework = await AgentFramework.create({
392
- storePath,
393
- membrane: membrane.asMembrane(),
394
- agents: [],
395
- modules: [new ScriptToolModule()],
396
- syncIntervalMs: 0,
397
- });
398
- try {
399
- assert.ok(!framework.getAllTools().some((t) => t.name === 'code_execution'));
400
- }
401
- finally {
402
- await framework.stop();
403
- rmSync(tempDir, { recursive: true, force: true });
404
- }
405
- const { tempDir: tempDir2, storePath: storePath2 } = tempStorePath('pytc-tools2-');
406
- const membrane2 = new MockMembrane();
407
- const framework2 = await createFrameworkWithCodeExecution(storePath2, membrane2, new ScriptToolModule());
408
- try {
409
- const tool = framework2.getAllTools().find((t) => t.name === 'code_execution');
410
- assert.ok(tool, 'code_execution tool should be synthesized');
411
- assert.match(tool.description, /async Python function/);
412
- assert.match(tool.description, /__/);
413
- }
414
- finally {
415
- await framework2.stop();
416
- rmSync(tempDir2, { recursive: true, force: true });
417
- }
418
- });
419
- it('runs a script that calls module tools; intermediates never reach the model', async () => {
420
- const { tempDir, storePath } = tempStorePath('pytc-run-');
421
- const membrane = new MockMembrane();
422
- const module = new ScriptToolModule();
423
- const code = [
424
- 'import json',
425
- 'names = []',
426
- 'for m in ["alpha", "beta"]:',
427
- ' r = json.loads(await test__echo({"message": m}))',
428
- ' names.append(r["echoed"]["message"].upper())',
429
- 'print("summary:", "+".join(names))',
430
- ].join('\n');
431
- membrane.pushResponse(createMockResponse([
432
- { type: 'text', text: 'Let me fan out.' },
433
- { type: 'tool_use', id: 'call_ce_1', name: 'code_execution', input: { code } },
434
- ], 'tool_use'));
435
- membrane.pushResponse(createMockResponse([
436
- { type: 'text', text: 'Final answer.' },
437
- ]));
438
- const framework = await createFrameworkWithCodeExecution(storePath, membrane, module);
439
- try {
440
- const created = await framework.createEphemeralAgent({
441
- name: 'worker',
442
- model: 'test-model',
443
- systemPrompt: 'Do the task.',
444
- allowedTools: 'all',
445
- });
446
- created.contextManager.addMessage('user', [{ type: 'text', text: 'Go.' }]);
447
- const promise = framework.runEphemeralToCompletion(created.agent, created.contextManager);
448
- framework.start();
449
- const result = await promise;
450
- assert.strictEqual(result.speech, 'Final answer.');
451
- // The module saw both inner calls, dispatched with pytc- IDs (waiter
452
- // path), not the model's call ID.
453
- const echoCalls = module.calls.filter((c) => c.name.endsWith('--echo') || c.name === 'echo');
454
- assert.strictEqual(echoCalls.length, 2);
455
- for (const call of echoCalls) {
456
- assert.match(String(call.id), /^pytc-/);
457
- }
458
- // The model-facing tool_result carries the script's stdout...
459
- const stream = membrane.lastStream;
460
- assert.strictEqual(stream.receivedToolResults.length, 1);
461
- const wireResult = stream.receivedToolResults[0][0];
462
- assert.strictEqual(wireResult.toolUseId, 'call_ce_1');
463
- assert.match(wireResult.content, /summary: ALPHA\+BETA/);
464
- assert.match(wireResult.content, /"return_code":\s*0/);
465
- // ...and NOT the intermediate tool results.
466
- assert.ok(!wireResult.content.includes('"echoed"'), 'intermediate tool results must not reach the model-facing result');
467
- }
468
- finally {
469
- await framework.stop();
470
- rmSync(tempDir, { recursive: true, force: true });
471
- }
472
- });
473
- it('two code_execution calls in one response: one runs, the other is refused, the turn completes', async () => {
474
- const { tempDir, storePath } = tempStorePath('pytc-two-');
475
- const membrane = new MockMembrane();
476
- const module = new ScriptToolModule();
477
- const code = (label) => [
478
- 'import asyncio, json',
479
- `r = json.loads(await test__echo({"message": "${label}"}))`,
480
- 'await asyncio.sleep(0.2)',
481
- `print("ran:", r["echoed"]["message"])`,
482
- ].join('\n');
483
- membrane.pushResponse(createMockResponse([
484
- { type: 'tool_use', id: 'call_two_a', name: 'code_execution', input: { code: code('a') } },
485
- { type: 'tool_use', id: 'call_two_b', name: 'code_execution', input: { code: code('b') } },
486
- ], 'tool_use'));
487
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'Both answered.' }]));
488
- const framework = await createFrameworkWithCodeExecution(storePath, membrane, module);
489
- try {
490
- const created = await framework.createEphemeralAgent({
491
- name: 'pair',
492
- model: 'test-model',
493
- systemPrompt: 'Run both.',
494
- allowedTools: 'all',
495
- });
496
- created.contextManager.addMessage('user', [{ type: 'text', text: 'Go.' }]);
497
- const promise = framework.runEphemeralToCompletion(created.agent, created.contextManager);
498
- framework.start();
499
- const result = await Promise.race([
500
- promise,
501
- new Promise((_, reject) => setTimeout(() => reject(new Error('the turn never completed')), 15_000)),
502
- ]);
503
- assert.strictEqual(result.speech, 'Both answered.');
504
- const [results] = membrane.lastStream.receivedToolResults;
505
- const byId = new Map(results.map((r) => [r.toolUseId, r.content]));
506
- assert.match(byId.get('call_two_a') ?? '', /ran: a/, 'the first script ran');
507
- assert.match(byId.get('call_two_b') ?? '', /already running/, 'the second was refused');
508
- assert.ok(!(byId.get('call_two_b') ?? '').includes('ran: b'));
509
- const echoed = module.calls.filter((c) => c.name.endsWith('--echo') || c.name === 'echo').map((c) => c.input.message);
510
- assert.deepStrictEqual(echoed, ['a']);
511
- }
512
- finally {
513
- await framework.stop();
514
- rmSync(tempDir, { recursive: true, force: true });
515
- }
516
- });
517
- it('defers endTurn from inner calls to the code_execution result', async () => {
518
- const { tempDir, storePath } = tempStorePath('pytc-endturn-');
519
- const membrane = new MockMembrane();
520
- const module = new ScriptToolModule();
521
- const code = [
522
- 'r = await test__finish({})',
523
- 'print("after finish:", r)',
524
- ].join('\n');
525
- membrane.pushResponse(createMockResponse([
526
- { type: 'text', text: 'Finishing via script.' },
527
- { type: 'tool_use', id: 'call_ce_2', name: 'code_execution', input: { code } },
528
- ], 'tool_use'));
529
- // No further response: endTurn must settle the turn without another round.
530
- const framework = await createFrameworkWithCodeExecution(storePath, membrane, module);
531
- try {
532
- const created = await framework.createEphemeralAgent({
533
- name: 'finisher',
534
- model: 'test-model',
535
- systemPrompt: 'Finish.',
536
- allowedTools: 'all',
537
- });
538
- created.contextManager.addMessage('user', [{ type: 'text', text: 'Wrap up.' }]);
539
- const promise = framework.runEphemeralToCompletion(created.agent, created.contextManager);
540
- framework.start();
541
- const result = await promise;
542
- // The script ran to completion (endTurn was deferred, not applied
543
- // mid-script), and the turn then ended without another model round.
544
- const finishCalls = module.calls.filter((c) => c.name.endsWith('--finish') || c.name === 'finish');
545
- assert.strictEqual(finishCalls.length, 1);
546
- assert.strictEqual(result.toolCallsCount, 1); // one model-visible call: code_execution
547
- }
548
- finally {
549
- await framework.stop();
550
- rmSync(tempDir, { recursive: true, force: true });
551
- }
552
- });
553
- it("a stopped script's late end-turn request does not end the next script's turn", async () => {
554
- let releaseFinish = () => { };
555
- const finishReleased = new Promise((resolve) => { releaseFinish = resolve; });
556
- let finishStarted = false;
557
- class SlowFinishModule extends ScriptToolModule {
558
- async handleToolCall(call) {
559
- if (call.name.endsWith('finish')) {
560
- finishStarted = true;
561
- await finishReleased;
562
- }
563
- return super.handleToolCall(call);
564
- }
565
- }
566
- const { tempDir, storePath } = tempStorePath('pytc-stale-endturn-');
567
- const framework = await createFrameworkWithCodeExecution(storePath, new MockMembrane(), new SlowFinishModule());
568
- const run = (input) => framework.executeToolCall({ id: `ce-${Math.random()}`, name: 'code_execution', input });
569
- framework.start();
570
- try {
571
- // The first script is stopped at its time limit while its call that ends the turn is running.
572
- const stopped = await run({ code: 'await test__finish({})', time_limit_ms: 1000 });
573
- assert.ok(finishStarted);
574
- assert.match(stopped.data.stderr, /reached its 1s time limit/);
575
- assert.ok(!stopped.endTurn);
576
- // The call finishes while the next script runs; its request belonged to the stopped script.
577
- const next = run({ code: 'import asyncio\nawait asyncio.sleep(1)\nprint("next done")' });
578
- await new Promise((r) => setTimeout(r, 300));
579
- releaseFinish();
580
- const result = await next;
581
- assert.strictEqual(result.data.stdout, 'next done\n');
582
- assert.ok(!result.endTurn, "the stopped script's request ended the next script's turn");
583
- }
584
- finally {
585
- await framework.stop();
586
- rmSync(tempDir, { recursive: true, force: true });
587
- }
588
- });
589
- });
590
- // ---------------------------------------------------------------------------
591
- // Background scripts (wake_agent) + spill-to-file
592
- // ---------------------------------------------------------------------------
593
- describe('PyRunner background mode (real python3)', () => {
594
- it('wake_agent reports the caller line and payload; log file journals output', async () => {
595
- const tempDir = mkdtempSync(join(tmpdir(), 'pytc-bg-'));
596
- const logPath = join(tempDir, 'nested', 'bg.log');
597
- const wakes = [];
598
- const runner = new PyRunner({ onToolCall: async () => '' });
599
- try {
600
- const code = [
601
- 'print("watcher starting")', // line 1
602
- 'x = 1', // line 2
603
- 'await wake_agent({"hit": x})', // line 3
604
- 'print("after wake")', // line 4
605
- ].join('\n');
606
- const result = await runner.exec(code, [], {
607
- logPath,
608
- lifetimeMs: 30_000,
609
- onWake: async (line, payload) => {
610
- wakes.push({ line, payload });
611
- return null;
612
- },
613
- });
614
- assert.strictEqual(result.returnCode, 0, result.tail ?? '');
615
- assert.strictEqual(wakes.length, 1);
616
- assert.strictEqual(wakes[0].line, 3);
617
- assert.deepStrictEqual(wakes[0].payload, { hit: 1 });
618
- const { readFileSync } = await import('node:fs');
619
- const log = readFileSync(logPath, 'utf8');
620
- assert.match(log, /watcher starting/);
621
- assert.match(log, /after wake/);
622
- assert.match(result.tail ?? '', /after wake/);
623
- }
624
- finally {
625
- runner.dispose();
626
- rmSync(tempDir, { recursive: true, force: true });
627
- }
628
- });
629
- it('a refused wake raises RuntimeError inside the script', async () => {
630
- const runner = new PyRunner({ onToolCall: async () => '' });
631
- try {
632
- const result = await runner.exec('await wake_agent({"n": 1})', [], {
633
- logPath: null,
634
- lifetimeMs: 30_000,
635
- onWake: async () => 'wake limit reached (test)',
636
- });
637
- assert.strictEqual(result.returnCode, 1);
638
- assert.match(result.tail ?? '', /RuntimeError: wake_agent refused by host: wake limit reached/);
639
- }
640
- finally {
641
- runner.dispose();
642
- }
643
- });
644
- it('wake_agent is absent in foreground scripts', async () => {
645
- const runner = new PyRunner({ onToolCall: async () => '' });
646
- try {
647
- const result = await runner.exec('print("wake_agent" in globals())', []);
648
- assert.strictEqual(result.returnCode, 0, result.stderr);
649
- assert.match(result.stdout, /False/);
650
- }
651
- finally {
652
- runner.dispose();
653
- }
654
- });
655
- });
656
- describe('framework background scripts + spill (real python3)', () => {
657
- async function createBgFramework(storePath, membrane, opts) {
658
- const { WorkspaceModule } = await import('../src/modules/workspace/index.js');
659
- const modules = [new ScriptToolModule()];
660
- let workspace = null;
661
- if (opts?.mountDir) {
662
- workspace = new WorkspaceModule({
663
- mounts: [{ name: 'files', path: opts.mountDir, mode: 'read-write', watch: 'never' }],
664
- });
665
- modules.push(workspace);
666
- }
667
- const framework = await AgentFramework.create({
668
- storePath,
669
- membrane: membrane.asMembrane(),
670
- agents: [{
671
- name: 'prime',
672
- model: 'test-model',
673
- systemPrompt: 'You are prime.',
674
- allowedTools: 'all',
675
- }],
676
- modules,
677
- syncIntervalMs: 0,
678
- codeExecution: {
679
- enabled: true,
680
- wakeMinIntervalMs: 0,
681
- ...(opts?.codeExecution ?? {}),
682
- },
683
- });
684
- // Host wiring (fkm does this in production): mounts populate in initStore.
685
- workspace?.initStore(framework.getStore());
686
- return framework;
687
- }
688
- it('background script detaches, wakes the agent with provenance, and triggers inference', async () => {
689
- const { tempDir, storePath } = tempStorePath('pytc-bgfw-');
690
- const mountDir = join(tempDir, 'mount');
691
- const { mkdirSync } = await import('node:fs');
692
- mkdirSync(mountDir, { recursive: true });
693
- const membrane = new MockMembrane();
694
- const bgCode = [
695
- 'import asyncio',
696
- 'await asyncio.sleep(0.2)',
697
- 'await wake_agent({"found": "signal"})',
698
- ].join('\\n');
699
- // Turn 1: spawn the background script, then finish the turn.
700
- membrane.pushResponse(createMockResponse([
701
- { type: 'text', text: 'Arming watcher.' },
702
- { type: 'tool_use', id: 'call_bg1', name: 'code_execution', input: { code: bgCode.split('\\n').join('\n'), background: true } },
703
- ], 'tool_use'));
704
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'Watcher armed; resting.' }]));
705
- // Turn 2 (the wake): agent answers.
706
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'Woke and handled.' }]));
707
- const framework = await createBgFramework(storePath, membrane, { mountDir });
708
- try {
709
- framework.start();
710
- framework.pushEvent({
711
- type: 'external-message',
712
- source: 'test',
713
- content: [{ type: 'text', text: 'arm a watcher' }],
714
- metadata: {},
715
- triggerInference: true,
716
- });
717
- // Wait for: turn 1 result mentions script started, then wake message lands.
718
- const deadline = Date.now() + 20_000;
719
- let wakeMessage = null;
720
- while (Date.now() < deadline && !wakeMessage) {
721
- await new Promise((r) => setTimeout(r, 100));
722
- const cm = framework.getAgent('prime')?.getContextManager();
723
- const msgs = (cm?.queryMessages({}).messages ?? []);
724
- wakeMessage = msgs.find((m) => m.metadata?.source === 'background-script') ?? null;
725
- }
726
- assert.ok(wakeMessage, 'wake message should be injected');
727
- const text = wakeMessage.content[0].text ?? '';
728
- assert.match(text, /\[background script bg-\d+\] Woke you/);
729
- assert.match(text, /line 3 of your script/);
730
- assert.match(text, /"found": "signal"/);
731
- assert.match(text, /workspace file files\/background-scripts\/bg-\d+\.log/);
732
- assert.match(text, /Script status: still running|Script status/);
733
- }
734
- finally {
735
- await framework.stop();
736
- rmSync(tempDir, { recursive: true, force: true });
737
- }
738
- });
739
- it('list and cancel manage the daemon fleet; cap enforced', async () => {
740
- const { tempDir, storePath } = tempStorePath('pytc-bglist-');
741
- const membrane = new MockMembrane();
742
- const framework = await createBgFramework(storePath, membrane, {
743
- codeExecution: { maxBackgroundScripts: 1 },
744
- });
745
- try {
746
- const idle = 'import asyncio\nawait asyncio.sleep(60)';
747
- const first = await framework.executeToolCall({
748
- id: 'c1', name: 'code_execution', input: { code: idle, background: true }, callerAgentName: 'prime',
749
- });
750
- assert.strictEqual(first.success, true, JSON.stringify(first));
751
- const firstId = first.data.script_id;
752
- const second = await framework.executeToolCall({
753
- id: 'c2', name: 'code_execution', input: { code: idle, background: true }, callerAgentName: 'prime',
754
- });
755
- assert.strictEqual(second.isError, true);
756
- assert.match(String(second.error), /limit reached/);
757
- const list = await framework.executeToolCall({
758
- id: 'c3', name: 'code_execution', input: { action: 'list' }, callerAgentName: 'prime',
759
- });
760
- const scripts = list.data.background_scripts;
761
- assert.strictEqual(scripts.filter((s) => s.status === 'running').length, 1);
762
- const cancel = await framework.executeToolCall({
763
- id: 'c4', name: 'code_execution', input: { action: 'cancel', script_id: firstId }, callerAgentName: 'prime',
764
- });
765
- assert.strictEqual(cancel.success, true);
766
- assert.strictEqual(cancel.data.status, 'cancelled');
767
- }
768
- finally {
769
- await framework.stop();
770
- rmSync(tempDir, { recursive: true, force: true });
771
- }
772
- });
773
- it('crashed background script wakes the agent with the error tail', async () => {
774
- const { tempDir, storePath } = tempStorePath('pytc-bgcrash-');
775
- const membrane = new MockMembrane();
776
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'noted' }]));
777
- const framework = await createBgFramework(storePath, membrane, {});
778
- try {
779
- framework.start();
780
- const started = await framework.executeToolCall({
781
- id: 'c1',
782
- name: 'code_execution',
783
- input: { code: 'import asyncio\nawait asyncio.sleep(0.1)\nraise ValueError("watcher exploded")', background: true },
784
- callerAgentName: 'prime',
785
- });
786
- assert.strictEqual(started.success, true, JSON.stringify(started));
787
- const deadline = Date.now() + 20_000;
788
- let crashMessage = null;
789
- while (Date.now() < deadline && !crashMessage) {
790
- await new Promise((r) => setTimeout(r, 100));
791
- const cm = framework.getAgent('prime')?.getContextManager();
792
- const msgs = (cm?.queryMessages({}).messages ?? []);
793
- crashMessage = msgs.find((m) => m.metadata?.source === 'background-script') ?? null;
794
- }
795
- assert.ok(crashMessage, 'crash wake should be injected');
796
- const text = crashMessage.content[0].text ?? '';
797
- assert.match(text, /DIED/);
798
- assert.match(text, /ValueError: watcher exploded/);
799
- }
800
- finally {
801
- await framework.stop();
802
- rmSync(tempDir, { recursive: true, force: true });
803
- }
804
- });
805
- it('oversized tool results spill to a workspace file with a reference', async () => {
806
- const { tempDir, storePath } = tempStorePath('pytc-spill-');
807
- const mountDir = join(tempDir, 'mount');
808
- const { mkdirSync } = await import('node:fs');
809
- mkdirSync(mountDir, { recursive: true });
810
- const membrane = new MockMembrane();
811
- // big module: returns ~600KB (strategy default maxMessageTokens absent →
812
- // maxChars undefined → no spill). Force the cap via agent_settings-style
813
- // override path by configuring a small maxMessageTokens on the agent.
814
- class BigToolModule {
815
- name = 'big';
816
- async start() { }
817
- async stop() { }
818
- getTools() {
819
- return [{ name: 'blob', description: 'Returns JSON: huge string.', inputSchema: { type: 'object', properties: {} } }];
820
- }
821
- async handleToolCall() {
822
- return { success: true, data: { blob: 'x'.repeat(600_000) } };
823
- }
824
- async onProcess(event) {
825
- if (event.type === 'external-message') {
826
- return {
827
- addMessages: [{ participant: 'User', content: event.content }],
828
- requestInference: true,
829
- };
830
- }
831
- return {};
832
- }
833
- }
834
- membrane.pushResponse(createMockResponse([
835
- { type: 'text', text: 'Fetching blob.' },
836
- { type: 'tool_use', id: 'call_blob', name: 'big--blob', input: {} },
837
- ], 'tool_use'));
838
- membrane.pushResponse(createMockResponse([{ type: 'text', text: 'Handled.' }]));
839
- const { WorkspaceModule } = await import('../src/modules/workspace/index.js');
840
- const spillWorkspace = new WorkspaceModule({
841
- mounts: [{ name: 'files', path: mountDir, mode: 'read-write', watch: 'never' }],
842
- });
843
- const framework = await AgentFramework.create({
844
- storePath,
845
- membrane: membrane.asMembrane(),
846
- agents: [{
847
- name: 'prime',
848
- model: 'test-model',
849
- systemPrompt: 'You are prime.',
850
- allowedTools: 'all',
851
- strategy: new CappedPassthroughStrategy(),
852
- }],
853
- modules: [
854
- new BigToolModule(),
855
- spillWorkspace,
856
- ],
857
- syncIntervalMs: 0,
858
- codeExecution: { enabled: true },
859
- });
860
- spillWorkspace.initStore(framework.getStore());
861
- try {
862
- framework.start();
863
- framework.pushEvent({
864
- type: 'external-message',
865
- source: 'test',
866
- content: [{ type: 'text', text: 'get the blob' }],
867
- metadata: {},
868
- triggerInference: true,
869
- });
870
- const deadline = Date.now() + 20_000;
871
- let toolResultText = null;
872
- while (Date.now() < deadline && !toolResultText) {
873
- await new Promise((r) => setTimeout(r, 100));
874
- const cm = framework.getAgent('prime')?.getContextManager();
875
- const msgs = (cm?.queryMessages({}).messages ?? []);
876
- for (const m of msgs) {
877
- for (const b of m.content ?? []) {
878
- if (b.type === 'tool_result' && typeof b.content === 'string' && b.content.includes('[truncated')) {
879
- toolResultText = b.content;
880
- }
881
- }
882
- }
883
- }
884
- assert.ok(toolResultText, 'spilled tool result should be stored');
885
- assert.match(toolResultText, /full content: workspace file files\/tool-results\//);
886
- assert.match(toolResultText, /tool_result_inline_max_chars/);
887
- assert.ok(toolResultText.length < 20_000, 'inline copy must be capped');
888
- // The full content lives in the chronicle tree — the same place the
889
- // agent's own read tool looks (disk materialization is async and
890
- // watch-mode-dependent; not what we're testing here).
891
- const refMatch = toolResultText.match(/workspace file (files\/tool-results\/\S+\.txt)/);
892
- assert.ok(refMatch, 'reference should name the spill file');
893
- const spilledFile = await spillWorkspace.readBinary(refMatch[1]);
894
- assert.ok('data' in spilledFile, `spill file should be readable: ${JSON.stringify(spilledFile)}`);
895
- assert.ok(spilledFile.data.byteLength >= 600_000, 'full content in file');
896
- }
897
- finally {
898
- await framework.stop();
899
- rmSync(tempDir, { recursive: true, force: true });
900
- }
901
- });
902
- });
903
- import { PassthroughStrategy } from '../src/index.js';
904
- class CappedPassthroughStrategy extends PassthroughStrategy {
905
- maxMessageTokens = 1000;
906
- }
907
- describe('code_execution per-call time limit (time_limit_ms)', () => {
908
- it('PyRunner: a per-exec deadline replaces scriptTimeoutMs, and the result says the time ran out', async () => {
909
- const runner = new PyRunner({ scriptTimeoutMs: 600_000, onToolCall: async () => '' });
910
- try {
911
- const started = Date.now();
912
- const result = await runner.exec('import asyncio\nawait asyncio.sleep(60)', [], undefined, { deadlineMs: 1000 });
913
- assert.strictEqual(result.returnCode, 1);
914
- assert.match(result.stderr, /script stopped: it reached its 1s time limit/);
915
- assert.ok(Date.now() - started < 15_000, 'the per-exec deadline was not applied');
916
- }
917
- finally {
918
- runner.dispose();
919
- }
920
- });
921
- it('PyRunner: a script that catches the cancellation and finishes is not reported as stopped', async () => {
922
- const runner = new PyRunner({ onToolCall: async () => '' });
923
- try {
924
- const code = 'import asyncio\ntry:\n await asyncio.sleep(60)\nexcept asyncio.CancelledError:\n print("cleaned up")';
925
- const result = await runner.exec(code, [], undefined, { deadlineMs: 1000 });
926
- assert.strictEqual(result.returnCode, 0, result.stderr);
927
- assert.match(result.stdout, /cleaned up/);
928
- assert.doesNotMatch(result.stderr, /script stopped/);
929
- }
930
- finally {
931
- runner.dispose();
932
- }
933
- });
934
- it('PyRunner: a deadline beyond Node timers (~24.8 days) is clamped, not fired at once', async () => {
935
- const runner = new PyRunner({ onToolCall: async () => '' });
936
- try {
937
- const result = await runner.exec('import asyncio\nawait asyncio.sleep(0.3)\nprint("done")', [], undefined, { deadlineMs: 30 * 86_400_000 });
938
- assert.strictEqual(result.returnCode, 0, result.stderr);
939
- assert.match(result.stdout, /done/);
940
- }
941
- finally {
942
- runner.dispose();
943
- }
944
- });
945
- it('a ceiling below the default is refused when the framework is created', async () => {
946
- const { tempDir, storePath } = tempStorePath('pytc-badcfg-');
947
- try {
948
- await assert.rejects(AgentFramework.create({
949
- storePath,
950
- membrane: new MockMembrane().asMembrane(),
951
- agents: [],
952
- modules: [],
953
- syncIntervalMs: 0,
954
- codeExecution: { enabled: true, scriptTimeoutMs: 600_000, maxScriptTimeoutMs: 60_000 },
955
- }), /maxScriptTimeoutMs \(60000\) must be at least scriptTimeoutMs \(600000\)/);
956
- }
957
- finally {
958
- rmSync(tempDir, { recursive: true, force: true });
959
- }
960
- });
961
- async function withFramework(codeExecution, body) {
962
- const { tempDir, storePath } = tempStorePath('pytc-timeout-');
963
- const framework = await AgentFramework.create({
964
- storePath,
965
- membrane: new MockMembrane().asMembrane(),
966
- agents: [],
967
- modules: [new ScriptToolModule()],
968
- syncIntervalMs: 0,
969
- codeExecution: { enabled: true, ...codeExecution },
970
- });
971
- try {
972
- await body(framework);
973
- }
974
- finally {
975
- await framework.stop();
976
- rmSync(tempDir, { recursive: true, force: true });
977
- }
978
- }
979
- const run = (framework, input) => framework.executeToolCall({ id: `ce-${Math.random()}`, name: 'code_execution', input });
980
- const dataOf = (r) => r.data;
981
- it('the tool tells the agent its time limit and offers time_limit_ms', async () => {
982
- await withFramework({ scriptTimeoutMs: 300_000, maxScriptTimeoutMs: 1_800_000 }, async (framework) => {
983
- const tool = framework.getAllTools().find((t) => t.name === 'code_execution');
984
- assert.match(tool.description, /A foreground script is stopped after 5 min; pass time_limit_ms to set this call's limit \(at most 30 min; a background script runs up to 24 h\)/);
985
- const props = tool.inputSchema.properties;
986
- assert.match(props.time_limit_ms.description, /Default 300000, at most 1800000/);
987
- });
988
- });
989
- it('time_limit_ms shortens one call; the deployment default is unchanged for the next', async () => {
990
- await withFramework({ scriptTimeoutMs: 600_000 }, async (framework) => {
991
- const short = dataOf(await run(framework, { code: 'import asyncio\nawait asyncio.sleep(60)', time_limit_ms: 1000 }));
992
- assert.strictEqual(short.return_code, 1);
993
- assert.match(short.stderr, /reached its 1s time limit/);
994
- const normal = dataOf(await run(framework, { code: 'import asyncio\nawait asyncio.sleep(1.5)\nprint("done")' }));
995
- assert.strictEqual(normal.return_code, 0);
996
- assert.match(normal.stdout, /done/);
997
- });
998
- });
999
- it('a request above the ceiling is capped, and the result says so', async () => {
1000
- await withFramework({ scriptTimeoutMs: 1000 }, async (framework) => {
1001
- const started = Date.now();
1002
- const r = dataOf(await run(framework, { code: 'import asyncio\nawait asyncio.sleep(60)', time_limit_ms: 60_000 }));
1003
- assert.strictEqual(r.return_code, 1);
1004
- assert.strictEqual(r.time_limit_note, "time_limit_ms 60000 was capped at 1000, this deployment's maximum");
1005
- assert.ok(Date.now() - started < 15_000, 'the cap was not applied');
1006
- });
1007
- });
1008
- it('maxScriptTimeoutMs lets an agent ask for longer than the default', async () => {
1009
- await withFramework({ scriptTimeoutMs: 1000, maxScriptTimeoutMs: 10_000 }, async (framework) => {
1010
- const code = 'import asyncio\nawait asyncio.sleep(2)\nprint("long done")';
1011
- const defaulted = dataOf(await run(framework, { code }));
1012
- assert.strictEqual(defaulted.return_code, 1, 'without time_limit_ms the 1s default applies');
1013
- const longer = dataOf(await run(framework, { code, time_limit_ms: 5000 }));
1014
- assert.strictEqual(longer.return_code, 0);
1015
- assert.match(longer.stdout, /long done/);
1016
- assert.strictEqual(longer.time_limit_note, undefined);
1017
- });
1018
- });
1019
- it('an invalid time_limit_ms is refused before anything runs', async () => {
1020
- await withFramework({}, async (framework) => {
1021
- for (const time_limit_ms of ['soon', 10, -5, Number.NaN]) {
1022
- const r = await run(framework, { code: 'print("ran")', time_limit_ms });
1023
- assert.strictEqual(r.success, false, String(time_limit_ms));
1024
- assert.match(String(r.error), /time_limit_ms/);
1025
- }
1026
- });
1027
- });
1028
- it('a background script: time_limit_ms shortens its lifetime, capped at the lifetime ceiling', async () => {
1029
- await withFramework({ backgroundMaxLifetimeMs: 1500 }, async (framework) => {
1030
- const r = await run(framework, { code: 'import asyncio\nawait asyncio.sleep(60)', background: true, time_limit_ms: 60_000 });
1031
- assert.strictEqual(r.success, true);
1032
- const data = r.data;
1033
- assert.strictEqual(data.time_limit_note, "time_limit_ms 60000 was capped at 1500, this deployment's maximum");
1034
- assert.strictEqual(data.lifetime, '2s', 'a short lifetime is reported exactly, not rounded to 0 hours');
1035
- let status = 'running';
1036
- const until = Date.now() + 15_000;
1037
- while (status === 'running' && Date.now() < until) {
1038
- await new Promise((res) => setTimeout(res, 200));
1039
- const listed = (await run(framework, { action: 'list' })).data;
1040
- status = listed.background_scripts.find((s) => s.script_id === data.script_id)?.status ?? 'gone';
1041
- }
1042
- assert.strictEqual(status, 'died', 'the script was stopped at its capped lifetime');
1043
- });
1044
- });
1045
- });
1046
- // The deadline's cancel op is an asyncio cancellation, which lands only when a
1047
- // script awaits; the SIGINT that follows it stops blocking code (#235 F3).
1048
- describe('code_execution time limit vs blocking code (real python3)', { skip: process.platform === 'win32' }, () => {
1049
- const pidOf = (stdout) => /pid=(\d+)/.exec(stdout)?.[1];
1050
- it('blocking code stops at the time limit, keeping its output, and the interpreter survives', async () => {
1051
- const runner = new PyRunner({ onToolCall: async () => '' });
1052
- try {
1053
- const first = await runner.exec('import os\nprint(f"pid={os.getpid()}")', []);
1054
- const pid = pidOf(first.stdout);
1055
- assert.ok(pid, first.stdout);
1056
- const blocking = {
1057
- sleep: 'import time\nprint("before")\ntime.sleep(60)\nprint("after")',
1058
- 'busy loop': 'print("before")\nn = 0\nwhile True:\n n += 1\nprint("after")',
1059
- 'blocking read': 'import socket\nprint("before")\na, b = socket.socketpair()\na.recv(1)\nprint("after")',
1060
- };
1061
- for (const [shape, code] of Object.entries(blocking)) {
1062
- const started = Date.now();
1063
- const result = await runner.exec(code, [], undefined, { deadlineMs: 1000 });
1064
- const elapsed = Date.now() - started;
1065
- assert.strictEqual(result.returnCode, 1, shape);
1066
- assert.strictEqual(result.aborted, undefined, `${shape}: interrupted, not killed`);
1067
- assert.strictEqual(result.stdout, 'before\n', `${shape}: output before the limit is kept`);
1068
- assert.match(result.stderr, /KeyboardInterrupt: script interrupted by host/, shape);
1069
- assert.match(result.stderr, /script stopped: it reached its 1s time limit/, shape);
1070
- assert.match(result.stderr, /File "<script>", line \d+/, `${shape}: says where the script was`);
1071
- assert.doesNotMatch(result.stderr, /runtime\.py/, `${shape}: no runtime frames`);
1072
- assert.ok(elapsed < 5000, `${shape}: stopped at the limit, not the kill grace (${elapsed}ms)`);
1073
- }
1074
- const last = await runner.exec('import os\nprint(f"pid={os.getpid()}")', []);
1075
- assert.strictEqual(pidOf(last.stdout), pid, 'same interpreter: nothing was killed');
1076
- }
1077
- finally {
1078
- runner.dispose();
1079
- }
1080
- });
1081
- it('variables survive an interrupted script', async () => {
1082
- const runner = new PyRunner({ onToolCall: async () => '' });
1083
- try {
1084
- const set = await runner.exec('x = 41', []);
1085
- assert.strictEqual(set.returnCode, 0, set.stderr);
1086
- const stopped = await runner.exec('import time\ny = 1\ntime.sleep(60)', [], undefined, { deadlineMs: 1000 });
1087
- assert.match(stopped.stderr, /reached its 1s time limit/);
1088
- const after = await runner.exec('print(x + y)', []);
1089
- assert.strictEqual(after.returnCode, 0, after.stderr);
1090
- assert.strictEqual(after.stdout, '42\n');
1091
- }
1092
- finally {
1093
- runner.dispose();
1094
- }
1095
- });
1096
- it('a script waiting on an await is still stopped by the cancel', async () => {
1097
- const runner = new PyRunner({
1098
- onToolCall: () => new Promise((resolve) => setTimeout(() => resolve('late'), 5000)),
1099
- });
1100
- try {
1101
- const started = Date.now();
1102
- const result = await runner.exec('print("before")\nawait test__echo({})\nprint("after")', ECHO_TOOLS, undefined, { deadlineMs: 1000 });
1103
- assert.strictEqual(result.returnCode, 1);
1104
- assert.strictEqual(result.stdout, 'before\n');
1105
- assert.match(result.stderr, /KeyboardInterrupt: script cancelled by host/);
1106
- assert.doesNotMatch(result.stderr, /interrupted by host/);
1107
- assert.match(result.stderr, /script stopped: it reached its 1s time limit/);
1108
- assert.ok(Date.now() - started < 5000, 'stopped at the limit');
1109
- }
1110
- finally {
1111
- runner.dispose();
1112
- }
1113
- });
1114
- it('SIGINT between scripts does nothing', async () => {
1115
- const runner = new PyRunner({ onToolCall: async () => '' });
1116
- try {
1117
- const first = await runner.exec('import os\nx = 1\nprint(f"pid={os.getpid()}")', []);
1118
- const pid = Number(pidOf(first.stdout));
1119
- assert.ok(pid > 0, first.stdout);
1120
- process.kill(pid, 'SIGINT');
1121
- await new Promise((r) => setTimeout(r, 200));
1122
- const result = await runner.exec('print(x)\nprint(f"pid={os.getpid()}")', []);
1123
- assert.strictEqual(result.returnCode, 0, result.stderr);
1124
- assert.strictEqual(result.stdout, `1\npid=${pid}\n`);
1125
- }
1126
- finally {
1127
- runner.dispose();
1128
- }
1129
- });
1130
- it('SIGINT while a script waits cancels it before it can resume into blocking code', async () => {
1131
- let pid = 0;
1132
- const runner = new PyRunner({
1133
- onToolCall: async () => {
1134
- // The script is suspended on this call. The signal arrives with no cancel op behind it;
1135
- // the result does, and the script would then block.
1136
- process.kill(pid, 'SIGINT');
1137
- await new Promise((r) => setTimeout(r, 200));
1138
- return 'tool ok';
1139
- },
1140
- });
1141
- try {
1142
- const first = await runner.exec('import os\nprint(f"pid={os.getpid()}")', []);
1143
- pid = Number(pidOf(first.stdout));
1144
- assert.ok(pid > 0, first.stdout);
1145
- const started = Date.now();
1146
- const result = await runner.exec('import time\nprint(await test__echo({}))\ntime.sleep(30)', ECHO_TOOLS);
1147
- assert.strictEqual(result.returnCode, 1);
1148
- assert.strictEqual(result.stdout, '');
1149
- // Cancelled at its await (or, if the signal beat the script to it, interrupted there).
1150
- assert.match(result.stderr, /KeyboardInterrupt: script (cancelled|interrupted) by host/);
1151
- assert.ok(Date.now() - started < 5000, 'stopped at once, not after the blocking call');
1152
- const after = await runner.exec('print(f"pid={os.getpid()}")', []);
1153
- assert.strictEqual(after.stdout, `pid=${pid}\n`, 'same interpreter');
1154
- }
1155
- finally {
1156
- runner.dispose();
1157
- }
1158
- });
1159
- it('a script that resumes from an await into blocking code at its limit is stopped', async () => {
1160
- const runner = new PyRunner({ onToolCall: async () => '' });
1161
- try {
1162
- for (let i = 0; i < 3; i++) {
1163
- const started = Date.now();
1164
- const result = await runner.exec('import asyncio, time\nawait asyncio.sleep(1)\ntime.sleep(5)\nprint("after")', [], undefined, { deadlineMs: 1000 });
1165
- assert.strictEqual(result.returnCode, 1, result.stderr);
1166
- assert.strictEqual(result.stdout, '');
1167
- assert.match(result.stderr, /script stopped: it reached its 1s time limit/);
1168
- assert.ok(Date.now() - started < 3000, `stopped at the limit (${Date.now() - started}ms)`);
1169
- }
1170
- }
1171
- finally {
1172
- runner.dispose();
1173
- }
1174
- });
1175
- it("the runtime's stop table: each place a signal or cancel op can land stops the script once", () => {
1176
- const dir = mkdtempSync(join(tmpdir(), 'pytc-stop-rows-'));
1177
- try {
1178
- writeFileSync(join(dir, 'runtime.py'), PYTHON_RUNTIME_SOURCE);
1179
- writeFileSync(join(dir, 'driver.py'), PYTC_STOP_ROWS_DRIVER);
1180
- const rows = JSON.parse(execFileSync('python3', [join(dir, 'driver.py'), join(dir, 'runtime.py')], { encoding: 'utf8', timeout: 60_000 }));
1181
- const printed = (row, line) => row.tail.split('\n').includes(line);
1182
- const interrupted = /KeyboardInterrupt: script interrupted by host/;
1183
- const cancelled = /KeyboardInterrupt: script cancelled by host/;
1184
- const stopped = (name, how, has = [], hasNot = []) => {
1185
- const row = rows[name];
1186
- assert.ok(row, name);
1187
- assert.strictEqual(row.rc, 1, `${name}: ${row.tail}`);
1188
- assert.match(row.tail, how, name);
1189
- for (const w of has)
1190
- assert.ok(printed(row, w), `${name}: printed ${w}`);
1191
- for (const w of hasNot)
1192
- assert.ok(!printed(row, w), `${name}: did not print ${w}`);
1193
- assert.ok(row.secs < 2, `${name}: stopped at once (${row.secs}s)`);
1194
- };
1195
- const finished = (name, has) => {
1196
- const row = rows[name];
1197
- assert.ok(row, name);
1198
- assert.strictEqual(row.rc, 0, `${name}: ${row.tail}`);
1199
- for (const w of has)
1200
- assert.ok(printed(row, w), `${name}: printed ${w}`);
1201
- assert.doesNotMatch(row.tail, /by host/, `${name}: never stopped (again) by us`);
1202
- };
1203
- stopped('1 own code', interrupted, ['a'], ['b']);
1204
- stopped('2 waiting on an await', cancelled, [], ['b']);
1205
- // Cancelling inside the handler here raised InvalidStateError out of main().
1206
- stopped('3 mid-dispatch, reply behind', cancelled, [], ['woke']);
1207
- stopped('4 protocol write', cancelled, [], ['b']);
1208
- assert.ok(rows['4 protocol write'].ops.includes('tool_call'), 'the protocol line stayed whole');
1209
- stopped('5 reply ahead, then await', cancelled, ['woke'], ['b']);
1210
- // Interrupted in its blocking code; the queued cancellation must not cut its cleanup short.
1211
- finished('6 reply ahead, then block', ['woke', 'cleaned']);
1212
- finished('7 reply ahead, then finish', ['woke']);
1213
- finished('7 next script', ['ok']);
1214
- stopped('8 before start, signal', cancelled, [], ['ran']);
1215
- // The cancel op before the script started used to leave it reporting nothing.
1216
- stopped('8 before start, cancel op', cancelled, [], ['ran']);
1217
- finished('9 cancel op first', ['cleaned']);
1218
- finished('10 after delivery', ['cleaned']);
1219
- finished('11 reporting', ['done']);
1220
- finished('12 between scripts', ['ok']);
1221
- if (rows['13 next script blocks']) {
1222
- // Python 3.12+: an eager task factory left installed by an earlier script.
1223
- finished('13 eager task factory installed', ['installed']);
1224
- stopped('13 next script blocks', interrupted, ['a'], ['b']);
1225
- }
1226
- }
1227
- finally {
1228
- rmSync(dir, { recursive: true, force: true });
1229
- }
1230
- });
1231
- it('an eager task factory installed by an earlier script does not hide the next one from the deadline', async (t) => {
1232
- const runner = new PyRunner({ onToolCall: async () => '', cancelGraceMs: 3000 });
1233
- try {
1234
- const install = await runner.exec('import asyncio, sys\nif sys.version_info >= (3, 12):\n asyncio.get_running_loop().set_task_factory(asyncio.eager_task_factory)\n print("installed")', []);
1235
- assert.strictEqual(install.returnCode, 0, install.stderr);
1236
- if (install.stdout !== 'installed\n') {
1237
- t.skip('asyncio.eager_task_factory needs Python 3.12+');
1238
- return;
1239
- }
1240
- const started = Date.now();
1241
- const result = await runner.exec('import time\nprint("before")\ntime.sleep(30)', [], undefined, { deadlineMs: 1000 });
1242
- assert.strictEqual(result.aborted, undefined, 'interrupted, not killed');
1243
- assert.strictEqual(result.stdout, 'before\n');
1244
- assert.match(result.stderr, /KeyboardInterrupt: script interrupted by host/);
1245
- assert.match(result.stderr, /script stopped: it reached its 1s time limit/);
1246
- assert.ok(Date.now() - started < 2500, `stopped at the limit (${Date.now() - started}ms)`);
1247
- }
1248
- finally {
1249
- runner.dispose();
1250
- }
1251
- });
1252
- it('a script that ignores the interrupt is killed after the grace, and its interpreter state is gone', async () => {
1253
- const runner = new PyRunner({ onToolCall: async () => '', cancelGraceMs: 500 });
1254
- try {
1255
- assert.strictEqual((await runner.exec('x = 41', [])).returnCode, 0);
1256
- const code = 'import time\ntry:\n time.sleep(60)\nexcept KeyboardInterrupt:\n time.sleep(60)';
1257
- const started = Date.now();
1258
- const killed = await runner.exec(code, [], undefined, { deadlineMs: 1000 });
1259
- assert.strictEqual(killed.aborted, true);
1260
- assert.strictEqual(killed.returnCode, 1);
1261
- assert.match(killed.stderr, /script stopped: it reached its 1s time limit and did not respond, so it was killed and the interpreter restarted \(variables from earlier scripts are gone\)/);
1262
- assert.ok(Date.now() - started < 5000, `killed after the grace (${Date.now() - started}ms)`);
1263
- const after = await runner.exec('print(x)', []);
1264
- assert.strictEqual(after.returnCode, 1);
1265
- assert.match(after.stderr, /NameError: name 'x' is not defined/);
1266
- }
1267
- finally {
1268
- runner.dispose();
1269
- }
1270
- });
1271
- });
1272
- //# sourceMappingURL=code-execution.test.js.map