@code-yeongyu/senpi 2026.9.21-2 → 2026.9.22-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 +56 -0
- package/dist/bundle/chunks/{anthropic-messages-C3ZY2ANT.js → anthropic-messages-2P7APOOQ.js} +1 -1
- package/dist/bundle/chunks/{app-server-command-HSX5RFAP.js → app-server-command-PRNA6QQU.js} +1 -1
- package/dist/bundle/chunks/{azure-openai-responses-7TAGS2VU.js → azure-openai-responses-3AH7MJWF.js} +1 -1
- package/dist/bundle/chunks/{bun-extension-importer-UO7V2MZ7.js → bun-extension-importer-H4IJCWWQ.js} +1 -1
- package/dist/bundle/chunks/{chunk-OSDJ2SYO.js → chunk-5XC2FNJ5.js} +1 -1
- package/dist/bundle/chunks/{chunk-D5CL7PPD.js → chunk-64JQFFHH.js} +2 -2
- package/dist/bundle/chunks/{chunk-Z3KQB2R3.js → chunk-6OD4TT33.js} +95 -39
- package/dist/bundle/chunks/{chunk-NEEAKXDT.js → chunk-7EH64T6E.js} +2 -2
- package/dist/bundle/chunks/{chunk-MIVOQT3Q.js → chunk-ARJJU3MS.js} +1 -1
- package/dist/bundle/chunks/chunk-BOZ37BOT.js +14 -0
- package/dist/bundle/chunks/{chunk-POMPEGWQ.js → chunk-F3DXL5MX.js} +1 -1
- package/dist/bundle/chunks/{chunk-GLKVDRNP.js → chunk-GXUYWMN2.js} +1 -1
- package/dist/bundle/chunks/chunk-HH3KJDWA.js +2 -0
- package/dist/bundle/chunks/{chunk-B7W62BM7.js → chunk-IPRJIKLL.js} +1 -1
- package/dist/bundle/chunks/chunk-JEFYJDHP.js +8 -0
- package/dist/bundle/chunks/{chunk-T6XA2IZR.js → chunk-JR4X6ECO.js} +1 -1
- package/dist/bundle/chunks/{chunk-I425CMTO.js → chunk-JZ6YQU3A.js} +4 -4
- package/dist/bundle/chunks/{chunk-KNM6SRPE.js → chunk-K3HEWWSS.js} +1 -1
- package/dist/bundle/chunks/{chunk-M2XAY6I7.js → chunk-M2HSO7MH.js} +1 -1
- package/dist/bundle/chunks/{chunk-UYVPLEOL.js → chunk-NK2RNX25.js} +1 -1
- package/dist/bundle/chunks/chunk-NMB4C6ZZ.js +2 -0
- package/dist/bundle/chunks/{chunk-VEO3ABZR.js → chunk-OLJ7LQJY.js} +1 -1
- package/dist/bundle/chunks/{chunk-GVI2HP7N.js → chunk-OPNVT7BU.js} +1 -1
- package/dist/bundle/chunks/{chunk-XHMOFY4T.js → chunk-R6LVC7SP.js} +4 -4
- package/dist/bundle/chunks/{chunk-3SLCHXZO.js → chunk-UCPWK6TZ.js} +2 -2
- package/dist/bundle/chunks/{chunk-B2IM56LB.js → chunk-VYIJQQ5I.js} +1 -1
- package/dist/bundle/chunks/{chunk-47GF6XYZ.js → chunk-YT2ZSZOY.js} +1 -1
- package/dist/bundle/chunks/{cli-main-GQTEEZ2V.js → cli-main-AFJNXUSL.js} +1 -1
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{google-generative-ai-JWIH75VF.js → google-generative-ai-Y2IYMUVC.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-IRGAY5XR.js → google-vertex-TKBBMPHV.js} +1 -1
- package/dist/bundle/chunks/{host-command-KOEKIHIZ.js → host-command-OVA6GEWC.js} +1 -1
- package/dist/bundle/chunks/host-lifecycle-IDQPLYEA.js +3 -0
- package/dist/bundle/chunks/host-lifecycle.js +10 -6
- package/dist/bundle/chunks/{interactive-host-runtime-2KTFEW7O.js → interactive-host-runtime-F5L4USLR.js} +1 -1
- package/dist/bundle/chunks/{interactive-mode-I3ILWXQB.js → interactive-mode-UFBSCOP7.js} +1 -1
- package/dist/bundle/chunks/kimi-coding.js +2 -2
- package/dist/bundle/chunks/{list-tips-2KX65NWZ.js → list-tips-2QG7ASHS.js} +1 -1
- package/dist/bundle/chunks/{mistral-conversations-VOBYATVJ.js → mistral-conversations-ZYDVFRYF.js} +1 -1
- package/dist/bundle/chunks/multi-session-host-EUTEGVLS.js +21 -0
- package/dist/bundle/chunks/openai-codex-responses-MEIUNROM.js +10 -0
- package/dist/bundle/chunks/{openai-completions-YK6QTYOH.js → openai-completions-4V76L5ZJ.js} +1 -1
- package/dist/bundle/chunks/openai-responses-YR7WRBG3.js +2 -0
- package/dist/bundle/chunks/{package-manager-cli-HOL6CAI6.js → package-manager-cli-JPUWMUEJ.js} +1 -1
- package/dist/bundle/chunks/rotation-stream-S56Y7WMM.js +2 -0
- package/dist/bundle/chunks/{rpc-mode-L3DSGTVM.js → rpc-mode-JDVHUBZY.js} +1 -1
- package/dist/bundle/chunks/{session-picker-L3FYEQBL.js → session-picker-PFPJVVIC.js} +1 -1
- package/dist/bundle/chunks/session-worker.js +225 -164
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/agent-session.d.ts +6 -4
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +9 -7
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/credential-pool/classify.d.ts.map +1 -1
- package/dist/core/credential-pool/classify.js +12 -0
- package/dist/core/credential-pool/classify.js.map +1 -1
- package/dist/core/credential-pool/failover.d.ts +37 -5
- package/dist/core/credential-pool/failover.d.ts.map +1 -1
- package/dist/core/credential-pool/failover.js +60 -24
- package/dist/core/credential-pool/failover.js.map +1 -1
- package/dist/core/credential-pool/rotation-events.d.ts +5 -0
- package/dist/core/credential-pool/rotation-events.d.ts.map +1 -0
- package/dist/core/credential-pool/rotation-events.js +25 -0
- package/dist/core/credential-pool/rotation-events.js.map +1 -0
- package/dist/core/credential-pool/rotation-stream.d.ts +4 -3
- package/dist/core/credential-pool/rotation-stream.d.ts.map +1 -1
- package/dist/core/credential-pool/rotation-stream.js +8 -11
- package/dist/core/credential-pool/rotation-stream.js.map +1 -1
- package/dist/core/extensions/builtin/ask-user/schema.d.ts +2 -25
- package/dist/core/extensions/builtin/ask-user/schema.d.ts.map +1 -1
- package/dist/core/extensions/builtin/ask-user/schema.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/api-id.d.ts +7 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/api-id.d.ts.map +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/api-id.js +7 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/api-id.js.map +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/index.js +3 -2
- package/dist/core/extensions/builtin/claude-sdk-oauth/index.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js +22 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.d.ts +2 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.js +4 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js +18 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.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 +18 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +2 -7
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +2 -8
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/extension.js +1 -1
- package/dist/core/extensions/builtin/gpt-apply-patch/extension.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/expose/naming.d.ts +0 -1
- package/dist/core/extensions/builtin/mcp/expose/naming.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/expose/naming.js +0 -3
- package/dist/core/extensions/builtin/mcp/expose/naming.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/expose/register.d.ts +1 -2
- package/dist/core/extensions/builtin/mcp/expose/register.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/expose/register.js +0 -21
- package/dist/core/extensions/builtin/mcp/expose/register.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/expose/schema-compat.d.ts +2 -2
- package/dist/core/extensions/builtin/mcp/expose/schema-compat.d.ts.map +1 -1
- package/dist/core/extensions/builtin/mcp/expose/schema-compat.js +2 -2
- package/dist/core/extensions/builtin/mcp/expose/schema-compat.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/file-operations.js +1 -1
- package/dist/core/extensions/builtin/prompt-preset/file-operations.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/grok-4.7.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset/grok-4.7.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/grok-4.7.js +105 -0
- package/dist/core/extensions/builtin/prompt-preset/grok-4.7.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 +14 -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/bun-extension-importer.d.ts +1 -0
- package/dist/core/extensions/bun-extension-importer.d.ts.map +1 -1
- package/dist/core/extensions/bun-extension-importer.js +4 -0
- package/dist/core/extensions/bun-extension-importer.js.map +1 -1
- package/dist/core/extensions/extension-module-cache.d.ts +48 -0
- package/dist/core/extensions/extension-module-cache.d.ts.map +1 -0
- package/dist/core/extensions/extension-module-cache.js +111 -0
- package/dist/core/extensions/extension-module-cache.js.map +1 -0
- package/dist/core/extensions/loader.d.ts +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +14 -56
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts +29 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +30 -3
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/model-resolver.js +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/retry-fallback/billing.d.ts.map +1 -1
- package/dist/core/retry-fallback/billing.js +20 -1
- package/dist/core/retry-fallback/billing.js.map +1 -1
- package/dist/core/session-manager.d.ts +3 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +12 -6
- package/dist/core/session-manager.js.map +1 -1
- package/dist/modes/interactive/components/assistant-render-descriptors.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-render-descriptors.js +8 -7
- package/dist/modes/interactive/components/assistant-render-descriptors.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +7 -5
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/custom-capability.d.ts +7 -0
- package/dist/modes/rpc/custom-capability.d.ts.map +1 -1
- package/dist/modes/rpc/custom-capability.js +7 -0
- package/dist/modes/rpc/custom-capability.js.map +1 -1
- package/dist/modes/rpc/handoff-activity.d.ts +4 -0
- package/dist/modes/rpc/handoff-activity.d.ts.map +1 -0
- package/dist/modes/rpc/handoff-activity.js +6 -0
- package/dist/modes/rpc/handoff-activity.js.map +1 -0
- package/dist/modes/rpc/host-child-exit.d.ts +19 -0
- package/dist/modes/rpc/host-child-exit.d.ts.map +1 -0
- package/dist/modes/rpc/host-child-exit.js +35 -0
- package/dist/modes/rpc/host-child-exit.js.map +1 -0
- package/dist/modes/rpc/host-crash-record.d.ts +24 -0
- package/dist/modes/rpc/host-crash-record.d.ts.map +1 -0
- package/dist/modes/rpc/host-crash-record.js +75 -0
- package/dist/modes/rpc/host-crash-record.js.map +1 -0
- package/dist/modes/rpc/host-crash-record.test.d.ts +2 -0
- package/dist/modes/rpc/host-crash-record.test.d.ts.map +1 -0
- package/dist/modes/rpc/host-crash-record.test.js +91 -0
- package/dist/modes/rpc/host-crash-record.test.js.map +1 -0
- package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
- package/dist/modes/rpc/host-ensure.js +60 -17
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts +4 -10
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +83 -51
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/host-supersession.d.ts +13 -2
- package/dist/modes/rpc/host-supersession.d.ts.map +1 -1
- package/dist/modes/rpc/host-supersession.js +40 -7
- package/dist/modes/rpc/host-supersession.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.d.ts +1 -0
- package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +40 -7
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/observer-link.d.ts +48 -0
- package/dist/modes/rpc/observer-link.d.ts.map +1 -0
- package/dist/modes/rpc/observer-link.js +56 -0
- package/dist/modes/rpc/observer-link.js.map +1 -0
- package/dist/modes/rpc/rpc-mode.d.ts +12 -2
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +12 -2
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +36 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +8 -0
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts +10 -4
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +95 -19
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +5 -3
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +21 -2
- package/dist/modes/rpc/session-event-writer.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +6 -1
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +34 -3
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/dist/modes/rpc/session-worker-client.d.ts +1 -0
- package/dist/modes/rpc/session-worker-client.d.ts.map +1 -1
- package/dist/modes/rpc/session-worker-client.js +3 -0
- package/dist/modes/rpc/session-worker-client.js.map +1 -1
- package/dist/modes/rpc/session-worker-protocol.d.ts +3 -1
- package/dist/modes/rpc/session-worker-protocol.d.ts.map +1 -1
- package/dist/modes/rpc/session-worker-protocol.js.map +1 -1
- package/dist/modes/rpc/session-worker.js +7 -1
- package/dist/modes/rpc/session-worker.js.map +1 -1
- package/dist/modes/rpc/socket-ownership.d.ts +16 -0
- package/dist/modes/rpc/socket-ownership.d.ts.map +1 -1
- package/dist/modes/rpc/socket-ownership.js +28 -4
- package/dist/modes/rpc/socket-ownership.js.map +1 -1
- package/dist/modes/rpc/worker-session-registry.d.ts +12 -5
- package/dist/modes/rpc/worker-session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/worker-session-registry.js +10 -2
- package/dist/modes/rpc/worker-session-registry.js.map +1 -1
- package/docs/providers.md +9 -1
- package/docs/rpc.md +72 -10
- package/docs/settings.md +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +29 -0
- package/node_modules/@code-yeongyu/senpi-codemode/README.md +51 -13
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/agent-bridge.ts +53 -9
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/reserved-dispatch.ts +1 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/config/settings.ts +18 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/eval-status.ts +9 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +6 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/AGENTS.md +2 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/prelude.jl +10 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +18 -6
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +2 -6
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/prelude.ts +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/run-queue.ts +17 -7
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +6 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel-contract.ts +2 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +37 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py +11 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/prelude.rb +6 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-contract.ts +2 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +14 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-queue.ts +18 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt-template.ts +3 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/timeouts/idle-timeout.ts +9 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/AGENTS.md +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-execution.ts +19 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-runtime.ts +11 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-contract.ts +6 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +72 -20
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-snapshot.ts +28 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-state.ts +5 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-status.ts +5 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-eval-result.ts +76 -25
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-execution-event.ts +4 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-kernel-reset-refused-error.ts +12 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-request.ts +2 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool-options.ts +3 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +58 -37
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/managed-cell.ts +15 -8
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +32 -8
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/run-eval-cell.ts +89 -25
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/terminal-snapshot-store.ts +4 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +51 -10
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts +2 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +2 -13
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/tool-arguments.d.ts +5 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/tool-arguments.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/tool-arguments.js +20 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/tool-arguments.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/README.md +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +14 -56
- 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.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +18 -27
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/websocket-transport-failure.d.ts +40 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/websocket-transport-failure.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/websocket-transport-failure.js +102 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/websocket-transport-failure.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.d.ts +4 -3
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.js +28 -14
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-coding.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-region.d.ts +44 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-region.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-region.js +120 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/kimi-region.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/failover.d.ts +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/failover.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/failover.js +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/failover.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.d.ts +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.js +21 -15
- package/node_modules/@earendil-works/pi-ai/dist/auth/pool/slots.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/compat.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/legacy-provider-ids.d.ts +8 -0
- package/node_modules/@earendil-works/pi-ai/dist/legacy-provider-ids.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/legacy-provider-ids.js +26 -0
- package/node_modules/@earendil-works/pi-ai/dist/legacy-provider-ids.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.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/cloudflare-workers-ai.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/huggingface.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/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/venice.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/data/xai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-ams.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi-token-plan-sgp.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/xiaomi.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding-auth.d.ts +9 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding-auth.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding-auth.js +43 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding-auth.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.js +3 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/kimi-coding.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-failure-description.d.ts +19 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-failure-description.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-failure-description.js +63 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/provider-failure-description.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry-profile/classifiers.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry-profile/classifiers.js +5 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry-profile/classifiers.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts +17 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +22 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.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/dist/tui.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +12 -2
- package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
- package/dist/bundle/chunks/chunk-5J5R5AMP.js +0 -8
- package/dist/bundle/chunks/chunk-I3F6VZZF.js +0 -2
- package/dist/bundle/chunks/chunk-IDTLJYJ5.js +0 -10
- package/dist/bundle/chunks/chunk-U6Z6OBX4.js +0 -2
- package/dist/bundle/chunks/host-lifecycle-DGZJTBDJ.js +0 -3
- package/dist/bundle/chunks/multi-session-host-S5FWCIWE.js +0 -20
- package/dist/bundle/chunks/openai-codex-responses-L2PO2D6F.js +0 -10
- package/dist/bundle/chunks/openai-responses-YDUBVF4S.js +0 -2
- package/dist/bundle/chunks/rotation-stream-Y4YU6HV4.js +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-crash-record.test.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-crash-record.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdtempSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { afterEach, describe, expect, it } from \"vitest\";\nimport { noteChildExit } from \"./host-child-exit.ts\";\nimport {\n\tHOST_CRASH_RECORD_LIMIT,\n\thostCrashRecordFile,\n\treadHostCrashRecords,\n\trecordHostCrash,\n} from \"./host-crash-record.ts\";\n\n/**\n * senpi#1950: the supervisor already separated a crashed child from a clean idle exit and then\n * reported the crash only to stderr - which the next host start truncates. A host dying hourly\n * left nothing countable behind, so every investigation had to correlate OS crash reports by hand.\n */\nconst directories: string[] = [];\nconst daemonDir = (): string => {\n\tconst dir = mkdtempSync(join(tmpdir(), \"senpi-crash-record-\"));\n\tdirectories.push(dir);\n\treturn dir;\n};\n\nafterEach(() => {\n\tfor (const dir of directories.splice(0)) rmSync(dir, { recursive: true, force: true });\n});\n\n/**\n * Drives the REAL seam the supervisor's exit handler calls, so removing that call or inverting the\n * crash condition fails these tests. A local re-implementation would pass either way.\n */\nconst observeChildExit = (dir: string, code: number | null, signal: NodeJS.Signals | null, uptimeMs: number): void => {\n\tconst now = Date.now();\n\tnoteChildExit(dir, code, signal, now - uptimeMs, now);\n};\n\ndescribe(\"host crash records\", () => {\n\tit(\"keeps the first crash readable after a replacement host starts\", () => {\n\t\tconst dir = daemonDir();\n\t\tobserveChildExit(dir, null, \"SIGBUS\", 3_061_000);\n\n\t\t// A replacement generation boots and writes its own daemon state. This is exactly the\n\t\t// moment the stderr log was lost, so it is the moment the record has to survive.\n\t\twriteFileSync(join(dir, \"settings.json\"), JSON.stringify({ coldStart: \"transient\" }));\n\n\t\tconst records = readHostCrashRecords(dir);\n\t\texpect(records).toHaveLength(1);\n\t\texpect(records[0]?.signal).toBe(\"SIGBUS\");\n\t\texpect(records[0]?.uptimeMs).toBe(3_061_000);\n\t});\n\n\tit(\"accumulates repeated crashes in order so they can be counted\", () => {\n\t\tconst dir = daemonDir();\n\t\tobserveChildExit(dir, null, \"SIGSEGV\", 5_000);\n\t\tobserveChildExit(dir, null, \"SIGBUS\", 9_000);\n\n\t\tconst records = readHostCrashRecords(dir);\n\t\texpect(records).toHaveLength(2);\n\t\texpect(records.map((record) => record.signal)).toEqual([\"SIGSEGV\", \"SIGBUS\"]);\n\t});\n\n\tit(\"records a non-zero exit code when the child exited instead of signalling\", () => {\n\t\tconst dir = daemonDir();\n\t\tobserveChildExit(dir, 1, null, 1_500);\n\n\t\tconst records = readHostCrashRecords(dir);\n\t\texpect(records).toHaveLength(1);\n\t\texpect(records[0]?.code).toBe(1);\n\t\texpect(records[0]?.signal).toBeUndefined();\n\t});\n\n\tit(\"writes nothing for a clean idle exit, so the line count is a crash count\", () => {\n\t\tconst dir = daemonDir();\n\t\tobserveChildExit(dir, 0, null, 900_000);\n\n\t\texpect(readHostCrashRecords(dir)).toEqual([]);\n\t\texpect(() => readFileSync(hostCrashRecordFile(dir), \"utf8\")).toThrow();\n\t});\n\n\tit(\"bounds the file so a crash loop cannot grow it forever\", () => {\n\t\tconst dir = daemonDir();\n\t\tfor (let index = 0; index < HOST_CRASH_RECORD_LIMIT + 10; index++) {\n\t\t\trecordHostCrash(dir, { at: new Date().toISOString(), signal: \"SIGBUS\", uptimeMs: index });\n\t\t}\n\n\t\tconst records = readHostCrashRecords(dir);\n\t\texpect(records).toHaveLength(HOST_CRASH_RECORD_LIMIT);\n\t\t// Pruning keeps the NEWEST: the oldest uptimes are the ones that must be gone.\n\t\texpect(records[0]?.uptimeMs).toBe(10);\n\t\texpect(records.at(-1)?.uptimeMs).toBe(HOST_CRASH_RECORD_LIMIT + 9);\n\t});\n\n\tit(\"survives an unwritable directory rather than throwing into the shutdown path\", () => {\n\t\texpect(() =>\n\t\t\trecordHostCrash(join(\"/nonexistent-root-for-senpi-1950\", \"daemon\"), {\n\t\t\t\tat: new Date().toISOString(),\n\t\t\t\tsignal: \"SIGBUS\",\n\t\t\t\tuptimeMs: 1,\n\t\t\t}),\n\t\t).not.toThrow();\n\t});\n\n\tit(\"skips a torn final line from a host killed mid-append\", () => {\n\t\tconst dir = daemonDir();\n\t\trecordHostCrash(dir, { at: new Date().toISOString(), signal: \"SIGSEGV\", uptimeMs: 42 });\n\t\twriteFileSync(hostCrashRecordFile(dir), `${readFileSync(hostCrashRecordFile(dir), \"utf8\")}{\"at\":\"tru`);\n\n\t\tconst records = readHostCrashRecords(dir);\n\t\texpect(records).toHaveLength(1);\n\t\texpect(records[0]?.uptimeMs).toBe(42);\n\t});\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-ensure.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-ensure.ts"],"names":[],"mappings":"AA0CA,OAAO,EAA6B,KAAK,aAAa,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"host-ensure.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-ensure.ts"],"names":[],"mappings":"AA0CA,OAAO,EAA6B,KAAK,aAAa,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAOnH,OAAO,EACN,qBAAqB,EACrB,mBAAmB,EACnB,KAAK,eAAe,EACpB,oBAAoB,EACpB,KAAK,mBAAmB,GACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,+BAA+B,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACnE,YAAY,EAAE,aAAa,EAAE,wBAAwB,EAAE,CAAC;AAExD;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,mBAAmB,CAAC;AAE9D,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,sFAAsF;IACtF,QAAQ,CAAC,MAAM,CAAC,EAAE,wBAAwB,CAAC;IAC3C,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,sFAAsF;IACtF,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IACvD,iGAAiG;IACjG,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,KAAK,CAAC,EAAE;QAChB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;QACrC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,KAAK,CAAC,EAAE;YAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;SAAE,CAAC;QAChF,6FAA6F;QAC7F,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK;YAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;SAAE,CAAC;QAC9G,oFAAoF;QACpF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QACjD;;;;WAIG;QACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QAClD,4EAA4E;QAC5E,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;KAC7E,CAAC;CACF;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CACzB;AAsBD,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAsBjF"}
|
|
@@ -17,6 +17,7 @@ import { DEFAULT_HOST_IDLE_EXIT_MS } from "./host-lifecycle.js";
|
|
|
17
17
|
import { probeProtocolInfo, probeSocketReachable } from "./host-probe.js";
|
|
18
18
|
import { acquireOwnershipSafeLock } from "./ownership-safe-lock.js";
|
|
19
19
|
import { HOST_GENERATION_ENV, HOST_INSTANCE_ID_ENV, hostLaunchProfile } from "./protocol-identity.js";
|
|
20
|
+
import { statSocketIdentity } from "./socket-ownership.js";
|
|
20
21
|
import { createSocketSecret, resolveSocketTransportAddress, socketSecretPath } from "./socket-transport.js";
|
|
21
22
|
export { createHostDaemonPaths, daemonDirectoryName, HostDaemonStateError, } from "./host-daemon-paths.js";
|
|
22
23
|
export { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from "./host-launch.js";
|
|
@@ -91,29 +92,66 @@ async function ensureHostLocked(paths, socket, options) {
|
|
|
91
92
|
}
|
|
92
93
|
const probe = testOptions?.readProcessStartTime ?? readProcessStartTime;
|
|
93
94
|
const pidMatches = registeredHere && registered ? await matchesPidFileOrUnknown(registered.record, probe) : false;
|
|
95
|
+
// The generation this start leaves RUNNING beside the new one, when there is one.
|
|
96
|
+
let stranded;
|
|
94
97
|
if (registered && pidMatches) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
if (!startedByUs) {
|
|
99
|
+
// I1: the socket is silent, but the process behind it is alive. Only the process that WROTE
|
|
100
|
+
// this record may end it - anyone else refuses rather than signalling somebody else's host.
|
|
101
|
+
if (await publicEndpointAccepts(socket))
|
|
102
|
+
throw new HostEnsureRefusedError(socket, "foreign_writer", protocol);
|
|
103
|
+
// A foreign record whose public endpoint accepts NOTHING names a generation nobody can reach:
|
|
104
|
+
// its entry was replaced (so it is already draining, #1893) or removed, or a dead listener
|
|
105
|
+
// left the entry behind. Refusing here locked every client out until that process happened
|
|
106
|
+
// to exit (#1936). Binding a fresh generation there signals nothing, so that is what happens -
|
|
107
|
+
// the stranded one keeps its record.
|
|
108
|
+
stranded = registered;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// Silent is not the same as gone. A host serving many sessions can miss a probe budget
|
|
112
|
+
// while its event loop is busy; its socket still ACCEPTS the connection. Ending it then
|
|
113
|
+
// would destroy every live session to replace a host that was never broken, so a
|
|
114
|
+
// reachable socket is refused instead of signalled - the caller retries or falls back.
|
|
115
|
+
if (await probeSocketReachable(socket, EXISTING_HOST_PROBE_TIMEOUT_MS)) {
|
|
116
|
+
throw new HostEnsureRefusedError(socket, "host_busy", protocol);
|
|
117
|
+
}
|
|
118
|
+
await stopManagedHost(registered.record, testOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, probe);
|
|
105
119
|
}
|
|
106
|
-
await stopManagedHost(registered.record, testOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, probe);
|
|
107
120
|
}
|
|
108
121
|
// A host from before this layout registered itself in the FLAT directory. Its files are another
|
|
109
122
|
// process's state: never read as ours, never signalled, never removed - and while it is alive,
|
|
110
123
|
// this ensure refuses instead of binding a socket it may still be serving.
|
|
111
124
|
if (await legacyHostIsLive(paths, probe))
|
|
112
125
|
throw new HostEnsureRefusedError(socket, "legacy_host", protocol);
|
|
126
|
+
if (stranded !== undefined)
|
|
127
|
+
return startHost(paths, socket, options, stranded.generation + 1);
|
|
113
128
|
if (registeredHere)
|
|
114
129
|
await clearHostRegistration(paths);
|
|
115
130
|
return startHost(paths, socket, options);
|
|
116
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Only the connect matters here, never an answer: the kernel completes it from the listen backlog
|
|
134
|
+
* without the host's event loop, so a live owner under load still accepts within this budget.
|
|
135
|
+
*/
|
|
136
|
+
const FOREIGN_ENDPOINT_PROBE_TIMEOUT_MS = 2_000;
|
|
137
|
+
/**
|
|
138
|
+
* Whether SOMETHING still accepts connections at the public path - the one fact that says a
|
|
139
|
+
* registered process may still own the endpoint. A missing entry and an entry nobody listens
|
|
140
|
+
* behind (connection refused) both answer no; an accepted connection, however silent, answers yes.
|
|
141
|
+
* A named pipe has no entry to lose and an abstract socket has no path, so both read as owned;
|
|
142
|
+
* so does an entry this process cannot stat, because an owner that cannot be ruled out is one
|
|
143
|
+
* this ensure must not bind over.
|
|
144
|
+
*/
|
|
145
|
+
async function publicEndpointAccepts(socket) {
|
|
146
|
+
if (process.platform === "win32" || socket.startsWith("\0"))
|
|
147
|
+
return true;
|
|
148
|
+
const entry = await statSocketIdentity(socket).then((identity) => (identity === undefined ? "absent" : "present"), () => "unknown");
|
|
149
|
+
if (entry === "absent")
|
|
150
|
+
return false;
|
|
151
|
+
if (entry === "unknown")
|
|
152
|
+
return true;
|
|
153
|
+
return probeSocketReachable(socket, FOREIGN_ENDPOINT_PROBE_TIMEOUT_MS);
|
|
154
|
+
}
|
|
117
155
|
/** `fallback` belongs to clients that can live without a host; an ensure must produce one or fail. */
|
|
118
156
|
function decide(options, startedByUs, protocol) {
|
|
119
157
|
if (options.upgrade !== "if-engine-differs") {
|
|
@@ -169,7 +207,11 @@ function registersSocket(registered, socket) {
|
|
|
169
207
|
return false;
|
|
170
208
|
return registered.socket === undefined || registered.socket === socket;
|
|
171
209
|
}
|
|
172
|
-
|
|
210
|
+
/**
|
|
211
|
+
* `generation` is 0 for a fresh endpoint; a start that leaves a stranded generation running beside
|
|
212
|
+
* the new one numbers it after that generation, as a handoff would.
|
|
213
|
+
*/
|
|
214
|
+
async function startHost(paths, socket, options, generation = 0) {
|
|
173
215
|
const testOptions = options._test;
|
|
174
216
|
// The generation this ensure is about to spawn, chosen HERE so its directory exists before the
|
|
175
217
|
// host boots and the pointer can name it the moment the host is registered.
|
|
@@ -183,10 +225,11 @@ async function startHost(paths, socket, options) {
|
|
|
183
225
|
capabilities: PINNED_HOST_CLIENT_CAPABILITIES,
|
|
184
226
|
coldStart: options.policy?.coldStart ?? "transient",
|
|
185
227
|
idleExitMs: options.policy?.idleExitMs ?? DEFAULT_HOST_IDLE_EXIT_MS,
|
|
186
|
-
generation
|
|
228
|
+
generation,
|
|
187
229
|
instanceId,
|
|
188
230
|
});
|
|
189
|
-
|
|
231
|
+
// A stranded generation is still writing its diagnostics here; only a fresh endpoint starts over.
|
|
232
|
+
const stderr = await open(paths.stderrLog, generation === 0 ? "w" : "a", 0o600);
|
|
190
233
|
let pidFile;
|
|
191
234
|
let child;
|
|
192
235
|
let exitedEarly;
|
|
@@ -197,7 +240,7 @@ async function startHost(paths, socket, options) {
|
|
|
197
240
|
child = spawn(launch.command, [...launch.args], {
|
|
198
241
|
detached: true,
|
|
199
242
|
windowsHide: true,
|
|
200
|
-
env: hostEnv(options, { paths, instanceId }),
|
|
243
|
+
env: hostEnv(options, { paths, instanceId, generation }),
|
|
201
244
|
stdio: ["ignore", "ignore", stderr.fd],
|
|
202
245
|
});
|
|
203
246
|
childExit = new Promise((resolveExit) => {
|
|
@@ -232,7 +275,7 @@ async function startHost(paths, socket, options) {
|
|
|
232
275
|
record: pidFile,
|
|
233
276
|
socket,
|
|
234
277
|
instanceId,
|
|
235
|
-
generation
|
|
278
|
+
generation,
|
|
236
279
|
launchProfileId: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()).profile_id,
|
|
237
280
|
});
|
|
238
281
|
child.unref();
|
|
@@ -432,7 +475,7 @@ function hostEnv(options, generation) {
|
|
|
432
475
|
// its own host's identity to the one it spawns, and two hosts claiming one instance id make a
|
|
433
476
|
// handoff - which completes exactly when the instance id changes - impossible to observe.
|
|
434
477
|
env[HOST_INSTANCE_ID_ENV] = generation.instanceId;
|
|
435
|
-
env[HOST_GENERATION_ENV] =
|
|
478
|
+
env[HOST_GENERATION_ENV] = String(generation.generation);
|
|
436
479
|
env[HOST_DAEMON_DIR_ENV] = generation.paths.dir;
|
|
437
480
|
return env;
|
|
438
481
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-ensure.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-ensure.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, open, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { ENV_AGENT_DIR, getAgentDir } from \"../../config.ts\";\nimport { engineBuildIdentity } from \"../../core/engine-build-identity.ts\";\nimport {\n\ttype DaemonPidFile,\n\tProcessIdentityUnreadableError,\n\tprocessIsLive,\n\tprocessMatchesPidFile,\n\treadProcessStartTime,\n\twaitForStartTime,\n} from \"../app-server/daemon/process.ts\";\nimport { RPC_CLIENT_CAPABILITIES_ENV } from \"./custom-capability.ts\";\nimport {\n\tcreateDaemonDirectories,\n\tcreateHostDaemonPaths,\n\tHOST_DAEMON_DIR_ENV,\n\ttype HostDaemonPaths,\n} from \"./host-daemon-paths.ts\";\nimport {\n\tclearHostRegistration,\n\tlegacyHostIsLive,\n\ttype RegisteredHost,\n\treadHostRegistration,\n\twriteHostRegistration,\n\twrittenByThisProcess,\n} from \"./host-daemon-registration.ts\";\nimport { writeHostSettings } from \"./host-daemon-state.ts\";\nimport {\n\tdecideHostAction,\n\tHOST_PROTOCOL_VERSION,\n\ttype HostDecision,\n\ttype HostDecisionClient,\n\tHostEnsureRefusedError,\n\ttype HostProtocolInfo,\n\tREQUIRED_HOST_CAPABILITIES,\n} from \"./host-decision.ts\";\nimport { handoffHost } from \"./host-handoff.ts\";\nimport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nimport { DEFAULT_HOST_IDLE_EXIT_MS, type HostColdStart, type HostLifecyclePolicyInput } from \"./host-lifecycle.ts\";\nimport { probeProtocolInfo, probeSocketReachable } from \"./host-probe.ts\";\nimport { acquireOwnershipSafeLock } from \"./ownership-safe-lock.ts\";\nimport { HOST_GENERATION_ENV, HOST_INSTANCE_ID_ENV, hostLaunchProfile } from \"./protocol-identity.ts\";\nimport { createSocketSecret, resolveSocketTransportAddress, socketSecretPath } from \"./socket-transport.ts\";\n\nexport {\n\tcreateHostDaemonPaths,\n\tdaemonDirectoryName,\n\ttype HostDaemonPaths,\n\tHostDaemonStateError,\n\ttype HostGenerationPaths,\n} from \"./host-daemon-paths.ts\";\nexport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nexport { type ProbeHostOptions, probeHost } from \"./host-probe.ts\";\nexport type { HostColdStart, HostLifecyclePolicyInput };\n\n/**\n * What an ensure may do to a host that is already running.\n *\n * `never` (the default) attaches or starts, and touches nothing that is already serving the\n * socket. `if-engine-differs` additionally allows a GENERATION HANDOFF when `decideHostAction`\n * finds this build strictly newer and its extension set a superset of the running host's - the\n * running host then drains instead of dying, so no session is ever ended by an upgrade.\n */\nexport type HostUpgradePolicy = \"never\" | \"if-engine-differs\";\n\nexport interface EnsureHostOptions {\n\treadonly socket: string;\n\treadonly agentDir?: string;\n\t/** Host lifecycle policy recorded in settings.json (env overrides win at runtime). */\n\treadonly policy?: HostLifecyclePolicyInput;\n\t/** Extra CLI arguments forwarded through the supervisor to the host process. */\n\treadonly hostArgs?: readonly string[];\n\t/** Environment for the spawned host; a `null` value removes an inherited variable. */\n\treadonly env?: Readonly<Record<string, string | null>>;\n\t/** Whether a newer build may take the socket over from the running host. Defaults to `never`. */\n\treadonly upgrade?: HostUpgradePolicy;\n\treadonly _test?: {\n\t\treadonly readinessTimeoutMs?: number;\n\t\treadonly stopTimeoutMs?: number;\n\t\treadonly spawn?: { readonly command: string; readonly args: readonly string[] };\n\t\t/** Builds the spawnable command from supervisor argv; tests point it at the source entry. */\n\t\treadonly launch?: (args: readonly string[]) => { readonly command: string; readonly args: readonly string[] };\n\t\t/** Runs after endpoint ownership is locked; deterministic concurrency-test gate. */\n\t\treadonly afterLockAcquired?: () => Promise<void>;\n\t\t/**\n\t\t * Runs after the child is spawned but before its pidfile is registered, so a\n\t\t * test can force the startup failure a loaded runner produces without having\n\t\t * to stall the real process-identity probe.\n\t\t */\n\t\treadonly beforePidFileWrite?: () => Promise<void>;\n\t\t/** Overrides the process-identity probe so a test can force its failure. */\n\t\treadonly readProcessStartTime?: (pid: number) => Promise<string | undefined>;\n\t};\n}\n\nexport interface EnsuredHost {\n\treadonly pid: number;\n\treadonly socket: string;\n\treadonly reused: boolean;\n}\n\nconst SPAWNED_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst EXISTING_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst DEFAULT_READINESS_TIMEOUT_MS = 10_000;\nconst DEFAULT_STOP_TIMEOUT_MS = 10_000;\nconst SIGKILL_GRACE_MS = 2_000;\n/**\n * A lock waiter must outlast the longest critical section a holder can run:\n * probing an existing host, stopping an incompatible one (SIGTERM wait plus the\n * SIGKILL grace), then spawning the replacement and waiting for it to answer.\n * Each SQLite busy wait stays short because it blocks the event loop; this\n * cumulative budget is what covers the whole section, with headroom for a slow\n * runner. A waiter that gives up early surfaces as a raw \"database is locked\"\n * failure on the second of two concurrent starts.\n */\nconst ENSURE_LOCK_WAIT_MS =\n\tEXISTING_HOST_PROBE_TIMEOUT_MS + DEFAULT_STOP_TIMEOUT_MS + SIGKILL_GRACE_MS + DEFAULT_READINESS_TIMEOUT_MS + 10_000;\nconst LOCK_BUSY_WAIT_MS = 100;\nconst lockOptions = {\n\tretries: { retries: ENSURE_LOCK_WAIT_MS / LOCK_BUSY_WAIT_MS, minTimeout: 20, maxTimeout: LOCK_BUSY_WAIT_MS },\n} as const;\nexport async function ensureHost(options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst socket = normalizeSocketPath(options.socket);\n\tconst paths = createHostDaemonPaths({ socket, ...(options.agentDir ? { agentDir: options.agentDir } : {}) });\n\tawait createDaemonDirectories(paths);\n\t// The public socket is the shared resource; agent directories are not a\n\t// sufficient lock scope when two installations target the same endpoint.\n\tconst lockTarget = join(tmpdir(), \"senpi-rpc-host-locks\", createSocketLockName(socket));\n\tawait mkdir(dirname(lockTarget), { recursive: true });\n\tawait writeFile(lockTarget, \"\", { flag: \"a\", mode: 0o600 });\n\t// Opportunistic GC of other installs' leftovers stays OUTSIDE the endpoint lock.\n\t// Its cost scales with the whole tmpdir and, on win32, adds a ~1s process probe per\n\t// candidate; inside the critical section that inflated the hold for every concurrent\n\t// ensureHost until a waiter exhausted its budget and surfaced a raw \"database is\n\t// locked\". Its own guards (60s age, dead owner pid) already make it safe unlocked.\n\tawait reapOrphanedInternalHostDirs();\n\tconst release = await acquireOwnershipSafeLock(`${lockTarget}.lock`, lockOptions);\n\ttry {\n\t\tawait options._test?.afterLockAcquired?.();\n\t\treturn await ensureHostLocked(paths, socket, options);\n\t} finally {\n\t\tawait release();\n\t}\n}\n\nasync function ensureHostLocked(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\tconst registered = await readHostRegistration(paths);\n\t// A record naming ANOTHER endpoint is not about this ensure's host. The per-socket directory\n\t// makes that structural, and the field stays as the second guard for a directory that was\n\t// somehow reused: a second socket must never read the first socket's daemon as its own.\n\tconst registeredHere = registersSocket(registered, socket);\n\tconst protocol = await probeProtocolInfo(socket, EXISTING_HOST_PROBE_TIMEOUT_MS);\n\tconst startedByUs = registeredHere && (await writtenByThisProcess(registered?.writer));\n\tconst attachedPid = registeredHere ? (registered?.record.pid ?? 0) : 0;\n\tconst decision = decide(options, startedByUs, protocol);\n\tswitch (decision.action) {\n\t\tcase \"reuse\":\n\t\t\t// A compatible socket is attachable even when another client surface\n\t\t\t// started it. Only hosts we spawned are eligible for lifecycle management.\n\t\t\treturn { pid: attachedPid, socket, reused: true };\n\t\tcase \"refuse\":\n\t\t\tthrow new HostEnsureRefusedError(socket, decision.reason, protocol);\n\t\tcase \"handoff\":\n\t\t\treturn upgradeGeneration(paths, socket, options, attachedPid);\n\t\tcase \"start\":\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn assertNever(decision);\n\t}\n\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\tconst pidMatches = registeredHere && registered ? await matchesPidFileOrUnknown(registered.record, probe) : false;\n\tif (registered && pidMatches) {\n\t\t// I1: the socket is silent, but the process behind it is alive. Only the process that WROTE\n\t\t// this record may end it - anyone else refuses rather than signalling somebody else's host.\n\t\tif (!startedByUs) throw new HostEnsureRefusedError(socket, \"foreign_writer\", protocol);\n\t\t// ... and silent is not the same as gone. A host serving many sessions can miss a probe\n\t\t// budget while its event loop is busy; its socket still ACCEPTS the connection. Ending it\n\t\t// then would destroy every live session to replace a host that was never broken, so a\n\t\t// reachable socket is refused instead of signalled - the caller retries or falls back.\n\t\tif (await probeSocketReachable(socket, EXISTING_HOST_PROBE_TIMEOUT_MS)) {\n\t\t\tthrow new HostEnsureRefusedError(socket, \"host_busy\", protocol);\n\t\t}\n\t\tawait stopManagedHost(registered.record, testOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, probe);\n\t}\n\t// A host from before this layout registered itself in the FLAT directory. Its files are another\n\t// process's state: never read as ours, never signalled, never removed - and while it is alive,\n\t// this ensure refuses instead of binding a socket it may still be serving.\n\tif (await legacyHostIsLive(paths, probe)) throw new HostEnsureRefusedError(socket, \"legacy_host\", protocol);\n\tif (registeredHere) await clearHostRegistration(paths);\n\treturn startHost(paths, socket, options);\n}\n\n/** `fallback` belongs to clients that can live without a host; an ensure must produce one or fail. */\nfunction decide(\n\toptions: EnsureHostOptions,\n\tstartedByUs: boolean,\n\tprotocol: HostProtocolInfo | undefined,\n): Exclude<HostDecision, { action: \"fallback\" }> {\n\tif (options.upgrade !== \"if-engine-differs\") {\n\t\treturn decideHostAction(ensureClient(options, startedByUs), protocol, \"never\");\n\t}\n\tconst decision = decideHostAction(ensureClient(options, startedByUs), protocol, \"upgrade\");\n\treturn decision.action === \"fallback\" ? { action: \"reuse\", reason: \"compatible\", upgradeable: false } : decision;\n}\n\n/**\n * The upgrade, when the decision allows one: a new generation takes the socket and the running\n * host drains. A refused handoff ATTACHES - an upgrade that cannot happen must never become a stop.\n */\nasync function upgradeGeneration(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n\tattachedPid: number,\n): Promise<EnsuredHost> {\n\tconst result = await handoffHost({\n\t\tsocket,\n\t\tagentDir: options.agentDir ?? getAgentDir(),\n\t\thostArgs: options.hostArgs ?? [],\n\t\t...(options.env ? { env: options.env } : {}),\n\t\t...(options.policy ? { policy: options.policy } : {}),\n\t\t_test: {\n\t\t\t...(options._test?.launch ? { launch: options._test.launch } : {}),\n\t\t\t...(options._test?.readinessTimeoutMs ? { readinessTimeoutMs: options._test.readinessTimeoutMs } : {}),\n\t\t},\n\t});\n\tif (result.action === \"handoff\") return { pid: result.pid, socket, reused: false };\n\tawait appendStderr(\n\t\tpaths,\n\t\t`generation handoff refused: ${result.reason}${result.detail ? ` (${result.detail})` : \"\"}`,\n\t);\n\treturn { pid: attachedPid, socket, reused: true };\n}\n\n/** This build as a client: which protocol it speaks, what it needs from a host, and which build it is. */\nfunction ensureClient(options: EnsureHostOptions, startedByUs: boolean): HostDecisionClient {\n\treturn {\n\t\tprotocolVersion: HOST_PROTOCOL_VERSION,\n\t\trequiredCapabilities: REQUIRED_HOST_CAPABILITIES,\n\t\tidentity: engineBuildIdentity(),\n\t\t// An ensure that may upgrade has to say what it would launch: the superset rule refuses to\n\t\t// hand off to a generation that would drop what the running host loads. An ensure that may\n\t\t// not upgrade declares nothing, and therefore can never be the newer candidate.\n\t\t...(options.upgrade === \"if-engine-differs\"\n\t\t\t? { launchProfile: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()) }\n\t\t\t: {}),\n\t\tstartedByUs,\n\t\tplatform: process.platform,\n\t};\n}\n\n/** The argv the supervisor gives its host child; the launch profile has to describe THAT host. */\nfunction hostChildArgv(hostArgs: readonly string[]): string[] {\n\treturn [\"--mode\", \"rpc\", \"--multi-session\", ...hostArgs];\n}\n\n/** Whether a registration is about this endpoint. A record written before the field existed is. */\nfunction registersSocket(registered: RegisteredHost | undefined, socket: string): boolean {\n\tif (registered === undefined) return false;\n\treturn registered.socket === undefined || registered.socket === socket;\n}\n\nasync function startHost(paths: HostDaemonPaths, socket: string, options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\t// The generation this ensure is about to spawn, chosen HERE so its directory exists before the\n\t// host boots and the pointer can name it the moment the host is registered.\n\tconst instanceId = randomUUID();\n\t// The settings file must exist before the supervisor reads it at boot, so it\n\t// records the policy before the spawn instead of beside the pidfile.\n\tif (process.platform === \"win32\") await createSocketSecret(socketSecretPath(socket));\n\tawait writeHostSettings(paths, {\n\t\tsocket,\n\t\tcapabilities: PINNED_HOST_CLIENT_CAPABILITIES,\n\t\tcoldStart: options.policy?.coldStart ?? \"transient\",\n\t\tidleExitMs: options.policy?.idleExitMs ?? DEFAULT_HOST_IDLE_EXIT_MS,\n\t\tgeneration: 0,\n\t\tinstanceId,\n\t});\n\tconst stderr = await open(paths.stderrLog, \"w\", 0o600);\n\tlet pidFile: DaemonPidFile | undefined;\n\tlet child: ReturnType<typeof spawn> | undefined;\n\tlet exitedEarly: ChildExit | undefined;\n\tlet childExit: Promise<ChildExit> | undefined;\n\ttry {\n\t\tconst supervisorArgs = [\"--socket\", socket, ...(options.hostArgs ?? [])];\n\t\tconst launch = testOptions?.spawn ?? testOptions?.launch?.(supervisorArgs) ?? defaultHostLaunch(supervisorArgs);\n\t\tchild = spawn(launch.command, [...launch.args], {\n\t\t\tdetached: true,\n\t\t\twindowsHide: true,\n\t\t\tenv: hostEnv(options, { paths, instanceId }),\n\t\t\tstdio: [\"ignore\", \"ignore\", stderr.fd],\n\t\t});\n\t\tchildExit = new Promise((resolveExit) => {\n\t\t\tchild!.once(\"exit\", (code, signal) => {\n\t\t\t\texitedEarly = { code, signal };\n\t\t\t\tresolveExit(exitedEarly);\n\t\t\t});\n\t\t});\n\t\tif (child.pid === undefined) throw new Error(\"failed to spawn RPC socket host\");\n\t\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\t\tconst observedStartTime = await Promise.race([\n\t\t\twaitForStartTime(child.pid, 10_000, probe),\n\t\t\tchildExit.then(() => {\n\t\t\t\tthrow new Error(\"RPC socket host exited before its start time could be read\");\n\t\t\t}),\n\t\t]);\n\t\t// UNKNOWN identity on a live child: the probe was starved, not the host. Give the CIM table\n\t\t// one unhurried read (the per-attempt win32 default is 1s, which a loaded runner exceeds on\n\t\t// every attempt) before deciding.\n\t\tconst unhurriedProbe = testOptions?.readProcessStartTime\n\t\t\t? testOptions.readProcessStartTime\n\t\t\t: (pid: number) => readProcessStartTime(pid, process.platform, 15_000);\n\t\tconst processStartTime = observedStartTime ?? (await unhurriedProbe(child.pid).catch(() => undefined));\n\t\t// Still unreadable: the host is ours, alive, and about to prove itself on the socket, so it is\n\t\t// registered WITHOUT an ownership guard instead of being torn down for a starved probe. A\n\t\t// guard-less record never claims ownership and never authorizes a signal - every later caller\n\t\t// reads it as unknown - so the worst case is a fresh host next time, not a killed healthy one.\n\t\tpidFile = { pid: child.pid, processStartTime: processStartTime ?? null };\n\t\tawait testOptions?.beforePidFileWrite?.();\n\t\tawait writeHostRegistration(paths, {\n\t\t\trecord: pidFile,\n\t\t\tsocket,\n\t\t\tinstanceId,\n\t\t\tgeneration: 0,\n\t\t\tlaunchProfileId: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()).profile_id,\n\t\t});\n\t\tchild.unref();\n\t} catch (error: unknown) {\n\t\t// Whether the child died on its own decides which diagnostic is true, and the\n\t\t// cleanup kill below records an `exitedEarly` indistinguishable from a real\n\t\t// self-exit. Latch it before killing, or the catch reports the SIGTERM it is\n\t\t// about to send and discards the actual startup failure.\n\t\tconst exitedBeforeCleanup = exitedEarly;\n\t\t// Keep the ChildProcess handle owned until registration succeeds. If startup\n\t\t// fails before the pidfile is written, terminate this exact child through\n\t\t// its still-attached handle rather than leaving an unmanaged daemon behind.\n\t\tif (!exitedBeforeCleanup && child && child.exitCode === null && child.signalCode === null) {\n\t\t\ttry {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t} catch {}\n\t\t\tif (childExit) await Promise.race([childExit, delay(2_000)]);\n\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\ttry {\n\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t}\n\t\tif (!exitedBeforeCleanup) {\n\t\t\tawait clearHostRegistration(paths);\n\t\t\tthrow error;\n\t\t}\n\t\tconst diagnostic = await appendStderr(\n\t\t\tpaths,\n\t\t\t`RPC socket host exited with code ${exitedBeforeCleanup.code ?? \"null\"}${exitedBeforeCleanup.signal ? ` (${exitedBeforeCleanup.signal})` : \"\"} before answering get_protocol_info`,\n\t\t);\n\t\tawait clearHostRegistration(paths);\n\t\tthrow new Error(diagnostic);\n\t} finally {\n\t\tawait stderr.close();\n\t}\n\tconst readinessTimeoutMs = testOptions?.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS;\n\tconst result = await pollProtocolInfo(socket, readinessTimeoutMs, childExit);\n\tif (isCompatible(result.protocol)) return { pid: pidFile.pid, socket, reused: false };\n\t// Teardown runs for the diagnostic's sake, so it must never replace it: a stop\n\t// failure here (unreadable identity, a host that outlives SIGKILL) would other-\n\t// wise propagate instead of the readiness message and skip cleanupState below,\n\t// leaving the pidfile and socket behind. Record it and keep going.\n\t// This child is ours and its handle is still attached: stop it through the handle.\n\t// Validating ownership through the pidfile would re-run the identity probe whose\n\t// failure is the very thing a loaded runner produces here.\n\tconst stopFailure = await stopSpawnedChild(\n\t\tchild,\n\t\tchildExit,\n\t\ttestOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS,\n\t).then(\n\t\t() => undefined,\n\t\t(error: unknown) => (error instanceof Error ? error.message : String(error)),\n\t);\n\tconst message = result.protocol\n\t\t? `RPC socket host answered get_protocol_info with protocolVersion ${result.protocol.protocolVersion}, serverVersion ${result.protocol.serverVersion} and capabilities ${JSON.stringify(result.protocol.capabilities)}, but is incompatible with protocol version ${HOST_PROTOCOL_VERSION} and required capabilities ${JSON.stringify(REQUIRED_HOST_CAPABILITIES)}`\n\t\t: result.exited\n\t\t\t? `RPC socket host exited with code ${result.exited.code ?? \"null\"}${result.exited.signal ? ` (${result.exited.signal})` : \"\"} before answering get_protocol_info`\n\t\t\t: `spawned RPC socket host did not answer get_protocol_info within ${readinessTimeoutMs}ms`;\n\tconst diagnostic = await appendStderr(\n\t\tpaths,\n\t\tstopFailure === undefined ? message : `${message} (teardown also reported: ${stopFailure})`,\n\t);\n\tawait clearHostRegistration(paths);\n\t// The supervisor may have failed before binding, or another owner may have\n\t// appeared while readiness was being checked. Never unlink an endpoint we\n\t// cannot prove this start owned.\n\tthrow new Error(diagnostic);\n}\n\n/**\n * Ownership for the reuse decision. An identity we cannot read proves nothing: it can neither\n * claim the host nor authorize a kill, so it reads as \"not ours\" and the caller starts fresh\n * rather than failing the whole ensure on an observation gap.\n */\nasync function matchesPidFileOrUnknown(\n\tpidFile: DaemonPidFile,\n\tprobe: (pid: number) => Promise<string | undefined>,\n): Promise<boolean> {\n\ttry {\n\t\treturn await processMatchesPidFile(pidFile, probe);\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return false;\n\t\tthrow error;\n\t}\n}\n\nasync function stopSpawnedChild(\n\tchild: ChildProcess,\n\tchildExit: Promise<ChildExit>,\n\ttermTimeoutMs: number,\n): Promise<void> {\n\tconst exited = () => child.exitCode !== null || child.signalCode !== null;\n\tif (exited()) return;\n\tconst signal = (name: NodeJS.Signals) => {\n\t\ttry {\n\t\t\tchild.kill(name);\n\t\t} catch (error: unknown) {\n\t\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t\t}\n\t};\n\tconst waitFor = (ms: number) => Promise.race([childExit.then(() => true), delay(ms).then(() => exited())]);\n\tsignal(\"SIGTERM\");\n\tif (await waitFor(termTimeoutMs)) return;\n\tsignal(\"SIGKILL\");\n\tif (!(await waitFor(SIGKILL_GRACE_MS))) {\n\t\tthrow new Error(`RPC socket host pid ${child.pid ?? \"?\"} remained alive after SIGKILL`);\n\t}\n}\n\nasync function stopManagedHost(\n\tpidFile: DaemonPidFile,\n\ttermTimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tawait signalValidated(pidFile, \"SIGTERM\", readStartTime);\n\tif (await waitForGone(pidFile, termTimeoutMs, readStartTime)) return;\n\tawait signalValidated(pidFile, \"SIGKILL\", readStartTime);\n\tif (!(await waitForGone(pidFile, SIGKILL_GRACE_MS, readStartTime))) {\n\t\tthrow new Error(`RPC socket host pid ${pidFile.pid} remained alive after SIGKILL`);\n\t}\n}\n\ntype PidFileOwnership = \"owns\" | \"gone\" | \"unknown\";\n\n// One probe per call: the teardown loops below are themselves the retry, so the\n// budget inside processMatchesPidFile would only multiply their wall time. A probe\n// that fails against a LIVE pid is \"unknown\" — it proves nothing about ownership, so\n// signalling on it would be unsafe and treating it as \"gone\" would abandon a host that\n// may still be running. A failed probe against a dead pid is \"gone\".\nasync function resolvePidFileOwnership(\n\tpidFile: DaemonPidFile,\n\treadStartTime: (pid: number) => Promise<string | undefined>,\n): Promise<PidFileOwnership> {\n\ttry {\n\t\treturn (await processMatchesPidFile(pidFile, readStartTime, processIsLive, { attempts: 1 })) ? \"owns\" : \"gone\";\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return \"unknown\";\n\t\tthrow error;\n\t}\n}\n\nasync function signalValidated(\n\tpidFile: DaemonPidFile,\n\tsignal: NodeJS.Signals,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) !== \"owns\") return;\n\ttry {\n\t\tprocess.kill(pidFile.pid, signal);\n\t} catch (error: unknown) {\n\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t}\n}\n\nasync function waitForGone(\n\tpidFile: DaemonPidFile,\n\ttimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<boolean> {\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() <= deadline) {\n\t\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\") return true;\n\t\tawait delay(50);\n\t}\n\treturn (await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\";\n}\n\ntype ChildExit = { readonly code: number | null; readonly signal: NodeJS.Signals | null };\n\ntype ProtocolPollResult = { readonly protocol?: HostProtocolInfo; readonly exited?: ChildExit };\n\nasync function pollProtocolInfo(\n\tsocket: string,\n\ttimeoutMs: number,\n\tchildExit?: Promise<ChildExit>,\n): Promise<ProtocolPollResult> {\n\tconst deadline = Date.now() + timeoutMs;\n\tlet lastProtocol: HostProtocolInfo | undefined;\n\twhile (Date.now() <= deadline) {\n\t\tconst probe = probeProtocolInfo(\n\t\t\tsocket,\n\t\t\tMath.min(SPAWNED_HOST_PROBE_TIMEOUT_MS, Math.max(1, deadline - Date.now())),\n\t\t);\n\t\tconst raced = childExit ? await Promise.race([probe, childExit]) : await probe;\n\t\tif (isChildExit(raced)) {\n\t\t\t// A supervisor exit can be triggered by the Windows identity watchdog\n\t\t\t// while a named-pipe client is still composing its protocol reply. Do\n\t\t\t// not terminate the host based solely on that exit until this probe has\n\t\t\t// had a chance to deliver an answer. A host that never answers still\n\t\t\t// resolves through probeProtocolInfo's bounded timeout/close handling.\n\t\t\tconst info = await probe;\n\t\t\tif (info) {\n\t\t\t\tlastProtocol = info;\n\t\t\t\tif (isCompatible(info)) return { protocol: info };\n\t\t\t} else {\n\t\t\t\treturn { protocol: lastProtocol, exited: raced };\n\t\t\t}\n\t\t} else if (raced) {\n\t\t\tlastProtocol = raced;\n\t\t\tif (isCompatible(raced)) return { protocol: raced };\n\t\t}\n\t\tawait delay(50);\n\t}\n\treturn { protocol: lastProtocol };\n}\n\nfunction isChildExit(value: HostProtocolInfo | ChildExit | undefined): value is ChildExit {\n\treturn !!value && \"code\" in value && \"signal\" in value;\n}\n\n/**\n * Compatibility, for the attach decision and the readiness gate alike: a host is compatible exactly\n * when a client that is forbidden to upgrade would attach to it. Never a version-string comparison (I2).\n */\nfunction isCompatible(protocol: HostProtocolInfo | undefined): boolean {\n\treturn decideHostAction(ensureClient({ socket: \"\" }, false), protocol, \"never\").action === \"reuse\";\n}\n\n/** The environment a spawned host inherits: this process's, the caller's overrides, then the fixed wiring. */\nfunction hostEnv(\n\toptions: EnsureHostOptions,\n\tgeneration: { readonly paths: HostDaemonPaths; readonly instanceId: string },\n): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env };\n\tfor (const [key, value] of Object.entries(options.env ?? {})) {\n\t\tif (value === null) delete env[key];\n\t\telse env[key] = value;\n\t}\n\tenv[ENV_AGENT_DIR] = options.agentDir ?? getAgentDir();\n\tenv[RPC_CLIENT_CAPABILITIES_ENV] = PINNED_HOST_CLIENT_CAPABILITIES.join(\",\");\n\t// Always SET, never inherited: an ensure run from inside a daemon session would otherwise hand\n\t// its own host's identity to the one it spawns, and two hosts claiming one instance id make a\n\t// handoff - which completes exactly when the instance id changes - impossible to observe.\n\tenv[HOST_INSTANCE_ID_ENV] = generation.instanceId;\n\tenv[HOST_GENERATION_ENV] = \"0\";\n\tenv[HOST_DAEMON_DIR_ENV] = generation.paths.dir;\n\treturn env;\n}\n\nasync function reapOrphanedInternalHostDirs(): Promise<void> {\n\ttry {\n\t\tconst entries = await readdir(tmpdir(), { withFileTypes: true });\n\t\tawait Promise.all(\n\t\t\tentries\n\t\t\t\t.filter((entry) => entry.isDirectory() && entry.name.startsWith(\"senpi-rpc-host-internal-\"))\n\t\t\t\t.map(async (entry) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst owner = JSON.parse(await readFile(join(tmpdir(), entry.name, \".owner\"), \"utf8\")) as {\n\t\t\t\t\t\t\tpid?: unknown;\n\t\t\t\t\t\t\tprocessStartTime?: unknown;\n\t\t\t\t\t\t\tcreatedAt?: unknown;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof owner.pid === \"number\" &&\n\t\t\t\t\t\t\ttypeof owner.processStartTime === \"string\" &&\n\t\t\t\t\t\t\ttypeof owner.createdAt === \"number\" &&\n\t\t\t\t\t\t\towner.processStartTime.length > 0 &&\n\t\t\t\t\t\t\towner.createdAt < Date.now() - 60_000 &&\n\t\t\t\t\t\t\t(await readdir(join(tmpdir(), entry.name))).length === 1 &&\n\t\t\t\t\t\t\t!(await processMatchesPidFile({ pid: owner.pid, processStartTime: owner.processStartTime }).catch(\n\t\t\t\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\t\t\t\t// Unreadable but live: assume the owner is alive rather than steal its lock.\n\t\t\t\t\t\t\t\t\tif (error instanceof ProcessIdentityUnreadableError) return true;\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tawait rm(join(tmpdir(), entry.name), { recursive: true, force: true });\n\t\t\t\t\t} catch {}\n\t\t\t\t}),\n\t\t);\n\t} catch {}\n}\n\nasync function appendStderr(paths: HostDaemonPaths, message: string): Promise<string> {\n\ttry {\n\t\tconst stderr = (await readFile(paths.stderrLog, \"utf8\")).trim();\n\t\treturn stderr ? `${message}\\n${stderr}` : message;\n\t} catch (error: unknown) {\n\t\tif (isNodeErrorCode(error, \"ENOENT\")) return message;\n\t\tthrow error;\n\t}\n}\n\nfunction createSocketLockName(socket: string): string {\n\treturn createHash(\"sha256\")\n\t\t.update(resolveSocketTransportAddress(socket, process.platform), \"utf8\")\n\t\t.digest(\"hex\")\n\t\t.slice(0, 32);\n}\n\nfunction normalizeSocketPath(value: string): string {\n\tif (value.startsWith(\"unix://\")) return value.slice(\"unix://\".length);\n\treturn value;\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolveDelay) => setTimeout(resolveDelay, ms));\n}\n\nfunction isNodeErrorCode(error: unknown, code: string): boolean {\n\treturn error instanceof Error && \"code\" in error && error.code === code;\n}\n\nfunction assertNever(value: never): never {\n\tthrow new Error(`unreachable host decision: ${JSON.stringify(value)}`);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"host-ensure.js","sourceRoot":"","sources":["../../../src/modes/rpc/host-ensure.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { mkdir, open, readdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { ENV_AGENT_DIR, getAgentDir } from \"../../config.ts\";\nimport { engineBuildIdentity } from \"../../core/engine-build-identity.ts\";\nimport {\n\ttype DaemonPidFile,\n\tProcessIdentityUnreadableError,\n\tprocessIsLive,\n\tprocessMatchesPidFile,\n\treadProcessStartTime,\n\twaitForStartTime,\n} from \"../app-server/daemon/process.ts\";\nimport { RPC_CLIENT_CAPABILITIES_ENV } from \"./custom-capability.ts\";\nimport {\n\tcreateDaemonDirectories,\n\tcreateHostDaemonPaths,\n\tHOST_DAEMON_DIR_ENV,\n\ttype HostDaemonPaths,\n} from \"./host-daemon-paths.ts\";\nimport {\n\tclearHostRegistration,\n\tlegacyHostIsLive,\n\ttype RegisteredHost,\n\treadHostRegistration,\n\twriteHostRegistration,\n\twrittenByThisProcess,\n} from \"./host-daemon-registration.ts\";\nimport { writeHostSettings } from \"./host-daemon-state.ts\";\nimport {\n\tdecideHostAction,\n\tHOST_PROTOCOL_VERSION,\n\ttype HostDecision,\n\ttype HostDecisionClient,\n\tHostEnsureRefusedError,\n\ttype HostProtocolInfo,\n\tREQUIRED_HOST_CAPABILITIES,\n} from \"./host-decision.ts\";\nimport { handoffHost } from \"./host-handoff.ts\";\nimport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nimport { DEFAULT_HOST_IDLE_EXIT_MS, type HostColdStart, type HostLifecyclePolicyInput } from \"./host-lifecycle.ts\";\nimport { probeProtocolInfo, probeSocketReachable } from \"./host-probe.ts\";\nimport { acquireOwnershipSafeLock } from \"./ownership-safe-lock.ts\";\nimport { HOST_GENERATION_ENV, HOST_INSTANCE_ID_ENV, hostLaunchProfile } from \"./protocol-identity.ts\";\nimport { statSocketIdentity } from \"./socket-ownership.ts\";\nimport { createSocketSecret, resolveSocketTransportAddress, socketSecretPath } from \"./socket-transport.ts\";\n\nexport {\n\tcreateHostDaemonPaths,\n\tdaemonDirectoryName,\n\ttype HostDaemonPaths,\n\tHostDaemonStateError,\n\ttype HostGenerationPaths,\n} from \"./host-daemon-paths.ts\";\nexport { defaultHostLaunch, PINNED_HOST_CLIENT_CAPABILITIES } from \"./host-launch.ts\";\nexport { type ProbeHostOptions, probeHost } from \"./host-probe.ts\";\nexport type { HostColdStart, HostLifecyclePolicyInput };\n\n/**\n * What an ensure may do to a host that is already running.\n *\n * `never` (the default) attaches or starts, and touches nothing that is already serving the\n * socket. `if-engine-differs` additionally allows a GENERATION HANDOFF when `decideHostAction`\n * finds this build strictly newer and its extension set a superset of the running host's - the\n * running host then drains instead of dying, so no session is ever ended by an upgrade.\n */\nexport type HostUpgradePolicy = \"never\" | \"if-engine-differs\";\n\nexport interface EnsureHostOptions {\n\treadonly socket: string;\n\treadonly agentDir?: string;\n\t/** Host lifecycle policy recorded in settings.json (env overrides win at runtime). */\n\treadonly policy?: HostLifecyclePolicyInput;\n\t/** Extra CLI arguments forwarded through the supervisor to the host process. */\n\treadonly hostArgs?: readonly string[];\n\t/** Environment for the spawned host; a `null` value removes an inherited variable. */\n\treadonly env?: Readonly<Record<string, string | null>>;\n\t/** Whether a newer build may take the socket over from the running host. Defaults to `never`. */\n\treadonly upgrade?: HostUpgradePolicy;\n\treadonly _test?: {\n\t\treadonly readinessTimeoutMs?: number;\n\t\treadonly stopTimeoutMs?: number;\n\t\treadonly spawn?: { readonly command: string; readonly args: readonly string[] };\n\t\t/** Builds the spawnable command from supervisor argv; tests point it at the source entry. */\n\t\treadonly launch?: (args: readonly string[]) => { readonly command: string; readonly args: readonly string[] };\n\t\t/** Runs after endpoint ownership is locked; deterministic concurrency-test gate. */\n\t\treadonly afterLockAcquired?: () => Promise<void>;\n\t\t/**\n\t\t * Runs after the child is spawned but before its pidfile is registered, so a\n\t\t * test can force the startup failure a loaded runner produces without having\n\t\t * to stall the real process-identity probe.\n\t\t */\n\t\treadonly beforePidFileWrite?: () => Promise<void>;\n\t\t/** Overrides the process-identity probe so a test can force its failure. */\n\t\treadonly readProcessStartTime?: (pid: number) => Promise<string | undefined>;\n\t};\n}\n\nexport interface EnsuredHost {\n\treadonly pid: number;\n\treadonly socket: string;\n\treadonly reused: boolean;\n}\n\nconst SPAWNED_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst EXISTING_HOST_PROBE_TIMEOUT_MS = 10_000;\nconst DEFAULT_READINESS_TIMEOUT_MS = 10_000;\nconst DEFAULT_STOP_TIMEOUT_MS = 10_000;\nconst SIGKILL_GRACE_MS = 2_000;\n/**\n * A lock waiter must outlast the longest critical section a holder can run:\n * probing an existing host, stopping an incompatible one (SIGTERM wait plus the\n * SIGKILL grace), then spawning the replacement and waiting for it to answer.\n * Each SQLite busy wait stays short because it blocks the event loop; this\n * cumulative budget is what covers the whole section, with headroom for a slow\n * runner. A waiter that gives up early surfaces as a raw \"database is locked\"\n * failure on the second of two concurrent starts.\n */\nconst ENSURE_LOCK_WAIT_MS =\n\tEXISTING_HOST_PROBE_TIMEOUT_MS + DEFAULT_STOP_TIMEOUT_MS + SIGKILL_GRACE_MS + DEFAULT_READINESS_TIMEOUT_MS + 10_000;\nconst LOCK_BUSY_WAIT_MS = 100;\nconst lockOptions = {\n\tretries: { retries: ENSURE_LOCK_WAIT_MS / LOCK_BUSY_WAIT_MS, minTimeout: 20, maxTimeout: LOCK_BUSY_WAIT_MS },\n} as const;\nexport async function ensureHost(options: EnsureHostOptions): Promise<EnsuredHost> {\n\tconst socket = normalizeSocketPath(options.socket);\n\tconst paths = createHostDaemonPaths({ socket, ...(options.agentDir ? { agentDir: options.agentDir } : {}) });\n\tawait createDaemonDirectories(paths);\n\t// The public socket is the shared resource; agent directories are not a\n\t// sufficient lock scope when two installations target the same endpoint.\n\tconst lockTarget = join(tmpdir(), \"senpi-rpc-host-locks\", createSocketLockName(socket));\n\tawait mkdir(dirname(lockTarget), { recursive: true });\n\tawait writeFile(lockTarget, \"\", { flag: \"a\", mode: 0o600 });\n\t// Opportunistic GC of other installs' leftovers stays OUTSIDE the endpoint lock.\n\t// Its cost scales with the whole tmpdir and, on win32, adds a ~1s process probe per\n\t// candidate; inside the critical section that inflated the hold for every concurrent\n\t// ensureHost until a waiter exhausted its budget and surfaced a raw \"database is\n\t// locked\". Its own guards (60s age, dead owner pid) already make it safe unlocked.\n\tawait reapOrphanedInternalHostDirs();\n\tconst release = await acquireOwnershipSafeLock(`${lockTarget}.lock`, lockOptions);\n\ttry {\n\t\tawait options._test?.afterLockAcquired?.();\n\t\treturn await ensureHostLocked(paths, socket, options);\n\t} finally {\n\t\tawait release();\n\t}\n}\n\nasync function ensureHostLocked(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\tconst registered = await readHostRegistration(paths);\n\t// A record naming ANOTHER endpoint is not about this ensure's host. The per-socket directory\n\t// makes that structural, and the field stays as the second guard for a directory that was\n\t// somehow reused: a second socket must never read the first socket's daemon as its own.\n\tconst registeredHere = registersSocket(registered, socket);\n\tconst protocol = await probeProtocolInfo(socket, EXISTING_HOST_PROBE_TIMEOUT_MS);\n\tconst startedByUs = registeredHere && (await writtenByThisProcess(registered?.writer));\n\tconst attachedPid = registeredHere ? (registered?.record.pid ?? 0) : 0;\n\tconst decision = decide(options, startedByUs, protocol);\n\tswitch (decision.action) {\n\t\tcase \"reuse\":\n\t\t\t// A compatible socket is attachable even when another client surface\n\t\t\t// started it. Only hosts we spawned are eligible for lifecycle management.\n\t\t\treturn { pid: attachedPid, socket, reused: true };\n\t\tcase \"refuse\":\n\t\t\tthrow new HostEnsureRefusedError(socket, decision.reason, protocol);\n\t\tcase \"handoff\":\n\t\t\treturn upgradeGeneration(paths, socket, options, attachedPid);\n\t\tcase \"start\":\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn assertNever(decision);\n\t}\n\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\tconst pidMatches = registeredHere && registered ? await matchesPidFileOrUnknown(registered.record, probe) : false;\n\t// The generation this start leaves RUNNING beside the new one, when there is one.\n\tlet stranded: RegisteredHost | undefined;\n\tif (registered && pidMatches) {\n\t\tif (!startedByUs) {\n\t\t\t// I1: the socket is silent, but the process behind it is alive. Only the process that WROTE\n\t\t\t// this record may end it - anyone else refuses rather than signalling somebody else's host.\n\t\t\tif (await publicEndpointAccepts(socket)) throw new HostEnsureRefusedError(socket, \"foreign_writer\", protocol);\n\t\t\t// A foreign record whose public endpoint accepts NOTHING names a generation nobody can reach:\n\t\t\t// its entry was replaced (so it is already draining, #1893) or removed, or a dead listener\n\t\t\t// left the entry behind. Refusing here locked every client out until that process happened\n\t\t\t// to exit (#1936). Binding a fresh generation there signals nothing, so that is what happens -\n\t\t\t// the stranded one keeps its record.\n\t\t\tstranded = registered;\n\t\t} else {\n\t\t\t// Silent is not the same as gone. A host serving many sessions can miss a probe budget\n\t\t\t// while its event loop is busy; its socket still ACCEPTS the connection. Ending it then\n\t\t\t// would destroy every live session to replace a host that was never broken, so a\n\t\t\t// reachable socket is refused instead of signalled - the caller retries or falls back.\n\t\t\tif (await probeSocketReachable(socket, EXISTING_HOST_PROBE_TIMEOUT_MS)) {\n\t\t\t\tthrow new HostEnsureRefusedError(socket, \"host_busy\", protocol);\n\t\t\t}\n\t\t\tawait stopManagedHost(registered.record, testOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS, probe);\n\t\t}\n\t}\n\t// A host from before this layout registered itself in the FLAT directory. Its files are another\n\t// process's state: never read as ours, never signalled, never removed - and while it is alive,\n\t// this ensure refuses instead of binding a socket it may still be serving.\n\tif (await legacyHostIsLive(paths, probe)) throw new HostEnsureRefusedError(socket, \"legacy_host\", protocol);\n\tif (stranded !== undefined) return startHost(paths, socket, options, stranded.generation + 1);\n\tif (registeredHere) await clearHostRegistration(paths);\n\treturn startHost(paths, socket, options);\n}\n\n/**\n * Only the connect matters here, never an answer: the kernel completes it from the listen backlog\n * without the host's event loop, so a live owner under load still accepts within this budget.\n */\nconst FOREIGN_ENDPOINT_PROBE_TIMEOUT_MS = 2_000;\n\n/**\n * Whether SOMETHING still accepts connections at the public path - the one fact that says a\n * registered process may still own the endpoint. A missing entry and an entry nobody listens\n * behind (connection refused) both answer no; an accepted connection, however silent, answers yes.\n * A named pipe has no entry to lose and an abstract socket has no path, so both read as owned;\n * so does an entry this process cannot stat, because an owner that cannot be ruled out is one\n * this ensure must not bind over.\n */\nasync function publicEndpointAccepts(socket: string): Promise<boolean> {\n\tif (process.platform === \"win32\" || socket.startsWith(\"\\0\")) return true;\n\tconst entry = await statSocketIdentity(socket).then(\n\t\t(identity) => (identity === undefined ? \"absent\" : \"present\"),\n\t\t() => \"unknown\",\n\t);\n\tif (entry === \"absent\") return false;\n\tif (entry === \"unknown\") return true;\n\treturn probeSocketReachable(socket, FOREIGN_ENDPOINT_PROBE_TIMEOUT_MS);\n}\n\n/** `fallback` belongs to clients that can live without a host; an ensure must produce one or fail. */\nfunction decide(\n\toptions: EnsureHostOptions,\n\tstartedByUs: boolean,\n\tprotocol: HostProtocolInfo | undefined,\n): Exclude<HostDecision, { action: \"fallback\" }> {\n\tif (options.upgrade !== \"if-engine-differs\") {\n\t\treturn decideHostAction(ensureClient(options, startedByUs), protocol, \"never\");\n\t}\n\tconst decision = decideHostAction(ensureClient(options, startedByUs), protocol, \"upgrade\");\n\treturn decision.action === \"fallback\" ? { action: \"reuse\", reason: \"compatible\", upgradeable: false } : decision;\n}\n\n/**\n * The upgrade, when the decision allows one: a new generation takes the socket and the running\n * host drains. A refused handoff ATTACHES - an upgrade that cannot happen must never become a stop.\n */\nasync function upgradeGeneration(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n\tattachedPid: number,\n): Promise<EnsuredHost> {\n\tconst result = await handoffHost({\n\t\tsocket,\n\t\tagentDir: options.agentDir ?? getAgentDir(),\n\t\thostArgs: options.hostArgs ?? [],\n\t\t...(options.env ? { env: options.env } : {}),\n\t\t...(options.policy ? { policy: options.policy } : {}),\n\t\t_test: {\n\t\t\t...(options._test?.launch ? { launch: options._test.launch } : {}),\n\t\t\t...(options._test?.readinessTimeoutMs ? { readinessTimeoutMs: options._test.readinessTimeoutMs } : {}),\n\t\t},\n\t});\n\tif (result.action === \"handoff\") return { pid: result.pid, socket, reused: false };\n\tawait appendStderr(\n\t\tpaths,\n\t\t`generation handoff refused: ${result.reason}${result.detail ? ` (${result.detail})` : \"\"}`,\n\t);\n\treturn { pid: attachedPid, socket, reused: true };\n}\n\n/** This build as a client: which protocol it speaks, what it needs from a host, and which build it is. */\nfunction ensureClient(options: EnsureHostOptions, startedByUs: boolean): HostDecisionClient {\n\treturn {\n\t\tprotocolVersion: HOST_PROTOCOL_VERSION,\n\t\trequiredCapabilities: REQUIRED_HOST_CAPABILITIES,\n\t\tidentity: engineBuildIdentity(),\n\t\t// An ensure that may upgrade has to say what it would launch: the superset rule refuses to\n\t\t// hand off to a generation that would drop what the running host loads. An ensure that may\n\t\t// not upgrade declares nothing, and therefore can never be the newer candidate.\n\t\t...(options.upgrade === \"if-engine-differs\"\n\t\t\t? { launchProfile: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()) }\n\t\t\t: {}),\n\t\tstartedByUs,\n\t\tplatform: process.platform,\n\t};\n}\n\n/** The argv the supervisor gives its host child; the launch profile has to describe THAT host. */\nfunction hostChildArgv(hostArgs: readonly string[]): string[] {\n\treturn [\"--mode\", \"rpc\", \"--multi-session\", ...hostArgs];\n}\n\n/** Whether a registration is about this endpoint. A record written before the field existed is. */\nfunction registersSocket(registered: RegisteredHost | undefined, socket: string): boolean {\n\tif (registered === undefined) return false;\n\treturn registered.socket === undefined || registered.socket === socket;\n}\n\n/**\n * `generation` is 0 for a fresh endpoint; a start that leaves a stranded generation running beside\n * the new one numbers it after that generation, as a handoff would.\n */\nasync function startHost(\n\tpaths: HostDaemonPaths,\n\tsocket: string,\n\toptions: EnsureHostOptions,\n\tgeneration = 0,\n): Promise<EnsuredHost> {\n\tconst testOptions = options._test;\n\t// The generation this ensure is about to spawn, chosen HERE so its directory exists before the\n\t// host boots and the pointer can name it the moment the host is registered.\n\tconst instanceId = randomUUID();\n\t// The settings file must exist before the supervisor reads it at boot, so it\n\t// records the policy before the spawn instead of beside the pidfile.\n\tif (process.platform === \"win32\") await createSocketSecret(socketSecretPath(socket));\n\tawait writeHostSettings(paths, {\n\t\tsocket,\n\t\tcapabilities: PINNED_HOST_CLIENT_CAPABILITIES,\n\t\tcoldStart: options.policy?.coldStart ?? \"transient\",\n\t\tidleExitMs: options.policy?.idleExitMs ?? DEFAULT_HOST_IDLE_EXIT_MS,\n\t\tgeneration,\n\t\tinstanceId,\n\t});\n\t// A stranded generation is still writing its diagnostics here; only a fresh endpoint starts over.\n\tconst stderr = await open(paths.stderrLog, generation === 0 ? \"w\" : \"a\", 0o600);\n\tlet pidFile: DaemonPidFile | undefined;\n\tlet child: ReturnType<typeof spawn> | undefined;\n\tlet exitedEarly: ChildExit | undefined;\n\tlet childExit: Promise<ChildExit> | undefined;\n\ttry {\n\t\tconst supervisorArgs = [\"--socket\", socket, ...(options.hostArgs ?? [])];\n\t\tconst launch = testOptions?.spawn ?? testOptions?.launch?.(supervisorArgs) ?? defaultHostLaunch(supervisorArgs);\n\t\tchild = spawn(launch.command, [...launch.args], {\n\t\t\tdetached: true,\n\t\t\twindowsHide: true,\n\t\t\tenv: hostEnv(options, { paths, instanceId, generation }),\n\t\t\tstdio: [\"ignore\", \"ignore\", stderr.fd],\n\t\t});\n\t\tchildExit = new Promise((resolveExit) => {\n\t\t\tchild!.once(\"exit\", (code, signal) => {\n\t\t\t\texitedEarly = { code, signal };\n\t\t\t\tresolveExit(exitedEarly);\n\t\t\t});\n\t\t});\n\t\tif (child.pid === undefined) throw new Error(\"failed to spawn RPC socket host\");\n\t\tconst probe = testOptions?.readProcessStartTime ?? readProcessStartTime;\n\t\tconst observedStartTime = await Promise.race([\n\t\t\twaitForStartTime(child.pid, 10_000, probe),\n\t\t\tchildExit.then(() => {\n\t\t\t\tthrow new Error(\"RPC socket host exited before its start time could be read\");\n\t\t\t}),\n\t\t]);\n\t\t// UNKNOWN identity on a live child: the probe was starved, not the host. Give the CIM table\n\t\t// one unhurried read (the per-attempt win32 default is 1s, which a loaded runner exceeds on\n\t\t// every attempt) before deciding.\n\t\tconst unhurriedProbe = testOptions?.readProcessStartTime\n\t\t\t? testOptions.readProcessStartTime\n\t\t\t: (pid: number) => readProcessStartTime(pid, process.platform, 15_000);\n\t\tconst processStartTime = observedStartTime ?? (await unhurriedProbe(child.pid).catch(() => undefined));\n\t\t// Still unreadable: the host is ours, alive, and about to prove itself on the socket, so it is\n\t\t// registered WITHOUT an ownership guard instead of being torn down for a starved probe. A\n\t\t// guard-less record never claims ownership and never authorizes a signal - every later caller\n\t\t// reads it as unknown - so the worst case is a fresh host next time, not a killed healthy one.\n\t\tpidFile = { pid: child.pid, processStartTime: processStartTime ?? null };\n\t\tawait testOptions?.beforePidFileWrite?.();\n\t\tawait writeHostRegistration(paths, {\n\t\t\trecord: pidFile,\n\t\t\tsocket,\n\t\t\tinstanceId,\n\t\t\tgeneration,\n\t\t\tlaunchProfileId: hostLaunchProfile(hostChildArgv(options.hostArgs ?? []), process.cwd()).profile_id,\n\t\t});\n\t\tchild.unref();\n\t} catch (error: unknown) {\n\t\t// Whether the child died on its own decides which diagnostic is true, and the\n\t\t// cleanup kill below records an `exitedEarly` indistinguishable from a real\n\t\t// self-exit. Latch it before killing, or the catch reports the SIGTERM it is\n\t\t// about to send and discards the actual startup failure.\n\t\tconst exitedBeforeCleanup = exitedEarly;\n\t\t// Keep the ChildProcess handle owned until registration succeeds. If startup\n\t\t// fails before the pidfile is written, terminate this exact child through\n\t\t// its still-attached handle rather than leaving an unmanaged daemon behind.\n\t\tif (!exitedBeforeCleanup && child && child.exitCode === null && child.signalCode === null) {\n\t\t\ttry {\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t} catch {}\n\t\t\tif (childExit) await Promise.race([childExit, delay(2_000)]);\n\t\t\tif (child.exitCode === null && child.signalCode === null) {\n\t\t\t\ttry {\n\t\t\t\t\tchild.kill(\"SIGKILL\");\n\t\t\t\t} catch {}\n\t\t\t}\n\t\t}\n\t\tif (!exitedBeforeCleanup) {\n\t\t\tawait clearHostRegistration(paths);\n\t\t\tthrow error;\n\t\t}\n\t\tconst diagnostic = await appendStderr(\n\t\t\tpaths,\n\t\t\t`RPC socket host exited with code ${exitedBeforeCleanup.code ?? \"null\"}${exitedBeforeCleanup.signal ? ` (${exitedBeforeCleanup.signal})` : \"\"} before answering get_protocol_info`,\n\t\t);\n\t\tawait clearHostRegistration(paths);\n\t\tthrow new Error(diagnostic);\n\t} finally {\n\t\tawait stderr.close();\n\t}\n\tconst readinessTimeoutMs = testOptions?.readinessTimeoutMs ?? DEFAULT_READINESS_TIMEOUT_MS;\n\tconst result = await pollProtocolInfo(socket, readinessTimeoutMs, childExit);\n\tif (isCompatible(result.protocol)) return { pid: pidFile.pid, socket, reused: false };\n\t// Teardown runs for the diagnostic's sake, so it must never replace it: a stop\n\t// failure here (unreadable identity, a host that outlives SIGKILL) would other-\n\t// wise propagate instead of the readiness message and skip cleanupState below,\n\t// leaving the pidfile and socket behind. Record it and keep going.\n\t// This child is ours and its handle is still attached: stop it through the handle.\n\t// Validating ownership through the pidfile would re-run the identity probe whose\n\t// failure is the very thing a loaded runner produces here.\n\tconst stopFailure = await stopSpawnedChild(\n\t\tchild,\n\t\tchildExit,\n\t\ttestOptions?.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS,\n\t).then(\n\t\t() => undefined,\n\t\t(error: unknown) => (error instanceof Error ? error.message : String(error)),\n\t);\n\tconst message = result.protocol\n\t\t? `RPC socket host answered get_protocol_info with protocolVersion ${result.protocol.protocolVersion}, serverVersion ${result.protocol.serverVersion} and capabilities ${JSON.stringify(result.protocol.capabilities)}, but is incompatible with protocol version ${HOST_PROTOCOL_VERSION} and required capabilities ${JSON.stringify(REQUIRED_HOST_CAPABILITIES)}`\n\t\t: result.exited\n\t\t\t? `RPC socket host exited with code ${result.exited.code ?? \"null\"}${result.exited.signal ? ` (${result.exited.signal})` : \"\"} before answering get_protocol_info`\n\t\t\t: `spawned RPC socket host did not answer get_protocol_info within ${readinessTimeoutMs}ms`;\n\tconst diagnostic = await appendStderr(\n\t\tpaths,\n\t\tstopFailure === undefined ? message : `${message} (teardown also reported: ${stopFailure})`,\n\t);\n\tawait clearHostRegistration(paths);\n\t// The supervisor may have failed before binding, or another owner may have\n\t// appeared while readiness was being checked. Never unlink an endpoint we\n\t// cannot prove this start owned.\n\tthrow new Error(diagnostic);\n}\n\n/**\n * Ownership for the reuse decision. An identity we cannot read proves nothing: it can neither\n * claim the host nor authorize a kill, so it reads as \"not ours\" and the caller starts fresh\n * rather than failing the whole ensure on an observation gap.\n */\nasync function matchesPidFileOrUnknown(\n\tpidFile: DaemonPidFile,\n\tprobe: (pid: number) => Promise<string | undefined>,\n): Promise<boolean> {\n\ttry {\n\t\treturn await processMatchesPidFile(pidFile, probe);\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return false;\n\t\tthrow error;\n\t}\n}\n\nasync function stopSpawnedChild(\n\tchild: ChildProcess,\n\tchildExit: Promise<ChildExit>,\n\ttermTimeoutMs: number,\n): Promise<void> {\n\tconst exited = () => child.exitCode !== null || child.signalCode !== null;\n\tif (exited()) return;\n\tconst signal = (name: NodeJS.Signals) => {\n\t\ttry {\n\t\t\tchild.kill(name);\n\t\t} catch (error: unknown) {\n\t\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t\t}\n\t};\n\tconst waitFor = (ms: number) => Promise.race([childExit.then(() => true), delay(ms).then(() => exited())]);\n\tsignal(\"SIGTERM\");\n\tif (await waitFor(termTimeoutMs)) return;\n\tsignal(\"SIGKILL\");\n\tif (!(await waitFor(SIGKILL_GRACE_MS))) {\n\t\tthrow new Error(`RPC socket host pid ${child.pid ?? \"?\"} remained alive after SIGKILL`);\n\t}\n}\n\nasync function stopManagedHost(\n\tpidFile: DaemonPidFile,\n\ttermTimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tawait signalValidated(pidFile, \"SIGTERM\", readStartTime);\n\tif (await waitForGone(pidFile, termTimeoutMs, readStartTime)) return;\n\tawait signalValidated(pidFile, \"SIGKILL\", readStartTime);\n\tif (!(await waitForGone(pidFile, SIGKILL_GRACE_MS, readStartTime))) {\n\t\tthrow new Error(`RPC socket host pid ${pidFile.pid} remained alive after SIGKILL`);\n\t}\n}\n\ntype PidFileOwnership = \"owns\" | \"gone\" | \"unknown\";\n\n// One probe per call: the teardown loops below are themselves the retry, so the\n// budget inside processMatchesPidFile would only multiply their wall time. A probe\n// that fails against a LIVE pid is \"unknown\" — it proves nothing about ownership, so\n// signalling on it would be unsafe and treating it as \"gone\" would abandon a host that\n// may still be running. A failed probe against a dead pid is \"gone\".\nasync function resolvePidFileOwnership(\n\tpidFile: DaemonPidFile,\n\treadStartTime: (pid: number) => Promise<string | undefined>,\n): Promise<PidFileOwnership> {\n\ttry {\n\t\treturn (await processMatchesPidFile(pidFile, readStartTime, processIsLive, { attempts: 1 })) ? \"owns\" : \"gone\";\n\t} catch (error: unknown) {\n\t\tif (error instanceof ProcessIdentityUnreadableError) return \"unknown\";\n\t\tthrow error;\n\t}\n}\n\nasync function signalValidated(\n\tpidFile: DaemonPidFile,\n\tsignal: NodeJS.Signals,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<void> {\n\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) !== \"owns\") return;\n\ttry {\n\t\tprocess.kill(pidFile.pid, signal);\n\t} catch (error: unknown) {\n\t\tif (!isNodeErrorCode(error, \"ESRCH\")) throw error;\n\t}\n}\n\nasync function waitForGone(\n\tpidFile: DaemonPidFile,\n\ttimeoutMs: number,\n\treadStartTime: (pid: number) => Promise<string | undefined> = readProcessStartTime,\n): Promise<boolean> {\n\tconst deadline = Date.now() + timeoutMs;\n\twhile (Date.now() <= deadline) {\n\t\tif ((await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\") return true;\n\t\tawait delay(50);\n\t}\n\treturn (await resolvePidFileOwnership(pidFile, readStartTime)) === \"gone\";\n}\n\ntype ChildExit = { readonly code: number | null; readonly signal: NodeJS.Signals | null };\n\ntype ProtocolPollResult = { readonly protocol?: HostProtocolInfo; readonly exited?: ChildExit };\n\nasync function pollProtocolInfo(\n\tsocket: string,\n\ttimeoutMs: number,\n\tchildExit?: Promise<ChildExit>,\n): Promise<ProtocolPollResult> {\n\tconst deadline = Date.now() + timeoutMs;\n\tlet lastProtocol: HostProtocolInfo | undefined;\n\twhile (Date.now() <= deadline) {\n\t\tconst probe = probeProtocolInfo(\n\t\t\tsocket,\n\t\t\tMath.min(SPAWNED_HOST_PROBE_TIMEOUT_MS, Math.max(1, deadline - Date.now())),\n\t\t);\n\t\tconst raced = childExit ? await Promise.race([probe, childExit]) : await probe;\n\t\tif (isChildExit(raced)) {\n\t\t\t// A supervisor exit can be triggered by the Windows identity watchdog\n\t\t\t// while a named-pipe client is still composing its protocol reply. Do\n\t\t\t// not terminate the host based solely on that exit until this probe has\n\t\t\t// had a chance to deliver an answer. A host that never answers still\n\t\t\t// resolves through probeProtocolInfo's bounded timeout/close handling.\n\t\t\tconst info = await probe;\n\t\t\tif (info) {\n\t\t\t\tlastProtocol = info;\n\t\t\t\tif (isCompatible(info)) return { protocol: info };\n\t\t\t} else {\n\t\t\t\treturn { protocol: lastProtocol, exited: raced };\n\t\t\t}\n\t\t} else if (raced) {\n\t\t\tlastProtocol = raced;\n\t\t\tif (isCompatible(raced)) return { protocol: raced };\n\t\t}\n\t\tawait delay(50);\n\t}\n\treturn { protocol: lastProtocol };\n}\n\nfunction isChildExit(value: HostProtocolInfo | ChildExit | undefined): value is ChildExit {\n\treturn !!value && \"code\" in value && \"signal\" in value;\n}\n\n/**\n * Compatibility, for the attach decision and the readiness gate alike: a host is compatible exactly\n * when a client that is forbidden to upgrade would attach to it. Never a version-string comparison (I2).\n */\nfunction isCompatible(protocol: HostProtocolInfo | undefined): boolean {\n\treturn decideHostAction(ensureClient({ socket: \"\" }, false), protocol, \"never\").action === \"reuse\";\n}\n\n/** The environment a spawned host inherits: this process's, the caller's overrides, then the fixed wiring. */\nfunction hostEnv(\n\toptions: EnsureHostOptions,\n\tgeneration: { readonly paths: HostDaemonPaths; readonly instanceId: string; readonly generation: number },\n): NodeJS.ProcessEnv {\n\tconst env: NodeJS.ProcessEnv = { ...process.env };\n\tfor (const [key, value] of Object.entries(options.env ?? {})) {\n\t\tif (value === null) delete env[key];\n\t\telse env[key] = value;\n\t}\n\tenv[ENV_AGENT_DIR] = options.agentDir ?? getAgentDir();\n\tenv[RPC_CLIENT_CAPABILITIES_ENV] = PINNED_HOST_CLIENT_CAPABILITIES.join(\",\");\n\t// Always SET, never inherited: an ensure run from inside a daemon session would otherwise hand\n\t// its own host's identity to the one it spawns, and two hosts claiming one instance id make a\n\t// handoff - which completes exactly when the instance id changes - impossible to observe.\n\tenv[HOST_INSTANCE_ID_ENV] = generation.instanceId;\n\tenv[HOST_GENERATION_ENV] = String(generation.generation);\n\tenv[HOST_DAEMON_DIR_ENV] = generation.paths.dir;\n\treturn env;\n}\n\nasync function reapOrphanedInternalHostDirs(): Promise<void> {\n\ttry {\n\t\tconst entries = await readdir(tmpdir(), { withFileTypes: true });\n\t\tawait Promise.all(\n\t\t\tentries\n\t\t\t\t.filter((entry) => entry.isDirectory() && entry.name.startsWith(\"senpi-rpc-host-internal-\"))\n\t\t\t\t.map(async (entry) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst owner = JSON.parse(await readFile(join(tmpdir(), entry.name, \".owner\"), \"utf8\")) as {\n\t\t\t\t\t\t\tpid?: unknown;\n\t\t\t\t\t\t\tprocessStartTime?: unknown;\n\t\t\t\t\t\t\tcreatedAt?: unknown;\n\t\t\t\t\t\t};\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\ttypeof owner.pid === \"number\" &&\n\t\t\t\t\t\t\ttypeof owner.processStartTime === \"string\" &&\n\t\t\t\t\t\t\ttypeof owner.createdAt === \"number\" &&\n\t\t\t\t\t\t\towner.processStartTime.length > 0 &&\n\t\t\t\t\t\t\towner.createdAt < Date.now() - 60_000 &&\n\t\t\t\t\t\t\t(await readdir(join(tmpdir(), entry.name))).length === 1 &&\n\t\t\t\t\t\t\t!(await processMatchesPidFile({ pid: owner.pid, processStartTime: owner.processStartTime }).catch(\n\t\t\t\t\t\t\t\t(error: unknown) => {\n\t\t\t\t\t\t\t\t\t// Unreadable but live: assume the owner is alive rather than steal its lock.\n\t\t\t\t\t\t\t\t\tif (error instanceof ProcessIdentityUnreadableError) return true;\n\t\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\tawait rm(join(tmpdir(), entry.name), { recursive: true, force: true });\n\t\t\t\t\t} catch {}\n\t\t\t\t}),\n\t\t);\n\t} catch {}\n}\n\nasync function appendStderr(paths: HostDaemonPaths, message: string): Promise<string> {\n\ttry {\n\t\tconst stderr = (await readFile(paths.stderrLog, \"utf8\")).trim();\n\t\treturn stderr ? `${message}\\n${stderr}` : message;\n\t} catch (error: unknown) {\n\t\tif (isNodeErrorCode(error, \"ENOENT\")) return message;\n\t\tthrow error;\n\t}\n}\n\nfunction createSocketLockName(socket: string): string {\n\treturn createHash(\"sha256\")\n\t\t.update(resolveSocketTransportAddress(socket, process.platform), \"utf8\")\n\t\t.digest(\"hex\")\n\t\t.slice(0, 32);\n}\n\nfunction normalizeSocketPath(value: string): string {\n\tif (value.startsWith(\"unix://\")) return value.slice(\"unix://\".length);\n\treturn value;\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolveDelay) => setTimeout(resolveDelay, ms));\n}\n\nfunction isNodeErrorCode(error: unknown, code: string): boolean {\n\treturn error instanceof Error && \"code\" in error && error.code === code;\n}\n\nfunction assertNever(value: never): never {\n\tthrow new Error(`unreachable host decision: ${JSON.stringify(value)}`);\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
export { classifyChildExit } from "./host-child-exit.ts";
|
|
2
3
|
import { type SocketFileIdentity } from "./socket-ownership.ts";
|
|
3
4
|
export type HostColdStart = "transient" | "persistent";
|
|
4
5
|
/** Environment override for the cold-start policy: `transient` or `persistent`. */
|
|
@@ -7,6 +8,9 @@ export declare const HOST_COLD_START_ENV = "SENPI_RPC_HOST_COLD_START";
|
|
|
7
8
|
export declare const HOST_IDLE_EXIT_MS_ENV = "SENPI_RPC_HOST_IDLE_EXIT_MS";
|
|
8
9
|
/** Default idle-exit window: 15 minutes of continuous no-connection, no-turn idle. */
|
|
9
10
|
export declare const DEFAULT_HOST_IDLE_EXIT_MS: number;
|
|
11
|
+
/** Soft handoff deadline: rescan and report, never interrupt turns or in-flight requests. */
|
|
12
|
+
export declare const HANDOFF_GRACE_MS_ENV = "SENPI_RPC_HANDOFF_GRACE_MS";
|
|
13
|
+
export declare const DEFAULT_HANDOFF_GRACE_MS: number;
|
|
10
14
|
/** The policy fields ensureHost() records in rpc-host-daemon/settings.json. */
|
|
11
15
|
export interface HostLifecyclePolicyInput {
|
|
12
16
|
readonly coldStart?: HostColdStart;
|
|
@@ -42,16 +46,6 @@ export interface HostActivity {
|
|
|
42
46
|
readonly connections: number;
|
|
43
47
|
readonly activeTurns: number;
|
|
44
48
|
}
|
|
45
|
-
/**
|
|
46
|
-
* How the supervisor reads its child's exit. The RPC host exits 0 only through
|
|
47
|
-
* its own clean shutdown path - including its idle/empty-host policy - so that
|
|
48
|
-
* is an intentional stop, not a crash: the supervisor mirrors its own idle exit
|
|
49
|
-
* instead of reporting failure. Any non-zero code or signal stays a crash.
|
|
50
|
-
*/
|
|
51
|
-
export declare function classifyChildExit(code: number | null, signal: NodeJS.Signals | null): {
|
|
52
|
-
reason: string;
|
|
53
|
-
exitCode: number;
|
|
54
|
-
};
|
|
55
49
|
export type IdleExitDecision = "active" | "idle" | "exit";
|
|
56
50
|
/**
|
|
57
51
|
* Pure idle-window decision core. `update()` must be called with the CURRENT
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-lifecycle.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"host-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/host-lifecycle.ts"],"names":[],"mappings":"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"}
|