@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-ownership.js","sourceRoot":"","sources":["../../../src/modes/rpc/socket-ownership.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Ownership-checked removal for Unix domain socket path entries.\n *\n * A socket path is a rendezvous name, not a capability: once a host unlinks\n * its entry, any other process may bind the same path. Teardown that removes\n * \"its\" socket by path alone can therefore delete a NEWER host's freshly\n * published entry after a takeover renamed a replacement over that path. The\n * startup path already refuses to touch a socket owned by a live server\n * (`prepareSocketPath`); these helpers give every teardown path the same rule:\n * capture the bound entry's filesystem identity (dev + ino) at listen time and\n * unlink only while the path still stat-matches that identity.\n */\nimport { mkdir, readFile, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\n/** Filesystem identity of one bound socket path entry. */\nexport interface SocketFileIdentity {\n\treadonly dev: number;\n\treadonly ino: number;\n}\n\n/**\n * Name of the sidecar, inside the lifecycle supervisor's private scratch\n * directory, that records which public socket entry that supervisor bound. The\n * directory is per-supervisor, so a replacement supervisor's token can never be\n * mistaken for the old one's.\n */\nexport const PUBLIC_SOCKET_IDENTITY_FILE = \"public-socket.owner\";\n\n/** Default bound on waiting for a supervisor to publish its ownership token. */\nexport const SOCKET_IDENTITY_WAIT_MS = 30_000;\n\n/**\n * `sun_path` is 104 bytes INCLUDING its terminator on macOS, so a bindable path fits in 103.\n * A generation's bind path is the longest name this daemon ever produces, which makes it the\n * one that must be checked before a bind turns the overflow into a truncated, silently wrong\n * endpoint.\n */\nexport const MAX_SOCKET_PATH_BYTES = 103;\n\n/**\n * Where a successor generation binds before it owns the public name. It never binds the live\n * public path: that path belongs to the running host until the successor is proven to work.\n */\nexport function generationBindPath(publicSocket: string, generation: number): string {\n\treturn `${publicSocket}.next-${generation}`;\n}\n\nfunction sameSocketIdentity(a: SocketFileIdentity, b: SocketFileIdentity): boolean {\n\treturn a.dev === b.dev && a.ino === b.ino;\n}\n\n/** Reads the current identity of a socket path entry; `undefined` once absent. */\nexport async function statSocketIdentity(socketPath: string): Promise<SocketFileIdentity | undefined> {\n\ttry {\n\t\tconst { dev, ino } = await stat(socketPath);\n\t\treturn { dev, ino };\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return undefined;\n\t\tthrow cause;\n\t}\n}\n\n/**\n * Whether `socketPath` is served by a DIFFERENT entry than `identity` describes: a successor\n * generation renamed its own socket over it, or something else took the name. A generation that\n * finds this true no longer owns the endpoint and cannot be reached by path any more.\n *\n * An absent entry and an unknown identity both answer `false`. Supersession has to be POSITIVELY\n * observed - a name that is merely missing is somebody's `rm`, not a newer host, and treating it as\n * one would make a healthy daemon drain itself.\n */\nexport async function socketEntryReplaced(\n\tsocketPath: string,\n\tidentity: SocketFileIdentity | undefined,\n): Promise<boolean> {\n\tif (identity === undefined || process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return false;\n\tconst current = await statSocketIdentity(socketPath).catch(() => undefined);\n\treturn current !== undefined && !sameSocketIdentity(current, identity);\n}\n\n/** Records a bound socket identity for teardown paths that cannot hold it in memory. */\nexport async function writeSocketIdentityFile(path: string, identity: SocketFileIdentity): Promise<void> {\n\tawait mkdir(dirname(path), { recursive: true, mode: 0o700 });\n\tconst tmpPath = `${path}.${process.pid}.tmp`;\n\tawait writeFile(tmpPath, `${JSON.stringify(identity)}\\n`, { mode: 0o600 });\n\tawait rename(tmpPath, path);\n}\n\nexport async function readSocketIdentityFile(path: string): Promise<SocketFileIdentity | undefined> {\n\tlet raw: string;\n\ttry {\n\t\traw = await readFile(path, \"utf8\");\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return undefined;\n\t\tthrow cause;\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\t// Truncated or in-progress JSON is not a token: callers wait or leave the path.\n\t\treturn undefined;\n\t}\n\tif (\n\t\ttypeof parsed !== \"object\" ||\n\t\tparsed === null ||\n\t\ttypeof (parsed as { dev?: unknown }).dev !== \"number\" ||\n\t\ttypeof (parsed as { ino?: unknown }).ino !== \"number\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn { dev: (parsed as { dev: number }).dev, ino: (parsed as { ino: number }).ino };\n}\n\n/**\n * Waits, bounded, for an identity file to appear. The lifecycle supervisor\n * publishes its public-socket token after this host's internal listener is\n * ready, so supervised hosts poll briefly instead of assuming ordering.\n */\nexport async function waitForSocketIdentityFile(\n\tpath: string,\n\ttimeoutMs: number = SOCKET_IDENTITY_WAIT_MS,\n\tintervalMs = 25,\n): Promise<SocketFileIdentity | undefined> {\n\tconst deadline = Date.now() + timeoutMs;\n\tfor (;;) {\n\t\tconst identity = await readSocketIdentityFile(path);\n\t\tif (identity !== undefined) return identity;\n\t\tif (Date.now() >= deadline) return undefined;\n\t\tawait delayUnrefed(intervalMs);\n\t}\n}\n\n/**\n * Unlinks a socket path only while it still refers to the entry `identity`\n * describes. An absent path is nothing to do; a mismatched identity means a\n * newer host owns the name now and is left alone; an unknown identity can\n * never be proven ours, so the path is left and the decision is logged.\n */\nexport async function unlinkOwnedSocket(\n\tsocketPath: string,\n\tidentity: SocketFileIdentity | undefined,\n\tlog: (message: string) => void,\n): Promise<void> {\n\tif (process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return;\n\tlet current: SocketFileIdentity | undefined;\n\ttry {\n\t\tcurrent = await statSocketIdentity(socketPath);\n\t} catch (cause) {\n\t\tlog(`socket ${socketPath} ownership could not be verified (${errorMessage(cause)}); leaving it`);\n\t\treturn;\n\t}\n\tif (current === undefined) return;\n\tif (identity === undefined) {\n\t\tlog(`socket path ${socketPath} ownership unknown; leaving it`);\n\t\treturn;\n\t}\n\tif (!sameSocketIdentity(current, identity)) {\n\t\tlog(`socket path ${socketPath} now owned by another host; leaving it`);\n\t\treturn;\n\t}\n\ttry {\n\t\tawait unlink(socketPath);\n\t} catch (cause) {\n\t\tif (!isNodeErrorCode(cause, \"ENOENT\")) {\n\t\t\tlog(`socket ${socketPath} removal failed (${errorMessage(cause)})`);\n\t\t}\n\t}\n}\n\n/**\n * Runs `close` with the socket path's current entry shielded from libuv's\n * close-time unlink.\n *\n * libuv unlinks the NAME a pipe was bound to when its listening handle\n * closes - even when a takeover has renamed a DIFFERENT socket over that\n * name, so an ordinary `server.close()` silently deletes the newer host's\n * freshly published entry. Moving the current entry aside for the duration\n * of the close (libuv's unlink then hits ENOENT) and restoring it afterwards\n * keeps the filesystem exactly as it was; ownership-checked removal via\n * `unlinkOwnedSocket` decides afterwards what may actually be deleted.\n */\nexport async function shieldSocketDuringClose(socketPath: string, close: () => Promise<void>): Promise<void> {\n\tif (process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return close();\n\tconst shieldPath = `${socketPath}.shield-${process.pid}`;\n\tlet shielded = false;\n\ttry {\n\t\tif ((await statSocketIdentity(socketPath)) !== undefined) {\n\t\t\tawait rename(socketPath, shieldPath);\n\t\t\tshielded = true;\n\t\t}\n\t} catch {\n\t\t// Without a shield the close degrades to today's behavior; the\n\t\t// ownership-checked unlink still runs afterwards.\n\t}\n\ttry {\n\t\tawait close();\n\t} finally {\n\t\tif (shielded) {\n\t\t\ttry {\n\t\t\t\tawait rename(shieldPath, socketPath);\n\t\t\t} catch {\n\t\t\t\t// Best effort: a failed restore leaves the entry under the shield\n\t\t\t\t// name; startup probes treat a dead entry as replaceable.\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction delayUnrefed(ms: number): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tconst timer = setTimeout(resolve, ms);\n\t\ttimer.unref?.();\n\t});\n}\n\nfunction errorMessage(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\nfunction isNodeErrorCode(cause: unknown, code: string): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === code;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"socket-ownership.js","sourceRoot":"","sources":["../../../src/modes/rpc/socket-ownership.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Ownership-checked removal for Unix domain socket path entries.\n *\n * A socket path is a rendezvous name, not a capability: once a host unlinks\n * its entry, any other process may bind the same path. Teardown that removes\n * \"its\" socket by path alone can therefore delete a NEWER host's freshly\n * published entry after a takeover renamed a replacement over that path. The\n * startup path already refuses to touch a socket owned by a live server\n * (`prepareSocketPath`); these helpers give every teardown path the same rule:\n * capture the bound entry's filesystem identity (dev + ino) at listen time and\n * unlink only while the path still stat-matches that identity.\n */\nimport { mkdir, readFile, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\n/** Filesystem identity of one bound socket path entry. */\nexport interface SocketFileIdentity {\n\treadonly dev: number;\n\treadonly ino: number;\n}\n\n/**\n * Name of the sidecar, inside the lifecycle supervisor's private scratch\n * directory, that records which public socket entry that supervisor bound. The\n * directory is per-supervisor, so a replacement supervisor's token can never be\n * mistaken for the old one's.\n */\nexport const PUBLIC_SOCKET_IDENTITY_FILE = \"public-socket.owner\";\n\n/** What the public path holds relative to the entry a generation bound there. */\nexport type EndpointOwnership = \"held\" | \"replaced\" | \"absent\" | \"unknown\";\n\n/** Default bound on waiting for a supervisor to publish its ownership token. */\nexport const SOCKET_IDENTITY_WAIT_MS = 30_000;\n\n/**\n * `sun_path` is 104 bytes INCLUDING its terminator on macOS, so a bindable path fits in 103.\n * A generation's bind path is the longest name this daemon ever produces, which makes it the\n * one that must be checked before a bind turns the overflow into a truncated, silently wrong\n * endpoint.\n */\nexport const MAX_SOCKET_PATH_BYTES = 103;\n\n/**\n * Where a successor generation binds before it owns the public name. It never binds the live\n * public path: that path belongs to the running host until the successor is proven to work.\n */\nexport function generationBindPath(publicSocket: string, generation: number): string {\n\treturn `${publicSocket}.next-${generation}`;\n}\n\nfunction sameSocketIdentity(a: SocketFileIdentity, b: SocketFileIdentity): boolean {\n\treturn a.dev === b.dev && a.ino === b.ino;\n}\n\n/** Reads the current identity of a socket path entry; `undefined` once absent. */\nexport async function statSocketIdentity(socketPath: string): Promise<SocketFileIdentity | undefined> {\n\ttry {\n\t\tconst { dev, ino } = await stat(socketPath);\n\t\treturn { dev, ino };\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return undefined;\n\t\tthrow cause;\n\t}\n}\n\n/**\n * What the public path now holds relative to the entry `identity` describes, in ONE stat so the\n * answers cannot disagree with each other:\n *\n * - `held`: still this generation's own entry.\n * - `replaced`: a successor renamed its socket over it, or something else took the name.\n * - `absent`: no entry at all - somebody's `rm`, a swept temp directory, a deleted workspace.\n * - `unknown`: nothing can be proven (no identity, win32, an abstract socket, a failed stat).\n *\n * `replaced` and `absent` are both losses of reachability, but they are NOT the same observation\n * and callers must keep them apart: a successor is serving clients on that name, while an absent\n * name serves nobody. `unknown` is never a claim.\n */\nexport async function classifyEndpointOwnership(\n\tsocketPath: string,\n\tidentity: SocketFileIdentity | undefined,\n): Promise<EndpointOwnership> {\n\tif (identity === undefined || process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return \"unknown\";\n\tlet current: SocketFileIdentity | undefined;\n\ttry {\n\t\tcurrent = await statSocketIdentity(socketPath);\n\t} catch {\n\t\treturn \"unknown\";\n\t}\n\tif (current === undefined) return \"absent\";\n\treturn sameSocketIdentity(current, identity) ? \"held\" : \"replaced\";\n}\n\n/**\n * Whether `socketPath` is served by a DIFFERENT entry than `identity` describes: a successor\n * generation renamed its own socket over it, or something else took the name. A generation that\n * finds this true no longer owns the endpoint and cannot be reached by path any more.\n *\n * An absent entry and an unknown identity both answer `false`. Supersession has to be POSITIVELY\n * observed - a name that is merely missing is somebody's `rm`, not a newer host, and treating it as\n * one would make a healthy daemon drain itself.\n */\nexport async function socketEntryReplaced(\n\tsocketPath: string,\n\tidentity: SocketFileIdentity | undefined,\n): Promise<boolean> {\n\treturn (await classifyEndpointOwnership(socketPath, identity)) === \"replaced\";\n}\n\n/** Records a bound socket identity for teardown paths that cannot hold it in memory. */\nexport async function writeSocketIdentityFile(path: string, identity: SocketFileIdentity): Promise<void> {\n\tawait mkdir(dirname(path), { recursive: true, mode: 0o700 });\n\tconst tmpPath = `${path}.${process.pid}.tmp`;\n\tawait writeFile(tmpPath, `${JSON.stringify(identity)}\\n`, { mode: 0o600 });\n\tawait rename(tmpPath, path);\n}\n\nexport async function readSocketIdentityFile(path: string): Promise<SocketFileIdentity | undefined> {\n\tlet raw: string;\n\ttry {\n\t\traw = await readFile(path, \"utf8\");\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return undefined;\n\t\tthrow cause;\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\t// Truncated or in-progress JSON is not a token: callers wait or leave the path.\n\t\treturn undefined;\n\t}\n\tif (\n\t\ttypeof parsed !== \"object\" ||\n\t\tparsed === null ||\n\t\ttypeof (parsed as { dev?: unknown }).dev !== \"number\" ||\n\t\ttypeof (parsed as { ino?: unknown }).ino !== \"number\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn { dev: (parsed as { dev: number }).dev, ino: (parsed as { ino: number }).ino };\n}\n\n/**\n * Waits, bounded, for an identity file to appear. The lifecycle supervisor\n * publishes its public-socket token after this host's internal listener is\n * ready, so supervised hosts poll briefly instead of assuming ordering.\n */\nexport async function waitForSocketIdentityFile(\n\tpath: string,\n\ttimeoutMs: number = SOCKET_IDENTITY_WAIT_MS,\n\tintervalMs = 25,\n): Promise<SocketFileIdentity | undefined> {\n\tconst deadline = Date.now() + timeoutMs;\n\tfor (;;) {\n\t\tconst identity = await readSocketIdentityFile(path);\n\t\tif (identity !== undefined) return identity;\n\t\tif (Date.now() >= deadline) return undefined;\n\t\tawait delayUnrefed(intervalMs);\n\t}\n}\n\n/**\n * Unlinks a socket path only while it still refers to the entry `identity`\n * describes. An absent path is nothing to do; a mismatched identity means a\n * newer host owns the name now and is left alone; an unknown identity can\n * never be proven ours, so the path is left and the decision is logged.\n */\nexport async function unlinkOwnedSocket(\n\tsocketPath: string,\n\tidentity: SocketFileIdentity | undefined,\n\tlog: (message: string) => void,\n): Promise<void> {\n\tif (process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return;\n\tlet current: SocketFileIdentity | undefined;\n\ttry {\n\t\tcurrent = await statSocketIdentity(socketPath);\n\t} catch (cause) {\n\t\tlog(`socket ${socketPath} ownership could not be verified (${errorMessage(cause)}); leaving it`);\n\t\treturn;\n\t}\n\tif (current === undefined) return;\n\tif (identity === undefined) {\n\t\tlog(`socket path ${socketPath} ownership unknown; leaving it`);\n\t\treturn;\n\t}\n\tif (!sameSocketIdentity(current, identity)) {\n\t\tlog(`socket path ${socketPath} now owned by another host; leaving it`);\n\t\treturn;\n\t}\n\ttry {\n\t\tawait unlink(socketPath);\n\t} catch (cause) {\n\t\tif (!isNodeErrorCode(cause, \"ENOENT\")) {\n\t\t\tlog(`socket ${socketPath} removal failed (${errorMessage(cause)})`);\n\t\t}\n\t}\n}\n\n/**\n * Runs `close` with the socket path's current entry shielded from libuv's\n * close-time unlink.\n *\n * libuv unlinks the NAME a pipe was bound to when its listening handle\n * closes - even when a takeover has renamed a DIFFERENT socket over that\n * name, so an ordinary `server.close()` silently deletes the newer host's\n * freshly published entry. Moving the current entry aside for the duration\n * of the close (libuv's unlink then hits ENOENT) and restoring it afterwards\n * keeps the filesystem exactly as it was; ownership-checked removal via\n * `unlinkOwnedSocket` decides afterwards what may actually be deleted.\n */\nexport async function shieldSocketDuringClose(socketPath: string, close: () => Promise<void>): Promise<void> {\n\tif (process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return close();\n\tconst shieldPath = `${socketPath}.shield-${process.pid}`;\n\tlet shielded = false;\n\ttry {\n\t\tif ((await statSocketIdentity(socketPath)) !== undefined) {\n\t\t\tawait rename(socketPath, shieldPath);\n\t\t\tshielded = true;\n\t\t}\n\t} catch {\n\t\t// Without a shield the close degrades to today's behavior; the\n\t\t// ownership-checked unlink still runs afterwards.\n\t}\n\ttry {\n\t\tawait close();\n\t} finally {\n\t\tif (shielded) {\n\t\t\ttry {\n\t\t\t\tawait rename(shieldPath, socketPath);\n\t\t\t} catch {\n\t\t\t\t// Best effort: a failed restore leaves the entry under the shield\n\t\t\t\t// name; startup probes treat a dead entry as replaceable.\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction delayUnrefed(ms: number): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tconst timer = setTimeout(resolve, ms);\n\t\ttimer.unref?.();\n\t});\n}\n\nfunction errorMessage(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\nfunction isNodeErrorCode(cause: unknown, code: string): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === code;\n}\n"]}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { CliRuntimeConfiguration } from "../../main.ts";
|
|
2
2
|
import { type OpenRpcSession, type RpcSessionEntry, type RpcSessionLaunchProfile, type RpcSessionOpenOptions, type RpcSessionRow } from "./session-registry.ts";
|
|
3
|
+
import { SessionWorkerClient } from "./session-worker-client.ts";
|
|
4
|
+
type SessionWorkerCallbacks = ConstructorParameters<typeof SessionWorkerClient>[0];
|
|
5
|
+
export interface WorkerSessionRegistryOptions {
|
|
6
|
+
readonly configuration: CliRuntimeConfiguration;
|
|
7
|
+
readonly closeGraceMs: number;
|
|
8
|
+
readonly now: () => number;
|
|
9
|
+
/** Production builds the real worker; a caller may supply one to drive a lifecycle path deterministically. */
|
|
10
|
+
readonly createWorker?: (callbacks: SessionWorkerCallbacks) => SessionWorkerClient;
|
|
11
|
+
}
|
|
3
12
|
/** Transport-side lifecycle owner. Caller paths are never inspected on this event loop. */
|
|
4
13
|
export declare class WorkerSessionRegistry {
|
|
5
14
|
private readonly entries;
|
|
@@ -8,11 +17,8 @@ export declare class WorkerSessionRegistry {
|
|
|
8
17
|
readonly closeGraceMs: number;
|
|
9
18
|
private readonly now;
|
|
10
19
|
private readonly options;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
closeGraceMs: number;
|
|
14
|
-
now: () => number;
|
|
15
|
-
});
|
|
20
|
+
private readonly createWorker;
|
|
21
|
+
constructor(options: WorkerSessionRegistryOptions);
|
|
16
22
|
get size(): number;
|
|
17
23
|
/**
|
|
18
24
|
* Memory admission is the in-process registry's concern: a worker-runtime session runs in
|
|
@@ -46,4 +52,5 @@ export declare class WorkerSessionRegistry {
|
|
|
46
52
|
private liveWorkerPaths;
|
|
47
53
|
private openResult;
|
|
48
54
|
}
|
|
55
|
+
export {};
|
|
49
56
|
//# sourceMappingURL=worker-session-registry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-session-registry.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/worker-session-registry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAM7D,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAE1B,KAAK,aAAa,EAElB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"worker-session-registry.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/worker-session-registry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAM7D,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAE1B,KAAK,aAAa,EAElB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGjE,KAAK,sBAAsB,GAAG,qBAAqB,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AAEnF,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,aAAa,EAAE,uBAAuB,CAAC;IAChD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,8GAA8G;IAC9G,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,sBAAsB,KAAK,mBAAmB,CAAC;CACnF;AAED,2FAA2F;AAC3F,qBAAa,qBAAqB;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsC;IAC9D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiC;IAC9D,OAAO,CAAC,MAAM,CAAK;IACnB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IAEnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6D;IAE1F,YAAY,OAAO,EAAE,4BAA4B,EAKhD;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;OAGG;IACH,kBAAkB,IAAI,IAAI,CAAG;IAEvB,WAAW,CAAC,OAAO,EAAE,uBAAuB,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC,CAwE5G;IAED,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAEhD;IAED,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,CAY9D;IAED,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,eAAe,CAsBjH;IAED,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGnC;IAEK,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB/C;IAED,IAAI,IAAI,aAAa,EAAE,CAgBtB;IAED,mGAAmG;IACnG,OAAO,CAAC,mBAAmB;IAiB3B,OAAO,CAAC,MAAM;IAad,wFAAwF;IACxF,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEvC;IAED,OAAO,CAAC,OAAO;IAQf;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IAUjB,mGAAmG;IACnG,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,UAAU;CAKlB"}
|
|
@@ -13,6 +13,7 @@ export class WorkerSessionRegistry {
|
|
|
13
13
|
this.options = options;
|
|
14
14
|
this.closeGraceMs = options.closeGraceMs;
|
|
15
15
|
this.now = options.now;
|
|
16
|
+
this.createWorker = options.createWorker ?? ((callbacks) => new SessionWorkerClient(callbacks));
|
|
16
17
|
}
|
|
17
18
|
get size() {
|
|
18
19
|
return this.entries.size;
|
|
@@ -46,7 +47,8 @@ export class WorkerSessionRegistry {
|
|
|
46
47
|
lastCommandAt: this.now(),
|
|
47
48
|
lifecycleMutex: Promise.resolve(),
|
|
48
49
|
};
|
|
49
|
-
|
|
50
|
+
let workerFailure;
|
|
51
|
+
const worker = this.createWorker({
|
|
50
52
|
reserve: (path) => this.reserve(handle, path),
|
|
51
53
|
reconcile: (livePaths) => this.reconcile(handle, livePaths),
|
|
52
54
|
exit: () => {
|
|
@@ -58,6 +60,10 @@ export class WorkerSessionRegistry {
|
|
|
58
60
|
entry.closeResolve?.();
|
|
59
61
|
},
|
|
60
62
|
failure: (error) => {
|
|
63
|
+
// The open below only learns that its entry left `opening`, never why. Without this the
|
|
64
|
+
// caller is told `session_closing` - a path whose owner is tearing down - for a worker
|
|
65
|
+
// that died, and the actual reason reaches stderr alone (#1953).
|
|
66
|
+
workerFailure = error;
|
|
61
67
|
entry.state = "quarantined";
|
|
62
68
|
process.stderr.write(`senpi rpc session ${handle} quarantined: ${error}\n`);
|
|
63
69
|
},
|
|
@@ -81,7 +87,9 @@ export class WorkerSessionRegistry {
|
|
|
81
87
|
entry.sessionPath = path;
|
|
82
88
|
const snapshot = await worker.commit();
|
|
83
89
|
if (entry.state !== "opening")
|
|
84
|
-
throw
|
|
90
|
+
throw workerFailure === undefined
|
|
91
|
+
? new RpcSessionRegistryError("session_closing")
|
|
92
|
+
: new RpcSessionRegistryError("open_failed", workerFailure);
|
|
85
93
|
entry.durableSessionId = snapshot.state.sessionId;
|
|
86
94
|
entry.cwd = snapshot.state.cwd;
|
|
87
95
|
entry.state = "open";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker-session-registry.js","sourceRoot":"","sources":["../../../src/modes/rpc/worker-session-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute } from \"node:path\";\nimport { ProviderScope } from \"@earendil-works/pi-ai/node/provider-scope\";\nimport type { CliRuntimeConfiguration } from \"../../main.ts\";\nimport {\n\ttype LiveWorkerPaths,\n\tRESERVATION_DENIAL_CODES,\n\tSessionPathReservations,\n} from \"./session-path-reservations.ts\";\nimport {\n\tfrozenProfile,\n\ttype OpenRpcSession,\n\ttype RpcSessionEntry,\n\ttype RpcSessionLaunchProfile,\n\ttype RpcSessionOpenOptions,\n\tRpcSessionRegistryError,\n\ttype RpcSessionRow,\n\tsessionIdentity,\n} from \"./session-registry.ts\";\nimport { SessionWorkerClient } from \"./session-worker-client.ts\";\nimport { SESSION_WORKER_LIMITS, type SessionWriteGrant } from \"./session-worker-protocol.ts\";\n\n/** Transport-side lifecycle owner. Caller paths are never inspected on this event loop. */\nexport class WorkerSessionRegistry {\n\tprivate readonly entries = new Map<string, RpcSessionEntry>();\n\tprivate readonly reservations = new SessionPathReservations();\n\tprivate serial = 0;\n\treadonly closeGraceMs: number;\n\tprivate readonly now: () => number;\n\n\tprivate readonly options: { configuration: CliRuntimeConfiguration; closeGraceMs: number; now: () => number };\n\n\tconstructor(options: { configuration: CliRuntimeConfiguration; closeGraceMs: number; now: () => number }) {\n\t\tthis.options = options;\n\t\tthis.closeGraceMs = options.closeGraceMs;\n\t\tthis.now = options.now;\n\t}\n\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\t/**\n\t * Memory admission is the in-process registry's concern: a worker-runtime session runs in\n\t * its own isolate and this registry already bounds occupancy with `too_many_sessions`.\n\t */\n\tsetWorkerAdmission(): void {}\n\n\tasync openSession(profile: RpcSessionLaunchProfile, options?: RpcSessionOpenOptions): Promise<OpenRpcSession> {\n\t\tif (!isAbsolute(profile.cwd) || (profile.sessionPath !== undefined && !isAbsolute(profile.sessionPath)))\n\t\t\tthrow new RpcSessionRegistryError(\"invalid_path\");\n\t\tif (profile.sessionPath) {\n\t\t\tconst key = this.knownReservationKey(profile.sessionPath);\n\t\t\tconst owner = key ? this.reservations.owner(key) : undefined;\n\t\t\tif (key && owner) return this.attach(owner, key, options);\n\t\t}\n\t\tif (this.size >= SESSION_WORKER_LIMITS.workers) throw new Error(\"too_many_sessions\");\n\t\tconst handle = `rpc-${++this.serial}`;\n\t\tconst storedProfile = frozenProfile(profile);\n\t\tconst entry: RpcSessionEntry = {\n\t\t\tstate: \"opening\",\n\t\t\tscope: new ProviderScope(),\n\t\t\tprofile: storedProfile,\n\t\t\t...sessionIdentity(storedProfile),\n\t\t\tcwd: profile.cwd,\n\t\t\tattachments: 1,\n\t\t\tretainOnDisconnect: options?.retainOnDisconnect === true,\n\t\t\tlastCommandAt: this.now(),\n\t\t\tlifecycleMutex: Promise.resolve(),\n\t\t};\n\t\tconst worker = new SessionWorkerClient({\n\t\t\treserve: (path) => this.reserve(handle, path),\n\t\t\treconcile: (livePaths) => this.reconcile(handle, livePaths),\n\t\t\texit: () => {\n\t\t\t\tif (this.entries.get(handle) !== entry) return;\n\t\t\t\tentry.state = \"closed\";\n\t\t\t\tthis.entries.delete(handle);\n\t\t\t\tthis.reservations.releaseAll(handle);\n\t\t\t\tentry.closeResolve?.();\n\t\t\t},\n\t\t\tfailure: (error) => {\n\t\t\t\tentry.state = \"quarantined\";\n\t\t\t\tprocess.stderr.write(`senpi rpc session ${handle} quarantined: ${error}\\n`);\n\t\t\t},\n\t\t});\n\t\tentry.worker = worker;\n\t\tthis.entries.set(handle, entry);\n\t\ttry {\n\t\t\tconst path = await worker.prepare(this.options.configuration, profile);\n\t\t\tconst owner = this.reservations.owner(path);\n\t\t\tif (owner) {\n\t\t\t\tconst attached = this.attach(owner, path, options);\n\t\t\t\tentry.state = \"quarantined\";\n\t\t\t\tworker.quarantine();\n\t\t\t\treturn attached;\n\t\t\t}\n\t\t\tconst grant = this.reserve(handle, path);\n\t\t\tif (grant !== \"granted\") throw new RpcSessionRegistryError(RESERVATION_DENIAL_CODES[grant]);\n\t\t\tentry.reservationKey = path;\n\t\t\tentry.requestedPathKey = profile.sessionPath ? path : undefined;\n\t\t\tentry.sessionPath = path;\n\t\t\tconst snapshot = await worker.commit();\n\t\t\tif (entry.state !== \"opening\") throw new RpcSessionRegistryError(\"session_closing\");\n\t\t\tentry.durableSessionId = snapshot.state.sessionId;\n\t\t\tentry.cwd = snapshot.state.cwd;\n\t\t\tentry.state = \"open\";\n\t\t\treturn this.openResult(handle, entry);\n\t\t} catch (cause) {\n\t\t\tentry.state = \"quarantined\";\n\t\t\tworker.quarantine();\n\t\t\tthrow cause;\n\t\t}\n\t}\n\n\tpeek(handle: string): RpcSessionEntry | undefined {\n\t\treturn this.entries.get(handle);\n\t}\n\n\tgetForCommand(handle: string, command: string): RpcSessionEntry {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (!entry) throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tif (\n\t\t\tentry.state === \"quarantined\" ||\n\t\t\t(entry.state === \"closing\" &&\n\t\t\t\t![\"abort\", \"abort_bash\", \"extension_ui_response\", \"extension_ui_progress\"].includes(command))\n\t\t)\n\t\t\tthrow new RpcSessionRegistryError(\"session_closing\");\n\t\tif (entry.state !== \"open\" && entry.state !== \"closing\") throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tentry.lastCommandAt = this.now();\n\t\treturn entry;\n\t}\n\n\tbeginClose(handle: string, onRole?: (finalizer: boolean) => void, options?: { detach?: boolean }): RpcSessionEntry {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (!entry) throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tif (entry.state === \"closing\" || entry.state === \"quarantined\") {\n\t\t\tonRole?.(false);\n\t\t\treturn entry;\n\t\t}\n\t\tif (entry.state !== \"open\" && entry.state !== \"opening\") throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tentry.attachments--;\n\t\tif (entry.attachments > 0) return entry;\n\t\t// A retained session answers a client's detach by staying open at zero\n\t\t// attachments; only an explicit close or eviction reaches the worker.\n\t\tif (options?.detach && entry.retainOnDisconnect) {\n\t\t\tentry.attachments = 0;\n\t\t\treturn entry;\n\t\t}\n\t\tentry.state = \"closing\";\n\t\tentry.closeCompletion = new Promise((resolve) => {\n\t\t\tentry.closeResolve = resolve;\n\t\t});\n\t\tonRole?.(true);\n\t\treturn entry;\n\t}\n\n\tclose(handle: string): Promise<void> {\n\t\tconst entry = this.beginClose(handle);\n\t\treturn entry.state === \"closing\" ? this.closeMarked(handle) : Promise.resolve();\n\t}\n\n\tasync closeMarked(handle: string): Promise<void> {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (entry?.state !== \"closing\" || !entry.worker) throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tif (entry.closeStarted) return entry.closeCompletion;\n\t\tentry.closeStarted = true;\n\t\t// Reply on a bounded deadline, but keep entry, attachments and reservations until exit.\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tawait Promise.race([\n\t\t\tentry.worker.close(this.closeGraceMs),\n\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tif (entry.state !== \"closed\") entry.state = \"quarantined\";\n\t\t\t\t\tresolve();\n\t\t\t\t}, this.closeGraceMs);\n\t\t\t}),\n\t\t]);\n\t\tif (timer) clearTimeout(timer);\n\t}\n\n\tlist(): RpcSessionRow[] {\n\t\treturn [...this.entries].map(([sessionId, entry]) => {\n\t\t\tconst state = entry.worker?.snapshot?.state;\n\t\t\treturn {\n\t\t\t\tsessionId,\n\t\t\t\tdurableSessionId: state?.sessionId ?? entry.durableSessionId,\n\t\t\t\tsessionPath: state?.sessionFile ?? entry.sessionPath,\n\t\t\t\tcwd: state?.cwd ?? entry.cwd,\n\t\t\t\tname: state?.sessionName,\n\t\t\t\tkind: entry.kind,\n\t\t\t\tcontext: entry.context,\n\t\t\t\t// A closing entry has already released its last attachment; never publish that as negative.\n\t\t\t\tattachments: Math.max(0, entry.attachments),\n\t\t\t\tstatus: entry.state === \"quarantined\" ? \"closing\" : entry.state,\n\t\t\t};\n\t\t});\n\t}\n\n\t/** Only compare spellings already tied to a granted identity; do not inspect caller paths here. */\n\tprivate knownReservationKey(path: string): string | undefined {\n\t\tif (this.reservations.owner(path)) return path;\n\t\tfor (const entry of this.entries.values()) {\n\t\t\t// An opening spelling maps only while the key granted for it is still held: a\n\t\t\t// superseded path belongs to nobody and must open its own worker.\n\t\t\tif (\n\t\t\t\tentry.profile.sessionPath === path &&\n\t\t\t\tentry.requestedPathKey &&\n\t\t\t\tthis.reservations.owner(entry.requestedPathKey)\n\t\t\t)\n\t\t\t\treturn entry.requestedPathKey;\n\t\t\tconst snapshot = entry.worker?.snapshot;\n\t\t\tif (snapshot?.state.sessionFile === path) return snapshot.sessionPath;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate attach(owner: string, path: string, options?: RpcSessionOpenOptions): OpenRpcSession {\n\t\tconst entry = this.entries.get(owner);\n\t\tif (entry?.state !== \"open\" || !entry.worker?.bindingReady || entry.worker.snapshot?.sessionPath !== path)\n\t\t\tthrow new RpcSessionRegistryError(\"session_path_in_use\");\n\t\tconst result = this.openResult(owner, entry);\n\t\tentry.attachments++;\n\t\t// Retention is a property of the live session: any attach may ask for it, and\n\t\t// no attach may revoke it for the clients that already rely on it.\n\t\tif (options?.retainOnDisconnect) entry.retainOnDisconnect = true;\n\t\tentry.lastCommandAt = this.now();\n\t\treturn { ...result, attached: true };\n\t}\n\n\t/** Granted paths currently held by a handle; the per-worker budget is bounded by it. */\n\treservationCount(handle: string): number {\n\t\treturn this.reservations.count(handle);\n\t}\n\n\tprivate reserve(handle: string, path: string): SessionWriteGrant {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (!entry) return \"conflict\";\n\t\tif (this.reservations.owner(path) === handle) return \"granted\";\n\t\tif (entry.state !== \"opening\" && entry.state !== \"open\") return \"conflict\";\n\t\treturn this.reservations.reserve(handle, path, this.liveWorkerPaths(entry));\n\t}\n\n\t/**\n\t * Releases the grants a worker's latest snapshot no longer claims.\n\t *\n\t * Only a fully open entry reports live writers. An entry still opening, closing or\n\t * quarantined keeps every grant until its real exit, because a worker stuck in a\n\t * syscall can still be writing a path it can no longer report.\n\t */\n\tprivate reconcile(handle: string, livePaths: readonly string[]): void {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (entry?.state !== \"open\") return;\n\t\tconst sessionPath = entry.worker?.snapshot?.sessionPath;\n\t\tthis.reservations.reconcile(handle, { livePaths, sessionPath });\n\t\tif (!sessionPath) return;\n\t\tentry.reservationKey = sessionPath;\n\t\tentry.sessionPath = sessionPath;\n\t}\n\n\t/** The live view a full budget is reconciled against; absent while the entry cannot report one. */\n\tprivate liveWorkerPaths(entry: RpcSessionEntry): LiveWorkerPaths | undefined {\n\t\tconst snapshot = entry.state === \"open\" ? entry.worker?.snapshot : undefined;\n\t\tif (!snapshot) return undefined;\n\t\treturn { livePaths: snapshot.liveSessionPaths, sessionPath: snapshot.sessionPath };\n\t}\n\n\tprivate openResult(handle: string, entry: RpcSessionEntry): OpenRpcSession {\n\t\tconst state = entry.worker?.snapshot?.state;\n\t\tif (!state) throw new RpcSessionRegistryError(\"open_failed\");\n\t\treturn { sessionId: handle, durableSessionId: state.sessionId, sessionPath: state.sessionFile };\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"worker-session-registry.js","sourceRoot":"","sources":["../../../src/modes/rpc/worker-session-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAbsolute } from \"node:path\";\nimport { ProviderScope } from \"@earendil-works/pi-ai/node/provider-scope\";\nimport type { CliRuntimeConfiguration } from \"../../main.ts\";\nimport {\n\ttype LiveWorkerPaths,\n\tRESERVATION_DENIAL_CODES,\n\tSessionPathReservations,\n} from \"./session-path-reservations.ts\";\nimport {\n\tfrozenProfile,\n\ttype OpenRpcSession,\n\ttype RpcSessionEntry,\n\ttype RpcSessionLaunchProfile,\n\ttype RpcSessionOpenOptions,\n\tRpcSessionRegistryError,\n\ttype RpcSessionRow,\n\tsessionIdentity,\n} from \"./session-registry.ts\";\nimport { SessionWorkerClient } from \"./session-worker-client.ts\";\nimport { SESSION_WORKER_LIMITS, type SessionWriteGrant } from \"./session-worker-protocol.ts\";\n\ntype SessionWorkerCallbacks = ConstructorParameters<typeof SessionWorkerClient>[0];\n\nexport interface WorkerSessionRegistryOptions {\n\treadonly configuration: CliRuntimeConfiguration;\n\treadonly closeGraceMs: number;\n\treadonly now: () => number;\n\t/** Production builds the real worker; a caller may supply one to drive a lifecycle path deterministically. */\n\treadonly createWorker?: (callbacks: SessionWorkerCallbacks) => SessionWorkerClient;\n}\n\n/** Transport-side lifecycle owner. Caller paths are never inspected on this event loop. */\nexport class WorkerSessionRegistry {\n\tprivate readonly entries = new Map<string, RpcSessionEntry>();\n\tprivate readonly reservations = new SessionPathReservations();\n\tprivate serial = 0;\n\treadonly closeGraceMs: number;\n\tprivate readonly now: () => number;\n\n\tprivate readonly options: WorkerSessionRegistryOptions;\n\tprivate readonly createWorker: (callbacks: SessionWorkerCallbacks) => SessionWorkerClient;\n\n\tconstructor(options: WorkerSessionRegistryOptions) {\n\t\tthis.options = options;\n\t\tthis.closeGraceMs = options.closeGraceMs;\n\t\tthis.now = options.now;\n\t\tthis.createWorker = options.createWorker ?? ((callbacks) => new SessionWorkerClient(callbacks));\n\t}\n\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\t/**\n\t * Memory admission is the in-process registry's concern: a worker-runtime session runs in\n\t * its own isolate and this registry already bounds occupancy with `too_many_sessions`.\n\t */\n\tsetWorkerAdmission(): void {}\n\n\tasync openSession(profile: RpcSessionLaunchProfile, options?: RpcSessionOpenOptions): Promise<OpenRpcSession> {\n\t\tif (!isAbsolute(profile.cwd) || (profile.sessionPath !== undefined && !isAbsolute(profile.sessionPath)))\n\t\t\tthrow new RpcSessionRegistryError(\"invalid_path\");\n\t\tif (profile.sessionPath) {\n\t\t\tconst key = this.knownReservationKey(profile.sessionPath);\n\t\t\tconst owner = key ? this.reservations.owner(key) : undefined;\n\t\t\tif (key && owner) return this.attach(owner, key, options);\n\t\t}\n\t\tif (this.size >= SESSION_WORKER_LIMITS.workers) throw new Error(\"too_many_sessions\");\n\t\tconst handle = `rpc-${++this.serial}`;\n\t\tconst storedProfile = frozenProfile(profile);\n\t\tconst entry: RpcSessionEntry = {\n\t\t\tstate: \"opening\",\n\t\t\tscope: new ProviderScope(),\n\t\t\tprofile: storedProfile,\n\t\t\t...sessionIdentity(storedProfile),\n\t\t\tcwd: profile.cwd,\n\t\t\tattachments: 1,\n\t\t\tretainOnDisconnect: options?.retainOnDisconnect === true,\n\t\t\tlastCommandAt: this.now(),\n\t\t\tlifecycleMutex: Promise.resolve(),\n\t\t};\n\t\tlet workerFailure: string | undefined;\n\t\tconst worker = this.createWorker({\n\t\t\treserve: (path) => this.reserve(handle, path),\n\t\t\treconcile: (livePaths) => this.reconcile(handle, livePaths),\n\t\t\texit: () => {\n\t\t\t\tif (this.entries.get(handle) !== entry) return;\n\t\t\t\tentry.state = \"closed\";\n\t\t\t\tthis.entries.delete(handle);\n\t\t\t\tthis.reservations.releaseAll(handle);\n\t\t\t\tentry.closeResolve?.();\n\t\t\t},\n\t\t\tfailure: (error) => {\n\t\t\t\t// The open below only learns that its entry left `opening`, never why. Without this the\n\t\t\t\t// caller is told `session_closing` - a path whose owner is tearing down - for a worker\n\t\t\t\t// that died, and the actual reason reaches stderr alone (#1953).\n\t\t\t\tworkerFailure = error;\n\t\t\t\tentry.state = \"quarantined\";\n\t\t\t\tprocess.stderr.write(`senpi rpc session ${handle} quarantined: ${error}\\n`);\n\t\t\t},\n\t\t});\n\t\tentry.worker = worker;\n\t\tthis.entries.set(handle, entry);\n\t\ttry {\n\t\t\tconst path = await worker.prepare(this.options.configuration, profile);\n\t\t\tconst owner = this.reservations.owner(path);\n\t\t\tif (owner) {\n\t\t\t\tconst attached = this.attach(owner, path, options);\n\t\t\t\tentry.state = \"quarantined\";\n\t\t\t\tworker.quarantine();\n\t\t\t\treturn attached;\n\t\t\t}\n\t\t\tconst grant = this.reserve(handle, path);\n\t\t\tif (grant !== \"granted\") throw new RpcSessionRegistryError(RESERVATION_DENIAL_CODES[grant]);\n\t\t\tentry.reservationKey = path;\n\t\t\tentry.requestedPathKey = profile.sessionPath ? path : undefined;\n\t\t\tentry.sessionPath = path;\n\t\t\tconst snapshot = await worker.commit();\n\t\t\tif (entry.state !== \"opening\")\n\t\t\t\tthrow workerFailure === undefined\n\t\t\t\t\t? new RpcSessionRegistryError(\"session_closing\")\n\t\t\t\t\t: new RpcSessionRegistryError(\"open_failed\", workerFailure);\n\t\t\tentry.durableSessionId = snapshot.state.sessionId;\n\t\t\tentry.cwd = snapshot.state.cwd;\n\t\t\tentry.state = \"open\";\n\t\t\treturn this.openResult(handle, entry);\n\t\t} catch (cause) {\n\t\t\tentry.state = \"quarantined\";\n\t\t\tworker.quarantine();\n\t\t\tthrow cause;\n\t\t}\n\t}\n\n\tpeek(handle: string): RpcSessionEntry | undefined {\n\t\treturn this.entries.get(handle);\n\t}\n\n\tgetForCommand(handle: string, command: string): RpcSessionEntry {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (!entry) throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tif (\n\t\t\tentry.state === \"quarantined\" ||\n\t\t\t(entry.state === \"closing\" &&\n\t\t\t\t![\"abort\", \"abort_bash\", \"extension_ui_response\", \"extension_ui_progress\"].includes(command))\n\t\t)\n\t\t\tthrow new RpcSessionRegistryError(\"session_closing\");\n\t\tif (entry.state !== \"open\" && entry.state !== \"closing\") throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tentry.lastCommandAt = this.now();\n\t\treturn entry;\n\t}\n\n\tbeginClose(handle: string, onRole?: (finalizer: boolean) => void, options?: { detach?: boolean }): RpcSessionEntry {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (!entry) throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tif (entry.state === \"closing\" || entry.state === \"quarantined\") {\n\t\t\tonRole?.(false);\n\t\t\treturn entry;\n\t\t}\n\t\tif (entry.state !== \"open\" && entry.state !== \"opening\") throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tentry.attachments--;\n\t\tif (entry.attachments > 0) return entry;\n\t\t// A retained session answers a client's detach by staying open at zero\n\t\t// attachments; only an explicit close or eviction reaches the worker.\n\t\tif (options?.detach && entry.retainOnDisconnect) {\n\t\t\tentry.attachments = 0;\n\t\t\treturn entry;\n\t\t}\n\t\tentry.state = \"closing\";\n\t\tentry.closeCompletion = new Promise((resolve) => {\n\t\t\tentry.closeResolve = resolve;\n\t\t});\n\t\tonRole?.(true);\n\t\treturn entry;\n\t}\n\n\tclose(handle: string): Promise<void> {\n\t\tconst entry = this.beginClose(handle);\n\t\treturn entry.state === \"closing\" ? this.closeMarked(handle) : Promise.resolve();\n\t}\n\n\tasync closeMarked(handle: string): Promise<void> {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (entry?.state !== \"closing\" || !entry.worker) throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tif (entry.closeStarted) return entry.closeCompletion;\n\t\tentry.closeStarted = true;\n\t\t// Reply on a bounded deadline, but keep entry, attachments and reservations until exit.\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tawait Promise.race([\n\t\t\tentry.worker.close(this.closeGraceMs),\n\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tif (entry.state !== \"closed\") entry.state = \"quarantined\";\n\t\t\t\t\tresolve();\n\t\t\t\t}, this.closeGraceMs);\n\t\t\t}),\n\t\t]);\n\t\tif (timer) clearTimeout(timer);\n\t}\n\n\tlist(): RpcSessionRow[] {\n\t\treturn [...this.entries].map(([sessionId, entry]) => {\n\t\t\tconst state = entry.worker?.snapshot?.state;\n\t\t\treturn {\n\t\t\t\tsessionId,\n\t\t\t\tdurableSessionId: state?.sessionId ?? entry.durableSessionId,\n\t\t\t\tsessionPath: state?.sessionFile ?? entry.sessionPath,\n\t\t\t\tcwd: state?.cwd ?? entry.cwd,\n\t\t\t\tname: state?.sessionName,\n\t\t\t\tkind: entry.kind,\n\t\t\t\tcontext: entry.context,\n\t\t\t\t// A closing entry has already released its last attachment; never publish that as negative.\n\t\t\t\tattachments: Math.max(0, entry.attachments),\n\t\t\t\tstatus: entry.state === \"quarantined\" ? \"closing\" : entry.state,\n\t\t\t};\n\t\t});\n\t}\n\n\t/** Only compare spellings already tied to a granted identity; do not inspect caller paths here. */\n\tprivate knownReservationKey(path: string): string | undefined {\n\t\tif (this.reservations.owner(path)) return path;\n\t\tfor (const entry of this.entries.values()) {\n\t\t\t// An opening spelling maps only while the key granted for it is still held: a\n\t\t\t// superseded path belongs to nobody and must open its own worker.\n\t\t\tif (\n\t\t\t\tentry.profile.sessionPath === path &&\n\t\t\t\tentry.requestedPathKey &&\n\t\t\t\tthis.reservations.owner(entry.requestedPathKey)\n\t\t\t)\n\t\t\t\treturn entry.requestedPathKey;\n\t\t\tconst snapshot = entry.worker?.snapshot;\n\t\t\tif (snapshot?.state.sessionFile === path) return snapshot.sessionPath;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\tprivate attach(owner: string, path: string, options?: RpcSessionOpenOptions): OpenRpcSession {\n\t\tconst entry = this.entries.get(owner);\n\t\tif (entry?.state !== \"open\" || !entry.worker?.bindingReady || entry.worker.snapshot?.sessionPath !== path)\n\t\t\tthrow new RpcSessionRegistryError(\"session_path_in_use\");\n\t\tconst result = this.openResult(owner, entry);\n\t\tentry.attachments++;\n\t\t// Retention is a property of the live session: any attach may ask for it, and\n\t\t// no attach may revoke it for the clients that already rely on it.\n\t\tif (options?.retainOnDisconnect) entry.retainOnDisconnect = true;\n\t\tentry.lastCommandAt = this.now();\n\t\treturn { ...result, attached: true };\n\t}\n\n\t/** Granted paths currently held by a handle; the per-worker budget is bounded by it. */\n\treservationCount(handle: string): number {\n\t\treturn this.reservations.count(handle);\n\t}\n\n\tprivate reserve(handle: string, path: string): SessionWriteGrant {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (!entry) return \"conflict\";\n\t\tif (this.reservations.owner(path) === handle) return \"granted\";\n\t\tif (entry.state !== \"opening\" && entry.state !== \"open\") return \"conflict\";\n\t\treturn this.reservations.reserve(handle, path, this.liveWorkerPaths(entry));\n\t}\n\n\t/**\n\t * Releases the grants a worker's latest snapshot no longer claims.\n\t *\n\t * Only a fully open entry reports live writers. An entry still opening, closing or\n\t * quarantined keeps every grant until its real exit, because a worker stuck in a\n\t * syscall can still be writing a path it can no longer report.\n\t */\n\tprivate reconcile(handle: string, livePaths: readonly string[]): void {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (entry?.state !== \"open\") return;\n\t\tconst sessionPath = entry.worker?.snapshot?.sessionPath;\n\t\tthis.reservations.reconcile(handle, { livePaths, sessionPath });\n\t\tif (!sessionPath) return;\n\t\tentry.reservationKey = sessionPath;\n\t\tentry.sessionPath = sessionPath;\n\t}\n\n\t/** The live view a full budget is reconciled against; absent while the entry cannot report one. */\n\tprivate liveWorkerPaths(entry: RpcSessionEntry): LiveWorkerPaths | undefined {\n\t\tconst snapshot = entry.state === \"open\" ? entry.worker?.snapshot : undefined;\n\t\tif (!snapshot) return undefined;\n\t\treturn { livePaths: snapshot.liveSessionPaths, sessionPath: snapshot.sessionPath };\n\t}\n\n\tprivate openResult(handle: string, entry: RpcSessionEntry): OpenRpcSession {\n\t\tconst state = entry.worker?.snapshot?.state;\n\t\tif (!state) throw new RpcSessionRegistryError(\"open_failed\");\n\t\treturn { sessionId: handle, durableSessionId: state.sessionId, sessionPath: state.sessionFile };\n\t}\n}\n"]}
|
package/docs/providers.md
CHANGED
|
@@ -22,6 +22,7 @@ Use `/login` in interactive mode, then select a provider:
|
|
|
22
22
|
- GitHub Copilot
|
|
23
23
|
- xAI (Grok/X subscription)
|
|
24
24
|
- OpenRouter (OAuth-minted API key billed from OpenRouter credits)
|
|
25
|
+
- Kimi Code (kimi.com / kimi.ai subscriptions)
|
|
25
26
|
- Radius
|
|
26
27
|
- Cursor (Pro/Ultra/Teams) — authentication only for now, see below
|
|
27
28
|
|
|
@@ -136,6 +137,13 @@ Upstreams that require explicit cache breakpoints get `cache_control` blocks thr
|
|
|
136
137
|
|
|
137
138
|
senpi sends `prompt_cache_key` (set to the session id) on Moonshot requests. Kimi documents the field as required for the Kimi Code Plan and recommended for any multi-turn agent ([Kimi context caching](https://platform.kimi.ai/docs/guide/use-context-caching-feature-of-kimi-api)). Kimi reports cache hits as a flat `usage.cached_tokens` field, which senpi parses as cache-read tokens.
|
|
138
139
|
|
|
140
|
+
### Kimi Code
|
|
141
|
+
|
|
142
|
+
- Run `/login kimi-coding`, then select **Sign in with Kimi Code** and pick the service that hosts your account: **Mainland China (kimi.com)** or **Outside mainland China (kimi.ai)**
|
|
143
|
+
- The region is stored with the credential; token refresh and model requests follow it (`auth.kimi.ai` + `api.kimi.ai/coding` for international accounts), so a `.ai` login keeps working after restarts and env changes
|
|
144
|
+
- **Use an API key** asks the same region question and routes `KIMI_API_KEY` requests by it; `KIMI_CODE_REGION=global` (or `mainland-cn`) answers it for headless setups
|
|
145
|
+
- `KIMI_CODE_OAUTH_HOST` / `KIMI_OAUTH_HOST` still override the auth host for new logins and for credentials saved before regions existed; a host other than the two official ones is kept verbatim and needs a `models.json` `baseUrl` for inference
|
|
146
|
+
|
|
139
147
|
### Radius
|
|
140
148
|
|
|
141
149
|
Radius is a dynamic `pi-messages` gateway. `/login radius` stores OAuth tokens in `auth.json`; the gateway catalog is refreshed independently and cached in `models-store.json`. Custom Radius gateways can be declared in `models.json` with `"oauth": "radius"` and a gateway `baseUrl`.
|
|
@@ -286,7 +294,7 @@ senpi
|
|
|
286
294
|
| Fireworks | `FIREWORKS_API_KEY` | `fireworks` |
|
|
287
295
|
| Together AI | `TOGETHER_API_KEY` | `together` |
|
|
288
296
|
| Baseten | `BASETEN_API_KEY` | `baseten` |
|
|
289
|
-
| Kimi For Coding | `KIMI_API_KEY` | `kimi-coding` |
|
|
297
|
+
| Kimi For Coding | `KIMI_API_KEY` (+ `KIMI_CODE_REGION`) | `kimi-coding` |
|
|
290
298
|
| MiniMax | `MINIMAX_API_KEY` | `minimax` |
|
|
291
299
|
| MiniMax (China) | `MINIMAX_CN_API_KEY` | `minimax-cn` |
|
|
292
300
|
| Qwen Token Plan (existing catalog) | `QWEN_TOKEN_PLAN_API_KEY` | `qwen-token-plan` |
|
package/docs/rpc.md
CHANGED
|
@@ -207,7 +207,13 @@ are what this decision encodes; the other two are what a client must not undo el
|
|
|
207
207
|
`ensureHost` records a `writer: { pid, startTime }` stamp in its pidfile and stops the host it names only when
|
|
208
208
|
that stamp is this process (the start time is what keeps a recycled pid from inheriting the right). A pidfile
|
|
209
209
|
written by anyone else fails the ensure with `HostEnsureRefusedError { reason: "foreign_writer" }` and the host
|
|
210
|
-
keeps running.
|
|
210
|
+
keeps running. That refusal is about signalling, so it holds only while something still accepts connections at
|
|
211
|
+
the public path: a registration that names a live process whose entry is GONE (it was replaced by another socket
|
|
212
|
+
and the replacement later exited) or whose entry nobody listens behind (a dead listener left it, a successor's
|
|
213
|
+
rename never landed) describes a generation nobody can reach, and binding a fresh generation to the free path
|
|
214
|
+
touches nothing of it. The ensure then starts one beside it, numbered after the stranded generation, which
|
|
215
|
+
keeps its registration until it exits; refusing there locked every client out until that process happened to
|
|
216
|
+
end (#1936). A named pipe has no entry to lose, so win32 keeps refusing.
|
|
211
217
|
- **I2 - compatibility is protocol version + capabilities, never semver equality.** An ordinal that cannot be
|
|
212
218
|
compared (a build without git metadata, a host that reports none) is EQUAL, and since a handoff requires
|
|
213
219
|
STRICTLY greater, such a pair attaches instead of upgrading.
|
|
@@ -238,10 +244,11 @@ client's sessions. A GENERATION HANDOFF replaces the process while its sessions
|
|
|
238
244
|
running host keeps serving.
|
|
239
245
|
3. `handoffHost` then registers the successor (its own `generations/<instanceId>/host.pid`, the pointer
|
|
240
246
|
moved onto it, and `settings.json { socket, generation }`) and sends
|
|
241
|
-
the predecessor SIGUSR1 = DRAIN: stop accepting,
|
|
242
|
-
|
|
243
|
-
see `session_closed { reason: "handoff_parked" }`
|
|
244
|
-
`open_session { sessionPath }
|
|
247
|
+
the predecessor SIGUSR1 = DRAIN: stop accepting, announce `host_superseded` to every connection,
|
|
248
|
+
park attached and unattached sessions as their turns and requests settle, then exit independently of
|
|
249
|
+
connection occupancy. Clients see `session_closed { reason: "handoff_parked", sessionPath }` before
|
|
250
|
+
their connection closes and reopen with `open_session { sessionPath }` on the successor. Losing
|
|
251
|
+
ownership of the public socket triggers the same sequence even if SIGUSR1 never arrives.
|
|
245
252
|
|
|
246
253
|
Two guards decide whether a handoff is attempted at all, and both fail closed:
|
|
247
254
|
|
|
@@ -272,6 +279,13 @@ the moment it does not, it applies the same drain to itself: park each retained
|
|
|
272
279
|
release its claims, and exit when it holds no attached session. Nothing else changes - a session mid-turn
|
|
273
280
|
still finishes, and a client still attached is still served.
|
|
274
281
|
|
|
282
|
+
That check separates a name that was TAKEN OVER from a name that is GONE, because the evidence differs in
|
|
283
|
+
strength. A different entry at the path proves supersession on sight and drains immediately. An absent entry
|
|
284
|
+
proves only that nobody is serving the name, so it has to be seen on several consecutive checks before it
|
|
285
|
+
counts, and a check that cannot answer resets the count rather than adding to it. Either way the outcome is
|
|
286
|
+
the same drain: an unlinked socket name can never accept a connection again, so the generation behind it is
|
|
287
|
+
unreachable no matter why the name went away.
|
|
288
|
+
|
|
275
289
|
`stopHost({ socket, agentDir, drain?, force? })` ends a generation: `drain: true` is always permitted (it
|
|
276
290
|
ends no work, it only stops the host from taking new work), while a hard stop requires a host that reports
|
|
277
291
|
no open sessions, or `force: true`. A hard stop also drops that generation's registration (the pointer and
|
|
@@ -485,7 +499,7 @@ exposing session content. The one exception is `session_closed` and `session_par
|
|
|
485
499
|
`kind: "worker"`: they are delivered only to
|
|
486
500
|
the connections attached to that session, so machine-driven work neither appears in nor disappears from a client that never
|
|
487
501
|
asked for it. Every other lifecycle record, and every record of an `interactive` session, keeps today's broadcast. Host-level
|
|
488
|
-
records (`host_stalled`, `host_memory_pressure`) are broadcast the same way and describe the host process rather than a
|
|
502
|
+
records (`host_superseded`, `host_stalled`, `host_memory_pressure`) are broadcast the same way and describe the host process rather than a
|
|
489
503
|
session. Correlated responses and dialog extension UI
|
|
490
504
|
requests (select, confirm, input, and editor) are requester-only; other extension UI state records go to the session's
|
|
491
505
|
attached connections. To observe a foreign session, open it by its existing
|
|
@@ -586,7 +600,12 @@ Environment overrides beat the file, and invalid values fall through to the next
|
|
|
586
600
|
(`transient`|`persistent`) and `SENPI_RPC_HOST_IDLE_EXIT_MS` (positive integer milliseconds).
|
|
587
601
|
|
|
588
602
|
The host exits only after the window elapses with NO attached client connections and NO active turns — continuously.
|
|
589
|
-
Any connection or agent turn resets the window, so a busy host never exits
|
|
603
|
+
Any connection or agent turn resets the window, so a busy host never exits. The supervisor learns about turns through
|
|
604
|
+
its observer connection to the host; while that connection is unhealthy it cannot see turns, so it treats activity as
|
|
605
|
+
unknown and keeps the host open as if a turn were running — but only for one idle window, during which it keeps
|
|
606
|
+
reconnecting. An observer that stays unhealthy for longer than the window stops counting as busy, and the connection
|
|
607
|
+
count alone decides from there. A `persistent` host has an infinite window and so keeps its infinite benefit of the
|
|
608
|
+
doubt. The exit itself is clean: the RPC host
|
|
590
609
|
receives SIGTERM first, flushes pending output, removes its socket, and the supervisor then removes `host.pid` and
|
|
591
610
|
`settings.json` (the stderr log stays for diagnostics). After an idle exit, the next `ensureHost()` transparently
|
|
592
611
|
starts a fresh host. `get_protocol_info` over the public socket behaves exactly as before; the supervisor is
|
|
@@ -599,6 +618,48 @@ internal directory. The supervisor also exports `SENPI_RPC_HOST_WATCH_PPID` as a
|
|
|
599
618
|
set only by the supervisor: a host started any other way (plain `senpi --mode rpc --listen …`, embedders, hand-started
|
|
600
619
|
hosts) sees neither variable and is unaffected. A host whose supervisor is alive is never touched by this binding.
|
|
601
620
|
|
|
621
|
+
### Supersession lifecycle: `host_superseded`
|
|
622
|
+
|
|
623
|
+
Before parking for a generation handoff, the old host sends every connected client one unsolicited record:
|
|
624
|
+
|
|
625
|
+
```json
|
|
626
|
+
{"type":"host_superseded","instanceId":"old-host-uuid","generation":0,"successor":{"socket":"/tmp/agent/rpc.sock"}}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
`instanceId` and `generation` identify the OLD host, matching its `get_protocol_info` response. `successor.socket`
|
|
630
|
+
identifies the public socket, never the supervisor's private hop. `successor` is `null` when a drain is requested
|
|
631
|
+
without a known replacement. This record has no request id or session handle; older clients can ignore it.
|
|
632
|
+
|
|
633
|
+
1. The announcement is queued on every connection before handoff parking changes any session.
|
|
634
|
+
2. Every parkable session, attached or unattached, is parked immediately. A session is **parkable** when no agent
|
|
635
|
+
run, bash/tool execution, compaction, queued session work, or in-flight host request remains. Accepted opens,
|
|
636
|
+
binding creation, close requests, and steer delivery must finish. This is the ordinary busy predicate with
|
|
637
|
+
**durable wake-source holds excluded**: persistent monitors, scheduled continuations and goal-loop holds do not
|
|
638
|
+
pin an old generation. They resume from the persisted session when reopened, as after a restart. Ordinary idle
|
|
639
|
+
eviction retains its broader activity predicate, including all wake sources.
|
|
640
|
+
3. Active sessions keep serving their existing connections and park when their turns and requests settle. Each
|
|
641
|
+
released handle emits `session_closed { sessionId, reason: "handoff_parked", sessionPath }` after releasing its
|
|
642
|
+
file. This is not a session deletion: reopen `sessionPath` on the successor to obtain a new routing handle.
|
|
643
|
+
That `sessionPath` is the CANONICAL path the host reserved the file under, which is not necessarily the string
|
|
644
|
+
the client passed to `open_session` (a scratch dir under `/var/...` on macOS is reported as `/private/var/...`).
|
|
645
|
+
A client matching the record against its own stored path must canonicalize before comparing; reopening with
|
|
646
|
+
either spelling resolves to the same reservation.
|
|
647
|
+
The connection closes after its last attached session parks; a connection shared with another active session
|
|
648
|
+
stays open until that session also settles. A command racing parking on the old connection is either served
|
|
649
|
+
or answered by that terminal record and close, never `unknown_session` for the parked handle. New opens on a
|
|
650
|
+
draining connection are refused with `host_draining`.
|
|
651
|
+
4. The **supervisor** starts `SENPI_RPC_HANDOFF_GRACE_MS` at supersession (default **600000 ms / 10 minutes**).
|
|
652
|
+
This is a soft grace, not an abort deadline or a delay before parking. At expiry it reports outstanding work
|
|
653
|
+
and requests another drain pass. Sessions still mid-turn or handling requests remain alive even beyond grace;
|
|
654
|
+
no turn is cut short. Parkable sessions never wait for grace. A bare socket host has no supervisor grace timer,
|
|
655
|
+
but follows the same announce/park/settle sequence.
|
|
656
|
+
5. Once every session has parked and its terminal records have drained, the generation exits even if an observer
|
|
657
|
+
or client never voluntarily disconnects. Neither the normal idle timeout nor persistent-host mode can pin it.
|
|
658
|
+
|
|
659
|
+
A client may react to `host_superseded` early, but the old generation retains each file until its work settles;
|
|
660
|
+
`session_path_in_use` on the successor remains a retryable response until parking releases that claim. A client
|
|
661
|
+
that ignores the announcement still receives the terminal record and connection close.
|
|
662
|
+
|
|
602
663
|
### Shared host occupancy (idle eviction, retention, empty-host exit)
|
|
603
664
|
|
|
604
665
|
**The daemon does not cap sessions.** On the in-process runtime - the default for a `--listen` socket host, i.e. the
|
|
@@ -619,8 +680,8 @@ What the host does enforce are lifecycle windows, and they only ever return memo
|
|
|
619
680
|
`session_parked { sessionId, sessionPath }` and there is no `close_session` response, because nothing closed the session -
|
|
620
681
|
the routing handle was released while the session itself stays on disk and reopens with `open_session { sessionPath }`
|
|
621
682
|
(as a NEW handle). A client that does not know `session_parked` ignores it and learns the handle is gone from its next
|
|
622
|
-
command's `unknown_session`. A GENERATION HANDOFF parks
|
|
623
|
-
|
|
683
|
+
command's `unknown_session`. A GENERATION HANDOFF also parks attached sessions, using the distinct activity
|
|
684
|
+
predicate below, and emits `session_closed { sessionId, reason: "handoff_parked", sessionPath }`, so a client can tell "the host handed
|
|
624
685
|
over, reopen by path" from "this session ended". An explicit `close_session` is `reason: "client_close"`; the host
|
|
625
686
|
process exiting is `reason: "host_shutdown"` (even for a retained session - the process is going away, so this is not
|
|
626
687
|
a park). `reason` is optional on the wire; a client that does not know a
|
|
@@ -831,11 +892,12 @@ Response-level `sessionId` = opaque **routing handle**, unique per process epoch
|
|
|
831
892
|
In the response `error` field, machine-matchable:
|
|
832
893
|
|
|
833
894
|
- `unknown_session`
|
|
834
|
-
- `session_closing`
|
|
895
|
+
- `session_closing` (an entry whose owner is tearing down). A worker that FAILS while its session is being opened is not that case and reports `open_failed` carrying the worker's own reason, so the code a client matches on always names what actually happened.
|
|
835
896
|
- `session_path_in_use` (path held by an opening or quarantined owner; a fully-open current owner is attached instead, an owner whose teardown is already in flight is waited out on the in-process runtime, and a path a live owner has superseded is released rather than held). A path held by ANOTHER GENERATION of the daemon carries `errorData { owner: { instanceId, pid, processStartTime, sessionPath, current }, retry_after_ms: 2000 }`: that generation is still writing the file and is parking it, so the open is a retry, not a failure. Only a claim whose owner serves the socket (`current: true`) or still has a client attached refuses an open at all - a superseded, attachment-less claim is reclaimed instead
|
|
836
897
|
- `session_reservation_limit` (this worker already holds 64 live session paths; the open or session replacement was refused without disturbing the existing session)
|
|
837
898
|
- `missing_session_id` (session-scoped command without `sessionId` in multi mode)
|
|
838
899
|
- `multi_session_disabled` (`open_session` in classic mode)
|
|
900
|
+
- `host_draining` (`open_session` on a connection whose generation is parking for a handoff; the successor already owns the public path, so re-resolve it and open there rather than retrying this connection)
|
|
839
901
|
- `invalid_path` (relative `sessionPath`/`cwd`)
|
|
840
902
|
- `open_failed: <detail>`
|
|
841
903
|
- `invalid_session_context: <detail>` (`open_session.context` past a documented cap: more than 32 keys, a key that does not match `^[a-z][a-z0-9_]*$`, a non-string or >16 KiB value, or more than 32 KiB of JSON in total; the detail names the cap and its byte budget)
|
package/docs/settings.md
CHANGED
|
@@ -88,7 +88,7 @@ Permission rules are a confirmation policy, not a sandbox. Senpi, extensions, pa
|
|
|
88
88
|
| `modelThinkingLevels` | object | - | Per-model reasoning effort memory (`"provider/id": "level"`) |
|
|
89
89
|
| `modelLastOnThinkingLevels` | object | - | Per-model last non-off reasoning level, used by `/reasoning on` to restore the previous effort |
|
|
90
90
|
| `modelServiceTiers` | object | - | Per-model service tier memory (`"provider/id": "auto" \| "priority"`) |
|
|
91
|
-
| `promptPreset` | string | `"auto"` | Force a system prompt preset: `"auto"`, `"kimi-k2-6"`, `"kimi-k2-7"`, `"kimi-k2-8"`, `"kimi-k3"`, `"glm-5.2"`, `"glm-5.3"`, `"grok-4.5"`, `"grok-4.6"`, `"claude-fable-5"`, `"claude-fable-5-1"`, `"claude-opus-5"`, `"claude-opus-4-5"`, `"claude-opus-4-6"`, `"claude-opus-4-7"`, `"claude-opus-4-8"`, `"deepseek-v4-flash"`, `"deepseek-v4-flash-0731"`, `"deepseek-v4-1-flash"`, `"deepseek-v4-pro"`, `"gpt-5"`, `"gpt-5.2"`, `"gpt-5.3-codex"`, `"gpt-5.4"`, `"gpt-5.5"`, `"gpt-5.6"`, or `"gpt-6-astra"` |
|
|
91
|
+
| `promptPreset` | string | `"auto"` | Force a system prompt preset: `"auto"`, `"kimi-k2-6"`, `"kimi-k2-7"`, `"kimi-k2-8"`, `"kimi-k3"`, `"glm-5.2"`, `"glm-5.3"`, `"grok-4.5"`, `"grok-4.6"`, `"grok-4.7"`, `"claude-fable-5"`, `"claude-fable-5-1"`, `"claude-opus-5"`, `"claude-opus-4-5"`, `"claude-opus-4-6"`, `"claude-opus-4-7"`, `"claude-opus-4-8"`, `"deepseek-v4-flash"`, `"deepseek-v4-flash-0731"`, `"deepseek-v4-1-flash"`, `"deepseek-v4-pro"`, `"gpt-5"`, `"gpt-5.2"`, `"gpt-5.3-codex"`, `"gpt-5.4"`, `"gpt-5.5"`, `"gpt-5.6"`, or `"gpt-6-astra"` |
|
|
92
92
|
| `hideThinkingBlock` | boolean | `false` | Hide thinking blocks in output |
|
|
93
93
|
| `showCacheMissNotices` | boolean | `false` | Show transcript notices for significant prompt-cache misses, compaction or branch-summary usage, and provider recovery diagnostics such as dropped Anthropic thinking blocks |
|
|
94
94
|
| `thinkingBudgets` | object | - | Custom token budgets per thinking level. Anthropic, Google, and Bedrock use these natively. OpenAI-compatible models use them when `compat.thinkingTokenBudgetField` (or `supportsThinkingTokenBudget`) is set. |
|
|
@@ -12,6 +12,35 @@
|
|
|
12
12
|
|
|
13
13
|
### Removed
|
|
14
14
|
|
|
15
|
+
## [2026.9.22-2] - 2026-09-22
|
|
16
|
+
|
|
17
|
+
### Breaking Changes
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- `wake_source_state` (live detached eval cells) is now published on the rpc channel as well as the in-process event bus, so out-of-process consumers see live cells the way the TUI footer does (#1943).
|
|
26
|
+
|
|
27
|
+
### Removed
|
|
28
|
+
|
|
29
|
+
## [2026.9.22] - 2026-09-21
|
|
30
|
+
|
|
31
|
+
### Breaking Changes
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- `eval` runs up to `maxDetachedCells` background cells per session (default 15, `SENPI_CODEMODE_MAX_DETACHED_CELLS`) and queues same-language cells on their kernel instead of rejecting them; `list` observes live and recent cells, and resetting a busy language refuses with `eval_kernel_busy_reset_refused` instead of stopping live work. ([#1908](https://github.com/code-yeongyu/senpi/issues/1908))
|
|
36
|
+
- `agent()` forwards `isolated`, `apply`, and `merge` when the task host advertises isolation. Hosts that do not still drop those options with the existing warning. A foreground call whose isolation did not apply now raises instead of looking successful; with `handle: true` the isolation result arrives on completion. ([#1910](https://github.com/code-yeongyu/senpi/issues/1910))
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
### Removed
|
|
43
|
+
|
|
15
44
|
## [2026.9.21-2] - 2026-09-21
|
|
16
45
|
|
|
17
46
|
### Breaking Changes
|