@code-yeongyu/senpi 2026.7.10 → 2026.7.11

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 (311) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +4 -2
  3. package/dist/core/extensions/builtin/bash-timeout/timeout.js +2 -2
  4. package/dist/core/extensions/builtin/bash-timeout/timeout.js.map +1 -1
  5. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  6. package/dist/core/extensions/builtin/compaction/index.js +10 -1
  7. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  8. package/dist/core/extensions/builtin/goal/store.d.ts.map +1 -1
  9. package/dist/core/extensions/builtin/goal/store.js +88 -3
  10. package/dist/core/extensions/builtin/goal/store.js.map +1 -1
  11. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts +3 -0
  12. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -0
  13. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +83 -0
  14. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -0
  15. package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/prompt-preset/presets.js +6 -0
  17. package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
  18. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
  19. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
  21. package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
  22. package/dist/core/extensions/builtin/terminal/notify.d.ts +3 -3
  23. package/dist/core/extensions/builtin/terminal/notify.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/terminal/notify.js +7 -4
  25. package/dist/core/extensions/builtin/terminal/notify.js.map +1 -1
  26. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts +5 -3
  27. package/dist/core/extensions/builtin/terminal/runtime-session.d.ts.map +1 -1
  28. package/dist/core/extensions/builtin/terminal/runtime-session.js +32 -16
  29. package/dist/core/extensions/builtin/terminal/runtime-session.js.map +1 -1
  30. package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts +1 -1
  31. package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/terminal/tools/bash-output.js +10 -4
  33. package/dist/core/extensions/builtin/terminal/tools/bash-output.js.map +1 -1
  34. package/dist/core/extensions/builtin/todotools/tools/todowrite.d.ts.map +1 -1
  35. package/dist/core/extensions/builtin/todotools/tools/todowrite.js +5 -8
  36. package/dist/core/extensions/builtin/todotools/tools/todowrite.js.map +1 -1
  37. package/dist/core/extensions/runner.d.ts +1 -0
  38. package/dist/core/extensions/runner.d.ts.map +1 -1
  39. package/dist/core/extensions/runner.js +5 -0
  40. package/dist/core/extensions/runner.js.map +1 -1
  41. package/dist/core/extensions/types.d.ts +5 -8
  42. package/dist/core/extensions/types.d.ts.map +1 -1
  43. package/dist/core/extensions/types.js.map +1 -1
  44. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  45. package/dist/core/extensions/wrapper.js +22 -3
  46. package/dist/core/extensions/wrapper.js.map +1 -1
  47. package/dist/core/keybindings.d.ts +8 -3
  48. package/dist/core/keybindings.d.ts.map +1 -1
  49. package/dist/core/keybindings.js +7 -3
  50. package/dist/core/keybindings.js.map +1 -1
  51. package/dist/core/model-registry.d.ts +1 -6
  52. package/dist/core/model-registry.d.ts.map +1 -1
  53. package/dist/core/model-registry.js +19 -6
  54. package/dist/core/model-registry.js.map +1 -1
  55. package/dist/core/settings-manager.d.ts +4 -3
  56. package/dist/core/settings-manager.d.ts.map +1 -1
  57. package/dist/core/settings-manager.js +2 -1
  58. package/dist/core/settings-manager.js.map +1 -1
  59. package/dist/index.d.ts +1 -0
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +1 -0
  62. package/dist/index.js.map +1 -1
  63. package/dist/modes/interactive/components/custom-editor.js +1 -1
  64. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  65. package/dist/modes/interactive/components/login-dialog.d.ts +4 -0
  66. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  67. package/dist/modes/interactive/components/login-dialog.js +9 -2
  68. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  69. package/dist/modes/interactive/components/settings-selector.js +1 -1
  70. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  71. package/dist/modes/interactive/components/thinking-selector.js +1 -1
  72. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  73. package/dist/modes/interactive/components/tool-execution-fallback.d.ts +6 -0
  74. package/dist/modes/interactive/components/tool-execution-fallback.d.ts.map +1 -0
  75. package/dist/modes/interactive/components/tool-execution-fallback.js +40 -0
  76. package/dist/modes/interactive/components/tool-execution-fallback.js.map +1 -0
  77. package/dist/modes/interactive/components/tool-execution-images.d.ts +23 -0
  78. package/dist/modes/interactive/components/tool-execution-images.d.ts.map +1 -0
  79. package/dist/modes/interactive/components/tool-execution-images.js +104 -0
  80. package/dist/modes/interactive/components/tool-execution-images.js.map +1 -0
  81. package/dist/modes/interactive/components/tool-execution-renderer.d.ts +28 -0
  82. package/dist/modes/interactive/components/tool-execution-renderer.d.ts.map +1 -0
  83. package/dist/modes/interactive/components/tool-execution-renderer.js +135 -0
  84. package/dist/modes/interactive/components/tool-execution-renderer.js.map +1 -0
  85. package/dist/modes/interactive/components/tool-execution-types.d.ts +23 -0
  86. package/dist/modes/interactive/components/tool-execution-types.d.ts.map +1 -0
  87. package/dist/modes/interactive/components/tool-execution-types.js +2 -0
  88. package/dist/modes/interactive/components/tool-execution-types.js.map +1 -0
  89. package/dist/modes/interactive/components/tool-execution.d.ts +13 -39
  90. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  91. package/dist/modes/interactive/components/tool-execution.js +51 -294
  92. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  93. package/dist/modes/interactive/components/tool-renderer-boundary.d.ts +17 -0
  94. package/dist/modes/interactive/components/tool-renderer-boundary.d.ts.map +1 -0
  95. package/dist/modes/interactive/components/tool-renderer-boundary.js +75 -0
  96. package/dist/modes/interactive/components/tool-renderer-boundary.js.map +1 -0
  97. package/dist/modes/interactive/components/tree-selector.d.ts +5 -0
  98. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  99. package/dist/modes/interactive/components/tree-selector.js +50 -17
  100. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  101. package/dist/modes/interactive/interactive-mode.d.ts +1 -2
  102. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  103. package/dist/modes/interactive/interactive-mode.js +44 -40
  104. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  105. package/dist/modes/interactive/theme/dark.json +1 -0
  106. package/dist/modes/interactive/theme/light.json +1 -0
  107. package/dist/modes/interactive/theme/theme-schema.json +6 -2
  108. package/dist/modes/interactive/theme/theme.d.ts +3 -2
  109. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  110. package/dist/modes/interactive/theme/theme.js +11 -4
  111. package/dist/modes/interactive/theme/theme.js.map +1 -1
  112. package/dist/utils/clipboard-native.d.ts +1 -0
  113. package/dist/utils/clipboard-native.d.ts.map +1 -1
  114. package/dist/utils/clipboard-native.js.map +1 -1
  115. package/dist/utils/clipboard.d.ts +2 -0
  116. package/dist/utils/clipboard.d.ts.map +1 -1
  117. package/dist/utils/clipboard.js +13 -0
  118. package/dist/utils/clipboard.js.map +1 -1
  119. package/docs/custom-provider.md +4 -3
  120. package/docs/extensions.md +140 -1
  121. package/docs/keybindings.md +1 -0
  122. package/docs/models.md +45 -4
  123. package/docs/providers.md +3 -0
  124. package/docs/quickstart.md +1 -1
  125. package/docs/rpc.md +2 -2
  126. package/docs/sdk.md +1 -1
  127. package/docs/settings.md +3 -3
  128. package/docs/themes.md +5 -3
  129. package/docs/tui.md +1 -1
  130. package/docs/usage.md +2 -1
  131. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  132. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  133. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  134. package/examples/extensions/gondolin/package-lock.json +2 -2
  135. package/examples/extensions/gondolin/package.json +1 -1
  136. package/examples/extensions/sandbox/package-lock.json +2 -2
  137. package/examples/extensions/sandbox/package.json +1 -1
  138. package/examples/extensions/with-deps/package-lock.json +2 -2
  139. package/examples/extensions/with-deps/package.json +1 -1
  140. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +3 -0
  141. package/node_modules/@code-yeongyu/senpi-codemode/package.json +3 -3
  142. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +7 -7
  143. package/node_modules/@code-yeongyu/senpi-codemode/src/completion/tool-bridge.ts +3 -0
  144. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +69 -8
  145. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +39 -13
  146. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +191 -148
  147. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/inline-worker-entry.js +23 -0
  148. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/inline-worker.ts +4 -175
  149. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +38 -0
  150. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/run-queue.ts +82 -0
  151. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +7 -3
  152. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-host.ts +117 -0
  153. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel-contract.ts +32 -0
  154. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +231 -199
  155. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/process.ts +119 -0
  156. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/transport.ts +237 -0
  157. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-contract.ts +22 -0
  158. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +214 -189
  159. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-process.ts +174 -0
  160. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-queue.ts +100 -0
  161. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-run.ts +98 -0
  162. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-handler.ts +205 -0
  163. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +208 -203
  164. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +215 -25
  165. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +1 -0
  166. package/node_modules/@earendil-works/pi-agent-core/README.md +1 -1
  167. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +2 -0
  168. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  169. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +4 -3
  170. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
  171. package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
  172. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  173. package/node_modules/@earendil-works/pi-ai/README.md +4 -2
  174. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  175. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +89 -40
  176. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  177. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.d.ts +1 -1
  178. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.d.ts.map +1 -1
  179. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js.map +1 -1
  180. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  181. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +7 -7
  182. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
  183. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts +1 -1
  184. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
  185. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +10 -3
  186. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
  187. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts +1 -1
  188. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
  189. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +5 -1
  190. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  191. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts +3 -1
  192. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts.map +1 -1
  193. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +35 -4
  194. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
  195. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts +1 -1
  196. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  197. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +7 -3
  198. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  199. package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.d.ts.map +1 -1
  200. package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js +8 -3
  201. package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js.map +1 -1
  202. package/node_modules/@earendil-works/pi-ai/dist/api/transform-messages.d.ts.map +1 -1
  203. package/node_modules/@earendil-works/pi-ai/dist/api/transform-messages.js +4 -3
  204. package/node_modules/@earendil-works/pi-ai/dist/api/transform-messages.js.map +1 -1
  205. package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts.map +1 -1
  206. package/node_modules/@earendil-works/pi-ai/dist/compat.js +2 -1
  207. package/node_modules/@earendil-works/pi-ai/dist/compat.js.map +1 -1
  208. package/node_modules/@earendil-works/pi-ai/dist/models.d.ts.map +1 -1
  209. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +483 -61
  210. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  211. package/node_modules/@earendil-works/pi-ai/dist/models.js +13 -4
  212. package/node_modules/@earendil-works/pi-ai/dist/models.js.map +1 -1
  213. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
  214. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +5 -1
  215. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  216. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts +62 -5
  217. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
  218. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js +32 -20
  219. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js.map +1 -1
  220. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.d.ts +11 -1
  221. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.d.ts.map +1 -1
  222. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js +6 -4
  223. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js.map +1 -1
  224. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.d.ts +7 -8
  225. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.d.ts.map +1 -1
  226. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.js +20 -21
  227. package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.js.map +1 -1
  228. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.d.ts +11 -1
  229. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
  230. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js +6 -4
  231. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  232. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-auth.d.ts.map +1 -1
  233. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-auth.js +9 -6
  234. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-auth.js.map +1 -1
  235. package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.d.ts +2 -2
  236. package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.d.ts.map +1 -1
  237. package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.js +2 -2
  238. package/node_modules/@earendil-works/pi-ai/dist/providers/deepseek.models.js.map +1 -1
  239. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.d.ts +2 -2
  240. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.d.ts.map +1 -1
  241. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js +2 -2
  242. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js.map +1 -1
  243. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts +100 -2
  244. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts.map +1 -1
  245. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js +64 -5
  246. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js.map +1 -1
  247. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.d.ts +56 -0
  248. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.d.ts.map +1 -1
  249. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.js +20 -9
  250. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.js.map +1 -1
  251. package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.d.ts +77 -8
  252. package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.d.ts.map +1 -1
  253. package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.js +37 -24
  254. package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.js.map +1 -1
  255. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.d.ts +3 -3
  256. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.d.ts.map +1 -1
  257. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js +3 -3
  258. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js.map +1 -1
  259. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts +80 -4
  260. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts.map +1 -1
  261. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js +64 -8
  262. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js.map +1 -1
  263. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts +25 -22
  264. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts.map +1 -1
  265. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +64 -79
  266. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
  267. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts +45 -1
  268. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
  269. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +43 -7
  270. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  271. package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.d.ts +1 -1
  272. package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.d.ts.map +1 -1
  273. package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.js +1 -1
  274. package/node_modules/@earendil-works/pi-ai/dist/providers/zai-coding-cn.models.js.map +1 -1
  275. package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.d.ts +1 -1
  276. package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.d.ts.map +1 -1
  277. package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.js +1 -1
  278. package/node_modules/@earendil-works/pi-ai/dist/providers/zai.models.js.map +1 -1
  279. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +30 -7
  280. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  281. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  282. package/node_modules/@earendil-works/pi-ai/dist/utils/deferred-tools.d.ts +9 -0
  283. package/node_modules/@earendil-works/pi-ai/dist/utils/deferred-tools.d.ts.map +1 -0
  284. package/node_modules/@earendil-works/pi-ai/dist/utils/deferred-tools.js +36 -0
  285. package/node_modules/@earendil-works/pi-ai/dist/utils/deferred-tools.js.map +1 -0
  286. package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.d.ts +3 -3
  287. package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.d.ts.map +1 -1
  288. package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js +35 -14
  289. package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js.map +1 -1
  290. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  291. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  292. package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts.map +1 -1
  293. package/node_modules/@earendil-works/pi-tui/dist/components/image.js +2 -1
  294. package/node_modules/@earendil-works/pi-tui/dist/components/image.js.map +1 -1
  295. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +2 -1
  296. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
  297. package/node_modules/@earendil-works/pi-tui/dist/index.js +2 -1
  298. package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
  299. package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts.map +1 -1
  300. package/node_modules/@earendil-works/pi-tui/dist/keys.js +6 -5
  301. package/node_modules/@earendil-works/pi-tui/dist/keys.js.map +1 -1
  302. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts +0 -1
  303. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +1 -1
  304. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +0 -9
  305. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +1 -1
  306. package/node_modules/@earendil-works/pi-tui/dist/terminal-text.d.ts +4 -0
  307. package/node_modules/@earendil-works/pi-tui/dist/terminal-text.d.ts.map +1 -0
  308. package/node_modules/@earendil-works/pi-tui/dist/terminal-text.js +18 -0
  309. package/node_modules/@earendil-works/pi-tui/dist/terminal-text.js.map +1 -0
  310. package/node_modules/@earendil-works/pi-tui/package.json +2 -2
  311. package/package.json +6 -6
@@ -1,39 +1,32 @@
1
- import { Worker } from "node:worker_threads";
2
1
  import type { HostToKernelMessage, KernelToHostMessage } from "../../bridge/protocol.ts";
3
2
  import { createInlineWorker, type WorkerLike } from "./inline-worker.ts";
3
+ import {
4
+ assertJavaScriptKernelOpen,
5
+ type JavaScriptKernelMode,
6
+ type JavaScriptKernelOptions,
7
+ type JavaScriptRunInput,
8
+ type LifecycleState,
9
+ type ResultMessage,
10
+ type ToolCallMessage,
11
+ } from "./kernel-contract.ts";
12
+ import { JavaScriptRunQueue, type PendingJavaScriptRun, stoppedResult } from "./run-queue.ts";
13
+ import { bridgeError, spawnNodeWorker, WorkerStartupCancelledError, waitForReady } from "./worker-host.ts";
4
14
 
5
- export type JavaScriptKernelMode = "worker" | "inline";
6
-
7
- export interface JavaScriptKernelOptions {
8
- readonly sessionId: string;
9
- readonly cwd: string;
10
- readonly parallelPoolWidth: number;
11
- readonly onMessage?: (message: KernelToHostMessage) => void;
12
- readonly workerEntryUrl?: URL;
13
- }
14
-
15
- export interface JavaScriptRunInput {
16
- readonly cellId: string;
17
- readonly code: string;
18
- readonly timeoutMs?: number;
19
- readonly onMessage?: (message: KernelToHostMessage) => void;
20
- }
21
-
22
- type ResultMessage = Extract<KernelToHostMessage, { type: "result" }>;
23
- type ToolCallMessage = Extract<KernelToHostMessage, { type: "tool-call" }>;
24
-
25
- interface QueuedRun {
26
- readonly input: JavaScriptRunInput;
27
- resolve(message: ResultMessage): void;
28
- }
15
+ export type { JavaScriptKernelMode, JavaScriptKernelOptions, JavaScriptRunInput } from "./kernel-contract.ts";
16
+ export { JavaScriptKernelClosedError } from "./kernel-contract.ts";
29
17
 
30
18
  export class JavaScriptKernel {
31
19
  readonly #options: JavaScriptKernelOptions;
32
20
  #worker: WorkerLike | null = null;
33
21
  #mode: JavaScriptKernelMode = "worker";
22
+ #lifecycle: LifecycleState = "open";
34
23
  #ready: Promise<void> | null = null;
35
- #queue: QueuedRun[] = [];
36
- #active: QueuedRun | null = null;
24
+ #startupAbort: AbortController | null = null;
25
+ #generation = 0;
26
+ #activation: Promise<void> | null = null;
27
+ #recovery: Promise<void> | null = null;
28
+ #closePromise: Promise<void> | null = null;
29
+ readonly #runs = new JavaScriptRunQueue();
37
30
  #timeout: NodeJS.Timeout | null = null;
38
31
  #toolWaiters: Array<(message: ToolCallMessage) => void> = [];
39
32
  #pendingToolCalls: ToolCallMessage[] = [];
@@ -47,22 +40,32 @@ export class JavaScriptKernel {
47
40
  }
48
41
 
49
42
  async run(input: JavaScriptRunInput): Promise<ResultMessage> {
50
- await this.#ensureReady();
51
- const promise = new Promise<ResultMessage>((resolve) => {
52
- this.#queue.push({ input, resolve });
53
- });
54
- this.#startNext();
43
+ assertJavaScriptKernelOpen(this.#lifecycle, "run");
44
+ const promise = this.#runs.enqueue(input);
45
+ this.#activate();
55
46
  return await promise;
56
47
  }
57
48
 
49
+ async interrupt(reason = "interrupted"): Promise<void> {
50
+ assertJavaScriptKernelOpen(this.#lifecycle, "interrupt");
51
+ const active = this.#runs.active;
52
+ const target = this.#runs.takeInterruptTarget();
53
+ if (!target) return;
54
+ if (target === active) this.#clearTimeout();
55
+ this.#runs.settle(target, stoppedResult(target.input.cellId, `JS cell interrupted: ${reason}`));
56
+ await this.#restartAfterStop();
57
+ }
58
+
58
59
  async reset(): Promise<void> {
60
+ assertJavaScriptKernelOpen(this.#lifecycle, "reset");
59
61
  await this.#terminate();
60
- this.#ready = null;
62
+ assertJavaScriptKernelOpen(this.#lifecycle, "reset");
61
63
  await this.#ensureReady();
64
+ this.#startNext();
62
65
  }
63
66
 
64
67
  deliverToolReply(message: Extract<HostToKernelMessage, { type: "tool-reply" }>): void {
65
- this.#worker?.postMessage(message);
68
+ if (this.#lifecycle === "open") this.#worker?.postMessage(message);
66
69
  }
67
70
 
68
71
  async nextToolCall(): Promise<ToolCallMessage> {
@@ -72,62 +75,123 @@ export class JavaScriptKernel {
72
75
  }
73
76
 
74
77
  async close(): Promise<void> {
78
+ if (this.#closePromise) return await this.#closePromise;
75
79
  this.#worker?.postMessage({ type: "close" });
76
- await this.#terminate();
80
+ this.#lifecycle = "closing";
81
+ this.#runs.settleAll("JS kernel closed");
82
+ const recovery = this.#recovery;
83
+ const closePromise = (async () => {
84
+ if (recovery) await recovery;
85
+ await this.#terminate();
86
+ })().finally(() => {
87
+ this.#lifecycle = "closed";
88
+ });
89
+ this.#closePromise = closePromise;
90
+ return await closePromise;
91
+ }
92
+
93
+ #activate(): void {
94
+ if (this.#activation || this.#lifecycle !== "open" || this.#runs.active || !this.#runs.hasWaiting) return;
95
+ const activation = this.#activateWhenReady();
96
+ this.#activation = activation;
97
+ void activation.then(() => {
98
+ if (this.#activation === activation) this.#activation = null;
99
+ if (this.#lifecycle === "open" && !this.#runs.active && this.#runs.hasWaiting) this.#activate();
100
+ });
101
+ }
102
+
103
+ async #activateWhenReady(): Promise<void> {
104
+ try {
105
+ await this.#ensureReady();
106
+ if (this.#lifecycle === "open") this.#startNext();
107
+ } catch (error) {
108
+ if (error instanceof WorkerStartupCancelledError) return;
109
+ this.#runs.rejectWaiting(error instanceof Error ? error : new Error(String(error)));
110
+ }
77
111
  }
78
112
 
79
113
  async #ensureReady(): Promise<void> {
80
- if (!this.#ready) this.#ready = this.#startWorker();
114
+ assertJavaScriptKernelOpen(this.#lifecycle, "run");
115
+ if (!this.#ready) {
116
+ const generation = ++this.#generation;
117
+ const controller = new AbortController();
118
+ this.#startupAbort = controller;
119
+ const ready = this.#startWorker(generation, controller.signal);
120
+ this.#ready = ready;
121
+ void ready.then(
122
+ () => {
123
+ if (this.#ready === ready) this.#startupAbort = null;
124
+ },
125
+ () => {
126
+ if (this.#ready === ready) {
127
+ this.#ready = null;
128
+ this.#startupAbort = null;
129
+ }
130
+ },
131
+ );
132
+ }
81
133
  return await this.#ready;
82
134
  }
83
135
 
84
- async #startWorker(): Promise<void> {
85
- const worker = this.#spawnWorker();
86
- this.#worker = worker;
87
- this.#mode = worker.mode;
88
- const ready = waitForReady(worker);
89
- worker.onMessage((message) => this.#handleMessage(message));
90
- worker.onError((error) => this.#handleCrash(error));
91
- worker.postMessage({ type: "init", sessionId: this.#options.sessionId, connection: { port: 1, token: "local" } });
136
+ async #startWorker(generation: number, signal: AbortSignal): Promise<void> {
137
+ let worker = this.#spawnWorker();
138
+ this.#publishWorker(worker, generation);
92
139
  try {
93
- await ready;
140
+ await this.#initializeWorker(worker, signal);
141
+ return;
94
142
  } catch (error) {
143
+ if (!this.#isCurrent(worker, generation) || error instanceof WorkerStartupCancelledError) {
144
+ await worker.terminate();
145
+ throw new WorkerStartupCancelledError();
146
+ }
95
147
  if (worker.mode === "inline") throw error;
96
- await worker.terminate().catch(() => undefined);
97
- const inline = createInlineWorker(this.#options.parallelPoolWidth);
98
- this.#worker = inline;
99
- this.#mode = "inline";
100
- const inlineReady = waitForReady(inline);
101
- inline.onMessage((message) => this.#handleMessage(message));
102
- inline.onError((crash) => this.#handleCrash(crash));
103
- inline.postMessage({
104
- type: "init",
105
- sessionId: this.#options.sessionId,
106
- connection: { port: 1, token: "local" },
107
- });
108
- await inlineReady;
148
+ this.#worker = null;
149
+ await worker.terminate();
109
150
  }
151
+ if (this.#lifecycle !== "open" || generation !== this.#generation) throw new WorkerStartupCancelledError();
152
+ worker = createInlineWorker(this.#options.cwd, this.#options.parallelPoolWidth);
153
+ this.#publishWorker(worker, generation);
154
+ await this.#initializeWorker(worker, signal);
110
155
  }
111
156
 
112
157
  #spawnWorker(): WorkerLike {
113
158
  try {
114
159
  const url = this.#options.workerEntryUrl ?? new URL("./worker-entry.js", import.meta.url);
115
- const worker = new Worker(url, {
116
- workerData: { cwd: this.#options.cwd, parallelPoolWidth: this.#options.parallelPoolWidth },
117
- });
118
- return wrapNodeWorker(worker);
119
- } catch {
120
- return createInlineWorker(this.#options.parallelPoolWidth);
160
+ return spawnNodeWorker(url, this.#options.cwd, this.#options.parallelPoolWidth);
161
+ } catch (error) {
162
+ if (!(error instanceof Error)) throw error;
163
+ return createInlineWorker(this.#options.cwd, this.#options.parallelPoolWidth);
121
164
  }
122
165
  }
123
166
 
167
+ #publishWorker(worker: WorkerLike, generation: number): void {
168
+ if (this.#lifecycle !== "open" || generation !== this.#generation) throw new WorkerStartupCancelledError();
169
+ this.#worker = worker;
170
+ this.#mode = worker.mode;
171
+ worker.onMessage((message) => {
172
+ if (this.#isCurrent(worker, generation)) this.#handleMessage(message);
173
+ });
174
+ worker.onError((error) => {
175
+ if (this.#isCurrent(worker, generation)) this.#handleCrash(error);
176
+ });
177
+ }
178
+
179
+ async #initializeWorker(worker: WorkerLike, signal: AbortSignal): Promise<void> {
180
+ const ready = waitForReady(worker, signal);
181
+ worker.postMessage({ type: "init", sessionId: this.#options.sessionId, connection: { port: 1, token: "local" } });
182
+ await ready;
183
+ }
184
+
185
+ #isCurrent(worker: WorkerLike, generation: number): boolean {
186
+ return this.#lifecycle === "open" && this.#worker === worker && this.#generation === generation;
187
+ }
188
+
124
189
  #startNext(): void {
125
- if (this.#active || !this.#worker) return;
126
- const next = this.#queue.shift();
190
+ if (this.#lifecycle !== "open" || this.#runs.active || !this.#worker) return;
191
+ const next = this.#runs.startNext(performance.now());
127
192
  if (!next) return;
128
- this.#active = next;
129
193
  if (next.input.timeoutMs) {
130
- this.#timeout = setTimeout(() => void this.#timeoutActive(next.input.cellId), next.input.timeoutMs);
194
+ this.#timeout = setTimeout(() => void this.#timeoutActive(next), next.input.timeoutMs);
131
195
  }
132
196
  this.#worker.postMessage({
133
197
  type: "run",
@@ -137,27 +201,45 @@ export class JavaScriptKernel {
137
201
  });
138
202
  }
139
203
 
140
- async #timeoutActive(cellId: string): Promise<void> {
141
- const active = this.#active;
142
- if (!active || active.input.cellId !== cellId) return;
143
- const durationMs = active.input.timeoutMs ?? 0;
144
- active.resolve({
204
+ async #timeoutActive(run: PendingJavaScriptRun): Promise<void> {
205
+ if (!this.#runs.releaseActive(run)) return;
206
+ const durationMs = run.input.timeoutMs ?? 0;
207
+ this.#runs.settle(run, {
145
208
  type: "result",
146
- cellId,
209
+ cellId: run.input.cellId,
147
210
  ok: false,
148
211
  error: { message: `JS cell timed out after ${durationMs}ms` },
149
212
  durationMs,
150
213
  });
151
- this.#active = null;
152
- await this.#terminate();
153
- this.#ready = null;
154
- await this.#ensureReady();
155
- this.#startNext();
214
+ await this.#restartAfterStop();
215
+ }
216
+
217
+ async #restartAfterStop(): Promise<void> {
218
+ if (this.#recovery) return await this.#recovery;
219
+ const recovery = this.#performRestartAfterStop();
220
+ this.#recovery = recovery;
221
+ try {
222
+ await recovery;
223
+ } finally {
224
+ if (this.#recovery === recovery) this.#recovery = null;
225
+ }
226
+ }
227
+
228
+ async #performRestartAfterStop(): Promise<void> {
229
+ try {
230
+ await this.#terminate();
231
+ if (this.#lifecycle !== "open") return;
232
+ await this.#ensureReady();
233
+ if (this.#lifecycle === "open") this.#startNext();
234
+ } catch (error) {
235
+ if (error instanceof WorkerStartupCancelledError) return;
236
+ this.#runs.rejectWaiting(error instanceof Error ? error : new Error(String(error)));
237
+ }
156
238
  }
157
239
 
158
240
  #handleMessage(message: KernelToHostMessage): void {
159
241
  this.#options.onMessage?.(message);
160
- this.#active?.input.onMessage?.(message);
242
+ this.#runs.active?.input.onMessage?.(message);
161
243
  if (message.type === "tool-call") {
162
244
  const waiter = this.#toolWaiters.shift();
163
245
  if (waiter) waiter(message);
@@ -165,83 +247,44 @@ export class JavaScriptKernel {
165
247
  return;
166
248
  }
167
249
  if (message.type !== "result") return;
168
- if (this.#timeout) clearTimeout(this.#timeout);
169
- this.#timeout = null;
170
- const active = this.#active;
171
- this.#active = null;
172
- active?.resolve(message);
250
+ const active = this.#runs.active;
251
+ if (!active || active.input.cellId !== message.cellId) return;
252
+ this.#clearTimeout();
253
+ this.#runs.releaseActive(active);
254
+ this.#runs.settle(active, message);
173
255
  this.#startNext();
174
256
  }
175
257
 
176
258
  #handleCrash(error: Error): void {
177
- const active = this.#active;
178
- if (!active) return;
179
- this.#active = null;
180
- active.resolve({
181
- type: "result",
182
- cellId: active.input.cellId,
183
- ok: false,
184
- error: bridgeError(error),
185
- durationMs: 0,
186
- });
259
+ const active = this.#runs.active;
260
+ if (!active && this.#startupAbort) return;
261
+ this.#clearTimeout();
262
+ if (active) {
263
+ this.#runs.releaseActive(active);
264
+ this.#runs.settle(active, {
265
+ type: "result",
266
+ cellId: active.input.cellId,
267
+ ok: false,
268
+ error: bridgeError(error),
269
+ durationMs: this.#runs.durationMs(active, performance.now()),
270
+ });
271
+ }
272
+ void this.#restartAfterStop();
187
273
  }
188
274
 
189
- async #terminate(): Promise<void> {
275
+ #clearTimeout(): void {
190
276
  if (this.#timeout) clearTimeout(this.#timeout);
191
277
  this.#timeout = null;
278
+ }
279
+
280
+ async #terminate(): Promise<void> {
281
+ this.#clearTimeout();
282
+ this.#generation += 1;
283
+ this.#startupAbort?.abort();
284
+ this.#startupAbort = null;
285
+ this.#ready = null;
192
286
  const worker = this.#worker;
193
287
  this.#worker = null;
194
- await worker?.terminate().catch(() => undefined);
288
+ if (worker) await worker.terminate();
195
289
  }
196
290
  }
197
-
198
- function waitForReady(worker: WorkerLike): Promise<void> {
199
- return new Promise((resolve, reject) => {
200
- const offMessage = worker.onMessage((message) => {
201
- if (message.type === "ready") {
202
- offMessage();
203
- offError();
204
- resolve();
205
- } else if (message.type === "init-failed") {
206
- offMessage();
207
- offError();
208
- reject(errorFromBridge(message.error));
209
- }
210
- });
211
- const offError = worker.onError((error) => {
212
- offMessage();
213
- offError();
214
- reject(error);
215
- });
216
- });
217
- }
218
-
219
- function wrapNodeWorker(worker: Worker): WorkerLike {
220
- return {
221
- mode: "worker",
222
- postMessage: (message) => worker.postMessage(message),
223
- onMessage(handler) {
224
- const listener = (message: KernelToHostMessage): void => handler(message);
225
- worker.on("message", listener);
226
- return () => worker.off("message", listener);
227
- },
228
- onError(handler) {
229
- worker.on("error", handler);
230
- return () => worker.off("error", handler);
231
- },
232
- async terminate() {
233
- await worker.terminate();
234
- },
235
- };
236
- }
237
-
238
- function errorFromBridge(error: { message: string; name?: string; stack?: string }): Error {
239
- const result = new Error(error.message);
240
- if (error.name) result.name = error.name;
241
- if (error.stack) result.stack = error.stack;
242
- return result;
243
- }
244
-
245
- function bridgeError(error: Error): { message: string; name?: string; stack?: string } {
246
- return { message: error.message, name: error.name, stack: error.stack };
247
- }
@@ -0,0 +1,23 @@
1
+ import { parentPort, workerData } from "node:worker_threads";
2
+ import { createWorkerCore } from "./worker-core.js";
3
+
4
+ if (!parentPort) throw new Error("JS kernel inline fallback worker missing parentPort");
5
+
6
+ const transport = {
7
+ send(message) {
8
+ parentPort.postMessage(message);
9
+ },
10
+ onMessage(handler) {
11
+ parentPort.on("message", handler);
12
+ return () => parentPort.off("message", handler);
13
+ },
14
+ close() {
15
+ parentPort.close();
16
+ setTimeout(() => process.exit(0), 0);
17
+ },
18
+ };
19
+
20
+ createWorkerCore(transport, {
21
+ cwd: workerData.cwd,
22
+ parallelPoolWidth: workerData.parallelPoolWidth,
23
+ });
@@ -1,11 +1,6 @@
1
- import { inspect } from "node:util";
2
1
  import type { HostToKernelMessage, KernelToHostMessage } from "../../bridge/protocol.ts";
3
- import type { JavaScriptKernelMode } from "./context-manager.ts";
4
-
5
- type AsyncFunctionType = new (...args: string[]) => (...values: unknown[]) => Promise<unknown>;
6
- const AsyncFunction = async function inlineAsyncFunction() {
7
- return undefined;
8
- }.constructor as AsyncFunctionType;
2
+ import type { JavaScriptKernelMode } from "./kernel-contract.ts";
3
+ import { spawnNodeWorker } from "./worker-host.ts";
9
4
 
10
5
  export interface WorkerLike {
11
6
  readonly mode: JavaScriptKernelMode;
@@ -15,172 +10,6 @@ export interface WorkerLike {
15
10
  terminate(): Promise<void>;
16
11
  }
17
12
 
18
- export function createInlineWorker(parallelPoolWidth: number): WorkerLike {
19
- return new InlineWorker(parallelPoolWidth);
20
- }
21
-
22
- class InlineWorker implements WorkerLike {
23
- readonly mode = "inline";
24
- readonly #parallelPoolWidth: number;
25
- readonly #state: Record<string, unknown> = {};
26
- #handlers: Array<(message: KernelToHostMessage) => void> = [];
27
- #errorHandlers: Array<(error: Error) => void> = [];
28
- #pendingTools = new Map<string, { resolve(value: unknown): void; reject(error: Error): void }>();
29
-
30
- constructor(parallelPoolWidth: number) {
31
- this.#parallelPoolWidth = parallelPoolWidth;
32
- }
33
-
34
- postMessage(message: HostToKernelMessage): void {
35
- if (message.type === "init") this.#emit({ type: "ready" });
36
- else if (message.type === "run") void this.#run(message.cellId, message.code);
37
- else if (message.type === "tool-reply") this.#deliverToolReply(message);
38
- else if (message.type === "close") this.#emit({ type: "closed" });
39
- }
40
-
41
- onMessage(handler: (message: KernelToHostMessage) => void): () => void {
42
- this.#handlers.push(handler);
43
- return () => {
44
- this.#handlers = this.#handlers.filter((existing) => existing !== handler);
45
- };
46
- }
47
-
48
- onError(handler: (error: Error) => void): () => void {
49
- this.#errorHandlers.push(handler);
50
- return () => {
51
- this.#errorHandlers = this.#errorHandlers.filter((existing) => existing !== handler);
52
- };
53
- }
54
-
55
- async terminate(): Promise<void> {
56
- this.#handlers = [];
57
- this.#errorHandlers = [];
58
- this.#pendingTools.clear();
59
- }
60
-
61
- async #run(cellId: string, code: string): Promise<void> {
62
- const started = Date.now();
63
- try {
64
- const value = await this.#execute(code);
65
- this.#emit({
66
- type: "result",
67
- cellId,
68
- ok: true,
69
- valueRepr: valueRepr(value),
70
- durationMs: Date.now() - started,
71
- });
72
- } catch (error) {
73
- this.#emit({ type: "result", cellId, ok: false, error: bridgeError(error), durationMs: Date.now() - started });
74
- }
75
- }
76
-
77
- async #execute(code: string): Promise<unknown> {
78
- const transformed = code.replace(/(^|\n)\s*(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/gu, "$1state.$2 =");
79
- const body = /\breturn\b/u.test(transformed) ? transformed : `return await (${transformed})`;
80
- const fn = new AsyncFunction(
81
- "state",
82
- "tool",
83
- "parallel",
84
- "print",
85
- "display",
86
- "log",
87
- "phase",
88
- `with (state) { ${body} }`,
89
- );
90
- return await fn(
91
- this.#state,
92
- this.#toolProxy(),
93
- this.#parallel.bind(this),
94
- this.#print.bind(this),
95
- this.#display.bind(this),
96
- this.#log.bind(this),
97
- this.#phase.bind(this),
98
- );
99
- }
100
-
101
- #toolProxy(): unknown {
102
- return new Proxy(
103
- {},
104
- {
105
- get: (_target, prop) => {
106
- if (typeof prop !== "string") return undefined;
107
- return async (args: unknown) => await this.#callTool(prop, args);
108
- },
109
- },
110
- );
111
- }
112
-
113
- async #callTool(toolName: string, args: unknown): Promise<unknown> {
114
- const callId = `js-${crypto.randomUUID()}`;
115
- const promise = new Promise<unknown>((resolve, reject) => this.#pendingTools.set(callId, { resolve, reject }));
116
- this.#emit({ type: "tool-call", callId, toolName, args });
117
- return await promise;
118
- }
119
-
120
- #deliverToolReply(message: Extract<HostToKernelMessage, { type: "tool-reply" }>): void {
121
- const pending = this.#pendingTools.get(message.callId);
122
- if (!pending) return;
123
- this.#pendingTools.delete(message.callId);
124
- if (message.ok) pending.resolve(message.value);
125
- else pending.reject(errorFromBridge(message.error));
126
- }
127
-
128
- async #parallel(thunks: Array<() => Promise<unknown>>): Promise<unknown[]> {
129
- const results = new Array<unknown>(thunks.length);
130
- let next = 0;
131
- await Promise.all(
132
- Array.from({ length: Math.min(this.#parallelPoolWidth, thunks.length) }, async () => {
133
- while (next < thunks.length) {
134
- const index = next;
135
- next += 1;
136
- results[index] = await thunks[index]();
137
- }
138
- }),
139
- );
140
- return results;
141
- }
142
-
143
- #print(...values: unknown[]): void {
144
- this.#emit({ type: "text", stream: "stdout", data: `${values.map(formatValue).join(" ")}\n` });
145
- }
146
-
147
- #display(value: unknown): void {
148
- this.#emit({
149
- type: "display",
150
- mimeType: "application/json",
151
- dataBase64: Buffer.from(JSON.stringify(value), "utf8").toString("base64"),
152
- });
153
- }
154
-
155
- #log(message: unknown): void {
156
- this.#emit({ type: "log", message: String(message) });
157
- }
158
-
159
- #phase(title: unknown): void {
160
- this.#emit({ type: "phase", title: String(title) });
161
- }
162
-
163
- #emit(message: KernelToHostMessage): void {
164
- for (const handler of this.#handlers) handler(message);
165
- }
166
- }
167
-
168
- function valueRepr(value: unknown): string | undefined {
169
- return value === undefined ? undefined : JSON.stringify(value);
170
- }
171
-
172
- function formatValue(value: unknown): string {
173
- return typeof value === "string" ? value : inspect(value, { colors: false, depth: 5 });
174
- }
175
-
176
- function errorFromBridge(error: { message: string; name?: string; stack?: string }): Error {
177
- const result = new Error(error.message);
178
- if (error.name) result.name = error.name;
179
- if (error.stack) result.stack = error.stack;
180
- return result;
181
- }
182
-
183
- function bridgeError(error: unknown): { message: string; name?: string; stack?: string } {
184
- if (error instanceof Error) return { message: error.message, name: error.name, stack: error.stack };
185
- return { message: String(error) };
13
+ export function createInlineWorker(cwd: string, parallelPoolWidth: number): WorkerLike {
14
+ return spawnNodeWorker(new URL("./inline-worker-entry.js", import.meta.url), cwd, parallelPoolWidth, "inline");
186
15
  }
@@ -0,0 +1,38 @@
1
+ import type { KernelToHostMessage } from "../../bridge/protocol.ts";
2
+
3
+ export type ResultMessage = Extract<KernelToHostMessage, { type: "result" }>;
4
+ export type ToolCallMessage = Extract<KernelToHostMessage, { type: "tool-call" }>;
5
+
6
+ export type JavaScriptKernelMode = "worker" | "inline";
7
+
8
+ export interface JavaScriptKernelOptions {
9
+ readonly sessionId: string;
10
+ readonly cwd: string;
11
+ readonly parallelPoolWidth: number;
12
+ readonly onMessage?: (message: KernelToHostMessage) => void;
13
+ readonly workerEntryUrl?: URL;
14
+ }
15
+
16
+ export interface JavaScriptRunInput {
17
+ readonly cellId: string;
18
+ readonly code: string;
19
+ readonly timeoutMs?: number;
20
+ readonly onMessage?: (message: KernelToHostMessage) => void;
21
+ }
22
+
23
+ export type KernelOperation = "run" | "reset" | "interrupt";
24
+ export type LifecycleState = "open" | "closing" | "closed";
25
+
26
+ export class JavaScriptKernelClosedError extends Error {
27
+ readonly name = "JavaScriptKernelClosedError";
28
+ readonly operation: KernelOperation;
29
+
30
+ constructor(operation: KernelOperation) {
31
+ super(`Cannot ${operation}: JavaScript kernel is closed`);
32
+ this.operation = operation;
33
+ }
34
+ }
35
+
36
+ export function assertJavaScriptKernelOpen(lifecycle: LifecycleState, operation: KernelOperation): void {
37
+ if (lifecycle !== "open") throw new JavaScriptKernelClosedError(operation);
38
+ }