@code-yeongyu/senpi 2026.9.15 → 2026.9.16

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 (227) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/core/extensions/builtin/config-reload/index.d.ts.map +1 -1
  3. package/dist/core/extensions/builtin/config-reload/index.js +25 -8
  4. package/dist/core/extensions/builtin/config-reload/index.js.map +1 -1
  5. package/dist/core/extensions/builtin/config-reload/watch-engine.d.ts +4 -5
  6. package/dist/core/extensions/builtin/config-reload/watch-engine.d.ts.map +1 -1
  7. package/dist/core/extensions/builtin/config-reload/watch-engine.js +14 -18
  8. package/dist/core/extensions/builtin/config-reload/watch-engine.js.map +1 -1
  9. package/dist/core/extensions/builtin/config-reload/watch-event-source.d.ts.map +1 -1
  10. package/dist/core/extensions/builtin/config-reload/watch-event-source.js +32 -9
  11. package/dist/core/extensions/builtin/config-reload/watch-event-source.js.map +1 -1
  12. package/dist/core/extensions/builtin/cursor-cli-oauth/catalog-refresh.d.ts +23 -0
  13. package/dist/core/extensions/builtin/cursor-cli-oauth/catalog-refresh.d.ts.map +1 -0
  14. package/dist/core/extensions/builtin/cursor-cli-oauth/catalog-refresh.js +48 -0
  15. package/dist/core/extensions/builtin/cursor-cli-oauth/catalog-refresh.js.map +1 -0
  16. package/dist/core/extensions/builtin/cursor-cli-oauth/environment.d.ts +13 -0
  17. package/dist/core/extensions/builtin/cursor-cli-oauth/environment.d.ts.map +1 -0
  18. package/dist/core/extensions/builtin/cursor-cli-oauth/environment.js +24 -0
  19. package/dist/core/extensions/builtin/cursor-cli-oauth/environment.js.map +1 -0
  20. package/dist/core/extensions/builtin/cursor-cli-oauth/executable.d.ts +1 -0
  21. package/dist/core/extensions/builtin/cursor-cli-oauth/executable.d.ts.map +1 -1
  22. package/dist/core/extensions/builtin/cursor-cli-oauth/executable.js +7 -1
  23. package/dist/core/extensions/builtin/cursor-cli-oauth/executable.js.map +1 -1
  24. package/dist/core/extensions/builtin/cursor-cli-oauth/index.d.ts +2 -0
  25. package/dist/core/extensions/builtin/cursor-cli-oauth/index.d.ts.map +1 -1
  26. package/dist/core/extensions/builtin/cursor-cli-oauth/index.js +11 -9
  27. package/dist/core/extensions/builtin/cursor-cli-oauth/index.js.map +1 -1
  28. package/dist/core/extensions/builtin/cursor-cli-oauth/models-probe.d.ts +18 -0
  29. package/dist/core/extensions/builtin/cursor-cli-oauth/models-probe.d.ts.map +1 -0
  30. package/dist/core/extensions/builtin/cursor-cli-oauth/models-probe.js +61 -0
  31. package/dist/core/extensions/builtin/cursor-cli-oauth/models-probe.js.map +1 -0
  32. package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts +4 -1
  33. package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts.map +1 -1
  34. package/dist/core/extensions/builtin/cursor-cli-oauth/models.js +1 -44
  35. package/dist/core/extensions/builtin/cursor-cli-oauth/models.js.map +1 -1
  36. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.d.ts +22 -0
  37. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.d.ts.map +1 -1
  38. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.js +1 -1
  39. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.js.map +1 -1
  40. package/dist/core/extensions/builtin/cursor-cli-oauth/transport.d.ts.map +1 -1
  41. package/dist/core/extensions/builtin/cursor-cli-oauth/transport.js +2 -13
  42. package/dist/core/extensions/builtin/cursor-cli-oauth/transport.js.map +1 -1
  43. package/dist/core/extensions/builtin/terminal/monitor-file-digest.d.ts +3 -0
  44. package/dist/core/extensions/builtin/terminal/monitor-file-digest.d.ts.map +1 -0
  45. package/dist/core/extensions/builtin/terminal/monitor-file-digest.js +25 -0
  46. package/dist/core/extensions/builtin/terminal/monitor-file-digest.js.map +1 -0
  47. package/dist/core/extensions/builtin/terminal/monitor-file-watch.d.ts +9 -0
  48. package/dist/core/extensions/builtin/terminal/monitor-file-watch.d.ts.map +1 -0
  49. package/dist/core/extensions/builtin/terminal/monitor-file-watch.js +33 -0
  50. package/dist/core/extensions/builtin/terminal/monitor-file-watch.js.map +1 -0
  51. package/dist/core/extensions/builtin/terminal/monitor-line-buffer.d.ts +6 -0
  52. package/dist/core/extensions/builtin/terminal/monitor-line-buffer.d.ts.map +1 -0
  53. package/dist/core/extensions/builtin/terminal/monitor-line-buffer.js +22 -0
  54. package/dist/core/extensions/builtin/terminal/monitor-line-buffer.js.map +1 -0
  55. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  56. package/dist/core/extensions/builtin/terminal/monitor-registry.js +14 -35
  57. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  58. package/dist/core/extensions/kernel-tools-context.d.ts +13 -0
  59. package/dist/core/extensions/kernel-tools-context.d.ts.map +1 -0
  60. package/dist/core/extensions/kernel-tools-context.js +3 -0
  61. package/dist/core/extensions/kernel-tools-context.js.map +1 -0
  62. package/dist/core/extensions/runner.d.ts +19 -0
  63. package/dist/core/extensions/runner.d.ts.map +1 -1
  64. package/dist/core/extensions/runner.js +103 -1
  65. package/dist/core/extensions/runner.js.map +1 -1
  66. package/dist/core/extensions/types.d.ts +21 -0
  67. package/dist/core/extensions/types.d.ts.map +1 -1
  68. package/dist/core/extensions/types.js.map +1 -1
  69. package/dist/core/resource-loader.d.ts.map +1 -1
  70. package/dist/core/resource-loader.js +9 -17
  71. package/dist/core/resource-loader.js.map +1 -1
  72. package/dist/core/session-manager.d.ts +3 -0
  73. package/dist/core/session-manager.d.ts.map +1 -1
  74. package/dist/core/session-manager.js +14 -0
  75. package/dist/core/session-manager.js.map +1 -1
  76. package/dist/core/settings-manager.d.ts +23 -0
  77. package/dist/core/settings-manager.d.ts.map +1 -1
  78. package/dist/core/settings-manager.js +41 -0
  79. package/dist/core/settings-manager.js.map +1 -1
  80. package/dist/core/tools/index.d.ts +1 -1
  81. package/dist/core/tools/index.d.ts.map +1 -1
  82. package/dist/core/tools/index.js +4 -3
  83. package/dist/core/tools/index.js.map +1 -1
  84. package/dist/core/tools/read.d.ts +3 -1
  85. package/dist/core/tools/read.d.ts.map +1 -1
  86. package/dist/core/tools/read.js +16 -2
  87. package/dist/core/tools/read.js.map +1 -1
  88. package/dist/index.d.ts +1 -0
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +1 -0
  91. package/dist/index.js.map +1 -1
  92. package/dist/modes/interactive/interactive-mode.js +3 -3
  93. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  94. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  95. package/dist/modes/rpc/host-watchdog.js +5 -24
  96. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  97. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  98. package/dist/modes/rpc/rpc-mode.js +3 -8
  99. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  100. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  101. package/dist/modes/rpc/session-command-router.js +3 -0
  102. package/dist/modes/rpc/session-command-router.js.map +1 -1
  103. package/dist/modes/rpc/session-worker-client.d.ts +3 -0
  104. package/dist/modes/rpc/session-worker-client.d.ts.map +1 -1
  105. package/dist/modes/rpc/session-worker-client.js +17 -6
  106. package/dist/modes/rpc/session-worker-client.js.map +1 -1
  107. package/dist/modes/rpc/shutdown.d.ts +3 -0
  108. package/dist/modes/rpc/shutdown.d.ts.map +1 -0
  109. package/dist/modes/rpc/shutdown.js +17 -0
  110. package/dist/modes/rpc/shutdown.js.map +1 -0
  111. package/docs/extensions.md +3 -0
  112. package/docs/settings.md +12 -0
  113. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +36 -0
  114. package/node_modules/@code-yeongyu/senpi-codemode/README.md +24 -0
  115. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  116. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/http-server.ts +7 -1
  117. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/kernel-tools-protocol.ts +85 -0
  118. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +6 -0
  119. package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/agent-bridge.ts +30 -11
  120. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +11 -0
  121. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +10 -0
  122. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/prelude.jl +46 -0
  123. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/runner.jl +1 -1
  124. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +58 -10
  125. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +12 -0
  126. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-context.d.ts +13 -0
  127. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-context.js +7 -0
  128. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-define.d.ts +10 -0
  129. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-define.js +12 -0
  130. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-errors.js +22 -0
  131. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-errors.ts +26 -0
  132. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-host.ts +134 -0
  133. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-naming.js +28 -0
  134. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-parse.d.ts +5 -0
  135. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-parse.js +255 -0
  136. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-pump.js +105 -0
  137. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-registry.d.ts +27 -0
  138. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-registry.js +127 -0
  139. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-schema.js +69 -0
  140. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-types.ts +42 -0
  141. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/prelude.ts +2 -0
  142. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +26 -3
  143. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +20 -8
  144. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-slot.ts +6 -1
  145. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-startup.ts +9 -7
  146. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/workpool.js +37 -0
  147. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/kernel-tools-unavailable.ts +5 -0
  148. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +13 -0
  149. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py +57 -3
  150. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/prelude.rb +13 -2
  151. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/workpool.rb +38 -0
  152. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +13 -0
  153. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-queue.ts +9 -1
  154. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt-template.ts +4 -2
  155. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-handler.ts +8 -1
  156. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +36 -71
  157. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image-resize.ts +52 -0
  158. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image.ts +37 -72
  159. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/managed-cell.ts +78 -0
  160. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +30 -5
  161. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/run-eval-cell.ts +49 -30
  162. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/terminal-snapshot-store.ts +35 -0
  163. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/tool-result-marshal.ts +23 -0
  164. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +2 -0
  165. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.d.ts.map +1 -1
  166. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js +51 -12
  167. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js.map +1 -1
  168. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.d.ts +3 -0
  169. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.d.ts.map +1 -1
  170. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.js +18 -3
  171. package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.js.map +1 -1
  172. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.d.ts +4 -0
  173. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.d.ts.map +1 -0
  174. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.js +268 -0
  175. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.js.map +1 -0
  176. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.d.ts +18 -0
  177. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.d.ts.map +1 -0
  178. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.js +69 -0
  179. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.js.map +1 -0
  180. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.d.ts +27 -0
  181. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.d.ts.map +1 -0
  182. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.js +118 -0
  183. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.js.map +1 -0
  184. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.d.ts +21 -0
  185. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.d.ts.map +1 -0
  186. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.js +24 -0
  187. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.js.map +1 -0
  188. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.d.ts +12 -0
  189. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.d.ts.map +1 -0
  190. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.js +92 -0
  191. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.js.map +1 -0
  192. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.d.ts +47 -0
  193. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.d.ts.map +1 -0
  194. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.js +8 -0
  195. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.js.map +1 -0
  196. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.d.ts +52 -0
  197. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.d.ts.map +1 -0
  198. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.js +155 -0
  199. package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.js.map +1 -0
  200. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +2 -0
  201. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
  202. package/node_modules/@earendil-works/pi-agent-core/dist/index.js +2 -0
  203. package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
  204. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  205. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
  206. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  207. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
  208. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  209. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  210. package/node_modules/@earendil-works/pi-ai/dist/providers/data/alibaba-token-plan.json +1 -1
  211. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  212. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  213. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  214. package/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan.json +1 -1
  215. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  216. package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.d.ts +15 -0
  217. package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.d.ts.map +1 -0
  218. package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.js +15 -0
  219. package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.js.map +1 -0
  220. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  221. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +10 -0
  222. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  223. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  224. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  225. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  226. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  227. package/package.json +7 -7
@@ -8,8 +8,12 @@ import {
8
8
  type JavaScriptRunInput,
9
9
  type LifecycleState,
10
10
  type ResultMessage,
11
+ resolveKernelToolNameSource,
11
12
  type ToolCallMessage,
12
13
  } from "./kernel-contract.ts";
14
+ import { kernelToolError } from "./kernel-tools-errors.ts";
15
+ import { KernelToolHostPump } from "./kernel-tools-host.ts";
16
+ import type { KernelToolsDescribeResult, KernelToolsInvokeRequest } from "./kernel-tools-types.ts";
13
17
  import { type JavaScriptKernelOptions, LocalModuleLoader } from "./local-module-loader.ts";
14
18
  import { terminateProcessTrees } from "./process-tree-host.ts";
15
19
  import { JavaScriptRunQueue, type PendingJavaScriptRun, stoppedResult } from "./run-queue.ts";
@@ -23,6 +27,10 @@ export { type JavaScriptWorkerEntryUrlOptions, resolveJsWorkerEntryUrl } from ".
23
27
  /** How long a lost worker's children get to honour SIGTERM before the host sends SIGKILL. */
24
28
  const WORKER_LOSS_CHILD_GRACE_MS = 1_000;
25
29
 
30
+ // Pull-API fallback queue bound: a nextToolCall consumer this far behind is already stalled, and the
31
+ // normal push path (onMessage) never reads the queue, so unbounded growth only pins tool args (#1695).
32
+ const MAX_PENDING_TOOL_CALLS = 256;
33
+
26
34
  export class JavaScriptKernel {
27
35
  readonly #options: JavaScriptKernelOptions;
28
36
  readonly #moduleLoader: LocalModuleLoader;
@@ -32,6 +40,10 @@ export class JavaScriptKernel {
32
40
  #recovery: Promise<void> | null = null;
33
41
  #closePromise: Promise<void> | null = null;
34
42
  readonly #runs = new JavaScriptRunQueue();
43
+ readonly #kernelTools = new KernelToolHostPump(
44
+ (message) => this.#slot.postMessage(message),
45
+ () => this.#lifecycle === "open" && this.#slot.present,
46
+ );
35
47
  #timeout: NodeJS.Timeout | null = null;
36
48
  #toolWaiters: Array<(message: ToolCallMessage) => void> = [];
37
49
  #pendingToolCalls: ToolCallMessage[] = [];
@@ -52,6 +64,18 @@ export class JavaScriptKernel {
52
64
  return this.#slot.mode;
53
65
  }
54
66
 
67
+ get kernelToolEvents(): EventTarget {
68
+ return this.#kernelTools.events;
69
+ }
70
+
71
+ describeKernelTools(names: readonly string[]): Promise<KernelToolsDescribeResult> {
72
+ return this.#kernelTools.describe(names);
73
+ }
74
+
75
+ invokeKernelTool(request: KernelToolsInvokeRequest, signal?: AbortSignal): Promise<unknown> {
76
+ return this.#kernelTools.invoke(request, signal);
77
+ }
78
+
55
79
  async run(input: JavaScriptRunInput): Promise<ResultMessage> {
56
80
  assertJavaScriptKernelOpen(this.#lifecycle, "run");
57
81
  const promise = this.#runs.enqueue(input);
@@ -77,6 +101,7 @@ export class JavaScriptKernel {
77
101
  async reset(): Promise<void> {
78
102
  assertJavaScriptKernelOpen(this.#lifecycle, "reset");
79
103
  await this.#terminate();
104
+ this.#clearToolCalls();
80
105
  assertJavaScriptKernelOpen(this.#lifecycle, "reset");
81
106
  await this.#ensureReady();
82
107
  this.#startNext();
@@ -97,6 +122,8 @@ export class JavaScriptKernel {
97
122
  this.#slot.postMessage({ type: "close" });
98
123
  this.#lifecycle = "closing";
99
124
  this.#runs.settleAll("JS kernel closed");
125
+ this.#kernelTools.rejectAll(kernelToolError("kernel_tool_stale", "JS kernel closed"));
126
+ this.#clearToolCalls();
100
127
  const recovery = this.#recovery;
101
128
  const closePromise = (async () => {
102
129
  if (recovery) await recovery;
@@ -140,6 +167,11 @@ export class JavaScriptKernel {
140
167
  if (next.input.timeoutMs) {
141
168
  this.#timeout = setTimeout(() => void this.#timeoutActive(next), next.input.timeoutMs);
142
169
  }
170
+ this.#slot.postMessage({
171
+ type: "kernel-tools-names",
172
+ hostToolNames: resolveKernelToolNameSource(this.#options.hostToolNames),
173
+ foreignLanguageNames: resolveKernelToolNameSource(this.#options.foreignLanguageNames),
174
+ });
143
175
  this.#slot.postMessage({
144
176
  type: "run",
145
177
  cellId: next.input.cellId,
@@ -170,15 +202,19 @@ export class JavaScriptKernel {
170
202
  message: string,
171
203
  durationMs = 0,
172
204
  ): Promise<{ readonly retained: boolean; readonly note?: string }> {
173
- run.interruptResult = { type: "result", cellId: run.input.cellId, ok: false, error: { message }, durationMs };
174
- run.interruptAck ??= Promise.withResolvers<void>();
175
- this.#slot.postMessage({ type: "interrupt", reason });
176
- if ((await awaitCooperativeSettlement(run)) === "settled") return { retained: run.settledByWorker };
177
- if (!this.#runs.releaseActive(run)) return { retained: run.settledByWorker };
178
- const retirement = await this.#terminate();
179
- this.#runs.settle(run, run.interruptResult ?? stoppedResult(run.input.cellId, message));
180
- void this.#recover(() => Promise.resolve());
181
- return retirement === "abandoned" ? { retained: false, note: abandonedWorkerNote() } : { retained: false };
205
+ try {
206
+ run.interruptResult = { type: "result", cellId: run.input.cellId, ok: false, error: { message }, durationMs };
207
+ run.interruptAck ??= Promise.withResolvers<void>();
208
+ this.#slot.postMessage({ type: "interrupt", reason });
209
+ if ((await awaitCooperativeSettlement(run)) === "settled") return { retained: run.settledByWorker };
210
+ if (!this.#runs.releaseActive(run)) return { retained: run.settledByWorker };
211
+ const retirement = await this.#terminate();
212
+ this.#runs.settle(run, run.interruptResult ?? stoppedResult(run.input.cellId, message));
213
+ void this.#recover(() => Promise.resolve());
214
+ return retirement === "abandoned" ? { retained: false, note: abandonedWorkerNote() } : { retained: false };
215
+ } finally {
216
+ this.#clearToolCalls();
217
+ }
182
218
  }
183
219
 
184
220
  async #restartAfterStop(): Promise<void> {
@@ -210,6 +246,7 @@ export class JavaScriptKernel {
210
246
  }
211
247
 
212
248
  #handleMessage(message: KernelToHostMessage): void {
249
+ if (this.#kernelTools.consume(message) && message.type !== "tool-call") return;
213
250
  if (message.type === "status" && message.event.op === INTERRUPT_ACK_OP) {
214
251
  this.#runs.active?.interruptAck?.resolve();
215
252
  return;
@@ -223,7 +260,10 @@ export class JavaScriptKernel {
223
260
  if (message.type === "tool-call") {
224
261
  const waiter = this.#toolWaiters.shift();
225
262
  if (waiter) waiter(message);
226
- else this.#pendingToolCalls.push(message);
263
+ else {
264
+ this.#pendingToolCalls.push(message);
265
+ if (this.#pendingToolCalls.length > MAX_PENDING_TOOL_CALLS) this.#pendingToolCalls.shift();
266
+ }
227
267
  return;
228
268
  }
229
269
  if (message.type !== "result") return;
@@ -240,6 +280,7 @@ export class JavaScriptKernel {
240
280
  const active = this.#runs.active;
241
281
  if (!active && this.#slot.startingUp) return;
242
282
  this.#clearTimeout();
283
+ this.#kernelTools.rejectAll(kernelToolError("kernel_tool_stale", error.message));
243
284
  if (active) {
244
285
  this.#runs.releaseActive(active);
245
286
  this.#runs.settle(active, {
@@ -250,9 +291,15 @@ export class JavaScriptKernel {
250
291
  durationMs: this.#runs.durationMs(active, performance.now()),
251
292
  });
252
293
  }
294
+ this.#clearToolCalls();
253
295
  void this.#restartAfterStop();
254
296
  }
255
297
 
298
+ #clearToolCalls(): void {
299
+ this.#pendingToolCalls.length = 0;
300
+ this.#toolWaiters.length = 0;
301
+ }
302
+
256
303
  #clearTimeout(): void {
257
304
  if (this.#timeout) clearTimeout(this.#timeout);
258
305
  this.#timeout = null;
@@ -271,6 +318,7 @@ export class JavaScriptKernel {
271
318
  */
272
319
  async #terminate(): Promise<WorkerRetirement> {
273
320
  this.#clearTimeout();
321
+ this.#kernelTools.rejectAll(kernelToolError("kernel_tool_stale", "JavaScript worker reset"));
274
322
  const retirement = await this.#slot.retire();
275
323
  await this.#retireWorkerChildren();
276
324
  return retirement;
@@ -6,6 +6,14 @@ export type ToolCallMessage = Extract<KernelToHostMessage, { type: "tool-call" }
6
6
 
7
7
  export type JavaScriptKernelMode = "worker" | "inline";
8
8
 
9
+ /** Snapshot or live provider consulted when the worker registry checks collisions. */
10
+ export type KernelToolNameSource = readonly string[] | (() => readonly string[]);
11
+
12
+ export function resolveKernelToolNameSource(names?: KernelToolNameSource): string[] {
13
+ if (typeof names === "function") return [...names()];
14
+ return names === undefined ? [] : [...names];
15
+ }
16
+
9
17
  export interface JavaScriptKernelOptions {
10
18
  readonly sessionId: string;
11
19
  readonly cwd: string;
@@ -14,6 +22,10 @@ export interface JavaScriptKernelOptions {
14
22
  readonly workerEntryUrl?: URL;
15
23
  /** Per-session PI_* values applied to the worker environment before the first cell runs. */
16
24
  readonly sessionEnv?: SessionEnvironment;
25
+ /** Host tool names denied as JS kernel-tool identifiers (init protocol). */
26
+ readonly hostToolNames?: KernelToolNameSource;
27
+ /** Tool names registered in another kernel language, denied as JS kernel-tool identifiers. */
28
+ readonly foreignLanguageNames?: KernelToolNameSource;
17
29
  }
18
30
 
19
31
  export interface JavaScriptRunInput {
@@ -0,0 +1,13 @@
1
+ export const kernelToolCallContext: {
2
+ run<T>(store: KernelToolCallStore, fn: () => T): T;
3
+ getStore(): KernelToolCallStore | undefined;
4
+ };
5
+
6
+ export type KernelToolCallStore = {
7
+ readonly pendingTools: Map<string, { resolve: (value: unknown) => void; reject: (error: Error) => void }>;
8
+ readonly callId: string;
9
+ readonly generation: number;
10
+ readonly signal: AbortSignal;
11
+ };
12
+
13
+ export function inKernelToolInvoke(): boolean;
@@ -0,0 +1,7 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
3
+ export const kernelToolCallContext = new AsyncLocalStorage();
4
+
5
+ export function inKernelToolInvoke() {
6
+ return kernelToolCallContext.getStore() != null;
7
+ }
@@ -0,0 +1,10 @@
1
+ export type KernelToolFunction = {
2
+ readonly name: string;
3
+ };
4
+
5
+ export function createToolNamespace(
6
+ define: (fn: KernelToolFunction, metadata?: unknown) => unknown,
7
+ callHost: (name: string, args: unknown) => Promise<unknown>,
8
+ ): ((fn: KernelToolFunction, metadata?: unknown) => unknown) & {
9
+ readonly [name: string]: (args?: unknown) => Promise<unknown>;
10
+ };
@@ -0,0 +1,12 @@
1
+ export function createToolNamespace(define, callHost) {
2
+ const registrar = function tool(fn, metadata) {
3
+ return define(fn, metadata);
4
+ };
5
+ return new Proxy(registrar, {
6
+ get(target, prop) {
7
+ if (typeof prop !== "string") return undefined;
8
+ if (prop in Function.prototype || prop === "arguments" || prop === "caller") return target[prop];
9
+ return async (args) => await callHost(prop, args ?? {});
10
+ },
11
+ });
12
+ }
@@ -0,0 +1,22 @@
1
+ export const KERNEL_TOOL_ERROR_CODES = Object.freeze([
2
+ "tools_unavailable",
3
+ "invalid_tool_definition",
4
+ "reserved_tool_name",
5
+ "tool_name_collision",
6
+ "kernel_tool_stale",
7
+ "kernel_tool_missing",
8
+ "kernel_tool_failed",
9
+ "kernel_tool_recursion",
10
+ ]);
11
+
12
+ export class KernelToolError extends Error {
13
+ constructor(code, message) {
14
+ super(message);
15
+ this.name = "KernelToolError";
16
+ this.code = code;
17
+ }
18
+ }
19
+
20
+ export function kernelToolError(code, message) {
21
+ return new KernelToolError(code, message);
22
+ }
@@ -0,0 +1,26 @@
1
+ export const KERNEL_TOOL_ERROR_CODES = [
2
+ "tools_unavailable",
3
+ "invalid_tool_definition",
4
+ "reserved_tool_name",
5
+ "tool_name_collision",
6
+ "kernel_tool_stale",
7
+ "kernel_tool_missing",
8
+ "kernel_tool_failed",
9
+ "kernel_tool_recursion",
10
+ ] as const;
11
+
12
+ export type KernelToolErrorCode = (typeof KERNEL_TOOL_ERROR_CODES)[number];
13
+
14
+ export class KernelToolError extends Error {
15
+ readonly name = "KernelToolError";
16
+ readonly code: KernelToolErrorCode;
17
+
18
+ constructor(code: KernelToolErrorCode, message: string) {
19
+ super(message);
20
+ this.code = code;
21
+ }
22
+ }
23
+
24
+ export function kernelToolError(code: KernelToolErrorCode, message: string): KernelToolError {
25
+ return new KernelToolError(code, message);
26
+ }
@@ -0,0 +1,134 @@
1
+ import type { HostToKernelMessage, KernelToHostMessage } from "../../bridge/protocol.ts";
2
+ import { generateCorrelationId } from "../../bridge/protocol.ts";
3
+ import { RESERVED_AGENT_TOOL } from "../../bridge/reserved.ts";
4
+ import { kernelToolError } from "./kernel-tools-errors.ts";
5
+ import type { KernelToolsDescribeResult, KernelToolsInvokeRequest } from "./kernel-tools-types.ts";
6
+
7
+ type KernelToolReply = Extract<
8
+ KernelToHostMessage,
9
+ { type: "kernel-tool-describe-reply" } | { type: "kernel-tool-invoke-reply" }
10
+ >;
11
+
12
+ type Waiter = {
13
+ readonly resolve: (message: KernelToolReply) => void;
14
+ readonly reject: (error: Error) => void;
15
+ };
16
+
17
+ export class KernelToolHostPump {
18
+ readonly events = new EventTarget();
19
+ readonly #waiters = new Map<string, Waiter>();
20
+ readonly #post: (message: HostToKernelMessage) => void;
21
+ readonly #isOpen: () => boolean;
22
+
23
+ constructor(post: (message: HostToKernelMessage) => void, isOpen: () => boolean) {
24
+ this.#post = post;
25
+ this.#isOpen = isOpen;
26
+ }
27
+
28
+ consume(message: KernelToHostMessage): boolean {
29
+ if (message.type === "tool-call" && message.toolName === RESERVED_AGENT_TOOL) {
30
+ this.events.dispatchEvent(new Event("outerAwaitingAgent"));
31
+ }
32
+ if (message.type !== "kernel-tool-describe-reply" && message.type !== "kernel-tool-invoke-reply") return false;
33
+ const waiter = this.#waiters.get(message.requestId);
34
+ if (!waiter) return true;
35
+ this.#waiters.delete(message.requestId);
36
+ waiter.resolve(message);
37
+ return true;
38
+ }
39
+
40
+ rejectAll(error: Error): void {
41
+ for (const [requestId, waiter] of this.#waiters) {
42
+ this.#waiters.delete(requestId);
43
+ waiter.reject(error);
44
+ }
45
+ }
46
+
47
+ async describe(names: readonly string[]): Promise<KernelToolsDescribeResult> {
48
+ const reply = await this.#request({
49
+ type: "kernel-tool-describe",
50
+ requestId: generateCorrelationId(),
51
+ names: [...names],
52
+ });
53
+ if (reply.type !== "kernel-tool-describe-reply") {
54
+ throw kernelToolError("kernel_tool_failed", "unexpected kernel-tool describe reply");
55
+ }
56
+ if (!reply.ok) {
57
+ throw kernelToolError(codeOf(reply.error.code), reply.error.message);
58
+ }
59
+ return { results: reply.results as KernelToolsDescribeResult["results"] };
60
+ }
61
+
62
+ async invoke(request: KernelToolsInvokeRequest, signal?: AbortSignal): Promise<unknown> {
63
+ this.events.dispatchEvent(new Event("nestedInvoke"));
64
+ const reply = await this.#request(
65
+ {
66
+ type: "kernel-tool-invoke",
67
+ requestId: generateCorrelationId(),
68
+ name: request.name,
69
+ kernel_generation: request.kernel_generation,
70
+ definition_revision: request.definition_revision,
71
+ args: request.args,
72
+ call_id: request.call_id,
73
+ },
74
+ signal,
75
+ );
76
+ if (reply.type !== "kernel-tool-invoke-reply") {
77
+ throw kernelToolError("kernel_tool_failed", "unexpected kernel-tool invoke reply");
78
+ }
79
+ if (!reply.ok) throw kernelToolError(codeOf(reply.error.code), reply.error.message);
80
+ return reply.value;
81
+ }
82
+
83
+ #request(
84
+ message: Extract<HostToKernelMessage, { requestId: string }>,
85
+ signal?: AbortSignal,
86
+ ): Promise<KernelToolReply> {
87
+ if (!this.#isOpen()) throw kernelToolError("tools_unavailable", "JavaScript worker is not available");
88
+ return new Promise((resolve, reject) => {
89
+ const onAbort = (): void => {
90
+ if (!this.#waiters.delete(message.requestId)) return;
91
+ this.#post({ type: "kernel-tool-cancel", requestId: message.requestId });
92
+ reject(kernelToolError("kernel_tool_stale", "Kernel tool call cancelled"));
93
+ };
94
+ const cleanup = (): void => signal?.removeEventListener("abort", onAbort);
95
+ this.#waiters.set(message.requestId, {
96
+ resolve: (reply) => {
97
+ cleanup();
98
+ resolve(reply);
99
+ },
100
+ reject: (error) => {
101
+ cleanup();
102
+ reject(error);
103
+ },
104
+ });
105
+ if (signal?.aborted) {
106
+ onAbort();
107
+ return;
108
+ }
109
+ signal?.addEventListener("abort", onAbort, { once: true });
110
+ this.#post(message);
111
+ });
112
+ }
113
+ }
114
+
115
+ function codeOf(
116
+ code: string | undefined,
117
+ ):
118
+ | "kernel_tool_failed"
119
+ | "kernel_tool_stale"
120
+ | "kernel_tool_missing"
121
+ | "kernel_tool_recursion"
122
+ | "tools_unavailable"
123
+ | "invalid_tool_definition" {
124
+ if (
125
+ code === "kernel_tool_stale" ||
126
+ code === "kernel_tool_missing" ||
127
+ code === "kernel_tool_recursion" ||
128
+ code === "tools_unavailable" ||
129
+ code === "invalid_tool_definition"
130
+ ) {
131
+ return code;
132
+ }
133
+ return "kernel_tool_failed";
134
+ }
@@ -0,0 +1,28 @@
1
+ /** MCP name rules from coding-agent mcp/expose/naming.ts, applied explicitly for kernel tools. */
2
+ export const MCP_TOOL_NAME_MAX_LENGTH = 64;
3
+
4
+ export function sanitizeNamePart(name) {
5
+ return name.replace(/[^a-zA-Z0-9_-]/g, "_");
6
+ }
7
+
8
+ export function matcherKey(name) {
9
+ return name.replace(/-/g, "_");
10
+ }
11
+
12
+ export function ellipsizeMiddle(value, maxLength) {
13
+ if (value.length <= maxLength) return value;
14
+ if (maxLength <= 3) return value.slice(0, maxLength);
15
+ const marker = "...";
16
+ const remaining = maxLength - marker.length;
17
+ const prefixLength = Math.ceil(remaining / 2);
18
+ const suffixLength = Math.floor(remaining / 2);
19
+ return `${value.slice(0, prefixLength)}${marker}${value.slice(value.length - suffixLength)}`;
20
+ }
21
+
22
+ export function normalizeKernelToolName(name) {
23
+ return ellipsizeMiddle(sanitizeNamePart(name), MCP_TOOL_NAME_MAX_LENGTH);
24
+ }
25
+
26
+ export function kernelToolKey(name) {
27
+ return matcherKey(normalizeKernelToolName(name));
28
+ }
@@ -0,0 +1,5 @@
1
+ export function parseToolFunction(fn: unknown): {
2
+ readonly name: string;
3
+ readonly params: readonly string[];
4
+ readonly async: boolean;
5
+ };