@animalabs/agent-framework 0.22.2 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (997) hide show
  1. package/README.md +108 -4
  2. package/dist/src/agent.d.ts +108 -17
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +207 -67
  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/index.d.ts +1 -1
  22. package/dist/src/code-execution/index.d.ts.map +1 -1
  23. package/dist/src/code-execution/index.js.map +1 -1
  24. package/dist/src/code-execution/py-runner.d.ts +50 -7
  25. package/dist/src/code-execution/py-runner.d.ts.map +1 -1
  26. package/dist/src/code-execution/py-runner.js +258 -90
  27. package/dist/src/code-execution/py-runner.js.map +1 -1
  28. package/dist/src/code-execution/runtime-py.d.ts +3 -2
  29. package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
  30. package/dist/src/code-execution/runtime-py.js +94 -8
  31. package/dist/src/code-execution/runtime-py.js.map +1 -1
  32. package/dist/src/code-execution/tool-definition.d.ts +17 -1
  33. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  34. package/dist/src/code-execution/tool-definition.js +72 -6
  35. package/dist/src/code-execution/tool-definition.js.map +1 -1
  36. package/dist/src/config-checks.d.ts +28 -0
  37. package/dist/src/config-checks.d.ts.map +1 -0
  38. package/dist/src/config-checks.js +56 -0
  39. package/dist/src/config-checks.js.map +1 -0
  40. package/dist/src/deadline-timer.d.ts +21 -0
  41. package/dist/src/deadline-timer.d.ts.map +1 -0
  42. package/dist/src/deadline-timer.js +43 -0
  43. package/dist/src/deadline-timer.js.map +1 -0
  44. package/dist/src/error-message.d.ts +30 -0
  45. package/dist/src/error-message.d.ts.map +1 -0
  46. package/dist/src/error-message.js +50 -0
  47. package/dist/src/error-message.js.map +1 -0
  48. package/dist/src/framework.d.ts +1529 -165
  49. package/dist/src/framework.d.ts.map +1 -1
  50. package/dist/src/framework.js +8264 -1939
  51. package/dist/src/framework.js.map +1 -1
  52. package/dist/src/gate/event-gate.d.ts +8 -0
  53. package/dist/src/gate/event-gate.d.ts.map +1 -1
  54. package/dist/src/gate/event-gate.js +36 -5
  55. package/dist/src/gate/event-gate.js.map +1 -1
  56. package/dist/src/held-writes.d.ts +16 -0
  57. package/dist/src/held-writes.d.ts.map +1 -0
  58. package/dist/src/held-writes.js +56 -0
  59. package/dist/src/held-writes.js.map +1 -0
  60. package/dist/src/index.d.ts +10 -4
  61. package/dist/src/index.d.ts.map +1 -1
  62. package/dist/src/index.js +11 -5
  63. package/dist/src/index.js.map +1 -1
  64. package/dist/src/mcpl/capability-grant.d.ts +4 -0
  65. package/dist/src/mcpl/capability-grant.d.ts.map +1 -1
  66. package/dist/src/mcpl/capability-grant.js +1 -1
  67. package/dist/src/mcpl/capability-grant.js.map +1 -1
  68. package/dist/src/mcpl/channel-registry.d.ts +233 -18
  69. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  70. package/dist/src/mcpl/channel-registry.js +975 -386
  71. package/dist/src/mcpl/channel-registry.js.map +1 -1
  72. package/dist/src/mcpl/checkpoint-manager.d.ts +21 -1
  73. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  74. package/dist/src/mcpl/checkpoint-manager.js +91 -30
  75. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  76. package/dist/src/mcpl/conversation-router.d.ts +19 -3
  77. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  78. package/dist/src/mcpl/conversation-router.js +23 -8
  79. package/dist/src/mcpl/conversation-router.js.map +1 -1
  80. package/dist/src/mcpl/convert-block.d.ts +20 -0
  81. package/dist/src/mcpl/convert-block.d.ts.map +1 -0
  82. package/dist/src/mcpl/convert-block.js +88 -0
  83. package/dist/src/mcpl/convert-block.js.map +1 -0
  84. package/dist/src/mcpl/errors.d.ts +13 -0
  85. package/dist/src/mcpl/errors.d.ts.map +1 -1
  86. package/dist/src/mcpl/errors.js +28 -0
  87. package/dist/src/mcpl/errors.js.map +1 -1
  88. package/dist/src/mcpl/hook-orchestrator.d.ts +23 -0
  89. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  90. package/dist/src/mcpl/hook-orchestrator.js +83 -72
  91. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  92. package/dist/src/mcpl/inbound-source.d.ts +114 -0
  93. package/dist/src/mcpl/inbound-source.d.ts.map +1 -0
  94. package/dist/src/mcpl/inbound-source.js +83 -0
  95. package/dist/src/mcpl/inbound-source.js.map +1 -0
  96. package/dist/src/mcpl/index.d.ts +7 -1
  97. package/dist/src/mcpl/index.d.ts.map +1 -1
  98. package/dist/src/mcpl/index.js +5 -1
  99. package/dist/src/mcpl/index.js.map +1 -1
  100. package/dist/src/mcpl/inference-router.d.ts +19 -13
  101. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  102. package/dist/src/mcpl/inference-router.js +155 -87
  103. package/dist/src/mcpl/inference-router.js.map +1 -1
  104. package/dist/src/mcpl/log-value.d.ts +7 -0
  105. package/dist/src/mcpl/log-value.d.ts.map +1 -1
  106. package/dist/src/mcpl/log-value.js +9 -0
  107. package/dist/src/mcpl/log-value.js.map +1 -1
  108. package/dist/src/mcpl/modern-connection.d.ts +227 -0
  109. package/dist/src/mcpl/modern-connection.d.ts.map +1 -0
  110. package/dist/src/mcpl/modern-connection.js +1088 -0
  111. package/dist/src/mcpl/modern-connection.js.map +1 -0
  112. package/dist/src/mcpl/prefix-rule.d.ts +18 -0
  113. package/dist/src/mcpl/prefix-rule.d.ts.map +1 -0
  114. package/dist/src/mcpl/prefix-rule.js +25 -0
  115. package/dist/src/mcpl/prefix-rule.js.map +1 -0
  116. package/dist/src/mcpl/protocol-family.d.ts +179 -0
  117. package/dist/src/mcpl/protocol-family.d.ts.map +1 -0
  118. package/dist/src/mcpl/protocol-family.js +697 -0
  119. package/dist/src/mcpl/protocol-family.js.map +1 -0
  120. package/dist/src/mcpl/push-coalescer.d.ts +58 -17
  121. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  122. package/dist/src/mcpl/push-coalescer.js +93 -32
  123. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  124. package/dist/src/mcpl/push-handler.d.ts +16 -9
  125. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  126. package/dist/src/mcpl/push-handler.js +18 -83
  127. package/dist/src/mcpl/push-handler.js.map +1 -1
  128. package/dist/src/mcpl/reference-fetcher.d.ts +76 -13
  129. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  130. package/dist/src/mcpl/reference-fetcher.js +275 -66
  131. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  132. package/dist/src/mcpl/references.d.ts +98 -18
  133. package/dist/src/mcpl/references.d.ts.map +1 -1
  134. package/dist/src/mcpl/references.js +170 -32
  135. package/dist/src/mcpl/references.js.map +1 -1
  136. package/dist/src/mcpl/server-connection.d.ts +406 -31
  137. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  138. package/dist/src/mcpl/server-connection.js +1331 -272
  139. package/dist/src/mcpl/server-connection.js.map +1 -1
  140. package/dist/src/mcpl/server-registry.d.ts +35 -6
  141. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  142. package/dist/src/mcpl/server-registry.js +63 -13
  143. package/dist/src/mcpl/server-registry.js.map +1 -1
  144. package/dist/src/mcpl/skip-reply-wake.d.ts +36 -0
  145. package/dist/src/mcpl/skip-reply-wake.d.ts.map +1 -0
  146. package/dist/src/mcpl/skip-reply-wake.js +55 -0
  147. package/dist/src/mcpl/skip-reply-wake.js.map +1 -0
  148. package/dist/src/mcpl/tool-result-normalize.d.ts +54 -0
  149. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -0
  150. package/dist/src/mcpl/tool-result-normalize.js +232 -0
  151. package/dist/src/mcpl/tool-result-normalize.js.map +1 -0
  152. package/dist/src/mcpl/transport.d.ts +104 -12
  153. package/dist/src/mcpl/transport.d.ts.map +1 -1
  154. package/dist/src/mcpl/transport.js +315 -85
  155. package/dist/src/mcpl/transport.js.map +1 -1
  156. package/dist/src/mcpl/types.d.ts +76 -19
  157. package/dist/src/mcpl/types.d.ts.map +1 -1
  158. package/dist/src/mcpl/types.js.map +1 -1
  159. package/dist/src/mcpl/visible-content.d.ts +16 -0
  160. package/dist/src/mcpl/visible-content.d.ts.map +1 -1
  161. package/dist/src/mcpl/visible-content.js +26 -0
  162. package/dist/src/mcpl/visible-content.js.map +1 -1
  163. package/dist/src/module-registry.d.ts +34 -6
  164. package/dist/src/module-registry.d.ts.map +1 -1
  165. package/dist/src/module-registry.js +119 -31
  166. package/dist/src/module-registry.js.map +1 -1
  167. package/dist/src/modules/api/index.d.ts +1 -1
  168. package/dist/src/modules/api/index.d.ts.map +1 -1
  169. package/dist/src/modules/api/index.js +3 -2
  170. package/dist/src/modules/api/index.js.map +1 -1
  171. package/dist/src/modules/discord/discord-js-client.d.ts +43 -2
  172. package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
  173. package/dist/src/modules/discord/discord-js-client.js +286 -67
  174. package/dist/src/modules/discord/discord-js-client.js.map +1 -1
  175. package/dist/src/modules/discord/index.d.ts +53 -5
  176. package/dist/src/modules/discord/index.d.ts.map +1 -1
  177. package/dist/src/modules/discord/index.js +712 -186
  178. package/dist/src/modules/discord/index.js.map +1 -1
  179. package/dist/src/modules/discord/refusal.d.ts +14 -0
  180. package/dist/src/modules/discord/refusal.d.ts.map +1 -0
  181. package/dist/src/modules/discord/refusal.js +14 -0
  182. package/dist/src/modules/discord/refusal.js.map +1 -0
  183. package/dist/src/modules/discord/types.d.ts +27 -4
  184. package/dist/src/modules/discord/types.d.ts.map +1 -1
  185. package/dist/src/modules/health/index.d.ts.map +1 -1
  186. package/dist/src/modules/health/index.js +8 -3
  187. package/dist/src/modules/health/index.js.map +1 -1
  188. package/dist/src/modules/history/index.d.ts +3 -1
  189. package/dist/src/modules/history/index.d.ts.map +1 -1
  190. package/dist/src/modules/history/index.js +359 -127
  191. package/dist/src/modules/history/index.js.map +1 -1
  192. package/dist/src/modules/history/semantic.d.ts +28 -1
  193. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  194. package/dist/src/modules/history/semantic.js +177 -32
  195. package/dist/src/modules/history/semantic.js.map +1 -1
  196. package/dist/src/modules/index.d.ts +1 -1
  197. package/dist/src/modules/index.d.ts.map +1 -1
  198. package/dist/src/modules/index.js +1 -1
  199. package/dist/src/modules/index.js.map +1 -1
  200. package/dist/src/modules/unknown-tool.d.ts +10 -0
  201. package/dist/src/modules/unknown-tool.d.ts.map +1 -0
  202. package/dist/src/modules/unknown-tool.js +17 -0
  203. package/dist/src/modules/unknown-tool.js.map +1 -0
  204. package/dist/src/modules/workspace/disk-agreement.d.ts +57 -4
  205. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
  206. package/dist/src/modules/workspace/disk-agreement.js +75 -7
  207. package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
  208. package/dist/src/modules/workspace/effects.d.ts +66 -7
  209. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  210. package/dist/src/modules/workspace/effects.js +303 -46
  211. package/dist/src/modules/workspace/effects.js.map +1 -1
  212. package/dist/src/modules/workspace/index.d.ts +313 -45
  213. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  214. package/dist/src/modules/workspace/index.js +1404 -304
  215. package/dist/src/modules/workspace/index.js.map +1 -1
  216. package/dist/src/modules/workspace/observe.d.ts +315 -16
  217. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  218. package/dist/src/modules/workspace/observe.js +727 -37
  219. package/dist/src/modules/workspace/observe.js.map +1 -1
  220. package/dist/src/modules/workspace/reconcile.d.ts +58 -7
  221. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  222. package/dist/src/modules/workspace/reconcile.js +521 -56
  223. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  224. package/dist/src/modules/workspace/types.d.ts +28 -5
  225. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  226. package/dist/src/modules/workspace/types.js.map +1 -1
  227. package/dist/src/modules/workspace/watcher.d.ts +49 -2
  228. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  229. package/dist/src/modules/workspace/watcher.js +158 -29
  230. package/dist/src/modules/workspace/watcher.js.map +1 -1
  231. package/dist/src/prose-drafts.d.ts +240 -0
  232. package/dist/src/prose-drafts.d.ts.map +1 -0
  233. package/dist/src/prose-drafts.js +362 -0
  234. package/dist/src/prose-drafts.js.map +1 -0
  235. package/dist/src/prose-segments.d.ts +21 -2
  236. package/dist/src/prose-segments.d.ts.map +1 -1
  237. package/dist/src/prose-segments.js +46 -9
  238. package/dist/src/prose-segments.js.map +1 -1
  239. package/dist/src/recovery/offline-branch.d.ts +1 -0
  240. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  241. package/dist/src/recovery/offline-branch.js +3 -1
  242. package/dist/src/recovery/offline-branch.js.map +1 -1
  243. package/dist/src/safe-slice.d.ts +5 -1
  244. package/dist/src/safe-slice.d.ts.map +1 -1
  245. package/dist/src/safe-slice.js +9 -1
  246. package/dist/src/safe-slice.js.map +1 -1
  247. package/dist/src/silent-heartbeat.d.ts +112 -39
  248. package/dist/src/silent-heartbeat.d.ts.map +1 -1
  249. package/dist/src/silent-heartbeat.js +100 -29
  250. package/dist/src/silent-heartbeat.js.map +1 -1
  251. package/dist/src/tool-args.d.ts +40 -0
  252. package/dist/src/tool-args.d.ts.map +1 -0
  253. package/dist/src/tool-args.js +207 -0
  254. package/dist/src/tool-args.js.map +1 -0
  255. package/dist/src/tool-image-ledger.d.ts +87 -19
  256. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  257. package/dist/src/tool-image-ledger.js +135 -32
  258. package/dist/src/tool-image-ledger.js.map +1 -1
  259. package/dist/src/tool-presentation.d.ts +9 -0
  260. package/dist/src/tool-presentation.d.ts.map +1 -1
  261. package/dist/src/tool-presentation.js +266 -62
  262. package/dist/src/tool-presentation.js.map +1 -1
  263. package/dist/src/tool-result-guard.d.ts.map +1 -1
  264. package/dist/src/tool-result-guard.js +7 -10
  265. package/dist/src/tool-result-guard.js.map +1 -1
  266. package/dist/src/tool-result-history.d.ts +32 -3
  267. package/dist/src/tool-result-history.d.ts.map +1 -1
  268. package/dist/src/tool-result-history.js +106 -19
  269. package/dist/src/tool-result-history.js.map +1 -1
  270. package/dist/src/tune-out/coordinator.d.ts +25 -1
  271. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  272. package/dist/src/tune-out/coordinator.js +260 -65
  273. package/dist/src/tune-out/coordinator.js.map +1 -1
  274. package/dist/src/types/agent.d.ts +44 -22
  275. package/dist/src/types/agent.d.ts.map +1 -1
  276. package/dist/src/types/events.d.ts +25 -0
  277. package/dist/src/types/events.d.ts.map +1 -1
  278. package/dist/src/types/framework.d.ts +38 -7
  279. package/dist/src/types/framework.d.ts.map +1 -1
  280. package/dist/src/types/inference-log.d.ts +1 -1
  281. package/dist/src/types/inference-log.d.ts.map +1 -1
  282. package/dist/src/types/module.d.ts +77 -14
  283. package/dist/src/types/module.d.ts.map +1 -1
  284. package/dist/src/types/trace.d.ts +97 -8
  285. package/dist/src/types/trace.d.ts.map +1 -1
  286. package/dist/src/usage/usage-tracker.d.ts +5 -0
  287. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  288. package/dist/src/usage/usage-tracker.js +35 -8
  289. package/dist/src/usage/usage-tracker.js.map +1 -1
  290. package/package.json +5 -4
  291. package/src/agent.ts +257 -72
  292. package/src/api/mcp-server-core.ts +1 -1
  293. package/src/api/server.ts +4 -1
  294. package/src/api/types.ts +1 -1
  295. package/src/branch-names.ts +19 -0
  296. package/src/closest-names.ts +94 -0
  297. package/src/code-execution/index.ts +1 -0
  298. package/src/code-execution/py-runner.ts +304 -93
  299. package/src/code-execution/runtime-py.ts +94 -8
  300. package/src/code-execution/tool-definition.ts +85 -7
  301. package/src/config-checks.ts +50 -0
  302. package/src/deadline-timer.ts +44 -0
  303. package/src/error-message.ts +48 -0
  304. package/src/framework.ts +8625 -1984
  305. package/src/gate/event-gate.ts +38 -5
  306. package/src/held-writes.ts +59 -0
  307. package/src/index.ts +29 -5
  308. package/src/mcpl/capability-grant.ts +1 -1
  309. package/src/mcpl/channel-registry.ts +1095 -411
  310. package/src/mcpl/checkpoint-manager.ts +104 -29
  311. package/src/mcpl/conversation-router.ts +28 -8
  312. package/src/mcpl/convert-block.ts +105 -0
  313. package/src/mcpl/errors.ts +28 -0
  314. package/src/mcpl/hook-orchestrator.ts +84 -77
  315. package/src/mcpl/inbound-source.ts +156 -0
  316. package/src/mcpl/index.ts +23 -1
  317. package/src/mcpl/inference-router.ts +179 -103
  318. package/src/mcpl/log-value.ts +10 -0
  319. package/src/mcpl/modern-connection.ts +1182 -0
  320. package/src/mcpl/prefix-rule.ts +32 -0
  321. package/src/mcpl/protocol-family.ts +691 -0
  322. package/src/mcpl/push-coalescer.ts +110 -36
  323. package/src/mcpl/push-handler.ts +20 -90
  324. package/src/mcpl/reference-fetcher.ts +318 -67
  325. package/src/mcpl/references.ts +183 -34
  326. package/src/mcpl/server-connection.ts +1404 -280
  327. package/src/mcpl/server-registry.ts +63 -14
  328. package/src/mcpl/skip-reply-wake.ts +73 -0
  329. package/src/mcpl/tool-result-normalize.ts +294 -0
  330. package/src/mcpl/transport.ts +320 -76
  331. package/src/mcpl/types.ts +77 -19
  332. package/src/mcpl/visible-content.ts +28 -0
  333. package/src/module-registry.ts +126 -36
  334. package/src/modules/api/index.ts +3 -2
  335. package/src/modules/discord/discord-js-client.ts +306 -74
  336. package/src/modules/discord/index.ts +737 -201
  337. package/src/modules/discord/refusal.ts +13 -0
  338. package/src/modules/discord/types.ts +27 -6
  339. package/src/modules/health/index.ts +8 -3
  340. package/src/modules/history/index.ts +410 -120
  341. package/src/modules/history/semantic.ts +189 -32
  342. package/src/modules/index.ts +1 -1
  343. package/src/modules/unknown-tool.ts +16 -0
  344. package/src/modules/workspace/disk-agreement.ts +97 -8
  345. package/src/modules/workspace/effects.ts +291 -36
  346. package/src/modules/workspace/index.ts +1469 -321
  347. package/src/modules/workspace/observe.ts +784 -41
  348. package/src/modules/workspace/reconcile.ts +619 -64
  349. package/src/modules/workspace/types.ts +28 -5
  350. package/src/modules/workspace/watcher.ts +154 -30
  351. package/src/prose-drafts.ts +481 -0
  352. package/src/prose-segments.ts +51 -8
  353. package/src/recovery/offline-branch.ts +4 -1
  354. package/src/safe-slice.ts +9 -1
  355. package/src/silent-heartbeat.ts +184 -59
  356. package/src/tool-args.ts +213 -0
  357. package/src/tool-image-ledger.ts +153 -34
  358. package/src/tool-presentation.ts +233 -42
  359. package/src/tool-result-guard.ts +7 -8
  360. package/src/tool-result-history.ts +124 -20
  361. package/src/tune-out/coordinator.ts +259 -65
  362. package/src/types/agent.ts +44 -24
  363. package/src/types/events.ts +25 -0
  364. package/src/types/framework.ts +38 -7
  365. package/src/types/inference-log.ts +1 -1
  366. package/src/types/module.ts +79 -14
  367. package/src/types/trace.ts +103 -8
  368. package/src/usage/usage-tracker.ts +32 -8
  369. package/dist/test/activation-request-sanitize.test.d.ts +0 -2
  370. package/dist/test/activation-request-sanitize.test.d.ts.map +0 -1
  371. package/dist/test/activation-request-sanitize.test.js +0 -89
  372. package/dist/test/activation-request-sanitize.test.js.map +0 -1
  373. package/dist/test/agent-cache-ttl.test.d.ts +0 -2
  374. package/dist/test/agent-cache-ttl.test.d.ts.map +0 -1
  375. package/dist/test/agent-cache-ttl.test.js +0 -41
  376. package/dist/test/agent-cache-ttl.test.js.map +0 -1
  377. package/dist/test/agent-runtime-settings.test.d.ts +0 -2
  378. package/dist/test/agent-runtime-settings.test.d.ts.map +0 -1
  379. package/dist/test/agent-runtime-settings.test.js +0 -1598
  380. package/dist/test/agent-runtime-settings.test.js.map +0 -1
  381. package/dist/test/api-admin-gate.test.d.ts +0 -2
  382. package/dist/test/api-admin-gate.test.d.ts.map +0 -1
  383. package/dist/test/api-admin-gate.test.js +0 -116
  384. package/dist/test/api-admin-gate.test.js.map +0 -1
  385. package/dist/test/assistant-persist-refusal.test.d.ts +0 -2
  386. package/dist/test/assistant-persist-refusal.test.d.ts.map +0 -1
  387. package/dist/test/assistant-persist-refusal.test.js +0 -63
  388. package/dist/test/assistant-persist-refusal.test.js.map +0 -1
  389. package/dist/test/batched-wake-locus.test.d.ts +0 -2
  390. package/dist/test/batched-wake-locus.test.d.ts.map +0 -1
  391. package/dist/test/batched-wake-locus.test.js +0 -166
  392. package/dist/test/batched-wake-locus.test.js.map +0 -1
  393. package/dist/test/busy-agent-scheduler.test.d.ts +0 -2
  394. package/dist/test/busy-agent-scheduler.test.d.ts.map +0 -1
  395. package/dist/test/busy-agent-scheduler.test.js +0 -92
  396. package/dist/test/busy-agent-scheduler.test.js.map +0 -1
  397. package/dist/test/channel-incoming-targeting.test.d.ts +0 -2
  398. package/dist/test/channel-incoming-targeting.test.d.ts.map +0 -1
  399. package/dist/test/channel-incoming-targeting.test.js +0 -81
  400. package/dist/test/channel-incoming-targeting.test.js.map +0 -1
  401. package/dist/test/channel-label-history.test.d.ts +0 -2
  402. package/dist/test/channel-label-history.test.d.ts.map +0 -1
  403. package/dist/test/channel-label-history.test.js +0 -424
  404. package/dist/test/channel-label-history.test.js.map +0 -1
  405. package/dist/test/channel-lifecycle-provenance.test.d.ts +0 -2
  406. package/dist/test/channel-lifecycle-provenance.test.d.ts.map +0 -1
  407. package/dist/test/channel-lifecycle-provenance.test.js +0 -47
  408. package/dist/test/channel-lifecycle-provenance.test.js.map +0 -1
  409. package/dist/test/channel-registry-routing.test.d.ts +0 -2
  410. package/dist/test/channel-registry-routing.test.d.ts.map +0 -1
  411. package/dist/test/channel-registry-routing.test.js +0 -446
  412. package/dist/test/channel-registry-routing.test.js.map +0 -1
  413. package/dist/test/channel-registry-typing.test.d.ts +0 -2
  414. package/dist/test/channel-registry-typing.test.d.ts.map +0 -1
  415. package/dist/test/channel-registry-typing.test.js +0 -91
  416. package/dist/test/channel-registry-typing.test.js.map +0 -1
  417. package/dist/test/channels-changed-respond-first.test.d.ts +0 -2
  418. package/dist/test/channels-changed-respond-first.test.d.ts.map +0 -1
  419. package/dist/test/channels-changed-respond-first.test.js +0 -74
  420. package/dist/test/channels-changed-respond-first.test.js.map +0 -1
  421. package/dist/test/checkpoint-attribution.test.d.ts +0 -2
  422. package/dist/test/checkpoint-attribution.test.d.ts.map +0 -1
  423. package/dist/test/checkpoint-attribution.test.js +0 -200
  424. package/dist/test/checkpoint-attribution.test.js.map +0 -1
  425. package/dist/test/code-execution.test.d.ts +0 -11
  426. package/dist/test/code-execution.test.d.ts.map +0 -1
  427. package/dist/test/code-execution.test.js +0 -1272
  428. package/dist/test/code-execution.test.js.map +0 -1
  429. package/dist/test/component-defaults.test.d.ts +0 -2
  430. package/dist/test/component-defaults.test.d.ts.map +0 -1
  431. package/dist/test/component-defaults.test.js +0 -54
  432. package/dist/test/component-defaults.test.js.map +0 -1
  433. package/dist/test/conversation-fork-injection-scoping.test.d.ts +0 -2
  434. package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +0 -1
  435. package/dist/test/conversation-fork-injection-scoping.test.js +0 -200
  436. package/dist/test/conversation-fork-injection-scoping.test.js.map +0 -1
  437. package/dist/test/conversation-router.test.d.ts +0 -2
  438. package/dist/test/conversation-router.test.d.ts.map +0 -1
  439. package/dist/test/conversation-router.test.js +0 -174
  440. package/dist/test/conversation-router.test.js.map +0 -1
  441. package/dist/test/conversation-routing.test.d.ts +0 -7
  442. package/dist/test/conversation-routing.test.d.ts.map +0 -1
  443. package/dist/test/conversation-routing.test.js +0 -621
  444. package/dist/test/conversation-routing.test.js.map +0 -1
  445. package/dist/test/deferred-wake-integrity.test.d.ts +0 -29
  446. package/dist/test/deferred-wake-integrity.test.d.ts.map +0 -1
  447. package/dist/test/deferred-wake-integrity.test.js +0 -320
  448. package/dist/test/deferred-wake-integrity.test.js.map +0 -1
  449. package/dist/test/discord-awareness-outbox.test.d.ts +0 -2
  450. package/dist/test/discord-awareness-outbox.test.d.ts.map +0 -1
  451. package/dist/test/discord-awareness-outbox.test.js +0 -371
  452. package/dist/test/discord-awareness-outbox.test.js.map +0 -1
  453. package/dist/test/discord-history-delete-window.test.d.ts +0 -2
  454. package/dist/test/discord-history-delete-window.test.d.ts.map +0 -1
  455. package/dist/test/discord-history-delete-window.test.js +0 -81
  456. package/dist/test/discord-history-delete-window.test.js.map +0 -1
  457. package/dist/test/discord-history-sync.test.d.ts +0 -2
  458. package/dist/test/discord-history-sync.test.d.ts.map +0 -1
  459. package/dist/test/discord-history-sync.test.js +0 -56
  460. package/dist/test/discord-history-sync.test.js.map +0 -1
  461. package/dist/test/discord-typing-keepalive.test.d.ts +0 -2
  462. package/dist/test/discord-typing-keepalive.test.d.ts.map +0 -1
  463. package/dist/test/discord-typing-keepalive.test.js +0 -77
  464. package/dist/test/discord-typing-keepalive.test.js.map +0 -1
  465. package/dist/test/ephemeral-settle.test.d.ts +0 -2
  466. package/dist/test/ephemeral-settle.test.d.ts.map +0 -1
  467. package/dist/test/ephemeral-settle.test.js +0 -354
  468. package/dist/test/ephemeral-settle.test.js.map +0 -1
  469. package/dist/test/event-gate-passthrough.test.d.ts +0 -15
  470. package/dist/test/event-gate-passthrough.test.d.ts.map +0 -1
  471. package/dist/test/event-gate-passthrough.test.js +0 -289
  472. package/dist/test/event-gate-passthrough.test.js.map +0 -1
  473. package/dist/test/event-gate-policy-mutation.test.d.ts +0 -2
  474. package/dist/test/event-gate-policy-mutation.test.d.ts.map +0 -1
  475. package/dist/test/event-gate-policy-mutation.test.js +0 -137
  476. package/dist/test/event-gate-policy-mutation.test.js.map +0 -1
  477. package/dist/test/event-gate-rate-limit.test.d.ts +0 -2
  478. package/dist/test/event-gate-rate-limit.test.d.ts.map +0 -1
  479. package/dist/test/event-gate-rate-limit.test.js +0 -627
  480. package/dist/test/event-gate-rate-limit.test.js.map +0 -1
  481. package/dist/test/event-gate-script.test.d.ts +0 -2
  482. package/dist/test/event-gate-script.test.d.ts.map +0 -1
  483. package/dist/test/event-gate-script.test.js +0 -95
  484. package/dist/test/event-gate-script.test.js.map +0 -1
  485. package/dist/test/event-gate-selfwake.test.d.ts +0 -15
  486. package/dist/test/event-gate-selfwake.test.d.ts.map +0 -1
  487. package/dist/test/event-gate-selfwake.test.js +0 -125
  488. package/dist/test/event-gate-selfwake.test.js.map +0 -1
  489. package/dist/test/event-gate-tags.test.d.ts +0 -2
  490. package/dist/test/event-gate-tags.test.d.ts.map +0 -1
  491. package/dist/test/event-gate-tags.test.js +0 -76
  492. package/dist/test/event-gate-tags.test.js.map +0 -1
  493. package/dist/test/event-gate.test.d.ts +0 -2
  494. package/dist/test/event-gate.test.d.ts.map +0 -1
  495. package/dist/test/event-gate.test.js +0 -919
  496. package/dist/test/event-gate.test.js.map +0 -1
  497. package/dist/test/explicit-prose-routing.test.d.ts +0 -11
  498. package/dist/test/explicit-prose-routing.test.d.ts.map +0 -1
  499. package/dist/test/explicit-prose-routing.test.js +0 -1715
  500. package/dist/test/explicit-prose-routing.test.js.map +0 -1
  501. package/dist/test/framework.test.d.ts +0 -5
  502. package/dist/test/framework.test.d.ts.map +0 -1
  503. package/dist/test/framework.test.js +0 -1048
  504. package/dist/test/framework.test.js.map +0 -1
  505. package/dist/test/gather-context-timeout.test.d.ts +0 -2
  506. package/dist/test/gather-context-timeout.test.d.ts.map +0 -1
  507. package/dist/test/gather-context-timeout.test.js +0 -120
  508. package/dist/test/gather-context-timeout.test.js.map +0 -1
  509. package/dist/test/health-snapshot.test.d.ts +0 -2
  510. package/dist/test/health-snapshot.test.d.ts.map +0 -1
  511. package/dist/test/health-snapshot.test.js +0 -74
  512. package/dist/test/health-snapshot.test.js.map +0 -1
  513. package/dist/test/helpers/coalescing-fixture.d.ts +0 -74
  514. package/dist/test/helpers/coalescing-fixture.d.ts.map +0 -1
  515. package/dist/test/helpers/coalescing-fixture.js +0 -152
  516. package/dist/test/helpers/coalescing-fixture.js.map +0 -1
  517. package/dist/test/helpers/coalescing-kill-child.d.ts +0 -2
  518. package/dist/test/helpers/coalescing-kill-child.d.ts.map +0 -1
  519. package/dist/test/helpers/coalescing-kill-child.js +0 -24
  520. package/dist/test/helpers/coalescing-kill-child.js.map +0 -1
  521. package/dist/test/helpers/mock-membrane.d.ts +0 -53
  522. package/dist/test/helpers/mock-membrane.d.ts.map +0 -1
  523. package/dist/test/helpers/mock-membrane.js +0 -168
  524. package/dist/test/helpers/mock-membrane.js.map +0 -1
  525. package/dist/test/helpers/pytc-stop-rows.d.ts +0 -11
  526. package/dist/test/helpers/pytc-stop-rows.d.ts.map +0 -1
  527. package/dist/test/helpers/pytc-stop-rows.js +0 -143
  528. package/dist/test/helpers/pytc-stop-rows.js.map +0 -1
  529. package/dist/test/helpers/quiesce-crash-child.d.ts +0 -2
  530. package/dist/test/helpers/quiesce-crash-child.d.ts.map +0 -1
  531. package/dist/test/helpers/quiesce-crash-child.js +0 -156
  532. package/dist/test/helpers/quiesce-crash-child.js.map +0 -1
  533. package/dist/test/hide-then-turn.test.d.ts +0 -2
  534. package/dist/test/hide-then-turn.test.d.ts.map +0 -1
  535. package/dist/test/hide-then-turn.test.js +0 -101
  536. package/dist/test/hide-then-turn.test.js.map +0 -1
  537. package/dist/test/history-module-overview.test.d.ts +0 -2
  538. package/dist/test/history-module-overview.test.d.ts.map +0 -1
  539. package/dist/test/history-module-overview.test.js +0 -711
  540. package/dist/test/history-module-overview.test.js.map +0 -1
  541. package/dist/test/history-module-realstore.test.d.ts +0 -2
  542. package/dist/test/history-module-realstore.test.d.ts.map +0 -1
  543. package/dist/test/history-module-realstore.test.js +0 -363
  544. package/dist/test/history-module-realstore.test.js.map +0 -1
  545. package/dist/test/history-module-semantic.test.d.ts +0 -2
  546. package/dist/test/history-module-semantic.test.d.ts.map +0 -1
  547. package/dist/test/history-module-semantic.test.js +0 -213
  548. package/dist/test/history-module-semantic.test.js.map +0 -1
  549. package/dist/test/history-module-ux.test.d.ts +0 -2
  550. package/dist/test/history-module-ux.test.d.ts.map +0 -1
  551. package/dist/test/history-module-ux.test.js +0 -304
  552. package/dist/test/history-module-ux.test.js.map +0 -1
  553. package/dist/test/history-module.test.d.ts +0 -2
  554. package/dist/test/history-module.test.d.ts.map +0 -1
  555. package/dist/test/history-module.test.js +0 -619
  556. package/dist/test/history-module.test.js.map +0 -1
  557. package/dist/test/history-semantic-review-findings.test.d.ts +0 -2
  558. package/dist/test/history-semantic-review-findings.test.d.ts.map +0 -1
  559. package/dist/test/history-semantic-review-findings.test.js +0 -338
  560. package/dist/test/history-semantic-review-findings.test.js.map +0 -1
  561. package/dist/test/history-semantic-sync-converges.test.d.ts +0 -2
  562. package/dist/test/history-semantic-sync-converges.test.d.ts.map +0 -1
  563. package/dist/test/history-semantic-sync-converges.test.js +0 -109
  564. package/dist/test/history-semantic-sync-converges.test.js.map +0 -1
  565. package/dist/test/host-quiesce.test.d.ts +0 -2
  566. package/dist/test/host-quiesce.test.d.ts.map +0 -1
  567. package/dist/test/host-quiesce.test.js +0 -988
  568. package/dist/test/host-quiesce.test.js.map +0 -1
  569. package/dist/test/inference-exhaustion-rewind.test.d.ts +0 -2
  570. package/dist/test/inference-exhaustion-rewind.test.d.ts.map +0 -1
  571. package/dist/test/inference-exhaustion-rewind.test.js +0 -387
  572. package/dist/test/inference-exhaustion-rewind.test.js.map +0 -1
  573. package/dist/test/inference-failure-bounds.test.d.ts +0 -2
  574. package/dist/test/inference-failure-bounds.test.d.ts.map +0 -1
  575. package/dist/test/inference-failure-bounds.test.js +0 -1202
  576. package/dist/test/inference-failure-bounds.test.js.map +0 -1
  577. package/dist/test/inference-failure-observability.test.d.ts +0 -2
  578. package/dist/test/inference-failure-observability.test.d.ts.map +0 -1
  579. package/dist/test/inference-failure-observability.test.js +0 -84
  580. package/dist/test/inference-failure-observability.test.js.map +0 -1
  581. package/dist/test/journal-tool.test.d.ts +0 -2
  582. package/dist/test/journal-tool.test.d.ts.map +0 -1
  583. package/dist/test/journal-tool.test.js +0 -33
  584. package/dist/test/journal-tool.test.js.map +0 -1
  585. package/dist/test/kv-unified-open-flight-retry.test.d.ts +0 -2
  586. package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +0 -1
  587. package/dist/test/kv-unified-open-flight-retry.test.js +0 -189
  588. package/dist/test/kv-unified-open-flight-retry.test.js.map +0 -1
  589. package/dist/test/kv-unified-wiring.test.d.ts +0 -2
  590. package/dist/test/kv-unified-wiring.test.d.ts.map +0 -1
  591. package/dist/test/kv-unified-wiring.test.js +0 -95
  592. package/dist/test/kv-unified-wiring.test.js.map +0 -1
  593. package/dist/test/late-events-after-stop.test.d.ts +0 -2
  594. package/dist/test/late-events-after-stop.test.d.ts.map +0 -1
  595. package/dist/test/late-events-after-stop.test.js +0 -122
  596. package/dist/test/late-events-after-stop.test.js.map +0 -1
  597. package/dist/test/liveness-watchdog.test.d.ts +0 -2
  598. package/dist/test/liveness-watchdog.test.d.ts.map +0 -1
  599. package/dist/test/liveness-watchdog.test.js +0 -94
  600. package/dist/test/liveness-watchdog.test.js.map +0 -1
  601. package/dist/test/machine-close-provenance.test.d.ts +0 -2
  602. package/dist/test/machine-close-provenance.test.d.ts.map +0 -1
  603. package/dist/test/machine-close-provenance.test.js +0 -116
  604. package/dist/test/machine-close-provenance.test.js.map +0 -1
  605. package/dist/test/maintenance-timer.test.d.ts +0 -2
  606. package/dist/test/maintenance-timer.test.d.ts.map +0 -1
  607. package/dist/test/maintenance-timer.test.js +0 -1827
  608. package/dist/test/maintenance-timer.test.js.map +0 -1
  609. package/dist/test/mcp-server-bin.test.d.ts +0 -2
  610. package/dist/test/mcp-server-bin.test.d.ts.map +0 -1
  611. package/dist/test/mcp-server-bin.test.js +0 -142
  612. package/dist/test/mcp-server-bin.test.js.map +0 -1
  613. package/dist/test/mcpl-access-provider.test.d.ts +0 -2
  614. package/dist/test/mcpl-access-provider.test.d.ts.map +0 -1
  615. package/dist/test/mcpl-access-provider.test.js +0 -20
  616. package/dist/test/mcpl-access-provider.test.js.map +0 -1
  617. package/dist/test/mcpl-awareness-barrier.test.d.ts +0 -2
  618. package/dist/test/mcpl-awareness-barrier.test.d.ts.map +0 -1
  619. package/dist/test/mcpl-awareness-barrier.test.js +0 -769
  620. package/dist/test/mcpl-awareness-barrier.test.js.map +0 -1
  621. package/dist/test/mcpl-capability-grant.test.d.ts +0 -10
  622. package/dist/test/mcpl-capability-grant.test.d.ts.map +0 -1
  623. package/dist/test/mcpl-capability-grant.test.js +0 -111
  624. package/dist/test/mcpl-capability-grant.test.js.map +0 -1
  625. package/dist/test/mcpl-capability-scoping.test.d.ts +0 -2
  626. package/dist/test/mcpl-capability-scoping.test.d.ts.map +0 -1
  627. package/dist/test/mcpl-capability-scoping.test.js +0 -266
  628. package/dist/test/mcpl-capability-scoping.test.js.map +0 -1
  629. package/dist/test/mcpl-child-env.test.d.ts +0 -2
  630. package/dist/test/mcpl-child-env.test.d.ts.map +0 -1
  631. package/dist/test/mcpl-child-env.test.js +0 -158
  632. package/dist/test/mcpl-child-env.test.js.map +0 -1
  633. package/dist/test/mcpl-coalesced-content-parity.test.d.ts +0 -2
  634. package/dist/test/mcpl-coalesced-content-parity.test.d.ts.map +0 -1
  635. package/dist/test/mcpl-coalesced-content-parity.test.js +0 -532
  636. package/dist/test/mcpl-coalesced-content-parity.test.js.map +0 -1
  637. package/dist/test/mcpl-coalescing.test.d.ts +0 -2
  638. package/dist/test/mcpl-coalescing.test.d.ts.map +0 -1
  639. package/dist/test/mcpl-coalescing.test.js +0 -1125
  640. package/dist/test/mcpl-coalescing.test.js.map +0 -1
  641. package/dist/test/mcpl-config-diagnostics.test.d.ts +0 -2
  642. package/dist/test/mcpl-config-diagnostics.test.d.ts.map +0 -1
  643. package/dist/test/mcpl-config-diagnostics.test.js +0 -452
  644. package/dist/test/mcpl-config-diagnostics.test.js.map +0 -1
  645. package/dist/test/mcpl-empty-content.test.d.ts +0 -2
  646. package/dist/test/mcpl-empty-content.test.d.ts.map +0 -1
  647. package/dist/test/mcpl-empty-content.test.js +0 -368
  648. package/dist/test/mcpl-empty-content.test.js.map +0 -1
  649. package/dist/test/mcpl-gate-roundtrip.test.d.ts +0 -2
  650. package/dist/test/mcpl-gate-roundtrip.test.d.ts.map +0 -1
  651. package/dist/test/mcpl-gate-roundtrip.test.js +0 -285
  652. package/dist/test/mcpl-gate-roundtrip.test.js.map +0 -1
  653. package/dist/test/mcpl-list-status.test.d.ts +0 -2
  654. package/dist/test/mcpl-list-status.test.d.ts.map +0 -1
  655. package/dist/test/mcpl-list-status.test.js +0 -82
  656. package/dist/test/mcpl-list-status.test.js.map +0 -1
  657. package/dist/test/mcpl-manifest-changed.test.d.ts +0 -2
  658. package/dist/test/mcpl-manifest-changed.test.d.ts.map +0 -1
  659. package/dist/test/mcpl-manifest-changed.test.js +0 -178
  660. package/dist/test/mcpl-manifest-changed.test.js.map +0 -1
  661. package/dist/test/mcpl-push-convert.test.d.ts +0 -2
  662. package/dist/test/mcpl-push-convert.test.d.ts.map +0 -1
  663. package/dist/test/mcpl-push-convert.test.js +0 -42
  664. package/dist/test/mcpl-push-convert.test.js.map +0 -1
  665. package/dist/test/mcpl-reconnect-events.test.d.ts +0 -2
  666. package/dist/test/mcpl-reconnect-events.test.d.ts.map +0 -1
  667. package/dist/test/mcpl-reconnect-events.test.js +0 -144
  668. package/dist/test/mcpl-reconnect-events.test.js.map +0 -1
  669. package/dist/test/mcpl-reconnect-reregistration.test.d.ts +0 -2
  670. package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +0 -1
  671. package/dist/test/mcpl-reconnect-reregistration.test.js +0 -238
  672. package/dist/test/mcpl-reconnect-reregistration.test.js.map +0 -1
  673. package/dist/test/mcpl-reference-fetcher.test.d.ts +0 -2
  674. package/dist/test/mcpl-reference-fetcher.test.d.ts.map +0 -1
  675. package/dist/test/mcpl-reference-fetcher.test.js +0 -427
  676. package/dist/test/mcpl-reference-fetcher.test.js.map +0 -1
  677. package/dist/test/mcpl-reference-fields.test.d.ts +0 -2
  678. package/dist/test/mcpl-reference-fields.test.d.ts.map +0 -1
  679. package/dist/test/mcpl-reference-fields.test.js +0 -149
  680. package/dist/test/mcpl-reference-fields.test.js.map +0 -1
  681. package/dist/test/mcpl-rejection-log-lines.test.d.ts +0 -2
  682. package/dist/test/mcpl-rejection-log-lines.test.d.ts.map +0 -1
  683. package/dist/test/mcpl-rejection-log-lines.test.js +0 -349
  684. package/dist/test/mcpl-rejection-log-lines.test.js.map +0 -1
  685. package/dist/test/mcpl-request-timeout.test.d.ts +0 -2
  686. package/dist/test/mcpl-request-timeout.test.d.ts.map +0 -1
  687. package/dist/test/mcpl-request-timeout.test.js +0 -138
  688. package/dist/test/mcpl-request-timeout.test.js.map +0 -1
  689. package/dist/test/mcpl-runtime-lifecycle.test.d.ts +0 -7
  690. package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +0 -1
  691. package/dist/test/mcpl-runtime-lifecycle.test.js +0 -142
  692. package/dist/test/mcpl-runtime-lifecycle.test.js.map +0 -1
  693. package/dist/test/mcpl-stderr-tail.test.d.ts +0 -2
  694. package/dist/test/mcpl-stderr-tail.test.d.ts.map +0 -1
  695. package/dist/test/mcpl-stderr-tail.test.js +0 -210
  696. package/dist/test/mcpl-stderr-tail.test.js.map +0 -1
  697. package/dist/test/mcpl-tool-class-listing.test.d.ts +0 -2
  698. package/dist/test/mcpl-tool-class-listing.test.d.ts.map +0 -1
  699. package/dist/test/mcpl-tool-class-listing.test.js +0 -147
  700. package/dist/test/mcpl-tool-class-listing.test.js.map +0 -1
  701. package/dist/test/mcpl-tool-classes.test.d.ts +0 -7
  702. package/dist/test/mcpl-tool-classes.test.d.ts.map +0 -1
  703. package/dist/test/mcpl-tool-classes.test.js +0 -80
  704. package/dist/test/mcpl-tool-classes.test.js.map +0 -1
  705. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts +0 -2
  706. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts.map +0 -1
  707. package/dist/test/mcpl-tool-lifecycle-e2e.test.js +0 -237
  708. package/dist/test/mcpl-tool-lifecycle-e2e.test.js.map +0 -1
  709. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts +0 -11
  710. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts.map +0 -1
  711. package/dist/test/mcpl-tool-lifecycle-emitter.test.js +0 -412
  712. package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +0 -1
  713. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +0 -2
  714. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +0 -1
  715. package/dist/test/mcpl-tool-lifecycle-script.test.js +0 -288
  716. package/dist/test/mcpl-tool-lifecycle-script.test.js.map +0 -1
  717. package/dist/test/mcpl-tool-lifecycle.test.d.ts +0 -11
  718. package/dist/test/mcpl-tool-lifecycle.test.d.ts.map +0 -1
  719. package/dist/test/mcpl-tool-lifecycle.test.js +0 -270
  720. package/dist/test/mcpl-tool-lifecycle.test.js.map +0 -1
  721. package/dist/test/mcpl-transport-close.test.d.ts +0 -2
  722. package/dist/test/mcpl-transport-close.test.d.ts.map +0 -1
  723. package/dist/test/mcpl-transport-close.test.js +0 -373
  724. package/dist/test/mcpl-transport-close.test.js.map +0 -1
  725. package/dist/test/mcpl-transport-spawn.test.d.ts +0 -2
  726. package/dist/test/mcpl-transport-spawn.test.d.ts.map +0 -1
  727. package/dist/test/mcpl-transport-spawn.test.js +0 -58
  728. package/dist/test/mcpl-transport-spawn.test.js.map +0 -1
  729. package/dist/test/mcpl-ws-transport.test.d.ts +0 -2
  730. package/dist/test/mcpl-ws-transport.test.d.ts.map +0 -1
  731. package/dist/test/mcpl-ws-transport.test.js +0 -259
  732. package/dist/test/mcpl-ws-transport.test.js.map +0 -1
  733. package/dist/test/module-context-integration.test.d.ts +0 -18
  734. package/dist/test/module-context-integration.test.d.ts.map +0 -1
  735. package/dist/test/module-context-integration.test.js +0 -181
  736. package/dist/test/module-context-integration.test.js.map +0 -1
  737. package/dist/test/offline-recovery-branch.test.d.ts +0 -2
  738. package/dist/test/offline-recovery-branch.test.d.ts.map +0 -1
  739. package/dist/test/offline-recovery-branch.test.js +0 -570
  740. package/dist/test/offline-recovery-branch.test.js.map +0 -1
  741. package/dist/test/operator-surgery.test.d.ts +0 -2
  742. package/dist/test/operator-surgery.test.d.ts.map +0 -1
  743. package/dist/test/operator-surgery.test.js +0 -595
  744. package/dist/test/operator-surgery.test.js.map +0 -1
  745. package/dist/test/ops-alert.test.d.ts +0 -2
  746. package/dist/test/ops-alert.test.d.ts.map +0 -1
  747. package/dist/test/ops-alert.test.js +0 -153
  748. package/dist/test/ops-alert.test.js.map +0 -1
  749. package/dist/test/overbudget-drain-breaker.test.d.ts +0 -2
  750. package/dist/test/overbudget-drain-breaker.test.d.ts.map +0 -1
  751. package/dist/test/overbudget-drain-breaker.test.js +0 -198
  752. package/dist/test/overbudget-drain-breaker.test.js.map +0 -1
  753. package/dist/test/pack-installs-alone.test.d.ts +0 -2
  754. package/dist/test/pack-installs-alone.test.d.ts.map +0 -1
  755. package/dist/test/pack-installs-alone.test.js +0 -284
  756. package/dist/test/pack-installs-alone.test.js.map +0 -1
  757. package/dist/test/per-agent-delivery.test.d.ts +0 -2
  758. package/dist/test/per-agent-delivery.test.d.ts.map +0 -1
  759. package/dist/test/per-agent-delivery.test.js +0 -108
  760. package/dist/test/per-agent-delivery.test.js.map +0 -1
  761. package/dist/test/physical-window-restart.test.d.ts +0 -16
  762. package/dist/test/physical-window-restart.test.d.ts.map +0 -1
  763. package/dist/test/physical-window-restart.test.js +0 -487
  764. package/dist/test/physical-window-restart.test.js.map +0 -1
  765. package/dist/test/placed-reaction-baseline.test.d.ts +0 -2
  766. package/dist/test/placed-reaction-baseline.test.d.ts.map +0 -1
  767. package/dist/test/placed-reaction-baseline.test.js +0 -312
  768. package/dist/test/placed-reaction-baseline.test.js.map +0 -1
  769. package/dist/test/present-while-acting.test.d.ts +0 -19
  770. package/dist/test/present-while-acting.test.d.ts.map +0 -1
  771. package/dist/test/present-while-acting.test.js +0 -1649
  772. package/dist/test/present-while-acting.test.js.map +0 -1
  773. package/dist/test/prose-segments.test.d.ts +0 -2
  774. package/dist/test/prose-segments.test.d.ts.map +0 -1
  775. package/dist/test/prose-segments.test.js +0 -52
  776. package/dist/test/prose-segments.test.js.map +0 -1
  777. package/dist/test/prose-stream-router.test.d.ts +0 -2
  778. package/dist/test/prose-stream-router.test.d.ts.map +0 -1
  779. package/dist/test/prose-stream-router.test.js +0 -171
  780. package/dist/test/prose-stream-router.test.js.map +0 -1
  781. package/dist/test/provider-acceleration-cooldown.test.d.ts +0 -2
  782. package/dist/test/provider-acceleration-cooldown.test.d.ts.map +0 -1
  783. package/dist/test/provider-acceleration-cooldown.test.js +0 -435
  784. package/dist/test/provider-acceleration-cooldown.test.js.map +0 -1
  785. package/dist/test/provider-waits.test.d.ts +0 -2
  786. package/dist/test/provider-waits.test.d.ts.map +0 -1
  787. package/dist/test/provider-waits.test.js +0 -2259
  788. package/dist/test/provider-waits.test.js.map +0 -1
  789. package/dist/test/puppet-tool-call.test.d.ts +0 -2
  790. package/dist/test/puppet-tool-call.test.d.ts.map +0 -1
  791. package/dist/test/puppet-tool-call.test.js +0 -226
  792. package/dist/test/puppet-tool-call.test.js.map +0 -1
  793. package/dist/test/puppet-turn-reservation.test.d.ts +0 -2
  794. package/dist/test/puppet-turn-reservation.test.d.ts.map +0 -1
  795. package/dist/test/puppet-turn-reservation.test.js +0 -139
  796. package/dist/test/puppet-turn-reservation.test.js.map +0 -1
  797. package/dist/test/reaction-baseline-platform.test.d.ts +0 -2
  798. package/dist/test/reaction-baseline-platform.test.d.ts.map +0 -1
  799. package/dist/test/reaction-baseline-platform.test.js +0 -124
  800. package/dist/test/reaction-baseline-platform.test.js.map +0 -1
  801. package/dist/test/record-journal.test.d.ts +0 -6
  802. package/dist/test/record-journal.test.d.ts.map +0 -1
  803. package/dist/test/record-journal.test.js +0 -269
  804. package/dist/test/record-journal.test.js.map +0 -1
  805. package/dist/test/refusal-reaction-baseline.test.d.ts +0 -2
  806. package/dist/test/refusal-reaction-baseline.test.d.ts.map +0 -1
  807. package/dist/test/refusal-reaction-baseline.test.js +0 -81
  808. package/dist/test/refusal-reaction-baseline.test.js.map +0 -1
  809. package/dist/test/refusal-rewind.test.d.ts +0 -2
  810. package/dist/test/refusal-rewind.test.d.ts.map +0 -1
  811. package/dist/test/refusal-rewind.test.js +0 -117
  812. package/dist/test/refusal-rewind.test.js.map +0 -1
  813. package/dist/test/refusal-single-call.test.d.ts +0 -2
  814. package/dist/test/refusal-single-call.test.d.ts.map +0 -1
  815. package/dist/test/refusal-single-call.test.js +0 -177
  816. package/dist/test/refusal-single-call.test.js.map +0 -1
  817. package/dist/test/release-bump.test.d.ts +0 -2
  818. package/dist/test/release-bump.test.d.ts.map +0 -1
  819. package/dist/test/release-bump.test.js +0 -248
  820. package/dist/test/release-bump.test.js.map +0 -1
  821. package/dist/test/release-changelog.test.d.ts +0 -2
  822. package/dist/test/release-changelog.test.d.ts.map +0 -1
  823. package/dist/test/release-changelog.test.js +0 -159
  824. package/dist/test/release-changelog.test.js.map +0 -1
  825. package/dist/test/release-holds.test.d.ts +0 -2
  826. package/dist/test/release-holds.test.d.ts.map +0 -1
  827. package/dist/test/release-holds.test.js +0 -456
  828. package/dist/test/release-holds.test.js.map +0 -1
  829. package/dist/test/same-round-think-policy.test.d.ts +0 -2
  830. package/dist/test/same-round-think-policy.test.d.ts.map +0 -1
  831. package/dist/test/same-round-think-policy.test.js +0 -853
  832. package/dist/test/same-round-think-policy.test.js.map +0 -1
  833. package/dist/test/save-recent-image.test.d.ts +0 -26
  834. package/dist/test/save-recent-image.test.d.ts.map +0 -1
  835. package/dist/test/save-recent-image.test.js +0 -974
  836. package/dist/test/save-recent-image.test.js.map +0 -1
  837. package/dist/test/shutdown-errors.test.d.ts +0 -2
  838. package/dist/test/shutdown-errors.test.d.ts.map +0 -1
  839. package/dist/test/shutdown-errors.test.js +0 -662
  840. package/dist/test/shutdown-errors.test.js.map +0 -1
  841. package/dist/test/silent-heartbeat-rows.test.d.ts +0 -2
  842. package/dist/test/silent-heartbeat-rows.test.d.ts.map +0 -1
  843. package/dist/test/silent-heartbeat-rows.test.js +0 -451
  844. package/dist/test/silent-heartbeat-rows.test.js.map +0 -1
  845. package/dist/test/silent-heartbeat.test.d.ts +0 -2
  846. package/dist/test/silent-heartbeat.test.d.ts.map +0 -1
  847. package/dist/test/silent-heartbeat.test.js +0 -165
  848. package/dist/test/silent-heartbeat.test.js.map +0 -1
  849. package/dist/test/state-scaling-e2e.test.d.ts +0 -22
  850. package/dist/test/state-scaling-e2e.test.d.ts.map +0 -1
  851. package/dist/test/state-scaling-e2e.test.js +0 -206
  852. package/dist/test/state-scaling-e2e.test.js.map +0 -1
  853. package/dist/test/state-scaling.test.d.ts +0 -12
  854. package/dist/test/state-scaling.test.d.ts.map +0 -1
  855. package/dist/test/state-scaling.test.js +0 -251
  856. package/dist/test/state-scaling.test.js.map +0 -1
  857. package/dist/test/stop-with-host-operations.test.d.ts +0 -20
  858. package/dist/test/stop-with-host-operations.test.d.ts.map +0 -1
  859. package/dist/test/stop-with-host-operations.test.js +0 -1174
  860. package/dist/test/stop-with-host-operations.test.js.map +0 -1
  861. package/dist/test/stop-with-work-in-flight.test.d.ts +0 -23
  862. package/dist/test/stop-with-work-in-flight.test.d.ts.map +0 -1
  863. package/dist/test/stop-with-work-in-flight.test.js +0 -784
  864. package/dist/test/stop-with-work-in-flight.test.js.map +0 -1
  865. package/dist/test/surgery-marks-receipt.test.d.ts +0 -2
  866. package/dist/test/surgery-marks-receipt.test.d.ts.map +0 -1
  867. package/dist/test/surgery-marks-receipt.test.js +0 -540
  868. package/dist/test/surgery-marks-receipt.test.js.map +0 -1
  869. package/dist/test/thinking-token-stamp.test.d.ts +0 -12
  870. package/dist/test/thinking-token-stamp.test.d.ts.map +0 -1
  871. package/dist/test/thinking-token-stamp.test.js +0 -129
  872. package/dist/test/thinking-token-stamp.test.js.map +0 -1
  873. package/dist/test/timezone.test.d.ts +0 -2
  874. package/dist/test/timezone.test.d.ts.map +0 -1
  875. package/dist/test/timezone.test.js +0 -18
  876. package/dist/test/timezone.test.js.map +0 -1
  877. package/dist/test/tool-batch-hook.test.d.ts +0 -8
  878. package/dist/test/tool-batch-hook.test.d.ts.map +0 -1
  879. package/dist/test/tool-batch-hook.test.js +0 -275
  880. package/dist/test/tool-batch-hook.test.js.map +0 -1
  881. package/dist/test/tool-policy.test.d.ts +0 -2
  882. package/dist/test/tool-policy.test.d.ts.map +0 -1
  883. package/dist/test/tool-policy.test.js +0 -51
  884. package/dist/test/tool-policy.test.js.map +0 -1
  885. package/dist/test/tool-presentation.test.d.ts +0 -2
  886. package/dist/test/tool-presentation.test.d.ts.map +0 -1
  887. package/dist/test/tool-presentation.test.js +0 -268
  888. package/dist/test/tool-presentation.test.js.map +0 -1
  889. package/dist/test/tool-result-content.test.d.ts +0 -2
  890. package/dist/test/tool-result-content.test.d.ts.map +0 -1
  891. package/dist/test/tool-result-content.test.js +0 -37
  892. package/dist/test/tool-result-content.test.js.map +0 -1
  893. package/dist/test/tool-result-guard.test.d.ts +0 -2
  894. package/dist/test/tool-result-guard.test.d.ts.map +0 -1
  895. package/dist/test/tool-result-guard.test.js +0 -3729
  896. package/dist/test/tool-result-guard.test.js.map +0 -1
  897. package/dist/test/tool-result-history.test.d.ts +0 -7
  898. package/dist/test/tool-result-history.test.d.ts.map +0 -1
  899. package/dist/test/tool-result-history.test.js +0 -156
  900. package/dist/test/tool-result-history.test.js.map +0 -1
  901. package/dist/test/tool-result-spill.test.d.ts +0 -13
  902. package/dist/test/tool-result-spill.test.d.ts.map +0 -1
  903. package/dist/test/tool-result-spill.test.js +0 -586
  904. package/dist/test/tool-result-spill.test.js.map +0 -1
  905. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts +0 -2
  906. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts.map +0 -1
  907. package/dist/test/tool-wrapper-prose-guard-integration.test.js +0 -485
  908. package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +0 -1
  909. package/dist/test/tool-wrapper-prose-guard.test.d.ts +0 -2
  910. package/dist/test/tool-wrapper-prose-guard.test.d.ts.map +0 -1
  911. package/dist/test/tool-wrapper-prose-guard.test.js +0 -34
  912. package/dist/test/tool-wrapper-prose-guard.test.js.map +0 -1
  913. package/dist/test/tools-observe-parser-conformance.test.d.ts +0 -2
  914. package/dist/test/tools-observe-parser-conformance.test.d.ts.map +0 -1
  915. package/dist/test/tools-observe-parser-conformance.test.js +0 -129
  916. package/dist/test/tools-observe-parser-conformance.test.js.map +0 -1
  917. package/dist/test/trunk-channel-routing.test.d.ts +0 -19
  918. package/dist/test/trunk-channel-routing.test.d.ts.map +0 -1
  919. package/dist/test/trunk-channel-routing.test.js +0 -652
  920. package/dist/test/trunk-channel-routing.test.js.map +0 -1
  921. package/dist/test/tune-out-e2e.test.d.ts +0 -2
  922. package/dist/test/tune-out-e2e.test.d.ts.map +0 -1
  923. package/dist/test/tune-out-e2e.test.js +0 -142
  924. package/dist/test/tune-out-e2e.test.js.map +0 -1
  925. package/dist/test/tune-out-gate-composition.test.d.ts +0 -2
  926. package/dist/test/tune-out-gate-composition.test.d.ts.map +0 -1
  927. package/dist/test/tune-out-gate-composition.test.js +0 -221
  928. package/dist/test/tune-out-gate-composition.test.js.map +0 -1
  929. package/dist/test/tune-out-lifecycle.test.d.ts +0 -2
  930. package/dist/test/tune-out-lifecycle.test.d.ts.map +0 -1
  931. package/dist/test/tune-out-lifecycle.test.js +0 -121
  932. package/dist/test/tune-out-lifecycle.test.js.map +0 -1
  933. package/dist/test/turn-trigger-provenance.test.d.ts +0 -2
  934. package/dist/test/turn-trigger-provenance.test.d.ts.map +0 -1
  935. package/dist/test/turn-trigger-provenance.test.js +0 -137
  936. package/dist/test/turn-trigger-provenance.test.js.map +0 -1
  937. package/dist/test/unconvertible-tool-result.test.d.ts +0 -2
  938. package/dist/test/unconvertible-tool-result.test.d.ts.map +0 -1
  939. package/dist/test/unconvertible-tool-result.test.js +0 -358
  940. package/dist/test/unconvertible-tool-result.test.js.map +0 -1
  941. package/dist/test/unsent-compiles.test.d.ts +0 -2
  942. package/dist/test/unsent-compiles.test.d.ts.map +0 -1
  943. package/dist/test/unsent-compiles.test.js +0 -139
  944. package/dist/test/unsent-compiles.test.js.map +0 -1
  945. package/dist/test/usage-accounting.test.d.ts +0 -2
  946. package/dist/test/usage-accounting.test.d.ts.map +0 -1
  947. package/dist/test/usage-accounting.test.js +0 -13
  948. package/dist/test/usage-accounting.test.js.map +0 -1
  949. package/dist/test/usage-tracker.test.d.ts +0 -2
  950. package/dist/test/usage-tracker.test.d.ts.map +0 -1
  951. package/dist/test/usage-tracker.test.js +0 -129
  952. package/dist/test/usage-tracker.test.js.map +0 -1
  953. package/dist/test/utils-surface.test.d.ts +0 -2
  954. package/dist/test/utils-surface.test.d.ts.map +0 -1
  955. package/dist/test/utils-surface.test.js +0 -145
  956. package/dist/test/utils-surface.test.js.map +0 -1
  957. package/dist/test/workspace-branch-continuation.test.d.ts +0 -15
  958. package/dist/test/workspace-branch-continuation.test.d.ts.map +0 -1
  959. package/dist/test/workspace-branch-continuation.test.js +0 -183
  960. package/dist/test/workspace-branch-continuation.test.js.map +0 -1
  961. package/dist/test/workspace-materialize-freshness.test.d.ts +0 -16
  962. package/dist/test/workspace-materialize-freshness.test.d.ts.map +0 -1
  963. package/dist/test/workspace-materialize-freshness.test.js +0 -252
  964. package/dist/test/workspace-materialize-freshness.test.js.map +0 -1
  965. package/dist/test/workspace-path-containment.test.d.ts +0 -19
  966. package/dist/test/workspace-path-containment.test.d.ts.map +0 -1
  967. package/dist/test/workspace-path-containment.test.js +0 -91
  968. package/dist/test/workspace-path-containment.test.js.map +0 -1
  969. package/dist/test/workspace-read-file-from-disk.test.d.ts +0 -17
  970. package/dist/test/workspace-read-file-from-disk.test.d.ts.map +0 -1
  971. package/dist/test/workspace-read-file-from-disk.test.js +0 -174
  972. package/dist/test/workspace-read-file-from-disk.test.js.map +0 -1
  973. package/dist/test/workspace-read-image.test.d.ts +0 -2
  974. package/dist/test/workspace-read-image.test.d.ts.map +0 -1
  975. package/dist/test/workspace-read-image.test.js +0 -597
  976. package/dist/test/workspace-read-image.test.js.map +0 -1
  977. package/dist/test/workspace-read-text.test.d.ts +0 -2
  978. package/dist/test/workspace-read-text.test.d.ts.map +0 -1
  979. package/dist/test/workspace-read-text.test.js +0 -101
  980. package/dist/test/workspace-read-text.test.js.map +0 -1
  981. package/dist/test/workspace-restore-order.test.d.ts +0 -16
  982. package/dist/test/workspace-restore-order.test.d.ts.map +0 -1
  983. package/dist/test/workspace-restore-order.test.js +0 -140
  984. package/dist/test/workspace-restore-order.test.js.map +0 -1
  985. package/dist/test/workspace-three-way.test.d.ts +0 -12
  986. package/dist/test/workspace-three-way.test.d.ts.map +0 -1
  987. package/dist/test/workspace-three-way.test.js +0 -4094
  988. package/dist/test/workspace-three-way.test.js.map +0 -1
  989. package/dist/test/workspace-watcher.test.d.ts +0 -2
  990. package/dist/test/workspace-watcher.test.d.ts.map +0 -1
  991. package/dist/test/workspace-watcher.test.js +0 -281
  992. package/dist/test/workspace-watcher.test.js.map +0 -1
  993. package/dist/test/xml-round-persist.test.d.ts +0 -19
  994. package/dist/test/xml-round-persist.test.d.ts.map +0 -1
  995. package/dist/test/xml-round-persist.test.js +0 -235
  996. package/dist/test/xml-round-persist.test.js.map +0 -1
  997. 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