@animalabs/agent-framework 0.23.2 → 0.24.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 (338) hide show
  1. package/README.md +212 -42
  2. package/dist/src/agent.d.ts +60 -9
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +92 -12
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/api/server.d.ts.map +1 -1
  7. package/dist/src/api/server.js +14 -12
  8. package/dist/src/api/server.js.map +1 -1
  9. package/dist/src/code-execution/engine.d.ts +177 -0
  10. package/dist/src/code-execution/engine.d.ts.map +1 -0
  11. package/dist/src/code-execution/engine.js +494 -0
  12. package/dist/src/code-execution/engine.js.map +1 -0
  13. package/dist/src/code-execution/index.d.ts +5 -10
  14. package/dist/src/code-execution/index.d.ts.map +1 -1
  15. package/dist/src/code-execution/index.js +5 -9
  16. package/dist/src/code-execution/index.js.map +1 -1
  17. package/dist/src/code-execution/isolate/calls.d.ts +114 -0
  18. package/dist/src/code-execution/isolate/calls.d.ts.map +1 -0
  19. package/dist/src/code-execution/isolate/calls.js +145 -0
  20. package/dist/src/code-execution/isolate/calls.js.map +1 -0
  21. package/dist/src/code-execution/isolate/child-entry.d.ts +2 -0
  22. package/dist/src/code-execution/isolate/child-entry.d.ts.map +1 -0
  23. package/dist/src/code-execution/isolate/child-entry.js +5 -0
  24. package/dist/src/code-execution/isolate/child-entry.js.map +1 -0
  25. package/dist/src/code-execution/isolate/child-main.d.ts +26 -0
  26. package/dist/src/code-execution/isolate/child-main.d.ts.map +1 -0
  27. package/dist/src/code-execution/isolate/child-main.js +132 -0
  28. package/dist/src/code-execution/isolate/child-main.js.map +1 -0
  29. package/dist/src/code-execution/isolate/child.d.ts +44 -0
  30. package/dist/src/code-execution/isolate/child.d.ts.map +1 -0
  31. package/dist/src/code-execution/isolate/child.js +315 -0
  32. package/dist/src/code-execution/isolate/child.js.map +1 -0
  33. package/dist/src/code-execution/isolate/frames.d.ts +80 -0
  34. package/dist/src/code-execution/isolate/frames.d.ts.map +1 -0
  35. package/dist/src/code-execution/isolate/frames.js +210 -0
  36. package/dist/src/code-execution/isolate/frames.js.map +1 -0
  37. package/dist/src/code-execution/isolate/output.d.ts +68 -0
  38. package/dist/src/code-execution/isolate/output.d.ts.map +1 -0
  39. package/dist/src/code-execution/isolate/output.js +143 -0
  40. package/dist/src/code-execution/isolate/output.js.map +1 -0
  41. package/dist/src/code-execution/isolate/prelude.d.ts +109 -0
  42. package/dist/src/code-execution/isolate/prelude.d.ts.map +1 -0
  43. package/dist/src/code-execution/isolate/prelude.js +401 -0
  44. package/dist/src/code-execution/isolate/prelude.js.map +1 -0
  45. package/dist/src/code-execution/isolate/protocol.d.ts +151 -0
  46. package/dist/src/code-execution/isolate/protocol.d.ts.map +1 -0
  47. package/dist/src/code-execution/isolate/protocol.js +150 -0
  48. package/dist/src/code-execution/isolate/protocol.js.map +1 -0
  49. package/dist/src/code-execution/isolate/render.d.ts +57 -0
  50. package/dist/src/code-execution/isolate/render.d.ts.map +1 -0
  51. package/dist/src/code-execution/isolate/render.js +214 -0
  52. package/dist/src/code-execution/isolate/render.js.map +1 -0
  53. package/dist/src/code-execution/isolate/runner.d.ts +250 -0
  54. package/dist/src/code-execution/isolate/runner.d.ts.map +1 -0
  55. package/dist/src/code-execution/isolate/runner.js +854 -0
  56. package/dist/src/code-execution/isolate/runner.js.map +1 -0
  57. package/dist/src/code-execution/isolate/scripts.d.ts +175 -0
  58. package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -0
  59. package/dist/src/code-execution/isolate/scripts.js +357 -0
  60. package/dist/src/code-execution/isolate/scripts.js.map +1 -0
  61. package/dist/src/code-execution/quickjs.wasm +0 -0
  62. package/dist/src/code-execution/tool-definition.d.ts +93 -43
  63. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  64. package/dist/src/code-execution/tool-definition.js +176 -142
  65. package/dist/src/code-execution/tool-definition.js.map +1 -1
  66. package/dist/src/deadline-timer.d.ts +3 -0
  67. package/dist/src/deadline-timer.d.ts.map +1 -1
  68. package/dist/src/deadline-timer.js +1 -1
  69. package/dist/src/deadline-timer.js.map +1 -1
  70. package/dist/src/framework.d.ts +1027 -440
  71. package/dist/src/framework.d.ts.map +1 -1
  72. package/dist/src/framework.js +4093 -2238
  73. package/dist/src/framework.js.map +1 -1
  74. package/dist/src/gate/event-gate.d.ts +28 -1
  75. package/dist/src/gate/event-gate.d.ts.map +1 -1
  76. package/dist/src/gate/event-gate.js +88 -10
  77. package/dist/src/gate/event-gate.js.map +1 -1
  78. package/dist/src/held-writes.d.ts +16 -3
  79. package/dist/src/held-writes.d.ts.map +1 -1
  80. package/dist/src/held-writes.js +16 -3
  81. package/dist/src/held-writes.js.map +1 -1
  82. package/dist/src/index.d.ts +6 -6
  83. package/dist/src/index.d.ts.map +1 -1
  84. package/dist/src/index.js +9 -8
  85. package/dist/src/index.js.map +1 -1
  86. package/dist/src/mcpl/channel-registry.d.ts +471 -75
  87. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  88. package/dist/src/mcpl/channel-registry.js +1673 -337
  89. package/dist/src/mcpl/channel-registry.js.map +1 -1
  90. package/dist/src/mcpl/conversation-router.d.ts +12 -7
  91. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  92. package/dist/src/mcpl/conversation-router.js +12 -7
  93. package/dist/src/mcpl/conversation-router.js.map +1 -1
  94. package/dist/src/mcpl/convert-block.d.ts.map +1 -1
  95. package/dist/src/mcpl/convert-block.js +9 -5
  96. package/dist/src/mcpl/convert-block.js.map +1 -1
  97. package/dist/src/mcpl/inbound-source.d.ts +50 -0
  98. package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
  99. package/dist/src/mcpl/inbound-source.js +130 -0
  100. package/dist/src/mcpl/inbound-source.js.map +1 -1
  101. package/dist/src/mcpl/index.d.ts +2 -1
  102. package/dist/src/mcpl/index.d.ts.map +1 -1
  103. package/dist/src/mcpl/index.js +3 -2
  104. package/dist/src/mcpl/index.js.map +1 -1
  105. package/dist/src/mcpl/log-value.d.ts +6 -0
  106. package/dist/src/mcpl/log-value.d.ts.map +1 -1
  107. package/dist/src/mcpl/log-value.js +8 -0
  108. package/dist/src/mcpl/log-value.js.map +1 -1
  109. package/dist/src/mcpl/modern-connection.d.ts +15 -6
  110. package/dist/src/mcpl/modern-connection.d.ts.map +1 -1
  111. package/dist/src/mcpl/modern-connection.js +168 -53
  112. package/dist/src/mcpl/modern-connection.js.map +1 -1
  113. package/dist/src/mcpl/prose-grammar.d.ts +4 -5
  114. package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
  115. package/dist/src/mcpl/prose-grammar.js +4 -5
  116. package/dist/src/mcpl/prose-grammar.js.map +1 -1
  117. package/dist/src/mcpl/protocol-family.d.ts +97 -10
  118. package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
  119. package/dist/src/mcpl/protocol-family.js +173 -16
  120. package/dist/src/mcpl/protocol-family.js.map +1 -1
  121. package/dist/src/mcpl/push-coalescer.d.ts +132 -14
  122. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  123. package/dist/src/mcpl/push-coalescer.js +227 -50
  124. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  125. package/dist/src/mcpl/push-handler.d.ts +6 -1
  126. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  127. package/dist/src/mcpl/push-handler.js +4 -1
  128. package/dist/src/mcpl/push-handler.js.map +1 -1
  129. package/dist/src/mcpl/reference-fetcher.d.ts +2 -2
  130. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  131. package/dist/src/mcpl/reference-fetcher.js +4 -4
  132. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  133. package/dist/src/mcpl/server-connection.d.ts +50 -21
  134. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  135. package/dist/src/mcpl/server-connection.js +158 -74
  136. package/dist/src/mcpl/server-connection.js.map +1 -1
  137. package/dist/src/mcpl/tags.d.ts +24 -0
  138. package/dist/src/mcpl/tags.d.ts.map +1 -1
  139. package/dist/src/mcpl/tags.js +33 -0
  140. package/dist/src/mcpl/tags.js.map +1 -1
  141. package/dist/src/mcpl/tool-result-normalize.d.ts +2 -1
  142. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
  143. package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
  144. package/dist/src/mcpl/transport.d.ts +55 -7
  145. package/dist/src/mcpl/transport.d.ts.map +1 -1
  146. package/dist/src/mcpl/transport.js +163 -34
  147. package/dist/src/mcpl/transport.js.map +1 -1
  148. package/dist/src/mcpl/types.d.ts +22 -6
  149. package/dist/src/mcpl/types.d.ts.map +1 -1
  150. package/dist/src/mcpl/types.js.map +1 -1
  151. package/dist/src/module-registry.d.ts +4 -2
  152. package/dist/src/module-registry.d.ts.map +1 -1
  153. package/dist/src/module-registry.js +100 -13
  154. package/dist/src/module-registry.js.map +1 -1
  155. package/dist/src/modules/discord/index.d.ts.map +1 -1
  156. package/dist/src/modules/discord/index.js +8 -13
  157. package/dist/src/modules/discord/index.js.map +1 -1
  158. package/dist/src/modules/history/index.d.ts.map +1 -1
  159. package/dist/src/modules/history/index.js +27 -4
  160. package/dist/src/modules/history/index.js.map +1 -1
  161. package/dist/src/modules/history/semantic.d.ts +15 -3
  162. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  163. package/dist/src/modules/history/semantic.js +27 -5
  164. package/dist/src/modules/history/semantic.js.map +1 -1
  165. package/dist/src/modules/index.d.ts +1 -1
  166. package/dist/src/modules/index.d.ts.map +1 -1
  167. package/dist/src/modules/index.js.map +1 -1
  168. package/dist/src/modules/unknown-tool.d.ts +4 -1
  169. package/dist/src/modules/unknown-tool.d.ts.map +1 -1
  170. package/dist/src/modules/unknown-tool.js +4 -1
  171. package/dist/src/modules/unknown-tool.js.map +1 -1
  172. package/dist/src/modules/workspace/branch-intent.d.ts +3 -1
  173. package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
  174. package/dist/src/modules/workspace/branch-intent.js +3 -1
  175. package/dist/src/modules/workspace/branch-intent.js.map +1 -1
  176. package/dist/src/modules/workspace/edit-plan.d.ts +265 -0
  177. package/dist/src/modules/workspace/edit-plan.d.ts.map +1 -0
  178. package/dist/src/modules/workspace/edit-plan.js +794 -0
  179. package/dist/src/modules/workspace/edit-plan.js.map +1 -0
  180. package/dist/src/modules/workspace/edit-text.d.ts +117 -0
  181. package/dist/src/modules/workspace/edit-text.d.ts.map +1 -0
  182. package/dist/src/modules/workspace/edit-text.js +584 -0
  183. package/dist/src/modules/workspace/edit-text.js.map +1 -0
  184. package/dist/src/modules/workspace/effects.d.ts +5 -4
  185. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  186. package/dist/src/modules/workspace/effects.js +78 -25
  187. package/dist/src/modules/workspace/effects.js.map +1 -1
  188. package/dist/src/modules/workspace/index.d.ts +484 -61
  189. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  190. package/dist/src/modules/workspace/index.js +2173 -645
  191. package/dist/src/modules/workspace/index.js.map +1 -1
  192. package/dist/src/modules/workspace/observe.d.ts +213 -38
  193. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  194. package/dist/src/modules/workspace/observe.js +416 -36
  195. package/dist/src/modules/workspace/observe.js.map +1 -1
  196. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  197. package/dist/src/modules/workspace/reconcile.js +15 -8
  198. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  199. package/dist/src/modules/workspace/text.d.ts +32 -0
  200. package/dist/src/modules/workspace/text.d.ts.map +1 -0
  201. package/dist/src/modules/workspace/text.js +67 -0
  202. package/dist/src/modules/workspace/text.js.map +1 -0
  203. package/dist/src/modules/workspace/types.d.ts +210 -6
  204. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  205. package/dist/src/modules/workspace/types.js.map +1 -1
  206. package/dist/src/modules/workspace/watcher.d.ts +3 -2
  207. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  208. package/dist/src/modules/workspace/watcher.js.map +1 -1
  209. package/dist/src/operator-log.d.ts +52 -24
  210. package/dist/src/operator-log.d.ts.map +1 -1
  211. package/dist/src/operator-log.js.map +1 -1
  212. package/dist/src/recovery/discord-awareness-outbox.d.ts +496 -72
  213. package/dist/src/recovery/discord-awareness-outbox.d.ts.map +1 -1
  214. package/dist/src/recovery/discord-awareness-outbox.js +1196 -289
  215. package/dist/src/recovery/discord-awareness-outbox.js.map +1 -1
  216. package/dist/src/recovery/offline-branch.d.ts +26 -1
  217. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  218. package/dist/src/recovery/offline-branch.js +164 -39
  219. package/dist/src/recovery/offline-branch.js.map +1 -1
  220. package/dist/src/recovery/recover-cli.js +23 -4
  221. package/dist/src/recovery/recover-cli.js.map +1 -1
  222. package/dist/src/send-tools.d.ts +11 -0
  223. package/dist/src/send-tools.d.ts.map +1 -0
  224. package/dist/src/send-tools.js +11 -0
  225. package/dist/src/send-tools.js.map +1 -0
  226. package/dist/src/tool-result-guard.d.ts +4 -3
  227. package/dist/src/tool-result-guard.d.ts.map +1 -1
  228. package/dist/src/tool-result-guard.js +5 -4
  229. package/dist/src/tool-result-guard.js.map +1 -1
  230. package/dist/src/tool-result-history.d.ts +1 -1
  231. package/dist/src/tool-result-history.js +1 -1
  232. package/dist/src/tune-out/coordinator.d.ts +21 -0
  233. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  234. package/dist/src/tune-out/coordinator.js +139 -37
  235. package/dist/src/tune-out/coordinator.js.map +1 -1
  236. package/dist/src/types/agent.d.ts +4 -0
  237. package/dist/src/types/agent.d.ts.map +1 -1
  238. package/dist/src/types/events.d.ts +62 -9
  239. package/dist/src/types/events.d.ts.map +1 -1
  240. package/dist/src/types/framework.d.ts +62 -58
  241. package/dist/src/types/framework.d.ts.map +1 -1
  242. package/dist/src/types/module.d.ts +40 -8
  243. package/dist/src/types/module.d.ts.map +1 -1
  244. package/dist/src/types/trace.d.ts +111 -10
  245. package/dist/src/types/trace.d.ts.map +1 -1
  246. package/dist/src/usage/types.d.ts +16 -0
  247. package/dist/src/usage/types.d.ts.map +1 -1
  248. package/dist/src/usage/usage-tracker.d.ts +14 -1
  249. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  250. package/dist/src/usage/usage-tracker.js +81 -41
  251. package/dist/src/usage/usage-tracker.js.map +1 -1
  252. package/package.json +6 -5
  253. package/src/agent.ts +93 -12
  254. package/src/api/server.ts +14 -12
  255. package/src/code-execution/engine.ts +595 -0
  256. package/src/code-execution/index.ts +5 -17
  257. package/src/code-execution/isolate/calls.ts +199 -0
  258. package/src/code-execution/isolate/child-entry.ts +5 -0
  259. package/src/code-execution/isolate/child-main.ts +132 -0
  260. package/src/code-execution/isolate/child.ts +327 -0
  261. package/src/code-execution/isolate/frames.ts +209 -0
  262. package/src/code-execution/isolate/output.ts +161 -0
  263. package/src/code-execution/isolate/prelude.ts +409 -0
  264. package/src/code-execution/isolate/protocol.ts +240 -0
  265. package/src/code-execution/isolate/render.ts +230 -0
  266. package/src/code-execution/isolate/runner.ts +911 -0
  267. package/src/code-execution/isolate/scripts.ts +479 -0
  268. package/src/code-execution/quickjs.wasm +0 -0
  269. package/src/code-execution/quickjs.wasm.LICENSES +149 -0
  270. package/src/code-execution/tool-definition.ts +236 -167
  271. package/src/deadline-timer.ts +1 -1
  272. package/src/framework.ts +4468 -2418
  273. package/src/gate/event-gate.ts +78 -11
  274. package/src/held-writes.ts +17 -3
  275. package/src/index.ts +30 -22
  276. package/src/mcpl/channel-registry.ts +1825 -368
  277. package/src/mcpl/conversation-router.ts +12 -7
  278. package/src/mcpl/convert-block.ts +9 -5
  279. package/src/mcpl/inbound-source.ts +131 -0
  280. package/src/mcpl/index.ts +4 -2
  281. package/src/mcpl/log-value.ts +9 -0
  282. package/src/mcpl/modern-connection.ts +162 -53
  283. package/src/mcpl/prose-grammar.ts +4 -5
  284. package/src/mcpl/protocol-family.ts +199 -21
  285. package/src/mcpl/push-coalescer.ts +306 -62
  286. package/src/mcpl/push-handler.ts +10 -2
  287. package/src/mcpl/reference-fetcher.ts +4 -4
  288. package/src/mcpl/server-connection.ts +180 -79
  289. package/src/mcpl/tags.ts +36 -0
  290. package/src/mcpl/tool-result-normalize.ts +2 -1
  291. package/src/mcpl/transport.ts +162 -33
  292. package/src/mcpl/types.ts +23 -6
  293. package/src/module-registry.ts +92 -13
  294. package/src/modules/discord/index.ts +8 -13
  295. package/src/modules/history/index.ts +25 -3
  296. package/src/modules/history/semantic.ts +29 -6
  297. package/src/modules/index.ts +1 -0
  298. package/src/modules/unknown-tool.ts +4 -1
  299. package/src/modules/workspace/branch-intent.ts +3 -1
  300. package/src/modules/workspace/edit-plan.ts +954 -0
  301. package/src/modules/workspace/edit-text.ts +618 -0
  302. package/src/modules/workspace/effects.ts +70 -19
  303. package/src/modules/workspace/index.ts +2247 -642
  304. package/src/modules/workspace/observe.ts +465 -56
  305. package/src/modules/workspace/reconcile.ts +14 -7
  306. package/src/modules/workspace/text.ts +68 -0
  307. package/src/modules/workspace/types.ts +211 -6
  308. package/src/modules/workspace/watcher.ts +3 -2
  309. package/src/operator-log.ts +47 -19
  310. package/src/recovery/discord-awareness-outbox.ts +1500 -356
  311. package/src/recovery/offline-branch.ts +189 -55
  312. package/src/recovery/recover-cli.ts +25 -4
  313. package/src/send-tools.ts +11 -0
  314. package/src/tool-result-guard.ts +6 -5
  315. package/src/tool-result-history.ts +1 -1
  316. package/src/tune-out/coordinator.ts +163 -42
  317. package/src/types/agent.ts +4 -0
  318. package/src/types/events.ts +62 -9
  319. package/src/types/framework.ts +62 -58
  320. package/src/types/module.ts +40 -8
  321. package/src/types/trace.ts +121 -15
  322. package/src/usage/types.ts +16 -0
  323. package/src/usage/usage-tracker.ts +86 -43
  324. package/dist/src/code-execution/py-runner.d.ts +0 -191
  325. package/dist/src/code-execution/py-runner.d.ts.map +0 -1
  326. package/dist/src/code-execution/py-runner.js +0 -655
  327. package/dist/src/code-execution/py-runner.js.map +0 -1
  328. package/dist/src/code-execution/runtime-py.d.ts +0 -50
  329. package/dist/src/code-execution/runtime-py.d.ts.map +0 -1
  330. package/dist/src/code-execution/runtime-py.js +0 -677
  331. package/dist/src/code-execution/runtime-py.js.map +0 -1
  332. package/dist/src/mcpl/prose-stream-router.d.ts +0 -88
  333. package/dist/src/mcpl/prose-stream-router.d.ts.map +0 -1
  334. package/dist/src/mcpl/prose-stream-router.js +0 -291
  335. package/dist/src/mcpl/prose-stream-router.js.map +0 -1
  336. package/src/code-execution/py-runner.ts +0 -791
  337. package/src/code-execution/runtime-py.ts +0 -677
  338. package/src/mcpl/prose-stream-router.ts +0 -268
@@ -1,677 +0,0 @@
1
- /**
2
- * Python runtime for client-side programmatic tool calling (PTC).
3
- *
4
- * This is the interpreter-side half of the `code_execution` tool: a small
5
- * asyncio REPL that mimics the environment Anthropic's server-side PTC was
6
- * trained on (see docs/en/agents-and-tools/tool-use/programmatic-tool-calling):
7
- *
8
- * - scripts run with top-level `await` (PyCF_ALLOW_TOP_LEVEL_AWAIT)
9
- * - host tools are injected as async functions taking a single dict and
10
- * returning the tool_result text as a str (ToolResult), which also
11
- * carries .is_error and .structured
12
- * - a pending tool call that gets no response raises TimeoutError with the
13
- * same message shape the managed runtime produces
14
- * - interpreter globals persist across execs (container-reuse semantics)
15
- *
16
- * BACKGROUND MODE (model-authored daemons): when init carries
17
- * `background: true`, the exec additionally gets:
18
- * - `wake_agent(payload)` — async; reports the caller's line number (which
19
- * indexes into the agent-authored script — self-correlation) and delivers
20
- * the payload to the host, which injects it into the agent's context and
21
- * triggers inference. Awaits a host ack so rate limiting backpressures
22
- * inside the script instead of dropping wakes.
23
- * - stdout/stderr tee'd line-buffered to `log_path` (agent-readable via
24
- * their file tools) with an in-memory tail kept for the crash envelope.
25
- *
26
- * Embedded as a TS string because tsc does not copy non-TS assets to dist/;
27
- * PyRunner materializes it to a temp file at spawn.
28
- *
29
- * Protocol (JSON lines):
30
- * host -> py : {op:"init", tools:[{py_name,tool_name}], call_timeout_s,
31
- * background?, log_path?, tail_chars?}
32
- * {op:"exec", id, code}
33
- * {op:"tool_result", id, result, is_error?, structured_json?}
34
- * {op:"wake_ack", id, error?}
35
- * {op:"cancel", id, reason?}
36
- * py -> host : {op:"ready"}
37
- * {op:"tool_call", id, exec_id, name, args}
38
- * {op:"wake", id, exec_id, line, payload}
39
- * {op:"exec_result", id, stdout, stderr, return_code, tail?}
40
- *
41
- * At a deadline the host sends cancel and then SIGINT, repeated until the
42
- * script reports (not on Windows): the cancel stops a script waiting on an
43
- * await, the signal also one stuck in blocking code (see _on_sigint).
44
- *
45
- * IMPORTANT: the python source below must not contain backticks or the
46
- * sequence dollar+brace (TS template literal syntax). String.raw preserves
47
- * backslashes, so \n inside python string literals is fine.
48
- */
49
- export const PYTHON_RUNTIME_SOURCE = String.raw `
50
- import ast
51
- import asyncio
52
- import functools
53
- import inspect
54
- import io
55
- import json
56
- import os
57
- import re
58
- import signal
59
- import sys
60
- import threading
61
- import traceback
62
-
63
- PROTO_OUT = sys.stdout # protocol channel (real stdout)
64
- REAL_STDERR = sys.stderr # runtime diagnostics outside execs
65
- CALL_TIMEOUT_S = 270.0
66
- OUTPUT_CAP_CHARS = 2_000_000
67
- LOG_FILE_CAP_BYTES = 50_000_000
68
-
69
- BACKGROUND = False
70
- LOG_PATH = None
71
- TAIL_CHARS = 4000
72
-
73
- _send_lock = threading.Lock()
74
-
75
- # A str can hold a lone surrogate, which UTF-8 can't encode: on POSIX,
76
- # os.fsdecode makes one of each byte in a filename that doesn't decode as
77
- # UTF-8, and json.loads makes one of a "\ud800" escape. PYTHONIOENCODING makes
78
- # the protocol channel strict UTF-8, so writing one would fail the whole
79
- # message, and a script's result would never arrive. Each goes as U+FFFD.
80
- _LONE_SURROGATE = re.compile(r"[\ud800-\udfff]")
81
-
82
-
83
- def send(obj):
84
- line = _LONE_SURROGATE.sub("\ufffd", json.dumps(obj, ensure_ascii=False, default=str))
85
- with _send_lock:
86
- PROTO_OUT.write(line + "\n")
87
- PROTO_OUT.flush()
88
-
89
-
90
- class CappedIO(io.StringIO):
91
- """StringIO that stops recording past a cap (keeps a truncation flag)."""
92
-
93
- def __init__(self, cap):
94
- super().__init__()
95
- self._cap = cap
96
- self.truncated = False
97
-
98
- def write(self, s):
99
- if not isinstance(s, str):
100
- s = str(s)
101
- if self.tell() >= self._cap:
102
- self.truncated = True
103
- return len(s)
104
- remaining = self._cap - self.tell()
105
- if len(s) > remaining:
106
- self.truncated = True
107
- return super().write(s[:remaining])
108
- return super().write(s)
109
-
110
-
111
- class LogTee:
112
- """Background-mode writer: line-buffered file append + rolling tail.
113
-
114
- The file is the agent-readable journal (their read/grep/shell tools work
115
- on it); the tail rides the exec_result so the host can build a crash
116
- envelope without re-reading the file.
117
- """
118
-
119
- def __init__(self, path, tail_chars):
120
- self._fh = None
121
- self._written = 0
122
- self._tail = ""
123
- self._tail_chars = tail_chars
124
- self.truncated = False
125
- if path:
126
- try:
127
- os.makedirs(os.path.dirname(path), exist_ok=True)
128
- self._fh = open(path, "a", encoding="utf-8", errors="replace")
129
- except Exception as exc:
130
- REAL_STDERR.write("[pytc-runtime] cannot open log file: " + str(exc) + "\n")
131
- REAL_STDERR.flush()
132
-
133
- def write(self, s):
134
- if not isinstance(s, str):
135
- s = str(s)
136
- self._tail = (self._tail + s)[-self._tail_chars:]
137
- if self._fh is not None and not self.truncated:
138
- if self._written + len(s) > LOG_FILE_CAP_BYTES:
139
- self.truncated = True
140
- try:
141
- self._fh.write("\n[log capped at " + str(LOG_FILE_CAP_BYTES) + " bytes]\n")
142
- self._fh.flush()
143
- except Exception:
144
- pass
145
- else:
146
- try:
147
- self._fh.write(s)
148
- if "\n" in s:
149
- self._fh.flush()
150
- self._written += len(s)
151
- except Exception:
152
- pass
153
- return len(s)
154
-
155
- def flush(self):
156
- if self._fh is not None:
157
- try:
158
- self._fh.flush()
159
- except Exception:
160
- pass
161
-
162
- def close(self):
163
- if self._fh is not None:
164
- try:
165
- self._fh.close()
166
- except Exception:
167
- pass
168
- self._fh = None
169
-
170
- def tail(self):
171
- return self._tail
172
-
173
-
174
- # Persistent interpreter state (mirrors container reuse: variables survive
175
- # between exec calls until the host reclaims the interpreter).
176
- SCRIPT_GLOBALS = {"__name__": "__main__", "__builtins__": __builtins__}
177
- _injected_py_names = set()
178
-
179
- _pending_tool_futures = {}
180
- _pending_wake_futures = {}
181
- _next_tool_call_id = 0
182
- _next_wake_id = 0
183
- _current_exec_id = None
184
- _current_exec_task = None
185
-
186
- # How the host stops a script (its time limit, or an abort): ONE per-script
187
- # state, so the script is stopped exactly once, by whichever path gets there
188
- # first. Every other path then does nothing to that script.
189
- # PENDING -> nothing asked yet.
190
- # REQUESTED -> asked while the script was outside its own code (waiting on
191
- # an await, writing a protocol line, or not started yet). A
192
- # cancellation is queued on the loop, or delivered as the
193
- # script starts.
194
- # DELIVERED -> KeyboardInterrupt raised in its code, or its task cancelled.
195
- # Final: whatever the script does next (except/finally, awaits
196
- # in its cleanup) runs to completion or to the host's kill.
197
- # Transitions happen only in the main thread: in the SIGINT handler (between
198
- # bytecodes, so it never cancels anything itself -- see _on_sigint) and in
199
- # loop callbacks. Neither can interleave a transition of the other halfway
200
- # in a way that matters: the handler only moves PENDING to REQUESTED or, with
201
- # the script's own frame on the stack, to DELIVERED; a loop callback never
202
- # runs while the script's frame is on the stack.
203
- #
204
- # Where the signal (or the cancel op) can land, and what happens:
205
- # 1. script's own code, running or blocking -> KeyboardInterrupt there
206
- # 2. script waiting on an await (loop idle) -> cancellation queued; it wakes
207
- # the loop and lands at that await
208
- # 3. main() mid-dispatch, between the done() check of a future the script
209
- # awaits and its set_result (reply behind the cancellation) -> queued;
210
- # set_result completes; the script gets CancelledError instead of it
211
- # 4. the script writing a protocol line (send) -> queued; the line stays
212
- # whole; the cancellation lands at the script's next await
213
- # 5. reply ahead of the queued cancellation, script then awaits -> the
214
- # cancellation lands at that await
215
- # 6. reply ahead, script then blocks -> the next signal (the host repeats
216
- # it) raises KeyboardInterrupt there; the queued cancellation then does
217
- # nothing, so cleanup that awaits is not cut short
218
- # 7. reply ahead, script finishes before the queued cancellation runs ->
219
- # it does nothing, and never reaches the next script
220
- # 8. script not started yet (signal, or the cancel op) -> delivered as
221
- # CancelledError when it starts, so it still reports
222
- # 9. cancel op before the signal -> delivered; the signal and its repeats
223
- # do nothing, so cleanup after a caught CancelledError runs on
224
- # 10. cancel op or repeated signal after delivery -> nothing
225
- # 11. script finished, reporting its result -> nothing
226
- # 12. between scripts -> nothing
227
- # 13. a previous script installed an eager task factory -> no effect: the
228
- # runtime builds its script task directly, and the state is in place
229
- # before the script's first step
230
- _PENDING = "pending"
231
- _REQUESTED = "requested"
232
- _DELIVERED = "delivered"
233
-
234
-
235
- class _ScriptStop:
236
- __slots__ = ("task", "running", "state", "interrupt")
237
-
238
- def __init__(self):
239
- self.task = None
240
- self.running = False # the script's own code may be on the stack
241
- self.state = _PENDING
242
- self.interrupt = None # the KeyboardInterrupt raised in its code
243
-
244
-
245
- _stop = None # the running script's _ScriptStop; None between scripts
246
-
247
-
248
- def _deliver_cancel(st):
249
- # Loop context only: the cancel op in main()'s dispatch, or queued by the
250
- # SIGINT handler. Never from inside the handler, where a cancellation
251
- # could land between main()'s done() check of a future the script awaits
252
- # and its set_result.
253
- if st is None or st is not _stop or st.state == _DELIVERED:
254
- return
255
- if not st.running:
256
- st.state = _REQUESTED # not started yet: delivered as it starts
257
- return
258
- st.state = _DELIVERED
259
- st.task.cancel()
260
-
261
-
262
- class ToolResult(str):
263
- """A tool's result: the string the tool gave, with two attributes.
264
-
265
- is_error: True if the result is an error, False if not, None if the
266
- host didn't say.
267
- structured: the parsed structuredContent of a structured result's JSON
268
- object, or None.
269
- """
270
-
271
- # Defaults, since pickle and copy rebuild a str subclass from its text
272
- # alone and then restore the attributes.
273
- def __new__(cls, text="", is_error=None, structured_json=False):
274
- self = str.__new__(cls, text)
275
- self.is_error = is_error
276
- self._structured_json = structured_json
277
- return self
278
-
279
- # Read from the string itself, the one copy sent, on first use only.
280
- @functools.cached_property
281
- def structured(self):
282
- if not self._structured_json:
283
- return None
284
- return json.loads(self).get("structuredContent")
285
-
286
-
287
- def _resolve(fut, value):
288
- # Defense in depth: the script may have been cancelled since the lookup.
289
- if fut is not None and not fut.done():
290
- try:
291
- fut.set_result(value)
292
- except asyncio.InvalidStateError:
293
- pass
294
-
295
-
296
- def _brief(value):
297
- # A value as a refusal shows it: by its type, such as <str>, which fails
298
- # loudly if copied rather than sending something else. The agent has its
299
- # values in the call it just wrote. No value's own code runs here: its
300
- # __repr__ isn't called, and its type is named through type's own
301
- # descriptor, so a metaclass's __name__ doesn't run either.
302
- return "<" + type.__dict__["__name__"].__get__(type(value)) + ">"
303
-
304
-
305
- def _make_tool_fn(tool_name, py_name):
306
- async def tool_fn(*positional, **kwargs):
307
- global _next_tool_call_id
308
- if len(positional) > 1:
309
- raise TypeError(
310
- py_name + " wasn't called: a tool function takes one dict of "
311
- "arguments, such as await " + py_name + "({...}), and this call "
312
- "gave it " + str(len(positional)) + " positional arguments"
313
- )
314
- # args= on its own is the dict, as it has always been. Beside other
315
- # keywords it's one of them, since a tool such as utils has a field
316
- # of that name, so the suggested dict keeps it.
317
- if positional:
318
- args = positional[0]
319
- elif list(kwargs) == ["args"]:
320
- args = kwargs.pop("args")
321
- else:
322
- args = None
323
- if kwargs:
324
- if args is not None and not isinstance(args, dict):
325
- raise TypeError(
326
- py_name + " wasn't called: a tool function takes one dict of "
327
- "arguments, not keyword arguments, and this call also gave it "
328
- + _brief(args) + ", so put every argument in one dict: await "
329
- + py_name + "({...})"
330
- )
331
- # The likely intent is plain: the same arguments in one dict.
332
- meant = dict(args) if isinstance(args, dict) else {}
333
- meant.update(kwargs)
334
- # A key is what the agent has to write, so a str key is shown
335
- # whole up to 60 characters, as names are elsewhere; a longer one,
336
- # or any other key, is shown as a value is. A tool's result counts
337
- # as a str: ToolResult keeps str's repr, so no script code runs.
338
- shown = ", ".join((repr(k) if (type(k) is str or type(k) is ToolResult) and len(k) <= 60 else _brief(k)) + ": " + _brief(v)
339
- for k, v in meant.items())
340
- raise TypeError(
341
- py_name + " wasn't called: a tool function takes one dict of "
342
- "arguments, not keyword arguments, so write await "
343
- + py_name + "({" + shown + "})"
344
- )
345
- if args is None:
346
- args = {}
347
- if not isinstance(args, dict):
348
- raise TypeError(
349
- py_name + " wasn't called: a tool function takes one dict of "
350
- "arguments, such as await " + py_name + "({...}), and this call "
351
- "gave it " + _brief(args)
352
- )
353
- _next_tool_call_id += 1
354
- call_id = "t" + str(_next_tool_call_id)
355
- fut = asyncio.get_running_loop().create_future()
356
- _pending_tool_futures[call_id] = fut
357
- send({
358
- "op": "tool_call",
359
- "id": call_id,
360
- "exec_id": _current_exec_id,
361
- "name": tool_name,
362
- "args": args,
363
- })
364
- try:
365
- return await asyncio.wait_for(fut, timeout=CALL_TIMEOUT_S)
366
- except asyncio.TimeoutError:
367
- raise TimeoutError(
368
- "Calling tool ['" + tool_name + "'] timed out (no response after "
369
- + str(int(CALL_TIMEOUT_S)) + "s)."
370
- )
371
- finally:
372
- _pending_tool_futures.pop(call_id, None)
373
- tool_fn.__name__ = py_name
374
- tool_fn.__qualname__ = py_name
375
- tool_fn.__doc__ = (
376
- "Host tool '" + tool_name + "'. Takes a single dict of arguments and "
377
- "returns the tool result as a string, with .is_error and .structured "
378
- "beside it."
379
- )
380
- return tool_fn
381
-
382
-
383
- async def wake_agent(payload=None):
384
- """Wake the agent: deliver payload into their context and trigger a turn.
385
-
386
- Reports the caller's line number (indexes into the script the agent
387
- wrote). Awaits the host ack — rate limiting backpressures here rather
388
- than dropping wakes. Raises RuntimeError when the host refuses (wake cap
389
- exceeded, script cancelled).
390
- """
391
- global _next_wake_id
392
- frame = inspect.currentframe()
393
- line = frame.f_back.f_lineno if frame is not None and frame.f_back is not None else -1
394
- _next_wake_id += 1
395
- wake_id = "w" + str(_next_wake_id)
396
- fut = asyncio.get_running_loop().create_future()
397
- _pending_wake_futures[wake_id] = fut
398
- send({
399
- "op": "wake",
400
- "id": wake_id,
401
- "exec_id": _current_exec_id,
402
- "line": line,
403
- "payload": payload,
404
- })
405
- try:
406
- error = await fut
407
- if error:
408
- raise RuntimeError("wake_agent refused by host: " + str(error))
409
- return None
410
- finally:
411
- _pending_wake_futures.pop(wake_id, None)
412
-
413
-
414
- def handle_init(msg):
415
- global CALL_TIMEOUT_S, BACKGROUND, LOG_PATH, TAIL_CHARS
416
- timeout = msg.get("call_timeout_s")
417
- if isinstance(timeout, (int, float)) and timeout > 0:
418
- CALL_TIMEOUT_S = float(timeout)
419
- BACKGROUND = bool(msg.get("background"))
420
- LOG_PATH = msg.get("log_path") or None
421
- tail = msg.get("tail_chars")
422
- if isinstance(tail, int) and tail > 0:
423
- TAIL_CHARS = tail
424
-
425
- # Remove functions injected by a previous init that are no longer present
426
- # (tool list changed) without touching user-defined globals.
427
- new_tools = msg.get("tools") or []
428
- new_py_names = set()
429
- tools_dict = {}
430
- for entry in new_tools:
431
- py_name = entry.get("py_name")
432
- tool_name = entry.get("tool_name")
433
- if not py_name or not tool_name:
434
- continue
435
- fn = _make_tool_fn(tool_name, py_name)
436
- SCRIPT_GLOBALS[py_name] = fn
437
- tools_dict[tool_name] = fn
438
- new_py_names.add(py_name)
439
- for stale in _injected_py_names - new_py_names:
440
- SCRIPT_GLOBALS.pop(stale, None)
441
- _injected_py_names.clear()
442
- _injected_py_names.update(new_py_names)
443
- # Exact-name escape hatch: tools["mcpl--server--tool"]({...})
444
- SCRIPT_GLOBALS["tools"] = tools_dict
445
- # The doorbell exists only in background mode — a foreground script's
446
- # agent is already awake (mid-turn).
447
- if BACKGROUND:
448
- SCRIPT_GLOBALS["wake_agent"] = wake_agent
449
- else:
450
- SCRIPT_GLOBALS.pop("wake_agent", None)
451
-
452
-
453
- async def _run_script(exec_id, code, st):
454
- global _current_exec_id, _current_exec_task, _stop
455
- if BACKGROUND:
456
- out = LogTee(LOG_PATH, TAIL_CHARS)
457
- err = out # interleave, terminal-style; tail is shared
458
- else:
459
- out = CappedIO(OUTPUT_CAP_CHARS)
460
- err = CappedIO(OUTPUT_CAP_CHARS)
461
- old_out, old_err, old_in = sys.stdout, sys.stderr, sys.stdin
462
- sys.stdout, sys.stderr = out, err
463
- sys.stdin = io.StringIO("") # input() must not steal the protocol channel
464
- return_code = 0
465
- try:
466
- compiled = compile(code, "<script>", "exec", flags=ast.PyCF_ALLOW_TOP_LEVEL_AWAIT)
467
- st.running = True
468
- try:
469
- if st.state == _REQUESTED:
470
- # Stopped before it started (row 8).
471
- st.state = _DELIVERED
472
- raise asyncio.CancelledError()
473
- result = eval(compiled, SCRIPT_GLOBALS)
474
- if inspect.iscoroutine(result):
475
- await result
476
- finally:
477
- st.running = False
478
- except asyncio.CancelledError:
479
- err.write("\nKeyboardInterrupt: script cancelled by host\n")
480
- return_code = 1
481
- except BaseException as exc:
482
- if exc is st.interrupt:
483
- # Where the script was when it was stopped, without the runtime's frames.
484
- frames = [
485
- f for f in traceback.extract_tb(exc.__traceback__)
486
- if f.filename != _RUN_SCRIPT_CODE.co_filename
487
- ]
488
- err.write("Traceback (most recent call last):\n")
489
- err.write("".join(traceback.format_list(frames)))
490
- err.write("KeyboardInterrupt: script interrupted by host\n")
491
- else:
492
- traceback.print_exc(file=err)
493
- return_code = 1
494
- finally:
495
- sys.stdout, sys.stderr, sys.stdin = old_out, old_err, old_in
496
- st.interrupt = None
497
- if _stop is st:
498
- _stop = None
499
- _current_exec_id = None
500
- _current_exec_task = None
501
- for fut in list(_pending_tool_futures.values()):
502
- if not fut.done():
503
- fut.cancel()
504
- _pending_tool_futures.clear()
505
- for fut in list(_pending_wake_futures.values()):
506
- if not fut.done():
507
- fut.cancel()
508
- _pending_wake_futures.clear()
509
-
510
- if BACKGROUND:
511
- out.flush()
512
- out.close()
513
- send({
514
- "op": "exec_result",
515
- "id": exec_id,
516
- "stdout": "",
517
- "stderr": "",
518
- "tail": out.tail(),
519
- "return_code": return_code,
520
- })
521
- return
522
-
523
- stdout_text = out.getvalue()
524
- if out.truncated:
525
- stdout_text += "\n[stdout truncated at " + str(OUTPUT_CAP_CHARS) + " chars]"
526
- stderr_text = err.getvalue()
527
- if err.truncated:
528
- stderr_text += "\n[stderr truncated at " + str(OUTPUT_CAP_CHARS) + " chars]"
529
- send({
530
- "op": "exec_result",
531
- "id": exec_id,
532
- "stdout": stdout_text,
533
- "stderr": stderr_text,
534
- "return_code": return_code,
535
- })
536
-
537
-
538
- def _dispatch(msg):
539
- """Handle one protocol message from the host; False means stop reading."""
540
- global _current_exec_id, _current_exec_task, _stop
541
- op = msg.get("op")
542
- if op == "init":
543
- handle_init(msg)
544
- elif op == "exec":
545
- if _current_exec_task is not None and not _current_exec_task.done():
546
- send({
547
- "op": "exec_result",
548
- "id": msg.get("id"),
549
- "stdout": "",
550
- "stderr": "RuntimeError: another script is already running in this interpreter",
551
- "return_code": 1,
552
- })
553
- return True
554
- # Everything _on_sigint reads is in place before the script can run
555
- # (row 13). The task is built directly, not through the loop's task
556
- # factory: a script may have installed one (interpreter state
557
- # persists), and an eager one would run the next script inside this
558
- # call, before the assignments below.
559
- st = _ScriptStop()
560
- _stop = st
561
- _current_exec_id = msg.get("id")
562
- _current_exec_task = asyncio.Task(
563
- _run_script(msg.get("id"), msg.get("code") or "", st),
564
- loop=asyncio.get_running_loop(),
565
- )
566
- st.task = _current_exec_task
567
- elif op == "tool_result":
568
- _resolve(
569
- _pending_tool_futures.get(msg.get("id")),
570
- ToolResult(str(msg.get("result", "")), msg.get("is_error"), msg.get("structured_json") is True),
571
- )
572
- elif op == "wake_ack":
573
- _resolve(_pending_wake_futures.get(msg.get("id")), msg.get("error") or None)
574
- elif op == "cancel":
575
- _deliver_cancel(_stop)
576
- elif op == "exit":
577
- return False
578
- return True
579
-
580
-
581
- async def main():
582
- loop = asyncio.get_running_loop()
583
- queue = asyncio.Queue()
584
-
585
- def reader():
586
- for raw_line in sys.stdin:
587
- loop.call_soon_threadsafe(queue.put_nowait, raw_line)
588
- loop.call_soon_threadsafe(queue.put_nowait, None)
589
-
590
- # The reader thread blocks SIGINT (it inherits the mask while it starts),
591
- # so the signal reaches the main thread, where a blocking call in the
592
- # script is waiting to be interrupted.
593
- masked = hasattr(signal, "pthread_sigmask") and hasattr(signal, "SIGINT")
594
- if masked:
595
- previous_mask = signal.pthread_sigmask(signal.SIG_BLOCK, {signal.SIGINT})
596
- try:
597
- threading.Thread(target=reader, daemon=True).start()
598
- finally:
599
- if masked:
600
- signal.pthread_sigmask(signal.SIG_SETMASK, previous_mask)
601
- send({"op": "ready"})
602
-
603
- while True:
604
- line = await queue.get()
605
- if line is None:
606
- break
607
- line = line.strip()
608
- if not line:
609
- continue
610
- try:
611
- msg = json.loads(line)
612
- except Exception:
613
- REAL_STDERR.write("[pytc-runtime] bad protocol line\n")
614
- REAL_STDERR.flush()
615
- continue
616
- if not _dispatch(msg):
617
- break
618
-
619
- # Drain: give a cancelled exec a moment to emit its exec_result.
620
- if _current_exec_task is not None and not _current_exec_task.done():
621
- _current_exec_task.cancel()
622
- try:
623
- await asyncio.wait_for(_current_exec_task, timeout=2)
624
- except BaseException:
625
- pass
626
-
627
-
628
- # At the deadline the host sends a cancel op, which lands only when the script
629
- # awaits: a script blocked in time.sleep(), a busy loop or a blocking read
630
- # never gives the event loop control back. So the host also sends SIGINT,
631
- # repeated until the script reports, and this stops the script through its
632
- # _ScriptStop (see the table there):
633
- # - With the script's own code on the stack (_run_script's frame, reached
634
- # before any send frame), it raises KeyboardInterrupt there; _run_script
635
- # catches it, so the output so far and the interpreter's globals survive.
636
- # Checking the current task instead is not enough: asyncio runs its own
637
- # code for the task between steps, and an exception raised there escapes
638
- # the event loop and ends the interpreter.
639
- # - Anywhere else it only queues the cancellation on the loop (which also
640
- # wakes select(), otherwise resuming its wait after a signal): cancelling
641
- # here could cancel a future between main()'s done() check and its
642
- # set_result, and an exception mid-send would tear a protocol line.
643
- _RUN_SCRIPT_CODE = _run_script.__code__
644
- _SEND_CODE = send.__code__
645
-
646
-
647
- def _on_sigint(signum, frame):
648
- st = _stop
649
- if st is None or st.state == _DELIVERED:
650
- return
651
- if st.running:
652
- while frame is not None:
653
- code = frame.f_code
654
- if code is _SEND_CODE:
655
- break
656
- if code is _RUN_SCRIPT_CODE:
657
- st.state = _DELIVERED
658
- st.interrupt = KeyboardInterrupt("script interrupted by host")
659
- raise st.interrupt
660
- frame = frame.f_back
661
- if st.state == _PENDING:
662
- st.state = _REQUESTED
663
- if st.running:
664
- st.task.get_loop().call_soon_threadsafe(_deliver_cancel, st)
665
-
666
-
667
- if __name__ == "__main__":
668
- # A handler of our own also keeps asyncio.run from installing one that
669
- # would cancel main() and end the interpreter.
670
- if hasattr(signal, "SIGINT"):
671
- try:
672
- signal.signal(signal.SIGINT, _on_sigint)
673
- except (ValueError, OSError):
674
- pass
675
- asyncio.run(main())
676
- `;
677
- //# sourceMappingURL=runtime-py.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"runtime-py.js","sourceRoot":"","sources":["../../../src/code-execution/runtime-py.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAW,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmnBtD,CAAC"}