@code-yeongyu/senpi 2026.7.10-2 → 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.
- package/CHANGELOG.md +41 -0
- package/README.md +3 -1
- package/dist/core/extensions/builtin/bash-timeout/timeout.js +2 -2
- package/dist/core/extensions/builtin/bash-timeout/timeout.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +10 -1
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/goal/store.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/store.js +88 -3
- package/dist/core/extensions/builtin/goal/store.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +83 -0
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/presets.js +6 -0
- package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.js +1 -0
- package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/notify.d.ts +3 -3
- package/dist/core/extensions/builtin/terminal/notify.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/notify.js +7 -4
- package/dist/core/extensions/builtin/terminal/notify.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/runtime-session.d.ts +5 -3
- package/dist/core/extensions/builtin/terminal/runtime-session.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/runtime-session.js +32 -16
- package/dist/core/extensions/builtin/terminal/runtime-session.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-output.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash-output.js +10 -4
- package/dist/core/extensions/builtin/terminal/tools/bash-output.js.map +1 -1
- package/dist/core/extensions/builtin/todotools/tools/todowrite.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todotools/tools/todowrite.js +5 -8
- package/dist/core/extensions/builtin/todotools/tools/todowrite.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +1 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +5 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +3 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js +22 -3
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/keybindings.d.ts +8 -3
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +7 -3
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +2 -0
- package/dist/core/model-registry.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +1 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +4 -0
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +9 -2
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution-fallback.d.ts +6 -0
- package/dist/modes/interactive/components/tool-execution-fallback.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution-fallback.js +40 -0
- package/dist/modes/interactive/components/tool-execution-fallback.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution-images.d.ts +23 -0
- package/dist/modes/interactive/components/tool-execution-images.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution-images.js +104 -0
- package/dist/modes/interactive/components/tool-execution-images.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution-renderer.d.ts +28 -0
- package/dist/modes/interactive/components/tool-execution-renderer.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution-renderer.js +135 -0
- package/dist/modes/interactive/components/tool-execution-renderer.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution-types.d.ts +23 -0
- package/dist/modes/interactive/components/tool-execution-types.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution-types.js +2 -0
- package/dist/modes/interactive/components/tool-execution-types.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +13 -39
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +51 -294
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-renderer-boundary.d.ts +17 -0
- package/dist/modes/interactive/components/tool-renderer-boundary.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-renderer-boundary.js +75 -0
- package/dist/modes/interactive/components/tool-renderer-boundary.js.map +1 -0
- package/dist/modes/interactive/components/tree-selector.d.ts +5 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +50 -17
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +44 -40
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/clipboard-native.d.ts +1 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -1
- package/dist/utils/clipboard-native.js.map +1 -1
- package/dist/utils/clipboard.d.ts +2 -0
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +13 -0
- package/dist/utils/clipboard.js.map +1 -1
- package/docs/extensions.md +139 -0
- package/docs/keybindings.md +1 -0
- package/docs/providers.md +3 -0
- package/docs/quickstart.md +1 -1
- package/docs/settings.md +1 -1
- package/docs/usage.md +1 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +3 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +7 -7
- package/node_modules/@code-yeongyu/senpi-codemode/src/completion/tool-bridge.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +69 -8
- package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +39 -13
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +191 -148
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/inline-worker-entry.js +23 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/inline-worker.ts +4 -175
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +38 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/run-queue.ts +82 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +7 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-host.ts +117 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel-contract.ts +32 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +231 -199
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/process.ts +119 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/transport.ts +237 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-contract.ts +22 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +214 -189
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-process.ts +174 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-queue.ts +100 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-run.ts +98 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-handler.ts +205 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +208 -203
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +215 -25
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +82 -32
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +4 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +5 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts +3 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +29 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +7 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js +8 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/simple-options.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +270 -21
- package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js +5 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.d.ts +17 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.js +17 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/azure-openai-responses.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-auth.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-auth.js +9 -6
- package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-auth.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts +92 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js +59 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.d.ts +18 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.js +6 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.d.ts +38 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.js +24 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/openai.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts +66 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js +55 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts +5 -21
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +48 -65
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts +34 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +37 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +15 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/deferred-tools.d.ts +9 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/deferred-tools.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/deferred-tools.js +36 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/deferred-tools.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js +18 -5
- package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +1 -1
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/image.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/image.js +2 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/image.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +2 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js +2 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/keys.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/keys.js +6 -5
- package/node_modules/@earendil-works/pi-tui/dist/keys.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts +0 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +0 -9
- package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal-text.d.ts +4 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal-text.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal-text.js +18 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal-text.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/package.json +2 -2
- 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
|
|
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
|
-
#
|
|
36
|
-
#
|
|
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
|
-
|
|
51
|
-
const promise =
|
|
52
|
-
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
86
|
-
this.#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
|
|
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
|
-
|
|
97
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return
|
|
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.#
|
|
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
|
|
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(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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.#
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
this.#
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
this.#active
|
|
172
|
-
active
|
|
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.#
|
|
180
|
-
active
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
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
|
|
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 "./
|
|
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
|
|
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
|
+
}
|