@code-yeongyu/senpi 2026.9.5 → 2026.9.7-2
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 +104 -0
- package/dist/core/agent-session.d.ts +60 -5
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +262 -78
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +6 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +6 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/stuck-overflow.d.ts +11 -0
- package/dist/core/compaction/stuck-overflow.d.ts.map +1 -0
- package/dist/core/compaction/stuck-overflow.js +17 -0
- package/dist/core/compaction/stuck-overflow.js.map +1 -0
- package/dist/core/extensions/builtin/cache-keepalive/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cache-keepalive/index.js +3 -21
- package/dist/core/extensions/builtin/cache-keepalive/index.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/prompt-bridge.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/prompt-bridge.js +2 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/prompt-bridge.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js +36 -19
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js +21 -10
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts +4 -17
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js +23 -33
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-pump.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-pump.js +15 -2
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-pump.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js +5 -3
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js +8 -3
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.d.ts +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.js +2 -3
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/tools.d.ts +6 -5
- package/dist/core/extensions/builtin/claude-sdk-oauth/tools.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/tools.js +6 -5
- package/dist/core/extensions/builtin/claude-sdk-oauth/tools.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts +8 -0
- package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/circuit-breaker.js +8 -0
- package/dist/core/extensions/builtin/compaction/circuit-breaker.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +4 -0
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +40 -20
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.d.ts +18 -0
- package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.js +20 -0
- package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/index.js +2 -2
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/lane-policy.d.ts +12 -0
- package/dist/core/extensions/builtin/compaction/lane-policy.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/lane-policy.js +47 -18
- package/dist/core/extensions/builtin/compaction/lane-policy.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote-convert.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote-convert.js +2 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-convert.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js +14 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +6 -4
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm.d.ts +19 -1
- package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm.js +23 -10
- package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
- package/dist/core/extensions/builtin/goal/continuation-recovery.d.ts +1 -0
- package/dist/core/extensions/builtin/goal/continuation-recovery.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/continuation-recovery.js +2 -0
- package/dist/core/extensions/builtin/goal/continuation-recovery.js.map +1 -1
- package/dist/core/extensions/builtin/goal/continuation.d.ts +3 -1
- package/dist/core/extensions/builtin/goal/continuation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/continuation.js +4 -0
- package/dist/core/extensions/builtin/goal/continuation.js.map +1 -1
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +3 -0
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +14 -3
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js.map +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +7 -3
- package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
- package/dist/core/extensions/builtin/hooks/plugin-manifest.js +2 -2
- package/dist/core/extensions/builtin/hooks/plugin-manifest.js.map +1 -1
- package/dist/core/extensions/builtin/hooks/safety.js +2 -2
- package/dist/core/extensions/builtin/hooks/safety.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.js +5 -22
- package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js +5 -7
- package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +2 -2
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +8 -2
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +14 -7
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
- package/dist/core/messages.d.ts +7 -0
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +2 -0
- package/dist/core/messages.js.map +1 -1
- package/dist/core/package-identity.d.ts +12 -0
- package/dist/core/package-identity.d.ts.map +1 -0
- package/dist/core/package-identity.js +57 -0
- package/dist/core/package-identity.js.map +1 -0
- package/dist/core/resource-loader.d.ts +0 -2
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +8 -60
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +5 -4
- package/dist/core/retry-fallback/controller.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +5 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +12 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +29 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +6 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/bounded-realpath.d.ts +28 -0
- package/dist/core/tools/bounded-realpath.d.ts.map +1 -0
- package/dist/core/tools/bounded-realpath.js +53 -0
- package/dist/core/tools/bounded-realpath.js.map +1 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +15 -13
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/filesystem-policy.d.ts +7 -0
- package/dist/core/tools/filesystem-policy.d.ts.map +1 -1
- package/dist/core/tools/filesystem-policy.js +25 -15
- package/dist/core/tools/filesystem-policy.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +13 -2
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +5 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/tips/catalog/memory-tips.d.ts.map +1 -1
- package/dist/modes/interactive/tips/catalog/memory-tips.js +6 -0
- package/dist/modes/interactive/tips/catalog/memory-tips.js.map +1 -1
- package/dist/modes/interactive/tips/catalog/subagent-tips.d.ts.map +1 -1
- package/dist/modes/interactive/tips/catalog/subagent-tips.js +6 -0
- package/dist/modes/interactive/tips/catalog/subagent-tips.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +31 -5
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/host-watchdog.d.ts +11 -0
- package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
- package/dist/modes/rpc/host-watchdog.js +16 -2
- package/dist/modes/rpc/host-watchdog.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.d.ts +3 -7
- package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +57 -29
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +1 -2
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +0 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +1 -4
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +0 -15
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/dist/modes/rpc/socket-ownership.d.ts +46 -0
- package/dist/modes/rpc/socket-ownership.d.ts.map +1 -0
- package/dist/modes/rpc/socket-ownership.js +178 -0
- package/dist/modes/rpc/socket-ownership.js.map +1 -0
- package/dist/utils/fs-watch.d.ts +17 -0
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js +38 -10
- package/dist/utils/fs-watch.js.map +1 -1
- package/dist/utils/paths.d.ts +33 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +109 -3
- package/dist/utils/paths.js.map +1 -1
- package/docs/compaction-guide.md +2 -2
- package/docs/compaction.md +2 -2
- package/docs/development.md +3 -2
- package/docs/providers.md +1 -1
- package/docs/rpc.md +6 -7
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +74 -0
- package/node_modules/@code-yeongyu/senpi-codemode/README.md +34 -5
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +1 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/reserved.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/runtime-factory.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +7 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/AGENTS.md +7 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/kernel.ts +6 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +73 -129
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/inline-worker.ts +2 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/interrupt-bounds.ts +66 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/run-queue.ts +18 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +59 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +10 -6
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +16 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-shell-capture.d.ts +25 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-shell-capture.js +70 -8
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-slot.ts +106 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-startup.ts +70 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel-contract.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/transport.ts +11 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/kernel.ts +6 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/session-env.ts +56 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/runtime-asset.ts +40 -14
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-contract.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +9 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/output/output-meta.ts +26 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +10 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-execution.ts +9 -11
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-contract.ts +45 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +43 -71
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-notification.ts +4 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-snapshot.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-status.ts +30 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-eval-result.ts +1 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-notification-queue.ts +2 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image.ts +12 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/interrupt-note.ts +29 -15
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +3 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js +17 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/reducer.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/reducer.js +6 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/reducer.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.d.ts +2 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.js +5 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.d.ts +7 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.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 +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js.map +1 -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 +15 -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/image-models.generated.js +4 -4
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/azure-openai-responses.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cerebras.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai-codex.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/faux.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +8 -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/estimate.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +14 -3
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -9,3 +9,5 @@ export const RESERVED_SCHEMA_TOOL = "__schema__" as const;
|
|
|
9
9
|
export const TIMEOUT_PAUSE_OP = "timeout-pause" as const;
|
|
10
10
|
/** Canonical oh-my-pi eval-timeout resume operation. */
|
|
11
11
|
export const TIMEOUT_RESUME_OP = "timeout-resume" as const;
|
|
12
|
+
/** Status op the JS worker emits the moment it receives `interrupt`; proves its event loop is not blocked. */
|
|
13
|
+
export const INTERRUPT_ACK_OP = "interrupt-ack" as const;
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
getInterpreterAvailability,
|
|
14
14
|
type InterpreterAvailability,
|
|
15
15
|
} from "../interpreters/detect.ts";
|
|
16
|
+
import { sessionEnvironmentFrom } from "../kernels/session-env.ts";
|
|
16
17
|
import { resolveSessionArtifactsDir } from "../output/streaming-output.ts";
|
|
17
18
|
import type { EnabledEvalLanguages, EvalLanguage, EvalRuntimes } from "../tool/types.ts";
|
|
18
19
|
import { jsRuntimeInfo, runtimesFromAvailability } from "./runtime-info.ts";
|
|
@@ -66,11 +67,13 @@ export async function createRuntime(
|
|
|
66
67
|
const executeTool = createExecuteTool(pi, activeTools);
|
|
67
68
|
const create = options.createSessionManager ?? createCodemodeSessionManager;
|
|
68
69
|
const sessionId = sessionIdFrom(event);
|
|
70
|
+
const sessionEnv = sessionEnvironmentFrom(ctx);
|
|
69
71
|
const configuredPoolWidth = settings.parallelPoolWidth;
|
|
70
72
|
const parallelPoolWidth = Number.isFinite(configuredPoolWidth) ? Math.max(1, Math.trunc(configuredPoolWidth)) : 1;
|
|
71
73
|
const manager = await create({
|
|
72
74
|
sessionId,
|
|
73
75
|
cwd: ctx.cwd,
|
|
76
|
+
sessionEnv,
|
|
74
77
|
settings,
|
|
75
78
|
availability,
|
|
76
79
|
artifactsDir: artifacts.dir,
|
|
@@ -11,6 +11,7 @@ import { JuliaKernel } from "../kernels/jl/kernel.ts";
|
|
|
11
11
|
import { JavaScriptKernel } from "../kernels/js/context-manager.ts";
|
|
12
12
|
import { PythonKernel } from "../kernels/py/kernel.ts";
|
|
13
13
|
import { RubyKernel } from "../kernels/rb/kernel.ts";
|
|
14
|
+
import type { SessionEnvironment } from "../kernels/session-env.ts";
|
|
14
15
|
import { marshalToolResult } from "../tool/image.ts";
|
|
15
16
|
import type { EvalKernel, EvalKernelManager, EvalLanguage, ExecuteTool } from "../tool/types.ts";
|
|
16
17
|
|
|
@@ -40,6 +41,8 @@ export interface CreateCodemodeSessionManagerOptions {
|
|
|
40
41
|
readonly localRoots?: Readonly<Record<string, string>>;
|
|
41
42
|
/** Session-adjacent directory used for persisted eval artifacts. */
|
|
42
43
|
readonly artifactsDir?: string;
|
|
44
|
+
/** Per-session PI_* values exposed to every kernel and the children it spawns. */
|
|
45
|
+
readonly sessionEnv?: SessionEnvironment;
|
|
43
46
|
readonly executeTool: ExecuteTool;
|
|
44
47
|
readonly listTools?: () => readonly EvalSchemaToolInfo[];
|
|
45
48
|
readonly complete: (request: CompletionRequest, ctx: ExtensionContext) => Promise<CompletionResult>;
|
|
@@ -212,6 +215,7 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
|
|
|
212
215
|
cwd: this.#options.cwd,
|
|
213
216
|
parallelPoolWidth,
|
|
214
217
|
onMessage,
|
|
218
|
+
...(this.#options.sessionEnv ? { sessionEnv: this.#options.sessionEnv } : {}),
|
|
215
219
|
...(localRoots ? { localRoots: { ...localRoots } } : {}),
|
|
216
220
|
...(this.#options.artifactsDir ? { artifactsDir: this.#options.artifactsDir } : {}),
|
|
217
221
|
});
|
|
@@ -230,6 +234,7 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
|
|
|
230
234
|
interpreterPath: detected.path,
|
|
231
235
|
sessionId: this.#options.sessionId,
|
|
232
236
|
cwd: this.#options.cwd,
|
|
237
|
+
...(this.#options.sessionEnv ? { sessionEnv: this.#options.sessionEnv } : {}),
|
|
233
238
|
connection,
|
|
234
239
|
onMessage,
|
|
235
240
|
});
|
|
@@ -239,6 +244,7 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
|
|
|
239
244
|
command: detected.path,
|
|
240
245
|
sessionId: this.#options.sessionId,
|
|
241
246
|
cwd: this.#options.cwd,
|
|
247
|
+
...(this.#options.sessionEnv ? { sessionEnv: this.#options.sessionEnv } : {}),
|
|
242
248
|
connection,
|
|
243
249
|
onMessage,
|
|
244
250
|
});
|
|
@@ -247,6 +253,7 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
|
|
|
247
253
|
command: detected.path,
|
|
248
254
|
sessionId: this.#options.sessionId,
|
|
249
255
|
cwd: this.#options.cwd,
|
|
256
|
+
...(this.#options.sessionEnv ? { sessionEnv: this.#options.sessionEnv } : {}),
|
|
250
257
|
connection,
|
|
251
258
|
onMessage,
|
|
252
259
|
});
|
|
@@ -15,6 +15,7 @@ runner/prelude assets).
|
|
|
15
15
|
| Ruby kernel | `rb/kernel.ts` + `rb/prelude.rb`, `rb/runner.rb` |
|
|
16
16
|
| Julia kernel | `jl/kernel.ts` + `jl/prelude.jl`, `jl/runner.jl` |
|
|
17
17
|
| Shared subprocess layer | `shared/subprocess-kernel.ts`, `subprocess-{contract,process,queue,run}.ts`, `runtime-asset.ts` |
|
|
18
|
+
| Session environment | `session-env.ts` (PI_* contract shared by all kernels; mirrors the core bash tool) |
|
|
18
19
|
|
|
19
20
|
## CONVENTIONS
|
|
20
21
|
|
|
@@ -29,6 +30,12 @@ runner/prelude assets).
|
|
|
29
30
|
framed subprocesses through `shared/`.
|
|
30
31
|
- Subprocess retirement/restart, worker recovery, timeout, and interrupt
|
|
31
32
|
semantics live here, never in the tool layer.
|
|
33
|
+
- Every kernel exposes the active session's `PI_*` environment (`session-env.ts`):
|
|
34
|
+
inherited values are deleted before the session's values are applied, so any
|
|
35
|
+
child spawned from a cell sees the same session environment a bash-tool child
|
|
36
|
+
sees. The JS worker applies it at init (`worker-core.js`; shell capture pins
|
|
37
|
+
the env view under Bun because `delete process.env.X` does not unsetenv),
|
|
38
|
+
and py/rb/jl spawn with it merged into the interpreter environment.
|
|
32
39
|
|
|
33
40
|
## ANTI-PATTERNS
|
|
34
41
|
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import type { BridgeConnectionConfig, KernelToHostMessage } from "../../bridge/protocol.ts";
|
|
3
|
-
import
|
|
3
|
+
import type { SessionEnvironment } from "../session-env.ts";
|
|
4
|
+
import { type CodemodeRuntimeAssetEnvironment, requireCodemodeRuntimeAsset } from "../shared/runtime-asset.ts";
|
|
4
5
|
import { SubprocessKernel, type SubprocessSpawn } from "../shared/subprocess-kernel.ts";
|
|
5
6
|
|
|
6
7
|
export interface JuliaKernelStartOptions {
|
|
7
8
|
readonly cwd: string;
|
|
8
9
|
readonly sessionId: string;
|
|
9
10
|
readonly connection: BridgeConnectionConfig;
|
|
11
|
+
/** Per-session PI_* values merged into the interpreter environment at spawn. */
|
|
12
|
+
readonly sessionEnv?: SessionEnvironment;
|
|
10
13
|
readonly command?: string;
|
|
11
14
|
readonly spawn?: SubprocessSpawn;
|
|
12
15
|
readonly onMessage?: (message: KernelToHostMessage) => void;
|
|
@@ -17,7 +20,7 @@ export interface JuliaRunnerPathOptions extends CodemodeRuntimeAssetEnvironment
|
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export function resolveJuliaRunnerPath(options: JuliaRunnerPathOptions = {}): string {
|
|
20
|
-
return
|
|
23
|
+
return requireCodemodeRuntimeAsset(
|
|
21
24
|
options.localPath ?? join(import.meta.dirname, "runner.jl"),
|
|
22
25
|
join("kernels", "jl", "runner.jl"),
|
|
23
26
|
options,
|
|
@@ -42,6 +45,7 @@ export class JuliaKernel extends SubprocessKernel {
|
|
|
42
45
|
],
|
|
43
46
|
cwd: options.cwd,
|
|
44
47
|
sessionId: options.sessionId,
|
|
48
|
+
sessionEnv: options.sessionEnv,
|
|
45
49
|
connection: options.connection,
|
|
46
50
|
spawn: options.spawn,
|
|
47
51
|
onMessage: options.onMessage,
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { dirname, join } from "node:path";
|
|
2
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
1
|
import type { HostToKernelMessage, KernelToHostMessage } from "../../bridge/protocol.ts";
|
|
2
|
+
import { INTERRUPT_ACK_OP } from "../../bridge/reserved.ts";
|
|
4
3
|
import type { KernelInterruptHandle } from "../../tool/types.ts";
|
|
5
|
-
import {
|
|
6
|
-
import { createInlineWorker, type WorkerLike } from "./inline-worker.ts";
|
|
4
|
+
import { abandonedWorkerNote, awaitCooperativeSettlement, type WorkerRetirement } from "./interrupt-bounds.ts";
|
|
7
5
|
import {
|
|
8
6
|
assertJavaScriptKernelOpen,
|
|
9
7
|
type JavaScriptKernelMode,
|
|
@@ -12,31 +10,20 @@ import {
|
|
|
12
10
|
type ResultMessage,
|
|
13
11
|
type ToolCallMessage,
|
|
14
12
|
} from "./kernel-contract.ts";
|
|
15
|
-
import { type JavaScriptKernelOptions, LocalModuleLoader
|
|
13
|
+
import { type JavaScriptKernelOptions, LocalModuleLoader } from "./local-module-loader.ts";
|
|
16
14
|
import { JavaScriptRunQueue, type PendingJavaScriptRun, stoppedResult } from "./run-queue.ts";
|
|
17
|
-
import { bridgeError,
|
|
15
|
+
import { bridgeError, WorkerStartupCancelledError } from "./worker-host.ts";
|
|
16
|
+
import { WorkerSlot } from "./worker-slot.ts";
|
|
18
17
|
|
|
19
18
|
export { JavaScriptKernelClosedError, type JavaScriptKernelMode, type JavaScriptRunInput } from "./kernel-contract.ts";
|
|
20
19
|
export type { JavaScriptKernelOptions } from "./local-module-loader.ts";
|
|
21
|
-
|
|
22
|
-
export interface JavaScriptWorkerEntryUrlOptions extends CodemodeRuntimeAssetEnvironment {
|
|
23
|
-
readonly localPath?: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function resolveJsWorkerEntryUrl(options: JavaScriptWorkerEntryUrlOptions = {}): URL {
|
|
27
|
-
const localPath = options.localPath ?? join(dirname(fileURLToPath(import.meta.url)), "worker-entry.js");
|
|
28
|
-
return pathToFileURL(resolveCodemodeRuntimeAsset(localPath, join("kernels", "js", "worker-entry.js"), options));
|
|
29
|
-
}
|
|
20
|
+
export { type JavaScriptWorkerEntryUrlOptions, resolveJsWorkerEntryUrl } from "./worker-startup.ts";
|
|
30
21
|
|
|
31
22
|
export class JavaScriptKernel {
|
|
32
23
|
readonly #options: JavaScriptKernelOptions;
|
|
33
24
|
readonly #moduleLoader: LocalModuleLoader;
|
|
34
|
-
#
|
|
35
|
-
#mode: JavaScriptKernelMode = "worker";
|
|
25
|
+
readonly #slot: WorkerSlot;
|
|
36
26
|
#lifecycle: LifecycleState = "open";
|
|
37
|
-
#ready: Promise<void> | null = null;
|
|
38
|
-
#startupAbort: AbortController | null = null;
|
|
39
|
-
#generation = 0;
|
|
40
27
|
#activation: Promise<void> | null = null;
|
|
41
28
|
#recovery: Promise<void> | null = null;
|
|
42
29
|
#closePromise: Promise<void> | null = null;
|
|
@@ -48,10 +35,15 @@ export class JavaScriptKernel {
|
|
|
48
35
|
constructor(options: JavaScriptKernelOptions) {
|
|
49
36
|
this.#options = options;
|
|
50
37
|
this.#moduleLoader = new LocalModuleLoader(options);
|
|
38
|
+
this.#slot = new WorkerSlot(options, {
|
|
39
|
+
isOpen: () => this.#lifecycle === "open",
|
|
40
|
+
onMessage: (message) => this.#handleMessage(message),
|
|
41
|
+
onCrash: (error) => this.#handleCrash(error),
|
|
42
|
+
});
|
|
51
43
|
}
|
|
52
44
|
|
|
53
45
|
get mode(): JavaScriptKernelMode {
|
|
54
|
-
return this.#mode;
|
|
46
|
+
return this.#slot.mode;
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
async run(input: JavaScriptRunInput): Promise<ResultMessage> {
|
|
@@ -64,13 +56,16 @@ export class JavaScriptKernel {
|
|
|
64
56
|
async interrupt(reason = "interrupted"): Promise<KernelInterruptHandle> {
|
|
65
57
|
assertJavaScriptKernelOpen(this.#lifecycle, "interrupt");
|
|
66
58
|
const active = this.#runs.active;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
if (!active) {
|
|
60
|
+
const queued = this.#runs.takeInterruptTarget();
|
|
61
|
+
if (!queued) return { stateRetained: Promise.resolve(true) };
|
|
62
|
+
this.#runs.settle(queued, stoppedResult(queued.input.cellId, `JS cell interrupted: ${reason}`));
|
|
63
|
+
await this.#restartAfterStop();
|
|
64
|
+
return { stateRetained: Promise.resolve(false) };
|
|
65
|
+
}
|
|
66
|
+
this.#clearTimeout();
|
|
67
|
+
const stop = await this.#stopActive(active, reason, `JS cell interrupted: ${reason}`);
|
|
68
|
+
return { stateRetained: Promise.resolve(stop.retained), ...(stop.note === undefined ? {} : { note: stop.note }) };
|
|
74
69
|
}
|
|
75
70
|
|
|
76
71
|
async reset(): Promise<void> {
|
|
@@ -82,7 +77,7 @@ export class JavaScriptKernel {
|
|
|
82
77
|
}
|
|
83
78
|
|
|
84
79
|
deliverToolReply(message: Extract<HostToKernelMessage, { type: "tool-reply" }>): void {
|
|
85
|
-
if (this.#lifecycle === "open") this.#
|
|
80
|
+
if (this.#lifecycle === "open") this.#slot.postMessage(message);
|
|
86
81
|
}
|
|
87
82
|
|
|
88
83
|
async nextToolCall(): Promise<ToolCallMessage> {
|
|
@@ -93,7 +88,7 @@ export class JavaScriptKernel {
|
|
|
93
88
|
|
|
94
89
|
async close(): Promise<void> {
|
|
95
90
|
if (this.#closePromise) return await this.#closePromise;
|
|
96
|
-
this.#
|
|
91
|
+
this.#slot.postMessage({ type: "close" });
|
|
97
92
|
this.#lifecycle = "closing";
|
|
98
93
|
this.#runs.settleAll("JS kernel closed");
|
|
99
94
|
const recovery = this.#recovery;
|
|
@@ -129,92 +124,17 @@ export class JavaScriptKernel {
|
|
|
129
124
|
|
|
130
125
|
async #ensureReady(): Promise<void> {
|
|
131
126
|
assertJavaScriptKernelOpen(this.#lifecycle, "run");
|
|
132
|
-
|
|
133
|
-
const generation = ++this.#generation;
|
|
134
|
-
const controller = new AbortController();
|
|
135
|
-
this.#startupAbort = controller;
|
|
136
|
-
const ready = this.#startWorker(generation, controller.signal);
|
|
137
|
-
this.#ready = ready;
|
|
138
|
-
void ready.then(
|
|
139
|
-
() => {
|
|
140
|
-
if (this.#ready === ready) this.#startupAbort = null;
|
|
141
|
-
},
|
|
142
|
-
() => {
|
|
143
|
-
if (this.#ready === ready) {
|
|
144
|
-
this.#ready = null;
|
|
145
|
-
this.#startupAbort = null;
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
return await this.#ready;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
async #startWorker(generation: number, signal: AbortSignal): Promise<void> {
|
|
154
|
-
let worker = this.#spawnWorker();
|
|
155
|
-
this.#publishWorker(worker, generation);
|
|
156
|
-
try {
|
|
157
|
-
await this.#initializeWorker(worker, signal);
|
|
158
|
-
return;
|
|
159
|
-
} catch (error) {
|
|
160
|
-
if (!this.#isCurrent(worker, generation) || error instanceof WorkerStartupCancelledError) {
|
|
161
|
-
await worker.terminate();
|
|
162
|
-
throw new WorkerStartupCancelledError();
|
|
163
|
-
}
|
|
164
|
-
if (worker.mode === "inline") throw error;
|
|
165
|
-
this.#worker = null;
|
|
166
|
-
await worker.terminate();
|
|
167
|
-
}
|
|
168
|
-
if (this.#lifecycle !== "open" || generation !== this.#generation) throw new WorkerStartupCancelledError();
|
|
169
|
-
worker = createInlineWorker(this.#options.cwd, this.#options.parallelPoolWidth);
|
|
170
|
-
this.#publishWorker(worker, generation);
|
|
171
|
-
await this.#initializeWorker(worker, signal);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
#spawnWorker(): WorkerLike {
|
|
175
|
-
try {
|
|
176
|
-
const url = this.#options.workerEntryUrl ?? resolveJsWorkerEntryUrl();
|
|
177
|
-
return spawnNodeWorker(url, this.#options.cwd, this.#options.parallelPoolWidth);
|
|
178
|
-
} catch (error) {
|
|
179
|
-
if (!(error instanceof Error)) throw error;
|
|
180
|
-
return createInlineWorker(this.#options.cwd, this.#options.parallelPoolWidth);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
#publishWorker(worker: WorkerLike, generation: number): void {
|
|
185
|
-
if (this.#lifecycle !== "open" || generation !== this.#generation) throw new WorkerStartupCancelledError();
|
|
186
|
-
this.#worker = worker;
|
|
187
|
-
this.#mode = worker.mode;
|
|
188
|
-
worker.onMessage((message) => {
|
|
189
|
-
if (this.#isCurrent(worker, generation)) this.#handleMessage(message);
|
|
190
|
-
});
|
|
191
|
-
worker.onError((error) => {
|
|
192
|
-
if (this.#isCurrent(worker, generation)) this.#handleCrash(error);
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async #initializeWorker(worker: WorkerLike, signal: AbortSignal): Promise<void> {
|
|
197
|
-
const ready = waitForReady(worker, signal);
|
|
198
|
-
worker.postMessage({
|
|
199
|
-
type: "init",
|
|
200
|
-
sessionId: this.#options.sessionId,
|
|
201
|
-
connection: localBridgeConnection(this.#options),
|
|
202
|
-
});
|
|
203
|
-
await ready;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
#isCurrent(worker: WorkerLike, generation: number): boolean {
|
|
207
|
-
return this.#lifecycle === "open" && this.#worker === worker && this.#generation === generation;
|
|
127
|
+
await this.#slot.ensureReady();
|
|
208
128
|
}
|
|
209
129
|
|
|
210
130
|
#startNext(): void {
|
|
211
|
-
if (this.#lifecycle !== "open" || this.#runs.active || !this.#
|
|
131
|
+
if (this.#lifecycle !== "open" || this.#runs.active || !this.#slot.present) return;
|
|
212
132
|
const next = this.#runs.startNext(performance.now());
|
|
213
133
|
if (!next) return;
|
|
214
134
|
if (next.input.timeoutMs) {
|
|
215
135
|
this.#timeout = setTimeout(() => void this.#timeoutActive(next), next.input.timeoutMs);
|
|
216
136
|
}
|
|
217
|
-
this.#
|
|
137
|
+
this.#slot.postMessage({
|
|
218
138
|
type: "run",
|
|
219
139
|
cellId: next.input.cellId,
|
|
220
140
|
code: this.#moduleLoader.prepareCell(next.input.code),
|
|
@@ -223,21 +143,46 @@ export class JavaScriptKernel {
|
|
|
223
143
|
}
|
|
224
144
|
|
|
225
145
|
async #timeoutActive(run: PendingJavaScriptRun): Promise<void> {
|
|
226
|
-
if (
|
|
146
|
+
if (this.#runs.active !== run || run.settled) return;
|
|
227
147
|
const durationMs = run.input.timeoutMs ?? 0;
|
|
228
|
-
this.#
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
error: { message: `JS cell timed out after ${durationMs}ms` },
|
|
148
|
+
await this.#stopActive(
|
|
149
|
+
run,
|
|
150
|
+
`timed out after ${durationMs}ms`,
|
|
151
|
+
`JS cell timed out after ${durationMs}ms`,
|
|
233
152
|
durationMs,
|
|
234
|
-
|
|
235
|
-
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Asks the worker to settle the active cell cooperatively (rejecting its bridge calls and killing its
|
|
158
|
+
* children); only a cell that stays unsettled past the grace costs the worker VM. Reports whether the
|
|
159
|
+
* worker state survived and, when a blocked worker had to be abandoned, the note that explains it.
|
|
160
|
+
*/
|
|
161
|
+
async #stopActive(
|
|
162
|
+
run: PendingJavaScriptRun,
|
|
163
|
+
reason: string,
|
|
164
|
+
message: string,
|
|
165
|
+
durationMs = 0,
|
|
166
|
+
): Promise<{ readonly retained: boolean; readonly note?: string }> {
|
|
167
|
+
run.interruptResult = { type: "result", cellId: run.input.cellId, ok: false, error: { message }, durationMs };
|
|
168
|
+
run.interruptAck ??= Promise.withResolvers<void>();
|
|
169
|
+
this.#slot.postMessage({ type: "interrupt", reason });
|
|
170
|
+
if ((await awaitCooperativeSettlement(run)) === "settled") return { retained: run.settledByWorker };
|
|
171
|
+
if (!this.#runs.releaseActive(run)) return { retained: run.settledByWorker };
|
|
172
|
+
const retirement = await this.#terminate();
|
|
173
|
+
this.#runs.settle(run, run.interruptResult ?? stoppedResult(run.input.cellId, message));
|
|
174
|
+
void this.#recover(() => Promise.resolve());
|
|
175
|
+
return retirement === "abandoned" ? { retained: false, note: abandonedWorkerNote() } : { retained: false };
|
|
236
176
|
}
|
|
237
177
|
|
|
238
178
|
async #restartAfterStop(): Promise<void> {
|
|
179
|
+
await this.#recover(() => this.#terminate());
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** One recovery at a time: retire through `retire` (a no-op when the worker is already gone), then bring a fresh worker up. */
|
|
183
|
+
async #recover(retire: () => Promise<unknown>): Promise<void> {
|
|
239
184
|
if (this.#recovery) return await this.#recovery;
|
|
240
|
-
const recovery = this.#
|
|
185
|
+
const recovery = this.#performRecovery(retire);
|
|
241
186
|
this.#recovery = recovery;
|
|
242
187
|
try {
|
|
243
188
|
await recovery;
|
|
@@ -246,9 +191,9 @@ export class JavaScriptKernel {
|
|
|
246
191
|
}
|
|
247
192
|
}
|
|
248
193
|
|
|
249
|
-
async #
|
|
194
|
+
async #performRecovery(retire: () => Promise<unknown>): Promise<void> {
|
|
250
195
|
try {
|
|
251
|
-
await
|
|
196
|
+
await retire();
|
|
252
197
|
if (this.#lifecycle !== "open") return;
|
|
253
198
|
await this.#ensureReady();
|
|
254
199
|
if (this.#lifecycle === "open") this.#startNext();
|
|
@@ -259,6 +204,10 @@ export class JavaScriptKernel {
|
|
|
259
204
|
}
|
|
260
205
|
|
|
261
206
|
#handleMessage(message: KernelToHostMessage): void {
|
|
207
|
+
if (message.type === "status" && message.event.op === INTERRUPT_ACK_OP) {
|
|
208
|
+
this.#runs.active?.interruptAck?.resolve();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
262
211
|
this.#options.onMessage?.(message);
|
|
263
212
|
this.#runs.active?.input.onMessage?.(message);
|
|
264
213
|
if (message.type === "tool-call") {
|
|
@@ -272,13 +221,14 @@ export class JavaScriptKernel {
|
|
|
272
221
|
if (!active || active.input.cellId !== message.cellId) return;
|
|
273
222
|
this.#clearTimeout();
|
|
274
223
|
this.#runs.releaseActive(active);
|
|
275
|
-
|
|
224
|
+
active.settledByWorker = true;
|
|
225
|
+
this.#runs.settle(active, active.interruptResult ?? message);
|
|
276
226
|
this.#startNext();
|
|
277
227
|
}
|
|
278
228
|
|
|
279
229
|
#handleCrash(error: Error): void {
|
|
280
230
|
const active = this.#runs.active;
|
|
281
|
-
if (!active && this.#
|
|
231
|
+
if (!active && this.#slot.startingUp) return;
|
|
282
232
|
this.#clearTimeout();
|
|
283
233
|
if (active) {
|
|
284
234
|
this.#runs.releaseActive(active);
|
|
@@ -298,14 +248,8 @@ export class JavaScriptKernel {
|
|
|
298
248
|
this.#timeout = null;
|
|
299
249
|
}
|
|
300
250
|
|
|
301
|
-
async #terminate(): Promise<
|
|
251
|
+
async #terminate(): Promise<WorkerRetirement> {
|
|
302
252
|
this.#clearTimeout();
|
|
303
|
-
this.#
|
|
304
|
-
this.#startupAbort?.abort();
|
|
305
|
-
this.#startupAbort = null;
|
|
306
|
-
this.#ready = null;
|
|
307
|
-
const worker = this.#worker;
|
|
308
|
-
this.#worker = null;
|
|
309
|
-
if (worker) await worker.terminate();
|
|
253
|
+
return await this.#slot.retire();
|
|
310
254
|
}
|
|
311
255
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { dirname, join } from "node:path";
|
|
2
2
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
3
|
import type { HostToKernelMessage, KernelToHostMessage } from "../../bridge/protocol.ts";
|
|
4
|
-
import { type CodemodeRuntimeAssetEnvironment,
|
|
4
|
+
import { type CodemodeRuntimeAssetEnvironment, requireCodemodeRuntimeAsset } from "../shared/runtime-asset.ts";
|
|
5
5
|
import type { JavaScriptKernelMode } from "./kernel-contract.ts";
|
|
6
6
|
import { spawnNodeWorker } from "./worker-host.ts";
|
|
7
7
|
|
|
@@ -12,7 +12,7 @@ export interface JavaScriptInlineWorkerEntryUrlOptions extends CodemodeRuntimeAs
|
|
|
12
12
|
export function resolveInlineWorkerEntryUrl(options: JavaScriptInlineWorkerEntryUrlOptions = {}): URL {
|
|
13
13
|
const localPath = options.localPath ?? join(dirname(fileURLToPath(import.meta.url)), "inline-worker-entry.js");
|
|
14
14
|
return pathToFileURL(
|
|
15
|
-
|
|
15
|
+
requireCodemodeRuntimeAsset(localPath, join("kernels", "js", "inline-worker-entry.js"), options),
|
|
16
16
|
);
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { WorkerLike } from "./inline-worker.ts";
|
|
2
|
+
import type { PendingJavaScriptRun } from "./run-queue.ts";
|
|
3
|
+
|
|
4
|
+
/** How long the worker gets to acknowledge `interrupt`; silence means its event loop is blocked in synchronous code. */
|
|
5
|
+
export const INTERRUPT_ACK_MS = 500;
|
|
6
|
+
/** How long an acknowledged cell gets to settle before the VM is replaced. */
|
|
7
|
+
export const JS_INTERRUPT_GRACE_MS = 2_000;
|
|
8
|
+
/** How long `worker.terminate()` may take before the worker is abandoned as blocked in a synchronous call. */
|
|
9
|
+
export const WORKER_TERMINATE_DEADLINE_MS = 3_000;
|
|
10
|
+
|
|
11
|
+
export type CooperativeSettlement = "settled" | "unresponsive";
|
|
12
|
+
export type WorkerRetirement = "terminated" | "abandoned";
|
|
13
|
+
|
|
14
|
+
export interface CooperativeSettlementBounds {
|
|
15
|
+
readonly ackMs: number;
|
|
16
|
+
readonly graceMs: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DEFAULT_SETTLEMENT_BOUNDS: CooperativeSettlementBounds = {
|
|
20
|
+
ackMs: INTERRUPT_ACK_MS,
|
|
21
|
+
graceMs: JS_INTERRUPT_GRACE_MS,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export async function awaitCooperativeSettlement(
|
|
25
|
+
run: PendingJavaScriptRun,
|
|
26
|
+
bounds = DEFAULT_SETTLEMENT_BOUNDS,
|
|
27
|
+
): Promise<CooperativeSettlement> {
|
|
28
|
+
if (run.settled) return "settled";
|
|
29
|
+
const settled = run.settlement.then((): "settled" => "settled");
|
|
30
|
+
const acked = run.interruptAck?.promise.then((): "acked" => "acked") ?? Promise.resolve<"acked">("acked");
|
|
31
|
+
const first = await raceDeadline(Promise.race([settled, acked]), bounds.ackMs, "unresponsive");
|
|
32
|
+
if (first !== "acked") return first;
|
|
33
|
+
return await raceDeadline(settled, bounds.graceMs, "unresponsive");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function retireWorker(
|
|
37
|
+
worker: WorkerLike,
|
|
38
|
+
deadlineMs = WORKER_TERMINATE_DEADLINE_MS,
|
|
39
|
+
): Promise<WorkerRetirement> {
|
|
40
|
+
const termination = worker.terminate().then((): WorkerRetirement => "terminated");
|
|
41
|
+
const outcome = await raceDeadline(termination, deadlineMs, "abandoned");
|
|
42
|
+
if (outcome === "abandoned") void termination.then(undefined, ignoreLateTerminationFailure);
|
|
43
|
+
return outcome;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function abandonedWorkerNote(deadlineMs = WORKER_TERMINATE_DEADLINE_MS): string {
|
|
47
|
+
return `JavaScript worker did not stop within ${deadlineMs}ms: a synchronous call (for example Bun.spawnSync or child_process.spawnSync) is blocking it. A fresh worker replaced it; the blocked call keeps running until it returns.\n`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function raceDeadline<T extends string, Fallback extends string>(
|
|
51
|
+
operation: Promise<T>,
|
|
52
|
+
deadlineMs: number,
|
|
53
|
+
fallback: Fallback,
|
|
54
|
+
): Promise<T | Fallback> {
|
|
55
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
56
|
+
const deadline = new Promise<Fallback>((resolve) => {
|
|
57
|
+
timer = setTimeout(() => resolve(fallback), deadlineMs);
|
|
58
|
+
});
|
|
59
|
+
try {
|
|
60
|
+
return await Promise.race([operation, deadline]);
|
|
61
|
+
} finally {
|
|
62
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function ignoreLateTerminationFailure(): void {}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { KernelToHostMessage } from "../../bridge/protocol.ts";
|
|
2
|
+
import type { SessionEnvironment } from "../session-env.ts";
|
|
2
3
|
|
|
3
4
|
export type ResultMessage = Extract<KernelToHostMessage, { type: "result" }>;
|
|
4
5
|
export type ToolCallMessage = Extract<KernelToHostMessage, { type: "tool-call" }>;
|
|
@@ -11,6 +12,8 @@ export interface JavaScriptKernelOptions {
|
|
|
11
12
|
readonly parallelPoolWidth: number;
|
|
12
13
|
readonly onMessage?: (message: KernelToHostMessage) => void;
|
|
13
14
|
readonly workerEntryUrl?: URL;
|
|
15
|
+
/** Per-session PI_* values applied to the worker environment before the first cell runs. */
|
|
16
|
+
readonly sessionEnv?: SessionEnvironment;
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
export interface JavaScriptRunInput {
|
|
@@ -7,8 +7,13 @@ export interface PendingJavaScriptRun {
|
|
|
7
7
|
readonly input: JavaScriptRunInput;
|
|
8
8
|
readonly resolve: (message: ResultMessage) => void;
|
|
9
9
|
readonly reject: (error: Error) => void;
|
|
10
|
+
readonly settlement: Promise<ResultMessage>;
|
|
10
11
|
startedAtMs: number | null;
|
|
11
12
|
settled: boolean;
|
|
13
|
+
/** Host-composed result that wins over whatever the worker reports once an interrupt is in flight. */
|
|
14
|
+
interruptResult: ResultMessage | null;
|
|
15
|
+
interruptAck: PromiseWithResolvers<void> | null;
|
|
16
|
+
settledByWorker: boolean;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
export class JavaScriptRunQueue {
|
|
@@ -24,9 +29,19 @@ export class JavaScriptRunQueue {
|
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
enqueue(input: JavaScriptRunInput): Promise<ResultMessage> {
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
const { promise, resolve, reject } = Promise.withResolvers<ResultMessage>();
|
|
33
|
+
this.#queue.push({
|
|
34
|
+
input,
|
|
35
|
+
resolve,
|
|
36
|
+
reject,
|
|
37
|
+
settlement: promise,
|
|
38
|
+
startedAtMs: null,
|
|
39
|
+
settled: false,
|
|
40
|
+
interruptResult: null,
|
|
41
|
+
interruptAck: null,
|
|
42
|
+
settledByWorker: false,
|
|
29
43
|
});
|
|
44
|
+
return promise;
|
|
30
45
|
}
|
|
31
46
|
|
|
32
47
|
startNext(startedAtMs: number): PendingJavaScriptRun | null {
|
|
@@ -64,7 +79,7 @@ export class JavaScriptRunQueue {
|
|
|
64
79
|
settleAll(message: string): void {
|
|
65
80
|
const active = this.#active;
|
|
66
81
|
this.#active = null;
|
|
67
|
-
if (active) this.settle(active, stoppedResult(active.input.cellId, message));
|
|
82
|
+
if (active) this.settle(active, active.interruptResult ?? stoppedResult(active.input.cellId, message));
|
|
68
83
|
for (const queued of this.#queue.splice(0)) this.settle(queued, stoppedResult(queued.input.cellId, message));
|
|
69
84
|
}
|
|
70
85
|
|