@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
|
@@ -14,7 +14,7 @@ import { createEndpointReservations } from "./host-reservations.js";
|
|
|
14
14
|
import { armHostWatchdog, readHostWatchdogConfigFromBrandEnv } from "./host-watchdog.js";
|
|
15
15
|
import { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from "./jsonl.js";
|
|
16
16
|
import { LoopLagWatchdog } from "./loop-lag-watchdog.js";
|
|
17
|
-
import { hostInstanceId } from "./protocol-identity.js";
|
|
17
|
+
import { hostGeneration, hostInstanceId } from "./protocol-identity.js";
|
|
18
18
|
import { rpcCommandShapeError } from "./rpc-input-validation.js";
|
|
19
19
|
import { SessionCommandRouter } from "./session-command-router.js";
|
|
20
20
|
import { SessionEventWriter } from "./session-event-writer.js";
|
|
@@ -109,6 +109,7 @@ export function createHostCore(options, writer, capabilities = parseClientCapabi
|
|
|
109
109
|
idleEvictionMs: policy.idleEvictionMs,
|
|
110
110
|
emptyExitMs: policy.emptyExitMs,
|
|
111
111
|
onEmptyExit: idle.onEmptyExit,
|
|
112
|
+
onHandoffParked: idle.onHandoffParked,
|
|
112
113
|
canExitWhenEmpty: idle.canExitWhenEmpty,
|
|
113
114
|
});
|
|
114
115
|
const handle = async (line) => {
|
|
@@ -175,6 +176,7 @@ async function runSocketHost(options, socketPath) {
|
|
|
175
176
|
const writer = new SessionEventWriter(() => { });
|
|
176
177
|
const connections = new Map();
|
|
177
178
|
let draining = false;
|
|
179
|
+
let handoffAnnounced = false;
|
|
178
180
|
const { router, handle } = createHostCore(options, writer, [
|
|
179
181
|
...parseClientCapabilities(envValue("RPC_CLIENT_CAPABILITIES")).filter((capability) => capability !== "rendered_components"),
|
|
180
182
|
// A socket host installs the SIGUSR1 drain below, so it can be handed off to a newer
|
|
@@ -188,7 +190,16 @@ async function runSocketHost(options, socketPath) {
|
|
|
188
190
|
// under it would drop its socket and read as a crash to the supervisor.
|
|
189
191
|
// While DRAINING the opposite is true: the successor generation owns the socket, so a
|
|
190
192
|
// sessionless connection must not hold this host open.
|
|
191
|
-
{
|
|
193
|
+
{
|
|
194
|
+
onEmptyExit: () => void shutdown(0),
|
|
195
|
+
canExitWhenEmpty: () => draining || connections.size === 0,
|
|
196
|
+
onHandoffParked: async (ids) => {
|
|
197
|
+
await Promise.all(ids.map(async (id) => {
|
|
198
|
+
await writer.flushConnection(id);
|
|
199
|
+
connections.get(id)?.close();
|
|
200
|
+
}));
|
|
201
|
+
},
|
|
202
|
+
});
|
|
192
203
|
const observers = startHostObservers(router, writer, {
|
|
193
204
|
// The shape #1893 measured: gigabytes resident with `sessions.total 0`. Say it once, and when
|
|
194
205
|
// this generation no longer owns the endpoint, leave - nobody can reach it to ask.
|
|
@@ -225,6 +236,10 @@ async function runSocketHost(options, socketPath) {
|
|
|
225
236
|
let supervisorPublicIdentity;
|
|
226
237
|
const server = createServer((socket) => {
|
|
227
238
|
const accept = () => {
|
|
239
|
+
if (draining || shuttingDown) {
|
|
240
|
+
socket.destroy();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
228
243
|
const id = `socket-${++nextConnection}`;
|
|
229
244
|
const sink = socketSink(socket);
|
|
230
245
|
writer.registerConnection(id, sink);
|
|
@@ -321,17 +336,35 @@ async function runSocketHost(options, socketPath) {
|
|
|
321
336
|
}
|
|
322
337
|
};
|
|
323
338
|
/**
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
339
|
+
* Announce through the record writer, never inside the supervisor's raw byte proxy. This
|
|
340
|
+
* preserves JSONL framing and FIFO before parking, including when a record spans proxy reads.
|
|
341
|
+
* Active turns/requests keep running; durable wake-source holds resume on reopen.
|
|
327
342
|
*/
|
|
328
343
|
const drainForHandoff = () => {
|
|
329
|
-
if (
|
|
344
|
+
if (shuttingDown)
|
|
345
|
+
return;
|
|
346
|
+
if (draining) {
|
|
347
|
+
if (handoffAnnounced)
|
|
348
|
+
router.beginDrain();
|
|
330
349
|
return;
|
|
350
|
+
}
|
|
331
351
|
draining = true;
|
|
332
352
|
crashCleanupPaths.length = 0;
|
|
333
353
|
hostLog("draining for a generation handoff");
|
|
334
|
-
|
|
354
|
+
void endpointSuperseded()
|
|
355
|
+
.then((superseded) => {
|
|
356
|
+
writer.broadcastHostRecord({
|
|
357
|
+
type: "host_superseded",
|
|
358
|
+
instanceId: hostInstanceId(),
|
|
359
|
+
generation: hostGeneration(process.env),
|
|
360
|
+
successor: superseded ? { socket: supervisorPublicSocketPath ?? socketPath } : null,
|
|
361
|
+
});
|
|
362
|
+
handoffAnnounced = true;
|
|
363
|
+
router.beginDrain();
|
|
364
|
+
})
|
|
365
|
+
.catch((cause) => {
|
|
366
|
+
hostLog(`handoff announcement failed: ${String(cause)}`);
|
|
367
|
+
});
|
|
335
368
|
};
|
|
336
369
|
/**
|
|
337
370
|
* Whether the endpoint this host serves is held by another socket entry now. A supervised host
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-session-host.js","sourceRoot":"","sources":["../../../src/modes/rpc/multi-session-host.ts"],"names":[],"mappings":"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","sourcesContent":["import { access, chmod, mkdir, unlink } from \"node:fs/promises\";\nimport { createConnection, createServer, type Server } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport type { CreateAgentSessionRuntimeFactory } from \"../../core/agent-session-runtime.ts\";\nimport { envValue } from \"../../core/brand.ts\";\nimport { HostMcpRegistry } from \"../../core/extensions/builtin/mcp/host-registry.ts\";\nimport {\n\tflushRawStdout,\n\ttakeOverStdout,\n\twaitForRawStdoutBackpressure,\n\twriteRawStdout,\n} from \"../../core/output-guard.ts\";\nimport type { CliRuntimeConfiguration } from \"../../main.ts\";\nimport { killTrackedDetachedChildren } from \"../../utils/shell.ts\";\nimport { startHostChildReaper } from \"./child-reaper.ts\";\nimport type { RpcConnectionSink } from \"./connection-handler.ts\";\nimport { parseClientCapabilities } from \"./custom-capability.ts\";\nimport { GENERATION_HANDOFF_CAPABILITY } from \"./host-decision.ts\";\nimport { parseIdleExitMs } from \"./host-lifecycle.ts\";\nimport { HostMemorySampler } from \"./host-memory-sampler.ts\";\nimport { createEndpointReservations } from \"./host-reservations.ts\";\nimport { armHostWatchdog, readHostWatchdogConfigFromBrandEnv } from \"./host-watchdog.ts\";\nimport { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from \"./jsonl.ts\";\nimport { LoopLagWatchdog } from \"./loop-lag-watchdog.ts\";\nimport { hostInstanceId } from \"./protocol-identity.ts\";\nimport { rpcCommandShapeError } from \"./rpc-input-validation.ts\";\nimport type { RpcCommand, RpcResponse } from \"./rpc-types.ts\";\nimport { type RpcBindingFactory, SessionCommandRouter } from \"./session-command-router.ts\";\nimport { SessionEventWriter } from \"./session-event-writer.ts\";\nimport { RpcSessionRegistry } from \"./session-registry.ts\";\nimport {\n\tPUBLIC_SOCKET_IDENTITY_FILE,\n\treadSocketIdentityFile,\n\ttype SocketFileIdentity,\n\tshieldSocketDuringClose,\n\tsocketEntryReplaced,\n\tstatSocketIdentity,\n\tunlinkOwnedSocket,\n\twaitForSocketIdentityFile,\n} from \"./socket-ownership.ts\";\nimport { socketSink } from \"./socket-sink.ts\";\nimport {\n\tauthenticateSocket,\n\tensureSocketSecret,\n\tresolveSocketTransportAddress,\n\tSOCKET_SECRET_FILE_ENV,\n\tsocketSecretPath,\n} from \"./socket-transport.ts\";\nimport { WorkerSessionRegistry } from \"./worker-session-registry.ts\";\n\nexport interface MultiSessionHostOptions {\n\tagentDir: string;\n\tcreateRuntime: CreateAgentSessionRuntimeFactory;\n\tworkerConfiguration?: CliRuntimeConfiguration;\n\tcwd: string;\n\tpermissionPreset?: string;\n\tcreationModel?: { provider: string; modelId: string };\n\tinitialThinkingLevel?: string;\n\tlisten?: string;\n\t/** Test seam: defaults to the real shared-session binding. */\n\tcreateBinding?: RpcBindingFactory;\n}\n\n/** Environment override for the idle-session eviction window, in milliseconds. */\nexport const RPC_SESSION_IDLE_EVICTION_MS_ENV = \"SENPI_RPC_SESSION_IDLE_EVICTION_MS\";\n/** Environment override for the empty-host exit window, in milliseconds. */\nexport const RPC_HOST_EMPTY_EXIT_MS_ENV = \"SENPI_RPC_HOST_EMPTY_EXIT_MS\";\n/** Environment override for the graceful close_session teardown window, in milliseconds. */\nexport const RPC_CLOSE_GRACE_MS_ENV = \"SENPI_RPC_CLOSE_GRACE_MS\";\n/** Default idle-eviction window: 30 minutes after a session's last routed command or settled turn. */\nexport const DEFAULT_SESSION_IDLE_EVICTION_MS = 30 * 60_000;\n/** Default empty-host exit: 15 minutes with zero open sessions, matching the supervisor's idle window. */\nexport const DEFAULT_HOST_EMPTY_EXIT_MS = 15 * 60_000;\n/** Win32 named-pipe close can leave libuv's server callback pending after handles are destroyed. */\nconst WINDOWS_SHUTDOWN_HARD_EXIT_MS = 2_000;\n\n/** Explicit occupancy-policy overrides for createHostCore; tests inject clocks and hooks here. */\nexport interface HostIdleOverrides {\n\tnow?: () => number;\n\tidleEvictionMs?: number;\n\temptyExitMs?: number;\n\tcloseGraceMs?: number;\n\t/** Shutdown hook the empty-exit window invokes; hosts pass their exit path. */\n\tonEmptyExit?: () => void;\n\t/** Gate consulted before the empty-exit window advances (connected clients block it). */\n\tcanExitWhenEmpty?: () => boolean;\n}\n\n/**\n * Resolve the host's idle lifecycle policy.\n */\nexport function resolveHostIdlePolicy(\n\tenv: Readonly<Record<string, string | undefined>>,\n\toverrides: HostIdleOverrides = {},\n): { now: () => number; idleEvictionMs: number; emptyExitMs: number } {\n\treturn {\n\t\tnow: overrides.now ?? Date.now,\n\t\tidleEvictionMs:\n\t\t\toverrides.idleEvictionMs ??\n\t\t\tparseIdleExitMs(env[RPC_SESSION_IDLE_EVICTION_MS_ENV]) ??\n\t\t\tDEFAULT_SESSION_IDLE_EVICTION_MS,\n\t\temptyExitMs:\n\t\t\toverrides.emptyExitMs ?? parseIdleExitMs(env[RPC_HOST_EMPTY_EXIT_MS_ENV]) ?? DEFAULT_HOST_EMPTY_EXIT_MS,\n\t};\n}\n\n/**\n * Arm the host's self-observation: event-loop stall detection with per-session\n * attribution, and RSS reporting that tightens idle parking under pressure and, above the\n * refuse watermark, declines NEW worker sessions (#1905). Both run on unref'd timers, and\n * neither aborts or kills anything the host already holds.\n */\nfunction startHostObservers(\n\trouter: SessionCommandRouter,\n\twriter: SessionEventWriter,\n\toptions: { onIdlePressure?: (rssMb: number) => void } = {},\n): { stop: () => void } {\n\tconst loopLag = new LoopLagWatchdog({ emit: (record) => writer.broadcastHostRecord(record) });\n\tconst memory = new HostMemorySampler({\n\t\temit: (record) => writer.broadcastHostRecord(record),\n\t\tsessions: () => router.sessionCount,\n\t\tonPressure: (pressure) => router.setMemoryPressure(pressure),\n\t\tonCritical: (critical, rssMb) => router.setMemoryCritical(critical, rssMb),\n\t\t...(options.onIdlePressure ? { onIdlePressure: options.onIdlePressure } : {}),\n\t});\n\tloopLag.start();\n\tmemory.start();\n\treturn {\n\t\tstop: () => {\n\t\t\tloopLag.stop();\n\t\t\tmemory.stop();\n\t\t},\n\t};\n}\n\ninterface Connection {\n\treadonly id: string;\n\treadonly sink: RpcConnectionSink;\n\treadonly detach: () => void;\n\treadonly close: () => void;\n}\n\n/**\n * Socket agent events are delivered only to connections attached to their\n * session, tagged with its routing sessionId. Content-free session lifecycle\n * events remain visible to every connection. Responses and extension UI\n * requests remain requester-only; foreign observation uses attach-on-open.\n */\nexport async function runMultiSessionHost(options: MultiSessionHostOptions): Promise<never> {\n\tif (options.listen === undefined || options.listen === \"stdio://\") return runStdioHost(options);\n\treturn runSocketHost(options, resolveSocketPath(options.listen, options.agentDir));\n}\n\nexport function createHostCore(\n\toptions: MultiSessionHostOptions,\n\twriter: SessionEventWriter,\n\tcapabilities = parseClientCapabilities(envValue(\"RPC_CLIENT_CAPABILITIES\")),\n\tidle: HostIdleOverrides = {},\n) {\n\tconst policy = resolveHostIdlePolicy(process.env, idle);\n\tconst router = new SessionCommandRouter(\n\t\toptions.workerConfiguration\n\t\t\t? new WorkerSessionRegistry({\n\t\t\t\t\tconfiguration: options.workerConfiguration,\n\t\t\t\t\tnow: policy.now,\n\t\t\t\t\tcloseGraceMs: idle.closeGraceMs ?? parseIdleExitMs(process.env[RPC_CLOSE_GRACE_MS_ENV]) ?? 10_000,\n\t\t\t\t})\n\t\t\t: new RpcSessionRegistry({\n\t\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t\tcreateRuntime: options.createRuntime,\n\t\t\t\t\tmcpRegistry: new HostMcpRegistry(),\n\t\t\t\t\tnow: policy.now,\n\t\t\t\t\tcloseGraceMs: idle.closeGraceMs ?? parseIdleExitMs(process.env[RPC_CLOSE_GRACE_MS_ENV]) ?? 10_000,\n\t\t\t\t\t// Two generations of this daemon can be alive at once during a handoff; the claims\n\t\t\t\t\t// they publish here are what keeps them off one session file.\n\t\t\t\t\tpathReservations: createEndpointReservations({\n\t\t\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t\t\tsocket: listenSocketPath(options),\n\t\t\t\t\t\tinstanceId: hostInstanceId(),\n\t\t\t\t\t\tonFailure: hostLog,\n\t\t\t\t\t}),\n\t\t\t\t}),\n\t\twriter,\n\t\toptions,\n\t\toptions.createBinding,\n\t\t{ capabilities },\n\t\t{\n\t\t\tnow: policy.now,\n\t\t\tidleEvictionMs: policy.idleEvictionMs,\n\t\t\temptyExitMs: policy.emptyExitMs,\n\t\t\tonEmptyExit: idle.onEmptyExit,\n\t\t\tcanExitWhenEmpty: idle.canExitWhenEmpty,\n\t\t},\n\t);\n\tconst handle = async (line: string): Promise<void> => {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(line);\n\t\t} catch (cause) {\n\t\t\tawait writer.enqueueControl(parseError(`Failed to parse command: ${errorMessage(cause)}`));\n\t\t\treturn;\n\t\t}\n\t\tconst shapeError = rpcCommandShapeError(parsed);\n\t\tif (shapeError) {\n\t\t\tawait writer.enqueueControl(parseError(shapeError));\n\t\t\treturn;\n\t\t}\n\t\tconst response = await router.handle(parsed as RpcCommand);\n\t\tif (response) await writer.enqueueControl(response);\n\t};\n\treturn { router, handle };\n}\n\n/** Plain-stdio host with no eagerly-created AgentSessionRuntime. */\nasync function runStdioHost(options: MultiSessionHostOptions): Promise<never> {\n\ttakeOverStdout();\n\tconst sink: RpcConnectionSink = { writeRaw: writeRawStdout, waitForBackpressure: waitForRawStdoutBackpressure };\n\tconst writer = new SessionEventWriter(sink.writeRaw, sink.waitForBackpressure);\n\t// An empty host (no session ever opened, or all closed) must not stay resident\n\t// forever: exit through the normal shutdown path once the window elapses.\n\tconst { router, handle } = createHostCore(options, writer, undefined, {\n\t\tonEmptyExit: () => void shutdown(0),\n\t});\n\tconst observers = startHostObservers(router, writer);\n\tlet shuttingDown = false;\n\tconst shutdown = async (exitCode = 0): Promise<never> => {\n\t\tif (shuttingDown) process.exit(exitCode);\n\t\tshuttingDown = true;\n\t\tobservers.stop();\n\t\tdetach();\n\t\tawait router.dispose();\n\t\tawait writer.flush();\n\t\tawait flushRawStdout();\n\t\tprocess.exit(exitCode);\n\t};\n\tconst onEnd = () => void shutdown();\n\tprocess.stdin.on(\"end\", onEnd);\n\tconst reportInputFailure = (cause: unknown): void => {\n\t\tprocess.stderr.write(`senpi rpc stdio request failed: ${errorMessage(cause)}\\n`);\n\t};\n\tconst detachReader = attachJsonlLineReader(process.stdin, (line) => void handle(line).catch(reportInputFailure), {\n\t\tmaxLineLength: MAX_RPC_LINE_CHARACTERS,\n\t\tonOversizedLine: () => void writer.enqueueControl(parseError(oversizedLineError())).catch(reportInputFailure),\n\t});\n\tconst detach = () => {\n\t\tdetachReader();\n\t\tprocess.stdin.off(\"end\", onEnd);\n\t};\n\tregisterShutdownSignals(shutdown);\n\treturn new Promise(() => {});\n}\n\nasync function runSocketHost(options: MultiSessionHostOptions, socketPath: string): Promise<never> {\n\tawait prepareSocketPath(socketPath);\n\tconst writer = new SessionEventWriter(() => {});\n\tconst connections = new Map<string, Connection>();\n\tlet draining = false;\n\tconst { router, handle } = createHostCore(\n\t\toptions,\n\t\twriter,\n\t\t[\n\t\t\t...parseClientCapabilities(envValue(\"RPC_CLIENT_CAPABILITIES\")).filter(\n\t\t\t\t(capability) => capability !== \"rendered_components\",\n\t\t\t),\n\t\t\t// A socket host installs the SIGUSR1 drain below, so it can be handed off to a newer\n\t\t\t// generation instead of being killed. A host that does not advertise this is never\n\t\t\t// signalled - SIGUSR1 would simply terminate it, sessions and all.\n\t\t\tGENERATION_HANDOFF_CAPABILITY,\n\t\t],\n\t\t// Supervised hosts idle-exit via the supervisor, but a socket host that\n\t\t// outlives its supervisor (or is started bare) still self-exits when empty.\n\t\t// A connected client counts as occupancy even with no session open: exiting\n\t\t// under it would drop its socket and read as a crash to the supervisor.\n\t\t// While DRAINING the opposite is true: the successor generation owns the socket, so a\n\t\t// sessionless connection must not hold this host open.\n\t\t{ onEmptyExit: () => void shutdown(0), canExitWhenEmpty: () => draining || connections.size === 0 },\n\t);\n\tconst observers = startHostObservers(router, writer, {\n\t\t// The shape #1893 measured: gigabytes resident with `sessions.total 0`. Say it once, and when\n\t\t// this generation no longer owns the endpoint, leave - nobody can reach it to ask.\n\t\tonIdlePressure: (rssMb) => {\n\t\t\thostLog(`memory pressure with no sessions: rssMb=${rssMb}`);\n\t\t\tvoid endpointSuperseded().then((superseded) => {\n\t\t\t\tif (superseded) drainForHandoff();\n\t\t\t}, noop);\n\t\t},\n\t});\n\tlet nextConnection = 0;\n\tlet shuttingDown = false;\n\tconst secret =\n\t\tprocess.platform === \"win32\"\n\t\t\t? await ensureSocketSecret(process.env[SOCKET_SECRET_FILE_ENV] ?? socketSecretPath(socketPath))\n\t\t\t: undefined;\n\tconst watchdogConfig = readHostWatchdogConfigFromBrandEnv();\n\t// This host's OWN copy of the crash-path cleanup list. A drain empties it: once a successor\n\t// generation is registered, the daemon state files under those paths describe the successor,\n\t// and a crash of this (already replaced) host must not take them with it.\n\tconst crashCleanupPaths = [...(watchdogConfig?.cleanupPaths ?? [])];\n\t// Crash-path ownership for the supervisor's PUBLIC socket: the supervisor\n\t// records the identity of the entry it bound (inside its private scratch\n\t// directory, which no replacement supervisor writes) right after its listen,\n\t// and this host removes that path only while the identity still matches. A\n\t// blind path removal here would unlink a newer host's freshly published\n\t// entry after a takeover - the startup path already refuses to touch a\n\t// socket owned by a live server; teardown follows the same rule.\n\tconst supervisorPublicSocketPath =\n\t\tprocess.platform === \"win32\" || socketPath.startsWith(\"\\0\") ? undefined : watchdogConfig?.publicSocket;\n\tconst supervisorPublicOwnerFile =\n\t\tsupervisorPublicSocketPath && watchdogConfig?.scratchDir\n\t\t\t? join(watchdogConfig.scratchDir, PUBLIC_SOCKET_IDENTITY_FILE)\n\t\t\t: undefined;\n\tlet boundIdentity: SocketFileIdentity | undefined;\n\tlet supervisorPublicIdentity: SocketFileIdentity | undefined;\n\tconst server = createServer((socket) => {\n\t\tconst accept = (): void => {\n\t\t\tconst id = `socket-${++nextConnection}`;\n\t\t\tconst sink = socketSink(socket);\n\t\t\twriter.registerConnection(id, sink);\n\t\t\tconst detachReader = attachJsonlLineReader(\n\t\t\t\tsocket,\n\t\t\t\t(line) => {\n\t\t\t\t\t// Do not serialize awaited commands: extension_ui_response and other\n\t\t\t\t\t// re-entrant frames must be able to resolve a command already awaiting them.\n\t\t\t\t\tvoid writer\n\t\t\t\t\t\t.withConnection(id, () => handle(line))\n\t\t\t\t\t\t.catch((cause) => {\n\t\t\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} failed: ${errorMessage(cause)}\\n`);\n\t\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmaxLineLength: MAX_RPC_LINE_CHARACTERS,\n\t\t\t\t\tonOversizedLine: () => {\n\t\t\t\t\t\tvoid writer\n\t\t\t\t\t\t\t.withConnection(id, () => writer.enqueueControl(parseError(oversizedLineError())))\n\t\t\t\t\t\t\t.catch((cause) =>\n\t\t\t\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} failed: ${errorMessage(cause)}\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t\tlet detached = false;\n\t\t\tconst detach = () => {\n\t\t\t\tif (detached) return;\n\t\t\t\tdetached = true;\n\t\t\t\tdetachReader();\n\t\t\t\twriter.unregisterConnection(id);\n\t\t\t\tconnections.delete(id);\n\t\t\t\t// A socket that dies without close_session still owns its sessions' attachments\n\t\t\t\t// and path reservations. Release them on the command chain so this runs after any\n\t\t\t\t// in-flight command for this connection settles, otherwise the path stays pinned\n\t\t\t\t// by a runtime whose client is gone and later resumes attach to that orphan.\n\t\t\t\tvoid router.releaseConnection(id).catch((cause) => {\n\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} release failed: ${errorMessage(cause)}\\n`);\n\t\t\t\t});\n\t\t\t};\n\t\t\tconnections.set(id, { id, sink, detach, close: () => socket.destroy() });\n\t\t\tsocket.once(\"close\", detach);\n\t\t\tsocket.once(\"error\", () => detach());\n\t\t};\n\t\tif (secret) authenticateSocket(socket, secret, accept);\n\t\telse accept();\n\t});\n\tserver.on(\"error\", (cause) => {\n\t\tif (!shuttingDown) process.stderr.write(`senpi rpc socket listener failed: ${errorMessage(cause)}\\n`);\n\t});\n\t// Long-lived hosts outlive many session workers, and a terminated worker thread\n\t// takes its children's exit watchers with it (measured: every spawn API leaks\n\t// that way). The reaper claims those abandoned children; it never touches one a\n\t// live thread could still be waiting for.\n\tconst stopChildReaper = await startHostChildReaper(hostLog);\n\tconst shutdown = async (exitCode = 0, watchdogCleanup?: Promise<void>): Promise<never> => {\n\t\tif (shuttingDown) process.exit(exitCode);\n\t\tshuttingDown = true;\n\t\tobservers.stop();\n\t\tstopChildReaper();\n\t\t// On Windows, destroying named-pipe sockets does not always make libuv's\n\t\t// server.close callback fire: connected pipe instances can remain in the\n\t\t// kernel after the JavaScript handles are destroyed. Keep the normal drain\n\t\t// path, but never let that platform-specific close stall orphan the host.\n\t\ttry {\n\t\t\t// Dispose while connections are still registered so `session_closed`\n\t\t\t// `{ reason: \"host_shutdown\" }` reaches clients before the sockets die.\n\t\t\tawait router.dispose();\n\t\t\tawait writer.flush();\n\t\t\tfor (const connection of connections.values()) {\n\t\t\t\tconnection.detach();\n\t\t\t\tconnection.close();\n\t\t\t}\n\t\t\t// libuv unlinks the bound NAME when the listening handle closes - which\n\t\t\t// would delete a newer host's entry renamed over this path. Shield the\n\t\t\t// current entry for the close, then let the ownership check decide.\n\t\t\tawait shieldSocketDuringClose(socketPath, () =>\n\t\t\t\tprocess.platform === \"win32\"\n\t\t\t\t\t? Promise.race([closeServer(server), delay(WINDOWS_SHUTDOWN_HARD_EXIT_MS)])\n\t\t\t\t\t: closeServer(server),\n\t\t\t);\n\t\t\t// Ownership-checked: unlink only the entry THIS process bound. After a\n\t\t\t// takeover renamed a newer host's socket over the same path, the\n\t\t\t// identity no longer matches and the replacement stays published.\n\t\t\tawait unlinkOwnedSocket(socketPath, boundIdentity, hostLog);\n\t\t\tif (supervisorPublicSocketPath) {\n\t\t\t\tawait unlinkOwnedSocket(supervisorPublicSocketPath, supervisorPublicIdentity, hostLog);\n\t\t\t}\n\t\t\tif (watchdogCleanup) await watchdogCleanup;\n\t\t} finally {\n\t\t\t// Explicitly terminate after every shutdown trigger. Windows named-pipe\n\t\t\t// handles are not fully controllable from JS, and an unresolved cleanup\n\t\t\t// must not leave this daemon or its public endpoint alive.\n\t\t\tprocess.exit(exitCode);\n\t\t}\n\t};\n\t/**\n\t * Drain for a generation handoff: keep serving every attached client and every running turn,\n\t * park each session as it settles, and exit once none is left. The successor already owns the\n\t * public socket by the time this signal arrives, so nothing new can reach this host.\n\t */\n\tconst drainForHandoff = (): void => {\n\t\tif (draining || shuttingDown) return;\n\t\tdraining = true;\n\t\tcrashCleanupPaths.length = 0;\n\t\thostLog(\"draining for a generation handoff\");\n\t\trouter.beginDrain();\n\t};\n\t/**\n\t * Whether the endpoint this host serves is held by another socket entry now. A supervised host\n\t * answers for the PUBLIC path its supervisor bound - its own listener is a private hop nobody\n\t * replaces - and a bare host for the path it bound itself.\n\t */\n\tconst endpointSuperseded = (): Promise<boolean> =>\n\t\tsupervisorPublicSocketPath === undefined\n\t\t\t? socketEntryReplaced(socketPath, boundIdentity)\n\t\t\t: socketEntryReplaced(supervisorPublicSocketPath, supervisorPublicIdentity);\n\tregisterShutdownSignals(shutdown);\n\tif (process.platform !== \"win32\") process.on(\"SIGUSR1\", drainForHandoff);\n\t// Arm before listen: a supervisor death during the listen transition must\n\t// still close the child and clean its private endpoint.\n\tconst watchdog =\n\t\twatchdogConfig && supervisorPublicOwnerFile\n\t\t\t? {\n\t\t\t\t\t...watchdogConfig,\n\t\t\t\t\tcleanupPaths: crashCleanupPaths,\n\t\t\t\t\t// The supervisor may die while this host is still waiting for the token\n\t\t\t\t\t// below; read it before the watchdog removes the scratch directory, or the\n\t\t\t\t\t// shutdown's ownership check has nothing to prove with and leaves the\n\t\t\t\t\t// public socket behind.\n\t\t\t\t\tbeforeCleanup: async () => {\n\t\t\t\t\t\tsupervisorPublicIdentity ??= await readSocketIdentityFile(supervisorPublicOwnerFile);\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: watchdogConfig && { ...watchdogConfig, cleanupPaths: crashCleanupPaths };\n\tarmHostWatchdog(watchdog, (reason, cleanup) => {\n\t\tprocess.stderr.write(`senpi rpc host: ${reason}; shutting down\\n`);\n\t\t// Enter shutdown before killing session-owned child processes. The Windows\n\t\t// tree killer is synchronous, while the shutdown fallback must be armed\n\t\t// before any such cleanup can delay the event loop.\n\t\tvoid shutdown(0, cleanup);\n\t\tsetImmediate(killTrackedDetachedChildren);\n\t});\n\tboundIdentity = await listen(server, socketPath, secret);\n\tif (supervisorPublicOwnerFile) {\n\t\t// The supervisor publishes its public-socket token after this internal\n\t\t// listener is ready, so a short bounded wait keeps the lifecycles in step\n\t\t// without delaying unsupervised hosts.\n\t\tsupervisorPublicIdentity = await waitForSocketIdentityFile(supervisorPublicOwnerFile);\n\t}\n\tprocess.stderr.write(`senpi rpc listening on ${formatSocketAddress(socketPath)}\\n`);\n\n\t// Opt-in only: set by the lifecycle supervisor so this host can never outlive\n\t// it, including when the supervisor is SIGKILLed and runs no handler at all.\n\treturn new Promise(() => {});\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction noop(): void {}\n\nfunction parseError(error: string): RpcResponse {\n\treturn { type: \"response\", command: \"parse\", success: false, error };\n}\n\nfunction oversizedLineError(): string {\n\treturn `RPC input line exceeds ${MAX_RPC_LINE_CHARACTERS} characters.`;\n}\n\nfunction errorMessage(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\n/** The endpoint this host listens on, or nothing when it speaks stdio and shares no socket. */\nfunction listenSocketPath(options: MultiSessionHostOptions): string | undefined {\n\tif (options.listen === undefined || options.listen === \"stdio://\") return undefined;\n\treturn resolveSocketPath(options.listen, options.agentDir);\n}\n\nfunction resolveSocketPath(value: string, agentDir: string): string {\n\tif (value === \"unix://\") return join(agentDir, \"rpc\", \"rpc.sock\");\n\tif (value.startsWith(\"unix://\")) {\n\t\tconst path = value.slice(\"unix://\".length);\n\t\tif (path.length === 0) return join(agentDir, \"rpc\", \"rpc.sock\");\n\t\tif (path.startsWith(\"@\") && process.platform === \"linux\") return `\\0${path.slice(1)}`;\n\t\treturn path;\n\t}\n\treturn value;\n}\n\nfunction formatSocketAddress(socketPath: string): string {\n\treturn socketPath.startsWith(\"\\0\") ? `unix://@${socketPath.slice(1)}` : `unix://${socketPath}`;\n}\n\nasync function prepareSocketPath(socketPath: string): Promise<void> {\n\tif (process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return;\n\tawait mkdir(dirname(socketPath), { recursive: true, mode: 0o700 });\n\ttry {\n\t\tawait access(socketPath);\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return;\n\t\tthrow cause;\n\t}\n\tif (await probeSocket(socketPath)) throw new Error(`${socketPath}: address already in use by a live server.`);\n\tawait unlink(socketPath);\n}\n\nfunction probeSocket(socketPath: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst socket = createConnection(resolveSocketTransportAddress(socketPath, process.platform));\n\t\tconst settle = (live: boolean) => {\n\t\t\tsocket.destroy();\n\t\t\tresolve(live);\n\t\t};\n\t\tsocket.once(\"connect\", () => settle(true));\n\t\tsocket.once(\"error\", () => settle(false));\n\t\tsocket.setTimeout(1_000, () => settle(false));\n\t});\n}\n\nfunction listen(server: Server, socketPath: string, secret?: Uint8Array): Promise<SocketFileIdentity | undefined> {\n\treturn new Promise((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(resolveSocketTransportAddress(socketPath, process.platform, secret), async () => {\n\t\t\tserver.off(\"error\", reject);\n\t\t\ttry {\n\t\t\t\tif (process.platform !== \"win32\" && !socketPath.startsWith(\"\\0\")) {\n\t\t\t\t\tawait chmod(socketPath, 0o600);\n\t\t\t\t\t// Record which filesystem entry THIS listener created; shutdown\n\t\t\t\t\t// removes the path only while this identity still matches.\n\t\t\t\t\treturn resolve(await statSocketIdentity(socketPath));\n\t\t\t\t}\n\t\t\t\tresolve(undefined);\n\t\t\t} catch (cause) {\n\t\t\t\treject(cause);\n\t\t\t}\n\t\t});\n\t});\n}\n\nfunction closeServer(server: Server): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tserver.close((cause) => (cause ? reject(cause) : resolve()));\n\t});\n}\n\nfunction hostLog(message: string): void {\n\tprocess.stderr.write(`senpi rpc host: ${message}\\n`);\n}\n\nfunction isNodeErrorCode(cause: unknown, code: string): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === code;\n}\n\nfunction registerShutdownSignals(shutdown: (exitCode?: number) => Promise<never>): void {\n\tfor (const signal of process.platform === \"win32\" ? ([\"SIGTERM\"] as const) : ([\"SIGTERM\", \"SIGHUP\"] as const)) {\n\t\tprocess.on(signal, () => {\n\t\t\tkillTrackedDetachedChildren();\n\t\t\tvoid shutdown(signal === \"SIGHUP\" ? 129 : 143);\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"multi-session-host.js","sourceRoot":"","sources":["../../../src/modes/rpc/multi-session-host.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAe,MAAM,UAAU,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,oDAAoD,CAAC;AACrF,OAAO,EACN,cAAc,EACd,cAAc,EACd,4BAA4B,EAC5B,cAAc,GACd,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,kCAAkC,EAAE,MAAM,oBAAoB,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE,OAAO,EAA0B,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EACN,2BAA2B,EAC3B,sBAAsB,EAEtB,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,yBAAyB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EACN,kBAAkB,EAClB,kBAAkB,EAClB,6BAA6B,EAC7B,sBAAsB,EACtB,gBAAgB,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAerE,kFAAkF;AAClF,MAAM,CAAC,MAAM,gCAAgC,GAAG,oCAAoC,CAAC;AACrF,4EAA4E;AAC5E,MAAM,CAAC,MAAM,0BAA0B,GAAG,8BAA8B,CAAC;AACzE,4FAA4F;AAC5F,MAAM,CAAC,MAAM,sBAAsB,GAAG,0BAA0B,CAAC;AACjE,sGAAsG;AACtG,MAAM,CAAC,MAAM,gCAAgC,GAAG,EAAE,GAAG,MAAM,CAAC;AAC5D,0GAA0G;AAC1G,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,MAAM,CAAC;AACtD,oGAAoG;AACpG,MAAM,6BAA6B,GAAG,KAAK,CAAC;AAe5C;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACpC,GAAiD,EACjD,SAAS,GAAsB,EAAE;IAEjC,OAAO;QACN,GAAG,EAAE,SAAS,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG;QAC9B,cAAc,EACb,SAAS,CAAC,cAAc;YACxB,eAAe,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;YACtD,gCAAgC;QACjC,WAAW,EACV,SAAS,CAAC,WAAW,IAAI,eAAe,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC,IAAI,0BAA0B;KACxG,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,kBAAkB,CAC1B,MAA4B,EAC5B,MAA0B,EAC1B,OAAO,GAAiD,EAAE;IAE1D,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC9F,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC;QACpC,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC;QACpD,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY;QACnC,UAAU,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC;QAC5D,UAAU,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC;QAC1E,GAAG,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7E,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,MAAM,CAAC,KAAK,EAAE,CAAC;IACf,OAAO;QACN,IAAI,EAAE,GAAG,EAAE;YACV,OAAO,CAAC,IAAI,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,EAAE,CAAC;QACf,CAAC;KACD,CAAC;AACH,CAAC;AASD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAgC;IACzE,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,YAAY,CAAC,OAAO,CAAC,CAAC;IAChG,OAAO,aAAa,CAAC,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,cAAc,CAC7B,OAAgC,EAChC,MAA0B,EAC1B,YAAY,GAAG,uBAAuB,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,EAC3E,IAAI,GAAsB,EAAE;IAE5B,MAAM,MAAM,GAAG,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,oBAAoB,CACtC,OAAO,CAAC,mBAAmB;QAC1B,CAAC,CAAC,IAAI,qBAAqB,CAAC;YAC1B,aAAa,EAAE,OAAO,CAAC,mBAAmB;YAC1C,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,IAAI,MAAM;SACjG,CAAC;QACH,CAAC,CAAC,IAAI,kBAAkB,CAAC;YACvB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,aAAa,EAAE,OAAO,CAAC,aAAa;YACpC,WAAW,EAAE,IAAI,eAAe,EAAE;YAClC,GAAG,EAAE,MAAM,CAAC,GAAG;YACf,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,IAAI,MAAM;YACjG,mFAAmF;YACnF,8DAA8D;YAC9D,gBAAgB,EAAE,0BAA0B,CAAC;gBAC5C,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC;gBACjC,UAAU,EAAE,cAAc,EAAE;gBAC5B,SAAS,EAAE,OAAO;aAClB,CAAC;SACF,CAAC,EACJ,MAAM,EACN,OAAO,EACP,OAAO,CAAC,aAAa,EACrB,EAAE,YAAY,EAAE,EAChB;QACC,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;KACvC,CACD,CAAC;IACF,MAAM,MAAM,GAAG,KAAK,EAAE,IAAY,EAAiB,EAAE;QACpD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,4BAA4B,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YAC3F,OAAO;QACR,CAAC;QACD,MAAM,UAAU,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAChD,IAAI,UAAU,EAAE,CAAC;YAChB,MAAM,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;YACpD,OAAO;QACR,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAoB,CAAC,CAAC;QAC3D,IAAI,QAAQ;YAAE,MAAM,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC;IACF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC;AAED,oEAAoE;AACpE,KAAK,UAAU,YAAY,CAAC,OAAgC;IAC3D,cAAc,EAAE,CAAC;IACjB,MAAM,IAAI,GAAsB,EAAE,QAAQ,EAAE,cAAc,EAAE,mBAAmB,EAAE,4BAA4B,EAAE,CAAC;IAChH,MAAM,MAAM,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC/E,+EAA+E;IAC/E,0EAA0E;IAC1E,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE;QACrE,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC;KACnC,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrD,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,MAAM,QAAQ,GAAG,KAAK,EAAE,QAAQ,GAAG,CAAC,EAAkB,EAAE;QACvD,IAAI,YAAY;YAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,YAAY,GAAG,IAAI,CAAC;QACpB,SAAS,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,EAAE,CAAC;QACT,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,cAAc,EAAE,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxB,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC;IACpC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC/B,MAAM,kBAAkB,GAAG,CAAC,KAAc,EAAQ,EAAE;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClF,CAAC,CAAC;IACF,MAAM,YAAY,GAAG,qBAAqB,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,EAAE;QAChH,aAAa,EAAE,uBAAuB;QACtC,eAAe,EAAE,GAAG,EAAE,CAAC,KAAK,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC;KAC7G,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,GAAG,EAAE;QACnB,YAAY,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC,CAAC;IACF,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAClC,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,KAAK,UAAU,aAAa,CAAC,OAAgC,EAAE,UAAkB;IAChF,MAAM,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAsB,CAAC;IAClD,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,cAAc,CACxC,OAAO,EACP,MAAM,EACN;QACC,GAAG,uBAAuB,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,CAAC,MAAM,CACrE,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,KAAK,qBAAqB,CACpD;QACD,qFAAqF;QACrF,mFAAmF;QACnF,mEAAmE;QACnE,6BAA6B;KAC7B;IACD,wEAAwE;IACxE,4EAA4E;IAC5E,4EAA4E;IAC5E,wEAAwE;IACxE,sFAAsF;IACtF,uDAAuD;IACvD;QACC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC;QACnC,gBAAgB,EAAE,GAAG,EAAE,CAAC,QAAQ,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC;QAC1D,eAAe,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;YAC9B,MAAM,OAAO,CAAC,GAAG,CAChB,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBACpB,MAAM,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;gBACjC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC;YAC9B,CAAC,CAAC,CACF,CAAC;QACH,CAAC;KACD,CACD,CAAC;IACF,MAAM,SAAS,GAAG,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE;QACpD,8FAA8F;QAC9F,mFAAmF;QACnF,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE;YACzB,OAAO,CAAC,2CAA2C,KAAK,EAAE,CAAC,CAAC;YAC5D,KAAK,kBAAkB,EAAE,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE;gBAC7C,IAAI,UAAU;oBAAE,eAAe,EAAE,CAAC;YACnC,CAAC,EAAE,IAAI,CAAC,CAAC;QACV,CAAC;KACD,CAAC,CAAC;IACH,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,MAAM,MAAM,GACX,OAAO,CAAC,QAAQ,KAAK,OAAO;QAC3B,CAAC,CAAC,MAAM,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAC/F,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,cAAc,GAAG,kCAAkC,EAAE,CAAC;IAC5D,4FAA4F;IAC5F,6FAA6F;IAC7F,0EAA0E;IAC1E,MAAM,iBAAiB,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC;IACpE,0EAA0E;IAC1E,yEAAyE;IACzE,6EAA6E;IAC7E,2EAA2E;IAC3E,wEAAwE;IACxE,uEAAuE;IACvE,iEAAiE;IACjE,MAAM,0BAA0B,GAC/B,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,EAAE,YAAY,CAAC;IACxG,MAAM,yBAAyB,GAC9B,0BAA0B,IAAI,cAAc,EAAE,UAAU;QACvD,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,2BAA2B,CAAC;QAC9D,CAAC,CAAC,SAAS,CAAC;IACd,IAAI,aAA6C,CAAC;IAClD,IAAI,wBAAwD,CAAC;IAC7D,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,MAAM,EAAE,EAAE;QACtC,MAAM,MAAM,GAAG,GAAS,EAAE;YACzB,IAAI,QAAQ,IAAI,YAAY,EAAE,CAAC;gBAC9B,MAAM,CAAC,OAAO,EAAE,CAAC;gBACjB,OAAO;YACR,CAAC;YACD,MAAM,EAAE,GAAG,UAAU,EAAE,cAAc,EAAE,CAAC;YACxC,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;YAChC,MAAM,CAAC,kBAAkB,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YACpC,MAAM,YAAY,GAAG,qBAAqB,CACzC,MAAM,EACN,CAAC,IAAI,EAAE,EAAE;gBACR,qEAAqE;gBACrE,6EAA6E;gBAC7E,KAAK,MAAM;qBACT,cAAc,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;qBACtC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,YAAY,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACrF,CAAC,CAAC,CAAC;YACL,CAAC,EACD;gBACC,aAAa,EAAE,uBAAuB;gBACtC,eAAe,EAAE,GAAG,EAAE;oBACrB,KAAK,MAAM;yBACT,cAAc,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;yBACjF,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,YAAY,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CACnF,CAAC;gBACJ,CAAC;aACD,CACD,CAAC;YACF,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,MAAM,MAAM,GAAG,GAAG,EAAE;gBACnB,IAAI,QAAQ;oBAAE,OAAO;gBACrB,QAAQ,GAAG,IAAI,CAAC;gBAChB,YAAY,EAAE,CAAC;gBACf,MAAM,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC;gBAChC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACvB,gFAAgF;gBAChF,kFAAkF;gBAClF,iFAAiF;gBACjF,6EAA6E;gBAC7E,KAAK,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;oBACjD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,oBAAoB,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC7F,CAAC,CAAC,CAAC;YACJ,CAAC,CAAC;YACF,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACzE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QACtC,CAAC,CAAC;QACF,IAAI,MAAM;YAAE,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;;YAClD,MAAM,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC5B,IAAI,CAAC,YAAY;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qCAAqC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;IACH,gFAAgF;IAChF,8EAA8E;IAC9E,gFAAgF;IAChF,0CAA0C;IAC1C,MAAM,eAAe,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,KAAK,EAAE,QAAQ,GAAG,CAAC,EAAE,eAA+B,EAAkB,EAAE;QACxF,IAAI,YAAY;YAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,YAAY,GAAG,IAAI,CAAC;QACpB,SAAS,CAAC,IAAI,EAAE,CAAC;QACjB,eAAe,EAAE,CAAC;QAClB,yEAAyE;QACzE,yEAAyE;QACzE,2EAA2E;QAC3E,0EAA0E;QAC1E,IAAI,CAAC;YACJ,qEAAqE;YACrE,wEAAwE;YACxE,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,KAAK,MAAM,UAAU,IAAI,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC;gBAC/C,UAAU,CAAC,MAAM,EAAE,CAAC;gBACpB,UAAU,CAAC,KAAK,EAAE,CAAC;YACpB,CAAC;YACD,wEAAwE;YACxE,uEAAuE;YACvE,oEAAoE;YACpE,MAAM,uBAAuB,CAAC,UAAU,EAAE,GAAG,EAAE,CAC9C,OAAO,CAAC,QAAQ,KAAK,OAAO;gBAC3B,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;gBAC3E,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CACtB,CAAC;YACF,uEAAuE;YACvE,iEAAiE;YACjE,kEAAkE;YAClE,MAAM,iBAAiB,CAAC,UAAU,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;YAC5D,IAAI,0BAA0B,EAAE,CAAC;gBAChC,MAAM,iBAAiB,CAAC,0BAA0B,EAAE,wBAAwB,EAAE,OAAO,CAAC,CAAC;YACxF,CAAC;YACD,IAAI,eAAe;gBAAE,MAAM,eAAe,CAAC;QAC5C,CAAC;gBAAS,CAAC;YACV,wEAAwE;YACxE,wEAAwE;YACxE,2DAA2D;YAC3D,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC;IACF,CAAC,CAAC;IACF;;;;OAIG;IACH,MAAM,eAAe,GAAG,GAAS,EAAE;QAClC,IAAI,YAAY;YAAE,OAAO;QACzB,IAAI,QAAQ,EAAE,CAAC;YACd,IAAI,gBAAgB;gBAAE,MAAM,CAAC,UAAU,EAAE,CAAC;YAC1C,OAAO;QACR,CAAC;QACD,QAAQ,GAAG,IAAI,CAAC;QAChB,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7B,OAAO,CAAC,mCAAmC,CAAC,CAAC;QAC7C,KAAK,kBAAkB,EAAE;aACvB,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE;YACpB,MAAM,CAAC,mBAAmB,CAAC;gBAC1B,IAAI,EAAE,iBAAiB;gBACvB,UAAU,EAAE,cAAc,EAAE;gBAC5B,UAAU,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC;gBACvC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,0BAA0B,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI;aACnF,CAAC,CAAC;YACH,gBAAgB,GAAG,IAAI,CAAC;YACxB,MAAM,CAAC,UAAU,EAAE,CAAC;QACrB,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACzB,OAAO,CAAC,gCAAgC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IACF;;;;OAIG;IACH,MAAM,kBAAkB,GAAG,GAAqB,EAAE,CACjD,0BAA0B,KAAK,SAAS;QACvC,CAAC,CAAC,mBAAmB,CAAC,UAAU,EAAE,aAAa,CAAC;QAChD,CAAC,CAAC,mBAAmB,CAAC,0BAA0B,EAAE,wBAAwB,CAAC,CAAC;IAC9E,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACzE,0EAA0E;IAC1E,wDAAwD;IACxD,MAAM,QAAQ,GACb,cAAc,IAAI,yBAAyB;QAC1C,CAAC,CAAC;YACA,GAAG,cAAc;YACjB,YAAY,EAAE,iBAAiB;YAC/B,wEAAwE;YACxE,2EAA2E;YAC3E,sEAAsE;YACtE,wBAAwB;YACxB,aAAa,EAAE,KAAK,IAAI,EAAE;gBACzB,wBAAwB,KAAK,MAAM,sBAAsB,CAAC,yBAAyB,CAAC,CAAC;YACtF,CAAC;SACD;QACF,CAAC,CAAC,cAAc,IAAI,EAAE,GAAG,cAAc,EAAE,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAC7E,eAAe,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;QAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,MAAM,mBAAmB,CAAC,CAAC;QACnE,2EAA2E;QAC3E,wEAAwE;QACxE,oDAAoD;QACpD,KAAK,QAAQ,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC1B,YAAY,CAAC,2BAA2B,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,aAAa,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;IACzD,IAAI,yBAAyB,EAAE,CAAC;QAC/B,uEAAuE;QACvE,0EAA0E;QAC1E,uCAAuC;QACvC,wBAAwB,GAAG,MAAM,yBAAyB,CAAC,yBAAyB,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,mBAAmB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAEpF,8EAA8E;IAC9E,6EAA6E;IAC7E,OAAO,IAAI,OAAO,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACxB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,IAAI,KAAU,CAAC;AAExB,SAAS,UAAU,CAAC,KAAa;IAChC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACtE,CAAC;AAED,SAAS,kBAAkB;IAC1B,OAAO,0BAA0B,uBAAuB,cAAc,CAAC;AACxE,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IACnC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED,+FAA+F;AAC/F,SAAS,gBAAgB,CAAC,OAAgC;IACzD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IACpF,OAAO,iBAAiB,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa,EAAE,QAAgB;IACzD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;IAClE,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAChE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;YAAE,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACtF,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,SAAS,mBAAmB,CAAC,UAAkB;IAC9C,OAAO,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,UAAU,EAAE,CAAC;AAChG,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,UAAkB;IAClD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO;IACxE,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACnE,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC;YAAE,OAAO;QAC7C,MAAM,KAAK,CAAC;IACb,CAAC;IACD,IAAI,MAAM,WAAW,CAAC,UAAU,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,GAAG,UAAU,4CAA4C,CAAC,CAAC;IAC9G,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,WAAW,CAAC,UAAkB;IACtC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,MAAM,MAAM,GAAG,gBAAgB,CAAC,6BAA6B,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7F,MAAM,MAAM,GAAG,CAAC,IAAa,EAAE,EAAE;YAChC,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CAAC,CAAC;QACf,CAAC,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,MAAc,EAAE,UAAkB,EAAE,MAAmB;IACtE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,MAAM,CAAC,6BAA6B,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,KAAK,IAAI,EAAE;YAC7F,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC5B,IAAI,CAAC;gBACJ,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBAClE,MAAM,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;oBAC/B,gEAAgE;oBAChE,2DAA2D;oBAC3D,OAAO,OAAO,CAAC,MAAM,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAC;gBACtD,CAAC;gBACD,OAAO,CAAC,SAAS,CAAC,CAAC;YACpB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,CAAC,KAAK,CAAC,CAAC;YACf,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,MAAc;IAClC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CAAC,OAAe;IAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,OAAO,IAAI,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,eAAe,CAAC,KAAc,EAAE,IAAY;IACpD,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AACzE,CAAC;AAED,SAAS,uBAAuB,CAAC,QAA+C;IAC/E,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAE,CAAC,SAAS,CAAW,CAAC,CAAC,CAAE,CAAC,SAAS,EAAE,QAAQ,CAAW,EAAE,CAAC;QAC/G,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACvB,2BAA2B,EAAE,CAAC;YAC9B,KAAK,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACJ,CAAC;AACF,CAAC","sourcesContent":["import { access, chmod, mkdir, unlink } from \"node:fs/promises\";\nimport { createConnection, createServer, type Server } from \"node:net\";\nimport { dirname, join } from \"node:path\";\nimport type { CreateAgentSessionRuntimeFactory } from \"../../core/agent-session-runtime.ts\";\nimport { envValue } from \"../../core/brand.ts\";\nimport { HostMcpRegistry } from \"../../core/extensions/builtin/mcp/host-registry.ts\";\nimport {\n\tflushRawStdout,\n\ttakeOverStdout,\n\twaitForRawStdoutBackpressure,\n\twriteRawStdout,\n} from \"../../core/output-guard.ts\";\nimport type { CliRuntimeConfiguration } from \"../../main.ts\";\nimport { killTrackedDetachedChildren } from \"../../utils/shell.ts\";\nimport { startHostChildReaper } from \"./child-reaper.ts\";\nimport type { RpcConnectionSink } from \"./connection-handler.ts\";\nimport { parseClientCapabilities } from \"./custom-capability.ts\";\nimport { GENERATION_HANDOFF_CAPABILITY } from \"./host-decision.ts\";\nimport { parseIdleExitMs } from \"./host-lifecycle.ts\";\nimport { HostMemorySampler } from \"./host-memory-sampler.ts\";\nimport { createEndpointReservations } from \"./host-reservations.ts\";\nimport { armHostWatchdog, readHostWatchdogConfigFromBrandEnv } from \"./host-watchdog.ts\";\nimport { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS } from \"./jsonl.ts\";\nimport { LoopLagWatchdog } from \"./loop-lag-watchdog.ts\";\nimport { hostGeneration, hostInstanceId } from \"./protocol-identity.ts\";\nimport { rpcCommandShapeError } from \"./rpc-input-validation.ts\";\nimport type { RpcCommand, RpcResponse } from \"./rpc-types.ts\";\nimport { type RpcBindingFactory, SessionCommandRouter } from \"./session-command-router.ts\";\nimport { SessionEventWriter } from \"./session-event-writer.ts\";\nimport { RpcSessionRegistry } from \"./session-registry.ts\";\nimport {\n\tPUBLIC_SOCKET_IDENTITY_FILE,\n\treadSocketIdentityFile,\n\ttype SocketFileIdentity,\n\tshieldSocketDuringClose,\n\tsocketEntryReplaced,\n\tstatSocketIdentity,\n\tunlinkOwnedSocket,\n\twaitForSocketIdentityFile,\n} from \"./socket-ownership.ts\";\nimport { socketSink } from \"./socket-sink.ts\";\nimport {\n\tauthenticateSocket,\n\tensureSocketSecret,\n\tresolveSocketTransportAddress,\n\tSOCKET_SECRET_FILE_ENV,\n\tsocketSecretPath,\n} from \"./socket-transport.ts\";\nimport { WorkerSessionRegistry } from \"./worker-session-registry.ts\";\n\nexport interface MultiSessionHostOptions {\n\tagentDir: string;\n\tcreateRuntime: CreateAgentSessionRuntimeFactory;\n\tworkerConfiguration?: CliRuntimeConfiguration;\n\tcwd: string;\n\tpermissionPreset?: string;\n\tcreationModel?: { provider: string; modelId: string };\n\tinitialThinkingLevel?: string;\n\tlisten?: string;\n\t/** Test seam: defaults to the real shared-session binding. */\n\tcreateBinding?: RpcBindingFactory;\n}\n\n/** Environment override for the idle-session eviction window, in milliseconds. */\nexport const RPC_SESSION_IDLE_EVICTION_MS_ENV = \"SENPI_RPC_SESSION_IDLE_EVICTION_MS\";\n/** Environment override for the empty-host exit window, in milliseconds. */\nexport const RPC_HOST_EMPTY_EXIT_MS_ENV = \"SENPI_RPC_HOST_EMPTY_EXIT_MS\";\n/** Environment override for the graceful close_session teardown window, in milliseconds. */\nexport const RPC_CLOSE_GRACE_MS_ENV = \"SENPI_RPC_CLOSE_GRACE_MS\";\n/** Default idle-eviction window: 30 minutes after a session's last routed command or settled turn. */\nexport const DEFAULT_SESSION_IDLE_EVICTION_MS = 30 * 60_000;\n/** Default empty-host exit: 15 minutes with zero open sessions, matching the supervisor's idle window. */\nexport const DEFAULT_HOST_EMPTY_EXIT_MS = 15 * 60_000;\n/** Win32 named-pipe close can leave libuv's server callback pending after handles are destroyed. */\nconst WINDOWS_SHUTDOWN_HARD_EXIT_MS = 2_000;\n\n/** Explicit occupancy-policy overrides for createHostCore; tests inject clocks and hooks here. */\nexport interface HostIdleOverrides {\n\tnow?: () => number;\n\tidleEvictionMs?: number;\n\temptyExitMs?: number;\n\tcloseGraceMs?: number;\n\t/** Shutdown hook the empty-exit window invokes; hosts pass their exit path. */\n\tonEmptyExit?: () => void;\n\tonHandoffParked?: (connections: readonly string[]) => Promise<void>;\n\t/** Gate consulted before the empty-exit window advances (connected clients block it). */\n\tcanExitWhenEmpty?: () => boolean;\n}\n\n/**\n * Resolve the host's idle lifecycle policy.\n */\nexport function resolveHostIdlePolicy(\n\tenv: Readonly<Record<string, string | undefined>>,\n\toverrides: HostIdleOverrides = {},\n): { now: () => number; idleEvictionMs: number; emptyExitMs: number } {\n\treturn {\n\t\tnow: overrides.now ?? Date.now,\n\t\tidleEvictionMs:\n\t\t\toverrides.idleEvictionMs ??\n\t\t\tparseIdleExitMs(env[RPC_SESSION_IDLE_EVICTION_MS_ENV]) ??\n\t\t\tDEFAULT_SESSION_IDLE_EVICTION_MS,\n\t\temptyExitMs:\n\t\t\toverrides.emptyExitMs ?? parseIdleExitMs(env[RPC_HOST_EMPTY_EXIT_MS_ENV]) ?? DEFAULT_HOST_EMPTY_EXIT_MS,\n\t};\n}\n\n/**\n * Arm the host's self-observation: event-loop stall detection with per-session\n * attribution, and RSS reporting that tightens idle parking under pressure and, above the\n * refuse watermark, declines NEW worker sessions (#1905). Both run on unref'd timers, and\n * neither aborts or kills anything the host already holds.\n */\nfunction startHostObservers(\n\trouter: SessionCommandRouter,\n\twriter: SessionEventWriter,\n\toptions: { onIdlePressure?: (rssMb: number) => void } = {},\n): { stop: () => void } {\n\tconst loopLag = new LoopLagWatchdog({ emit: (record) => writer.broadcastHostRecord(record) });\n\tconst memory = new HostMemorySampler({\n\t\temit: (record) => writer.broadcastHostRecord(record),\n\t\tsessions: () => router.sessionCount,\n\t\tonPressure: (pressure) => router.setMemoryPressure(pressure),\n\t\tonCritical: (critical, rssMb) => router.setMemoryCritical(critical, rssMb),\n\t\t...(options.onIdlePressure ? { onIdlePressure: options.onIdlePressure } : {}),\n\t});\n\tloopLag.start();\n\tmemory.start();\n\treturn {\n\t\tstop: () => {\n\t\t\tloopLag.stop();\n\t\t\tmemory.stop();\n\t\t},\n\t};\n}\n\ninterface Connection {\n\treadonly id: string;\n\treadonly sink: RpcConnectionSink;\n\treadonly detach: () => void;\n\treadonly close: () => void;\n}\n\n/**\n * Socket agent events are delivered only to connections attached to their\n * session, tagged with its routing sessionId. Content-free session lifecycle\n * events remain visible to every connection. Responses and extension UI\n * requests remain requester-only; foreign observation uses attach-on-open.\n */\nexport async function runMultiSessionHost(options: MultiSessionHostOptions): Promise<never> {\n\tif (options.listen === undefined || options.listen === \"stdio://\") return runStdioHost(options);\n\treturn runSocketHost(options, resolveSocketPath(options.listen, options.agentDir));\n}\n\nexport function createHostCore(\n\toptions: MultiSessionHostOptions,\n\twriter: SessionEventWriter,\n\tcapabilities = parseClientCapabilities(envValue(\"RPC_CLIENT_CAPABILITIES\")),\n\tidle: HostIdleOverrides = {},\n) {\n\tconst policy = resolveHostIdlePolicy(process.env, idle);\n\tconst router = new SessionCommandRouter(\n\t\toptions.workerConfiguration\n\t\t\t? new WorkerSessionRegistry({\n\t\t\t\t\tconfiguration: options.workerConfiguration,\n\t\t\t\t\tnow: policy.now,\n\t\t\t\t\tcloseGraceMs: idle.closeGraceMs ?? parseIdleExitMs(process.env[RPC_CLOSE_GRACE_MS_ENV]) ?? 10_000,\n\t\t\t\t})\n\t\t\t: new RpcSessionRegistry({\n\t\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t\tcreateRuntime: options.createRuntime,\n\t\t\t\t\tmcpRegistry: new HostMcpRegistry(),\n\t\t\t\t\tnow: policy.now,\n\t\t\t\t\tcloseGraceMs: idle.closeGraceMs ?? parseIdleExitMs(process.env[RPC_CLOSE_GRACE_MS_ENV]) ?? 10_000,\n\t\t\t\t\t// Two generations of this daemon can be alive at once during a handoff; the claims\n\t\t\t\t\t// they publish here are what keeps them off one session file.\n\t\t\t\t\tpathReservations: createEndpointReservations({\n\t\t\t\t\t\tagentDir: options.agentDir,\n\t\t\t\t\t\tsocket: listenSocketPath(options),\n\t\t\t\t\t\tinstanceId: hostInstanceId(),\n\t\t\t\t\t\tonFailure: hostLog,\n\t\t\t\t\t}),\n\t\t\t\t}),\n\t\twriter,\n\t\toptions,\n\t\toptions.createBinding,\n\t\t{ capabilities },\n\t\t{\n\t\t\tnow: policy.now,\n\t\t\tidleEvictionMs: policy.idleEvictionMs,\n\t\t\temptyExitMs: policy.emptyExitMs,\n\t\t\tonEmptyExit: idle.onEmptyExit,\n\t\t\tonHandoffParked: idle.onHandoffParked,\n\t\t\tcanExitWhenEmpty: idle.canExitWhenEmpty,\n\t\t},\n\t);\n\tconst handle = async (line: string): Promise<void> => {\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(line);\n\t\t} catch (cause) {\n\t\t\tawait writer.enqueueControl(parseError(`Failed to parse command: ${errorMessage(cause)}`));\n\t\t\treturn;\n\t\t}\n\t\tconst shapeError = rpcCommandShapeError(parsed);\n\t\tif (shapeError) {\n\t\t\tawait writer.enqueueControl(parseError(shapeError));\n\t\t\treturn;\n\t\t}\n\t\tconst response = await router.handle(parsed as RpcCommand);\n\t\tif (response) await writer.enqueueControl(response);\n\t};\n\treturn { router, handle };\n}\n\n/** Plain-stdio host with no eagerly-created AgentSessionRuntime. */\nasync function runStdioHost(options: MultiSessionHostOptions): Promise<never> {\n\ttakeOverStdout();\n\tconst sink: RpcConnectionSink = { writeRaw: writeRawStdout, waitForBackpressure: waitForRawStdoutBackpressure };\n\tconst writer = new SessionEventWriter(sink.writeRaw, sink.waitForBackpressure);\n\t// An empty host (no session ever opened, or all closed) must not stay resident\n\t// forever: exit through the normal shutdown path once the window elapses.\n\tconst { router, handle } = createHostCore(options, writer, undefined, {\n\t\tonEmptyExit: () => void shutdown(0),\n\t});\n\tconst observers = startHostObservers(router, writer);\n\tlet shuttingDown = false;\n\tconst shutdown = async (exitCode = 0): Promise<never> => {\n\t\tif (shuttingDown) process.exit(exitCode);\n\t\tshuttingDown = true;\n\t\tobservers.stop();\n\t\tdetach();\n\t\tawait router.dispose();\n\t\tawait writer.flush();\n\t\tawait flushRawStdout();\n\t\tprocess.exit(exitCode);\n\t};\n\tconst onEnd = () => void shutdown();\n\tprocess.stdin.on(\"end\", onEnd);\n\tconst reportInputFailure = (cause: unknown): void => {\n\t\tprocess.stderr.write(`senpi rpc stdio request failed: ${errorMessage(cause)}\\n`);\n\t};\n\tconst detachReader = attachJsonlLineReader(process.stdin, (line) => void handle(line).catch(reportInputFailure), {\n\t\tmaxLineLength: MAX_RPC_LINE_CHARACTERS,\n\t\tonOversizedLine: () => void writer.enqueueControl(parseError(oversizedLineError())).catch(reportInputFailure),\n\t});\n\tconst detach = () => {\n\t\tdetachReader();\n\t\tprocess.stdin.off(\"end\", onEnd);\n\t};\n\tregisterShutdownSignals(shutdown);\n\treturn new Promise(() => {});\n}\n\nasync function runSocketHost(options: MultiSessionHostOptions, socketPath: string): Promise<never> {\n\tawait prepareSocketPath(socketPath);\n\tconst writer = new SessionEventWriter(() => {});\n\tconst connections = new Map<string, Connection>();\n\tlet draining = false;\n\tlet handoffAnnounced = false;\n\tconst { router, handle } = createHostCore(\n\t\toptions,\n\t\twriter,\n\t\t[\n\t\t\t...parseClientCapabilities(envValue(\"RPC_CLIENT_CAPABILITIES\")).filter(\n\t\t\t\t(capability) => capability !== \"rendered_components\",\n\t\t\t),\n\t\t\t// A socket host installs the SIGUSR1 drain below, so it can be handed off to a newer\n\t\t\t// generation instead of being killed. A host that does not advertise this is never\n\t\t\t// signalled - SIGUSR1 would simply terminate it, sessions and all.\n\t\t\tGENERATION_HANDOFF_CAPABILITY,\n\t\t],\n\t\t// Supervised hosts idle-exit via the supervisor, but a socket host that\n\t\t// outlives its supervisor (or is started bare) still self-exits when empty.\n\t\t// A connected client counts as occupancy even with no session open: exiting\n\t\t// under it would drop its socket and read as a crash to the supervisor.\n\t\t// While DRAINING the opposite is true: the successor generation owns the socket, so a\n\t\t// sessionless connection must not hold this host open.\n\t\t{\n\t\t\tonEmptyExit: () => void shutdown(0),\n\t\t\tcanExitWhenEmpty: () => draining || connections.size === 0,\n\t\t\tonHandoffParked: async (ids) => {\n\t\t\t\tawait Promise.all(\n\t\t\t\t\tids.map(async (id) => {\n\t\t\t\t\t\tawait writer.flushConnection(id);\n\t\t\t\t\t\tconnections.get(id)?.close();\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t);\n\tconst observers = startHostObservers(router, writer, {\n\t\t// The shape #1893 measured: gigabytes resident with `sessions.total 0`. Say it once, and when\n\t\t// this generation no longer owns the endpoint, leave - nobody can reach it to ask.\n\t\tonIdlePressure: (rssMb) => {\n\t\t\thostLog(`memory pressure with no sessions: rssMb=${rssMb}`);\n\t\t\tvoid endpointSuperseded().then((superseded) => {\n\t\t\t\tif (superseded) drainForHandoff();\n\t\t\t}, noop);\n\t\t},\n\t});\n\tlet nextConnection = 0;\n\tlet shuttingDown = false;\n\tconst secret =\n\t\tprocess.platform === \"win32\"\n\t\t\t? await ensureSocketSecret(process.env[SOCKET_SECRET_FILE_ENV] ?? socketSecretPath(socketPath))\n\t\t\t: undefined;\n\tconst watchdogConfig = readHostWatchdogConfigFromBrandEnv();\n\t// This host's OWN copy of the crash-path cleanup list. A drain empties it: once a successor\n\t// generation is registered, the daemon state files under those paths describe the successor,\n\t// and a crash of this (already replaced) host must not take them with it.\n\tconst crashCleanupPaths = [...(watchdogConfig?.cleanupPaths ?? [])];\n\t// Crash-path ownership for the supervisor's PUBLIC socket: the supervisor\n\t// records the identity of the entry it bound (inside its private scratch\n\t// directory, which no replacement supervisor writes) right after its listen,\n\t// and this host removes that path only while the identity still matches. A\n\t// blind path removal here would unlink a newer host's freshly published\n\t// entry after a takeover - the startup path already refuses to touch a\n\t// socket owned by a live server; teardown follows the same rule.\n\tconst supervisorPublicSocketPath =\n\t\tprocess.platform === \"win32\" || socketPath.startsWith(\"\\0\") ? undefined : watchdogConfig?.publicSocket;\n\tconst supervisorPublicOwnerFile =\n\t\tsupervisorPublicSocketPath && watchdogConfig?.scratchDir\n\t\t\t? join(watchdogConfig.scratchDir, PUBLIC_SOCKET_IDENTITY_FILE)\n\t\t\t: undefined;\n\tlet boundIdentity: SocketFileIdentity | undefined;\n\tlet supervisorPublicIdentity: SocketFileIdentity | undefined;\n\tconst server = createServer((socket) => {\n\t\tconst accept = (): void => {\n\t\t\tif (draining || shuttingDown) {\n\t\t\t\tsocket.destroy();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst id = `socket-${++nextConnection}`;\n\t\t\tconst sink = socketSink(socket);\n\t\t\twriter.registerConnection(id, sink);\n\t\t\tconst detachReader = attachJsonlLineReader(\n\t\t\t\tsocket,\n\t\t\t\t(line) => {\n\t\t\t\t\t// Do not serialize awaited commands: extension_ui_response and other\n\t\t\t\t\t// re-entrant frames must be able to resolve a command already awaiting them.\n\t\t\t\t\tvoid writer\n\t\t\t\t\t\t.withConnection(id, () => handle(line))\n\t\t\t\t\t\t.catch((cause) => {\n\t\t\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} failed: ${errorMessage(cause)}\\n`);\n\t\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tmaxLineLength: MAX_RPC_LINE_CHARACTERS,\n\t\t\t\t\tonOversizedLine: () => {\n\t\t\t\t\t\tvoid writer\n\t\t\t\t\t\t\t.withConnection(id, () => writer.enqueueControl(parseError(oversizedLineError())))\n\t\t\t\t\t\t\t.catch((cause) =>\n\t\t\t\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} failed: ${errorMessage(cause)}\\n`),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t\tlet detached = false;\n\t\t\tconst detach = () => {\n\t\t\t\tif (detached) return;\n\t\t\t\tdetached = true;\n\t\t\t\tdetachReader();\n\t\t\t\twriter.unregisterConnection(id);\n\t\t\t\tconnections.delete(id);\n\t\t\t\t// A socket that dies without close_session still owns its sessions' attachments\n\t\t\t\t// and path reservations. Release them on the command chain so this runs after any\n\t\t\t\t// in-flight command for this connection settles, otherwise the path stays pinned\n\t\t\t\t// by a runtime whose client is gone and later resumes attach to that orphan.\n\t\t\t\tvoid router.releaseConnection(id).catch((cause) => {\n\t\t\t\t\tprocess.stderr.write(`senpi rpc connection ${id} release failed: ${errorMessage(cause)}\\n`);\n\t\t\t\t});\n\t\t\t};\n\t\t\tconnections.set(id, { id, sink, detach, close: () => socket.destroy() });\n\t\t\tsocket.once(\"close\", detach);\n\t\t\tsocket.once(\"error\", () => detach());\n\t\t};\n\t\tif (secret) authenticateSocket(socket, secret, accept);\n\t\telse accept();\n\t});\n\tserver.on(\"error\", (cause) => {\n\t\tif (!shuttingDown) process.stderr.write(`senpi rpc socket listener failed: ${errorMessage(cause)}\\n`);\n\t});\n\t// Long-lived hosts outlive many session workers, and a terminated worker thread\n\t// takes its children's exit watchers with it (measured: every spawn API leaks\n\t// that way). The reaper claims those abandoned children; it never touches one a\n\t// live thread could still be waiting for.\n\tconst stopChildReaper = await startHostChildReaper(hostLog);\n\tconst shutdown = async (exitCode = 0, watchdogCleanup?: Promise<void>): Promise<never> => {\n\t\tif (shuttingDown) process.exit(exitCode);\n\t\tshuttingDown = true;\n\t\tobservers.stop();\n\t\tstopChildReaper();\n\t\t// On Windows, destroying named-pipe sockets does not always make libuv's\n\t\t// server.close callback fire: connected pipe instances can remain in the\n\t\t// kernel after the JavaScript handles are destroyed. Keep the normal drain\n\t\t// path, but never let that platform-specific close stall orphan the host.\n\t\ttry {\n\t\t\t// Dispose while connections are still registered so `session_closed`\n\t\t\t// `{ reason: \"host_shutdown\" }` reaches clients before the sockets die.\n\t\t\tawait router.dispose();\n\t\t\tawait writer.flush();\n\t\t\tfor (const connection of connections.values()) {\n\t\t\t\tconnection.detach();\n\t\t\t\tconnection.close();\n\t\t\t}\n\t\t\t// libuv unlinks the bound NAME when the listening handle closes - which\n\t\t\t// would delete a newer host's entry renamed over this path. Shield the\n\t\t\t// current entry for the close, then let the ownership check decide.\n\t\t\tawait shieldSocketDuringClose(socketPath, () =>\n\t\t\t\tprocess.platform === \"win32\"\n\t\t\t\t\t? Promise.race([closeServer(server), delay(WINDOWS_SHUTDOWN_HARD_EXIT_MS)])\n\t\t\t\t\t: closeServer(server),\n\t\t\t);\n\t\t\t// Ownership-checked: unlink only the entry THIS process bound. After a\n\t\t\t// takeover renamed a newer host's socket over the same path, the\n\t\t\t// identity no longer matches and the replacement stays published.\n\t\t\tawait unlinkOwnedSocket(socketPath, boundIdentity, hostLog);\n\t\t\tif (supervisorPublicSocketPath) {\n\t\t\t\tawait unlinkOwnedSocket(supervisorPublicSocketPath, supervisorPublicIdentity, hostLog);\n\t\t\t}\n\t\t\tif (watchdogCleanup) await watchdogCleanup;\n\t\t} finally {\n\t\t\t// Explicitly terminate after every shutdown trigger. Windows named-pipe\n\t\t\t// handles are not fully controllable from JS, and an unresolved cleanup\n\t\t\t// must not leave this daemon or its public endpoint alive.\n\t\t\tprocess.exit(exitCode);\n\t\t}\n\t};\n\t/**\n\t * Announce through the record writer, never inside the supervisor's raw byte proxy. This\n\t * preserves JSONL framing and FIFO before parking, including when a record spans proxy reads.\n\t * Active turns/requests keep running; durable wake-source holds resume on reopen.\n\t */\n\tconst drainForHandoff = (): void => {\n\t\tif (shuttingDown) return;\n\t\tif (draining) {\n\t\t\tif (handoffAnnounced) router.beginDrain();\n\t\t\treturn;\n\t\t}\n\t\tdraining = true;\n\t\tcrashCleanupPaths.length = 0;\n\t\thostLog(\"draining for a generation handoff\");\n\t\tvoid endpointSuperseded()\n\t\t\t.then((superseded) => {\n\t\t\t\twriter.broadcastHostRecord({\n\t\t\t\t\ttype: \"host_superseded\",\n\t\t\t\t\tinstanceId: hostInstanceId(),\n\t\t\t\t\tgeneration: hostGeneration(process.env),\n\t\t\t\t\tsuccessor: superseded ? { socket: supervisorPublicSocketPath ?? socketPath } : null,\n\t\t\t\t});\n\t\t\t\thandoffAnnounced = true;\n\t\t\t\trouter.beginDrain();\n\t\t\t})\n\t\t\t.catch((cause: unknown) => {\n\t\t\t\thostLog(`handoff announcement failed: ${String(cause)}`);\n\t\t\t});\n\t};\n\t/**\n\t * Whether the endpoint this host serves is held by another socket entry now. A supervised host\n\t * answers for the PUBLIC path its supervisor bound - its own listener is a private hop nobody\n\t * replaces - and a bare host for the path it bound itself.\n\t */\n\tconst endpointSuperseded = (): Promise<boolean> =>\n\t\tsupervisorPublicSocketPath === undefined\n\t\t\t? socketEntryReplaced(socketPath, boundIdentity)\n\t\t\t: socketEntryReplaced(supervisorPublicSocketPath, supervisorPublicIdentity);\n\tregisterShutdownSignals(shutdown);\n\tif (process.platform !== \"win32\") process.on(\"SIGUSR1\", drainForHandoff);\n\t// Arm before listen: a supervisor death during the listen transition must\n\t// still close the child and clean its private endpoint.\n\tconst watchdog =\n\t\twatchdogConfig && supervisorPublicOwnerFile\n\t\t\t? {\n\t\t\t\t\t...watchdogConfig,\n\t\t\t\t\tcleanupPaths: crashCleanupPaths,\n\t\t\t\t\t// The supervisor may die while this host is still waiting for the token\n\t\t\t\t\t// below; read it before the watchdog removes the scratch directory, or the\n\t\t\t\t\t// shutdown's ownership check has nothing to prove with and leaves the\n\t\t\t\t\t// public socket behind.\n\t\t\t\t\tbeforeCleanup: async () => {\n\t\t\t\t\t\tsupervisorPublicIdentity ??= await readSocketIdentityFile(supervisorPublicOwnerFile);\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: watchdogConfig && { ...watchdogConfig, cleanupPaths: crashCleanupPaths };\n\tarmHostWatchdog(watchdog, (reason, cleanup) => {\n\t\tprocess.stderr.write(`senpi rpc host: ${reason}; shutting down\\n`);\n\t\t// Enter shutdown before killing session-owned child processes. The Windows\n\t\t// tree killer is synchronous, while the shutdown fallback must be armed\n\t\t// before any such cleanup can delay the event loop.\n\t\tvoid shutdown(0, cleanup);\n\t\tsetImmediate(killTrackedDetachedChildren);\n\t});\n\tboundIdentity = await listen(server, socketPath, secret);\n\tif (supervisorPublicOwnerFile) {\n\t\t// The supervisor publishes its public-socket token after this internal\n\t\t// listener is ready, so a short bounded wait keeps the lifecycles in step\n\t\t// without delaying unsupervised hosts.\n\t\tsupervisorPublicIdentity = await waitForSocketIdentityFile(supervisorPublicOwnerFile);\n\t}\n\tprocess.stderr.write(`senpi rpc listening on ${formatSocketAddress(socketPath)}\\n`);\n\n\t// Opt-in only: set by the lifecycle supervisor so this host can never outlive\n\t// it, including when the supervisor is SIGKILLed and runs no handler at all.\n\treturn new Promise(() => {});\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction noop(): void {}\n\nfunction parseError(error: string): RpcResponse {\n\treturn { type: \"response\", command: \"parse\", success: false, error };\n}\n\nfunction oversizedLineError(): string {\n\treturn `RPC input line exceeds ${MAX_RPC_LINE_CHARACTERS} characters.`;\n}\n\nfunction errorMessage(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\n/** The endpoint this host listens on, or nothing when it speaks stdio and shares no socket. */\nfunction listenSocketPath(options: MultiSessionHostOptions): string | undefined {\n\tif (options.listen === undefined || options.listen === \"stdio://\") return undefined;\n\treturn resolveSocketPath(options.listen, options.agentDir);\n}\n\nfunction resolveSocketPath(value: string, agentDir: string): string {\n\tif (value === \"unix://\") return join(agentDir, \"rpc\", \"rpc.sock\");\n\tif (value.startsWith(\"unix://\")) {\n\t\tconst path = value.slice(\"unix://\".length);\n\t\tif (path.length === 0) return join(agentDir, \"rpc\", \"rpc.sock\");\n\t\tif (path.startsWith(\"@\") && process.platform === \"linux\") return `\\0${path.slice(1)}`;\n\t\treturn path;\n\t}\n\treturn value;\n}\n\nfunction formatSocketAddress(socketPath: string): string {\n\treturn socketPath.startsWith(\"\\0\") ? `unix://@${socketPath.slice(1)}` : `unix://${socketPath}`;\n}\n\nasync function prepareSocketPath(socketPath: string): Promise<void> {\n\tif (process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return;\n\tawait mkdir(dirname(socketPath), { recursive: true, mode: 0o700 });\n\ttry {\n\t\tawait access(socketPath);\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return;\n\t\tthrow cause;\n\t}\n\tif (await probeSocket(socketPath)) throw new Error(`${socketPath}: address already in use by a live server.`);\n\tawait unlink(socketPath);\n}\n\nfunction probeSocket(socketPath: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst socket = createConnection(resolveSocketTransportAddress(socketPath, process.platform));\n\t\tconst settle = (live: boolean) => {\n\t\t\tsocket.destroy();\n\t\t\tresolve(live);\n\t\t};\n\t\tsocket.once(\"connect\", () => settle(true));\n\t\tsocket.once(\"error\", () => settle(false));\n\t\tsocket.setTimeout(1_000, () => settle(false));\n\t});\n}\n\nfunction listen(server: Server, socketPath: string, secret?: Uint8Array): Promise<SocketFileIdentity | undefined> {\n\treturn new Promise((resolve, reject) => {\n\t\tserver.once(\"error\", reject);\n\t\tserver.listen(resolveSocketTransportAddress(socketPath, process.platform, secret), async () => {\n\t\t\tserver.off(\"error\", reject);\n\t\t\ttry {\n\t\t\t\tif (process.platform !== \"win32\" && !socketPath.startsWith(\"\\0\")) {\n\t\t\t\t\tawait chmod(socketPath, 0o600);\n\t\t\t\t\t// Record which filesystem entry THIS listener created; shutdown\n\t\t\t\t\t// removes the path only while this identity still matches.\n\t\t\t\t\treturn resolve(await statSocketIdentity(socketPath));\n\t\t\t\t}\n\t\t\t\tresolve(undefined);\n\t\t\t} catch (cause) {\n\t\t\t\treject(cause);\n\t\t\t}\n\t\t});\n\t});\n}\n\nfunction closeServer(server: Server): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tserver.close((cause) => (cause ? reject(cause) : resolve()));\n\t});\n}\n\nfunction hostLog(message: string): void {\n\tprocess.stderr.write(`senpi rpc host: ${message}\\n`);\n}\n\nfunction isNodeErrorCode(cause: unknown, code: string): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === code;\n}\n\nfunction registerShutdownSignals(shutdown: (exitCode?: number) => Promise<never>): void {\n\tfor (const signal of process.platform === \"win32\" ? ([\"SIGTERM\"] as const) : ([\"SIGTERM\", \"SIGHUP\"] as const)) {\n\t\tprocess.on(signal, () => {\n\t\t\tkillTrackedDetachedChildren();\n\t\t\tvoid shutdown(signal === \"SIGHUP\" ? 129 : 143);\n\t\t});\n\t}\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The supervisor's always-on observer connection to its host's internal socket.
|
|
3
|
+
*
|
|
4
|
+
* The supervisor cannot see turns directly: it proxies the public socket, so it knows the
|
|
5
|
+
* connection count, and it learns about `agent_start` / `agent_settled` only through this one
|
|
6
|
+
* connection. That is why its health is load-bearing for the idle decision - an unhealthy
|
|
7
|
+
* observer means activity is UNKNOWN rather than zero, and the caller reports unknown as busy
|
|
8
|
+
* so that a broken observer can never kill a host mid-turn.
|
|
9
|
+
*
|
|
10
|
+
* Unknown must not be permanent, which is why the link also records WHEN it went unhealthy:
|
|
11
|
+
* the caller can bound how long unknown is allowed to hold a host open.
|
|
12
|
+
*/
|
|
13
|
+
export interface ObserverSocket {
|
|
14
|
+
/** Close and error both mean the same thing here, so the transport registers one handler. */
|
|
15
|
+
readonly onLost: (handler: () => void) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface ObserverLinkOptions {
|
|
18
|
+
readonly open: () => Promise<ObserverSocket>;
|
|
19
|
+
/** A supervisor already shutting down or draining has nothing left to observe. */
|
|
20
|
+
readonly settled: () => boolean;
|
|
21
|
+
readonly retryDelayMs: number;
|
|
22
|
+
readonly now: () => number;
|
|
23
|
+
readonly setTimer: (run: () => void, ms: number) => {
|
|
24
|
+
readonly cancel: () => void;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export interface ObserverLink {
|
|
28
|
+
readonly open: () => Promise<void>;
|
|
29
|
+
readonly healthy: () => boolean;
|
|
30
|
+
readonly unhealthySince: () => number | undefined;
|
|
31
|
+
readonly stop: () => void;
|
|
32
|
+
}
|
|
33
|
+
export interface UnknownActivityInput {
|
|
34
|
+
readonly healthy: boolean;
|
|
35
|
+
readonly unhealthySince: number | undefined;
|
|
36
|
+
readonly now: number;
|
|
37
|
+
readonly unknownGraceMs: number;
|
|
38
|
+
readonly observedBusy: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The turn count the idle decision should see. A healthy observer's count is the truth. An
|
|
42
|
+
* unhealthy one cannot see turns at all, so its count is UNKNOWN and reported as 1 (busy) so a
|
|
43
|
+
* momentary blip never kills a host mid-turn - but only for `graceMs`; past that, unknown has
|
|
44
|
+
* been allowed to hold the host open for a whole idle window and it stops counting as busy.
|
|
45
|
+
*/
|
|
46
|
+
export declare function activeTurnsForIdleDecision(input: UnknownActivityInput): number;
|
|
47
|
+
export declare function createObserverLink(options: ObserverLinkOptions): ObserverLink;
|
|
48
|
+
//# sourceMappingURL=observer-link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observer-link.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/observer-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,cAAc;IAC9B,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CAC/C;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7C,kFAAkF;IAClF,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;CACpF;AAED,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAI9E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CA0C7E"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The turn count the idle decision should see. A healthy observer's count is the truth. An
|
|
3
|
+
* unhealthy one cannot see turns at all, so its count is UNKNOWN and reported as 1 (busy) so a
|
|
4
|
+
* momentary blip never kills a host mid-turn - but only for `graceMs`; past that, unknown has
|
|
5
|
+
* been allowed to hold the host open for a whole idle window and it stops counting as busy.
|
|
6
|
+
*/
|
|
7
|
+
export function activeTurnsForIdleDecision(input) {
|
|
8
|
+
if (input.healthy)
|
|
9
|
+
return input.observedBusy;
|
|
10
|
+
if (input.unhealthySince === undefined)
|
|
11
|
+
return 1;
|
|
12
|
+
return input.now - input.unhealthySince < input.unknownGraceMs ? 1 : 0;
|
|
13
|
+
}
|
|
14
|
+
export function createObserverLink(options) {
|
|
15
|
+
let current;
|
|
16
|
+
let healthy = false;
|
|
17
|
+
let unhealthySince;
|
|
18
|
+
let retry;
|
|
19
|
+
// Arming is deliberately NOT guarded by socket identity. That guard belongs to `lost`, whose
|
|
20
|
+
// job is to collapse one socket's close AND error into a single transition; reusing it for a
|
|
21
|
+
// failed retry ended the chain, because by then the link owns no socket and the guard is
|
|
22
|
+
// always true. Only settling stops the retries now (#1979).
|
|
23
|
+
const armRetry = () => {
|
|
24
|
+
if (retry !== undefined || options.settled())
|
|
25
|
+
return;
|
|
26
|
+
retry = options.setTimer(() => {
|
|
27
|
+
retry = undefined;
|
|
28
|
+
void open().catch(armRetry);
|
|
29
|
+
}, options.retryDelayMs);
|
|
30
|
+
};
|
|
31
|
+
async function open() {
|
|
32
|
+
const socket = await options.open();
|
|
33
|
+
current = socket;
|
|
34
|
+
healthy = true;
|
|
35
|
+
unhealthySince = undefined;
|
|
36
|
+
socket.onLost(() => {
|
|
37
|
+
if (current !== socket || options.settled())
|
|
38
|
+
return;
|
|
39
|
+
healthy = false;
|
|
40
|
+
unhealthySince ??= options.now();
|
|
41
|
+
current = undefined;
|
|
42
|
+
armRetry();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
open,
|
|
47
|
+
healthy: () => healthy,
|
|
48
|
+
unhealthySince: () => unhealthySince,
|
|
49
|
+
stop: () => {
|
|
50
|
+
retry?.cancel();
|
|
51
|
+
retry = undefined;
|
|
52
|
+
current = undefined;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=observer-link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observer-link.js","sourceRoot":"","sources":["../../../src/modes/rpc/observer-link.ts"],"names":[],"mappings":"AAyCA;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAA2B;IACrE,IAAI,KAAK,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC,YAAY,CAAC;IAC7C,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS;QAAE,OAAO,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC,GAAG,GAAG,KAAK,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAA4B;IAC9D,IAAI,OAAmC,CAAC;IACxC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,cAAkC,CAAC;IACvC,IAAI,KAAkD,CAAC;IAEvD,6FAA6F;IAC7F,6FAA6F;IAC7F,yFAAyF;IACzF,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC3B,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,CAAC,OAAO,EAAE;YAAE,OAAO;QACrD,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE;YAC7B,KAAK,GAAG,SAAS,CAAC;YAClB,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7B,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1B,CAAC,CAAC;IAEF,KAAK,UAAU,IAAI;QAClB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;QACpC,OAAO,GAAG,MAAM,CAAC;QACjB,OAAO,GAAG,IAAI,CAAC;QACf,cAAc,GAAG,SAAS,CAAC;QAC3B,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE;YAClB,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,CAAC,OAAO,EAAE;gBAAE,OAAO;YACpD,OAAO,GAAG,KAAK,CAAC;YAChB,cAAc,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC;YACjC,OAAO,GAAG,SAAS,CAAC;YACpB,QAAQ,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,OAAO;QACN,IAAI;QACJ,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO;QACtB,cAAc,EAAE,GAAG,EAAE,CAAC,cAAc;QACpC,IAAI,EAAE,GAAG,EAAE;YACV,KAAK,EAAE,MAAM,EAAE,CAAC;YAChB,KAAK,GAAG,SAAS,CAAC;YAClB,OAAO,GAAG,SAAS,CAAC;QACrB,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["/**\n * The supervisor's always-on observer connection to its host's internal socket.\n *\n * The supervisor cannot see turns directly: it proxies the public socket, so it knows the\n * connection count, and it learns about `agent_start` / `agent_settled` only through this one\n * connection. That is why its health is load-bearing for the idle decision - an unhealthy\n * observer means activity is UNKNOWN rather than zero, and the caller reports unknown as busy\n * so that a broken observer can never kill a host mid-turn.\n *\n * Unknown must not be permanent, which is why the link also records WHEN it went unhealthy:\n * the caller can bound how long unknown is allowed to hold a host open.\n */\nexport interface ObserverSocket {\n\t/** Close and error both mean the same thing here, so the transport registers one handler. */\n\treadonly onLost: (handler: () => void) => void;\n}\n\nexport interface ObserverLinkOptions {\n\treadonly open: () => Promise<ObserverSocket>;\n\t/** A supervisor already shutting down or draining has nothing left to observe. */\n\treadonly settled: () => boolean;\n\treadonly retryDelayMs: number;\n\treadonly now: () => number;\n\treadonly setTimer: (run: () => void, ms: number) => { readonly cancel: () => void };\n}\n\nexport interface ObserverLink {\n\treadonly open: () => Promise<void>;\n\treadonly healthy: () => boolean;\n\treadonly unhealthySince: () => number | undefined;\n\treadonly stop: () => void;\n}\n\nexport interface UnknownActivityInput {\n\treadonly healthy: boolean;\n\treadonly unhealthySince: number | undefined;\n\treadonly now: number;\n\treadonly unknownGraceMs: number;\n\treadonly observedBusy: number;\n}\n\n/**\n * The turn count the idle decision should see. A healthy observer's count is the truth. An\n * unhealthy one cannot see turns at all, so its count is UNKNOWN and reported as 1 (busy) so a\n * momentary blip never kills a host mid-turn - but only for `graceMs`; past that, unknown has\n * been allowed to hold the host open for a whole idle window and it stops counting as busy.\n */\nexport function activeTurnsForIdleDecision(input: UnknownActivityInput): number {\n\tif (input.healthy) return input.observedBusy;\n\tif (input.unhealthySince === undefined) return 1;\n\treturn input.now - input.unhealthySince < input.unknownGraceMs ? 1 : 0;\n}\n\nexport function createObserverLink(options: ObserverLinkOptions): ObserverLink {\n\tlet current: ObserverSocket | undefined;\n\tlet healthy = false;\n\tlet unhealthySince: number | undefined;\n\tlet retry: { readonly cancel: () => void } | undefined;\n\n\t// Arming is deliberately NOT guarded by socket identity. That guard belongs to `lost`, whose\n\t// job is to collapse one socket's close AND error into a single transition; reusing it for a\n\t// failed retry ended the chain, because by then the link owns no socket and the guard is\n\t// always true. Only settling stops the retries now (#1979).\n\tconst armRetry = (): void => {\n\t\tif (retry !== undefined || options.settled()) return;\n\t\tretry = options.setTimer(() => {\n\t\t\tretry = undefined;\n\t\t\tvoid open().catch(armRetry);\n\t\t}, options.retryDelayMs);\n\t};\n\n\tasync function open(): Promise<void> {\n\t\tconst socket = await options.open();\n\t\tcurrent = socket;\n\t\thealthy = true;\n\t\tunhealthySince = undefined;\n\t\tsocket.onLost(() => {\n\t\t\tif (current !== socket || options.settled()) return;\n\t\t\thealthy = false;\n\t\t\tunhealthySince ??= options.now();\n\t\t\tcurrent = undefined;\n\t\t\tarmRetry();\n\t\t});\n\t}\n\n\treturn {\n\t\topen,\n\t\thealthy: () => healthy,\n\t\tunhealthySince: () => unhealthySince,\n\t\tstop: () => {\n\t\t\tretry?.cancel();\n\t\t\tretry = undefined;\n\t\t\tcurrent = undefined;\n\t\t},\n\t};\n}\n"]}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* | Command | Params | Success data | Notes |
|
|
29
29
|
* | ---------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------- | ----- |
|
|
30
30
|
* | `get_protocol_info` | - | `{ protocolVersion: 1, serverVersion, capabilities, mode: "classic"|"multi", instanceId, generation, engineVersion, engineOrdinal, launch_profile }` | Answered in BOTH modes; side-effect-free capability probe. The identity fields name the host process (`instanceId`), its daemon generation, its build (`engineVersion`, and `engineOrdinal` = `[y, m, d, postRelease, buildEpoch]`) and what it was launched with (`launch_profile { profile_id, core }`). Compatibility and upgrade decisions use `protocolVersion` + `capabilities` + `engineOrdinal`; `serverVersion` is informational and is NEVER compared for compatibility. |
|
|
31
|
-
* | `open_session` | `sessionPath?`, `cwd?`, `provider?`, `modelId?`, `thinkingLevel?`, `permissionPreset?`, `retain_on_disconnect?`, `kind?`, `context?`, `auto_title?` (all optional; paths MUST be absolute) | `{ sessionId, state: RpcSessionState, attached?: boolean }` | `sessionPath` = today's `--session` semantics (open-if-exists else create persisting there); `provider`/`modelId` applied only on create (resume restores the session's model); params form the immutable launch profile (D8). `retain_on_disconnect: true` (default false, host capability `retain_on_disconnect`) makes a dropped connection detach instead of closing the session. `kind: "interactive"|"worker"` (default `interactive`, host capability `session_kind`) sets the session's visibility class. `context` (host capability `session_context`) is an opaque `Record<string,string>` the host never interprets: at most 32 keys matching `^[a-z][a-z0-9_]*$`, each value <= 16 KiB, <= 32 KiB of JSON in total; it reaches that session's extensions as `pi.sessionContext` and nothing else. `auto_title` (host capability `auto_title_per_session`) opts this session into or out of engine-side titling; omitted keeps the host `--auto-title-sessions` / appMode default. |
|
|
31
|
+
* | `open_session` | `sessionPath?`, `cwd?`, `provider?`, `modelId?`, `thinkingLevel?`, `permissionPreset?`, `retain_on_disconnect?`, `kind?`, `context?`, `auto_title?`, `durableSessionId?` (all optional; paths MUST be absolute) | `{ sessionId, state: RpcSessionState, attached?: boolean }` | `sessionPath` = today's `--session` semantics (open-if-exists else create persisting there); `provider`/`modelId` applied only on create (resume restores the session's model); params form the immutable launch profile (D8). `retain_on_disconnect: true` (default false, host capability `retain_on_disconnect`) makes a dropped connection detach instead of closing the session. `kind: "interactive"|"worker"` (default `interactive`, host capability `session_kind`) sets the session's visibility class. `context` (host capability `session_context`) is an opaque `Record<string,string>` the host never interprets: at most 32 keys matching `^[a-z][a-z0-9_]*$`, each value <= 16 KiB, <= 32 KiB of JSON in total; it reaches that session's extensions as `pi.sessionContext` and nothing else. `auto_title` (host capability `auto_title_per_session`) opts this session into or out of engine-side titling; omitted keeps the host `--auto-title-sessions` / appMode default. |
|
|
32
32
|
* | `close_session` | `sessionId` | `{}` | `unknown_session` when the requesting connection never attached to that handle (a close releases the CALLER's attachment). Otherwise aborts active work and awaits teardown for the host grace window, then force-releases; the first closer's response is the LAST record tagged with that handle, while concurrent closes join and receive targeted success responses. |
|
|
33
33
|
* | `list_sessions` | `include_workers?` (default false) | `{ sessions: [{ sessionId, durableSessionId, sessionPath, cwd, name, status, attachments, kind, context? }] }` | Includes `opening`/`closing` entries with their status; `attachments` is the live client count (`0` = retained, detached). `kind: "worker"` rows are omitted unless `include_workers: true`, and `context` is published ONLY on that listing. |
|
|
34
34
|
* | `get_steering_messages` / `get_follow_up_messages` / `clear_queue` | queue read or clear parameters | host-authoritative queue values | Interactive attach clients must not read bootstrap queues. |
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
* exposes both. Clients store both, discard routing handles on child exit, verify
|
|
42
42
|
* only durable ids against cursors.
|
|
43
43
|
*
|
|
44
|
+
* A client that already owns a stable record id for the conversation may CHOOSE the
|
|
45
|
+
* durable id instead of mapping to a host-minted one: `open_session.durableSessionId`
|
|
46
|
+
* (host capability `durable_session_id`) creates the session under that id. It is named
|
|
47
|
+
* `durableSessionId` and not `sessionId` precisely because the routing envelope carries
|
|
48
|
+
* `sessionId` on every established command and this value is not a routing handle. It
|
|
49
|
+
* applies to CREATE only - re-opening an existing `sessionPath` keeps that file's header
|
|
50
|
+
* id, so identity is never rewritten by a resume.
|
|
51
|
+
*
|
|
44
52
|
* Stable error codes (in the response `error` field, machine-matchable):
|
|
45
53
|
* `unknown_session`, `session_closing`, `session_path_in_use`, `missing_session_id`
|
|
46
54
|
* (session-scoped command without `sessionId` in multi mode), `multi_session_disabled`
|
|
@@ -48,7 +56,9 @@
|
|
|
48
56
|
* `open_failed: <detail>`, `invalid_session_context: <detail>` (a `context` past a
|
|
49
57
|
* documented cap), `invalid_session_kind: <detail>` (a `kind` that is neither
|
|
50
58
|
* `interactive` nor `worker`), `invalid_launch_profile: <detail>` (a non-boolean
|
|
51
|
-
* `auto_title`)
|
|
59
|
+
* `auto_title`), `invalid_session_id: <detail>` (a `durableSessionId` that is not a
|
|
60
|
+
* legal session id), `session_id_in_use` (a `durableSessionId` a LIVE session already
|
|
61
|
+
* holds).
|
|
52
62
|
*
|
|
53
63
|
* Tagging: every response/event/`extension_ui_request` belonging to a session
|
|
54
64
|
* carries top-level `sessionId` (routing handle). `get_protocol_info`/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/rpc-mode.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rpc-mode.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/rpc-mode.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2FG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAgB/E,YAAY,EACX,UAAU,EACV,qBAAqB,EACrB,sBAAsB,EACtB,WAAW,EACX,eAAe,GACf,MAAM,gBAAgB,CAAC;AAExB;;;GAGG;AACH,wBAAsB,UAAU,CAAC,WAAW,EAAE,mBAAmB,GAAG,OAAO,CAAC,KAAK,CAAC,CAuGjF"}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* | Command | Params | Success data | Notes |
|
|
29
29
|
* | ---------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------- | ----- |
|
|
30
30
|
* | `get_protocol_info` | - | `{ protocolVersion: 1, serverVersion, capabilities, mode: "classic"|"multi", instanceId, generation, engineVersion, engineOrdinal, launch_profile }` | Answered in BOTH modes; side-effect-free capability probe. The identity fields name the host process (`instanceId`), its daemon generation, its build (`engineVersion`, and `engineOrdinal` = `[y, m, d, postRelease, buildEpoch]`) and what it was launched with (`launch_profile { profile_id, core }`). Compatibility and upgrade decisions use `protocolVersion` + `capabilities` + `engineOrdinal`; `serverVersion` is informational and is NEVER compared for compatibility. |
|
|
31
|
-
* | `open_session` | `sessionPath?`, `cwd?`, `provider?`, `modelId?`, `thinkingLevel?`, `permissionPreset?`, `retain_on_disconnect?`, `kind?`, `context?`, `auto_title?` (all optional; paths MUST be absolute) | `{ sessionId, state: RpcSessionState, attached?: boolean }` | `sessionPath` = today's `--session` semantics (open-if-exists else create persisting there); `provider`/`modelId` applied only on create (resume restores the session's model); params form the immutable launch profile (D8). `retain_on_disconnect: true` (default false, host capability `retain_on_disconnect`) makes a dropped connection detach instead of closing the session. `kind: "interactive"|"worker"` (default `interactive`, host capability `session_kind`) sets the session's visibility class. `context` (host capability `session_context`) is an opaque `Record<string,string>` the host never interprets: at most 32 keys matching `^[a-z][a-z0-9_]*$`, each value <= 16 KiB, <= 32 KiB of JSON in total; it reaches that session's extensions as `pi.sessionContext` and nothing else. `auto_title` (host capability `auto_title_per_session`) opts this session into or out of engine-side titling; omitted keeps the host `--auto-title-sessions` / appMode default. |
|
|
31
|
+
* | `open_session` | `sessionPath?`, `cwd?`, `provider?`, `modelId?`, `thinkingLevel?`, `permissionPreset?`, `retain_on_disconnect?`, `kind?`, `context?`, `auto_title?`, `durableSessionId?` (all optional; paths MUST be absolute) | `{ sessionId, state: RpcSessionState, attached?: boolean }` | `sessionPath` = today's `--session` semantics (open-if-exists else create persisting there); `provider`/`modelId` applied only on create (resume restores the session's model); params form the immutable launch profile (D8). `retain_on_disconnect: true` (default false, host capability `retain_on_disconnect`) makes a dropped connection detach instead of closing the session. `kind: "interactive"|"worker"` (default `interactive`, host capability `session_kind`) sets the session's visibility class. `context` (host capability `session_context`) is an opaque `Record<string,string>` the host never interprets: at most 32 keys matching `^[a-z][a-z0-9_]*$`, each value <= 16 KiB, <= 32 KiB of JSON in total; it reaches that session's extensions as `pi.sessionContext` and nothing else. `auto_title` (host capability `auto_title_per_session`) opts this session into or out of engine-side titling; omitted keeps the host `--auto-title-sessions` / appMode default. |
|
|
32
32
|
* | `close_session` | `sessionId` | `{}` | `unknown_session` when the requesting connection never attached to that handle (a close releases the CALLER's attachment). Otherwise aborts active work and awaits teardown for the host grace window, then force-releases; the first closer's response is the LAST record tagged with that handle, while concurrent closes join and receive targeted success responses. |
|
|
33
33
|
* | `list_sessions` | `include_workers?` (default false) | `{ sessions: [{ sessionId, durableSessionId, sessionPath, cwd, name, status, attachments, kind, context? }] }` | Includes `opening`/`closing` entries with their status; `attachments` is the live client count (`0` = retained, detached). `kind: "worker"` rows are omitted unless `include_workers: true`, and `context` is published ONLY on that listing. |
|
|
34
34
|
* | `get_steering_messages` / `get_follow_up_messages` / `clear_queue` | queue read or clear parameters | host-authoritative queue values | Interactive attach clients must not read bootstrap queues. |
|
|
@@ -41,6 +41,14 @@
|
|
|
41
41
|
* exposes both. Clients store both, discard routing handles on child exit, verify
|
|
42
42
|
* only durable ids against cursors.
|
|
43
43
|
*
|
|
44
|
+
* A client that already owns a stable record id for the conversation may CHOOSE the
|
|
45
|
+
* durable id instead of mapping to a host-minted one: `open_session.durableSessionId`
|
|
46
|
+
* (host capability `durable_session_id`) creates the session under that id. It is named
|
|
47
|
+
* `durableSessionId` and not `sessionId` precisely because the routing envelope carries
|
|
48
|
+
* `sessionId` on every established command and this value is not a routing handle. It
|
|
49
|
+
* applies to CREATE only - re-opening an existing `sessionPath` keeps that file's header
|
|
50
|
+
* id, so identity is never rewritten by a resume.
|
|
51
|
+
*
|
|
44
52
|
* Stable error codes (in the response `error` field, machine-matchable):
|
|
45
53
|
* `unknown_session`, `session_closing`, `session_path_in_use`, `missing_session_id`
|
|
46
54
|
* (session-scoped command without `sessionId` in multi mode), `multi_session_disabled`
|
|
@@ -48,7 +56,9 @@
|
|
|
48
56
|
* `open_failed: <detail>`, `invalid_session_context: <detail>` (a `context` past a
|
|
49
57
|
* documented cap), `invalid_session_kind: <detail>` (a `kind` that is neither
|
|
50
58
|
* `interactive` nor `worker`), `invalid_launch_profile: <detail>` (a non-boolean
|
|
51
|
-
* `auto_title`)
|
|
59
|
+
* `auto_title`), `invalid_session_id: <detail>` (a `durableSessionId` that is not a
|
|
60
|
+
* legal session id), `session_id_in_use` (a `durableSessionId` a LIVE session already
|
|
61
|
+
* holds).
|
|
52
62
|
*
|
|
53
63
|
* Tagging: every response/event/`extension_ui_request` belonging to a session
|
|
54
64
|
* carries top-level `sessionId` (routing handle). `get_protocol_info`/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-mode.js","sourceRoot":"","sources":["../../../src/modes/rpc/rpc-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * RPC mode: Headless operation with JSON stdin/stdout protocol.\n *\n * Used for embedding the agent in other applications.\n * Receives commands as JSON on stdin, outputs events and responses as JSON on stdout.\n *\n * Protocol:\n * - Commands: JSON objects with `type` field, optional `id` for correlation\n * - Responses: JSON objects with `type: \"response\"`, `command`, `success`, and optional `data`/`error`\n * - Events: AgentSessionEvent objects streamed as they occur\n * - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response\n *\n * This is the single-connection stdio host. The RPC command loop, extension-UI\n * bridge, and event subscription live in `connection-handler.ts` so the same\n * logic can also serve a caller-owned RPC transport. This file owns exactly the\n * process-level concerns that a per-connection handler must NOT\n * touch: stdout takeover, stdin wiring, signal handlers, and process exit.\n *\n * ── Multi-session mode (`--multi-session`) ─────────────────────────────────\n *\n * Startup: `senpi --mode rpc --multi-session` → NO default session is constructed\n * (no default `AgentSessionRuntime`, no default extension/watcher load). Classic\n * `senpi --mode rpc` is byte-identical to today. Mode is fixed at process start;\n * there is no runtime transition.\n *\n * D1 normative table (multi-session mode):\n *\n * | Command | Params | Success data | Notes |\n * | ---------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------- | ----- |\n * | `get_protocol_info` | - | `{ protocolVersion: 1, serverVersion, capabilities, mode: \"classic\"|\"multi\", instanceId, generation, engineVersion, engineOrdinal, launch_profile }` | Answered in BOTH modes; side-effect-free capability probe. The identity fields name the host process (`instanceId`), its daemon generation, its build (`engineVersion`, and `engineOrdinal` = `[y, m, d, postRelease, buildEpoch]`) and what it was launched with (`launch_profile { profile_id, core }`). Compatibility and upgrade decisions use `protocolVersion` + `capabilities` + `engineOrdinal`; `serverVersion` is informational and is NEVER compared for compatibility. |\n * | `open_session` | `sessionPath?`, `cwd?`, `provider?`, `modelId?`, `thinkingLevel?`, `permissionPreset?`, `retain_on_disconnect?`, `kind?`, `context?`, `auto_title?` (all optional; paths MUST be absolute) | `{ sessionId, state: RpcSessionState, attached?: boolean }` | `sessionPath` = today's `--session` semantics (open-if-exists else create persisting there); `provider`/`modelId` applied only on create (resume restores the session's model); params form the immutable launch profile (D8). `retain_on_disconnect: true` (default false, host capability `retain_on_disconnect`) makes a dropped connection detach instead of closing the session. `kind: \"interactive\"|\"worker\"` (default `interactive`, host capability `session_kind`) sets the session's visibility class. `context` (host capability `session_context`) is an opaque `Record<string,string>` the host never interprets: at most 32 keys matching `^[a-z][a-z0-9_]*$`, each value <= 16 KiB, <= 32 KiB of JSON in total; it reaches that session's extensions as `pi.sessionContext` and nothing else. `auto_title` (host capability `auto_title_per_session`) opts this session into or out of engine-side titling; omitted keeps the host `--auto-title-sessions` / appMode default. |\n * | `close_session` | `sessionId` | `{}` | `unknown_session` when the requesting connection never attached to that handle (a close releases the CALLER's attachment). Otherwise aborts active work and awaits teardown for the host grace window, then force-releases; the first closer's response is the LAST record tagged with that handle, while concurrent closes join and receive targeted success responses. |\n * | `list_sessions` | `include_workers?` (default false) | `{ sessions: [{ sessionId, durableSessionId, sessionPath, cwd, name, status, attachments, kind, context? }] }` | Includes `opening`/`closing` entries with their status; `attachments` is the live client count (`0` = retained, detached). `kind: \"worker\"` rows are omitted unless `include_workers: true`, and `context` is published ONLY on that listing. |\n * | `get_steering_messages` / `get_follow_up_messages` / `clear_queue` | queue read or clear parameters | host-authoritative queue values | Interactive attach clients must not read bootstrap queues. |\n * | `abort_branch_summary` / `record_bash_result` / `set_label` | narrow mutation payloads | `{}` | Routes interactive runtime mutations to the owning host session. |\n * | every existing command | + `sessionId` (REQUIRED in multi mode) | unchanged | Routed to that session. |\n *\n * Identities (D6): response-level `sessionId` = opaque routing handle, unique per\n * process epoch, ephemeral (dies with the child). `state.sessionId` = durable\n * JSONL session identity (what a resume cursor stores today). `list_sessions`\n * exposes both. Clients store both, discard routing handles on child exit, verify\n * only durable ids against cursors.\n *\n * Stable error codes (in the response `error` field, machine-matchable):\n * `unknown_session`, `session_closing`, `session_path_in_use`, `missing_session_id`\n * (session-scoped command without `sessionId` in multi mode), `multi_session_disabled`\n * (`open_session` in classic mode), `invalid_path` (relative `sessionPath`/`cwd`),\n * `open_failed: <detail>`, `invalid_session_context: <detail>` (a `context` past a\n * documented cap), `invalid_session_kind: <detail>` (a `kind` that is neither\n * `interactive` nor `worker`), `invalid_launch_profile: <detail>` (a non-boolean\n * `auto_title`).\n *\n * Tagging: every response/event/`extension_ui_request` belonging to a session\n * carries top-level `sessionId` (routing handle). `get_protocol_info`/\n * `list_sessions` responses are untagged. Classic mode: nothing tagged\n * (byte-identical).\n *\n * Lifecycle visibility: content-free lifecycle records are broadcast to every\n * connection, EXCEPT `session_closed` and `session_parked` for a `kind: \"worker\"`\n * session, which are delivered only to the connections attached to that session.\n * `session_parked { sessionId, sessionPath }` replaces `session_closed` when the\n * idle sweep parks a session opened with `retain_on_disconnect`: the routing handle\n * is released, the session itself reopens by `sessionPath`.\n *\n * Ordering guarantee (D9): strict FIFO per session; one total stdout order;\n * cross-session order unspecified; fair round-robin between sessions' queued\n * complete records; NO cross-session batch coalescing (per-session event buffers;\n * the process-wide single-array coalescer in `event-output-buffer.ts` must not\n * merge records of different sessions into one write). Starvation freedom is NOT\n * promised (single pipe); a giant tool record delays others — bounded only by\n * record completion.\n *\n * Duplicate/idempotency: duplicate `open_session` while a path reservation is\n * held → `session_path_in_use`; a reservation held by a session whose teardown is\n * already in flight is WAITED OUT on the in-process runtime (bounded by the close\n * grace window) and the path then opens fresh. `close_session` on\n * unknown/already-closed, or from a connection that never attached to that handle →\n * `unknown_session` error. Request `id`s are client-owned; the server echoes them\n * without dedup.\n *\n * Full prose docs: `packages/coding-agent/docs/rpc.md` (Multi-session mode).\n */\n\nimport type { AgentSessionEvent } from \"../../core/agent-session.ts\";\nimport type { AgentSessionRuntime } from \"../../core/agent-session-runtime.ts\";\nimport { envValue } from \"../../core/brand.ts\";\nimport {\n\tflushRawStdout,\n\ttakeOverStdout,\n\twaitForRawStdoutBackpressure,\n\twriteRawStdout,\n} from \"../../core/output-guard.ts\";\nimport { killTrackedDetachedChildren } from \"../../utils/shell.ts\";\nimport { toJsonEvent } from \"../json-event.ts\";\nimport { createRpcConnectionHandler, type RpcConnectionSink } from \"./connection-handler.ts\";\nimport { parseClientCapabilities } from \"./custom-capability.ts\";\nimport { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS, serializeJsonLine } from \"./jsonl.ts\";\nimport { createRpcShutdown } from \"./shutdown.ts\";\n\n// Re-export types for consumers\nexport type {\n\tRpcCommand,\n\tRpcExtensionUIRequest,\n\tRpcExtensionUIResponse,\n\tRpcResponse,\n\tRpcSessionState,\n} from \"./rpc-types.ts\";\n\n/**\n * Run in RPC mode.\n * Listens for JSON commands on stdin, outputs events and responses on stdout.\n */\nexport async function runRpcMode(runtimeHost: AgentSessionRuntime): Promise<never> {\n\ttakeOverStdout();\n\n\tconst sink: RpcConnectionSink = {\n\t\twriteRaw: (chunk) => {\n\t\t\tconst linearized = chunk\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.filter((line) => line.length > 0)\n\t\t\t\t.map((line) => {\n\t\t\t\t\tconst value = JSON.parse(line) as { type?: string };\n\t\t\t\t\treturn JSON.stringify(value.type === \"message_update\" ? toJsonEvent(value as AgentSessionEvent) : value);\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\twriteRawStdout(linearized.length > 0 ? `${linearized}\\n` : \"\");\n\t\t},\n\t\twaitForBackpressure: waitForRawStdoutBackpressure,\n\t};\n\n\t// Client capability flags reach this single-connection stdio host via the\n\t// SENPI_RPC_CLIENT_CAPABILITIES env var (comma-separated). A launcher can set\n\t// it from a client handshake; a plain stdio client that sets nothing gets\n\t// byte-identical default behavior.\n\tconst capabilities = parseClientCapabilities(envValue(\"RPC_CLIENT_CAPABILITIES\"));\n\tconst handler = createRpcConnectionHandler(runtimeHost, sink, { capabilities });\n\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid shutdown(signal === \"SIGHUP\" ? 129 : 143, signal);\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\tlet detachInput = () => {};\n\n\tconst shutdown = createRpcShutdown(\n\t\tasync (signal) => {\n\t\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\t\tcleanup();\n\t\t\t}\n\t\t\tawait handler.dispose();\n\t\t\tdetachInput();\n\t\t\tprocess.stdin.pause();\n\t\t\tif (signal !== \"SIGTERM\") {\n\t\t\t\tawait flushRawStdout();\n\t\t\t}\n\t\t},\n\t\t(exitCode) => process.exit(exitCode),\n\t);\n\n\tconst handleInputLine = async (line: string): Promise<void> => {\n\t\tawait handler.handleInputLine(line);\n\t\tif (handler.isShutdownRequested()) {\n\t\t\tawait shutdown();\n\t\t}\n\t};\n\n\tconst onInputEnd = () => {\n\t\tvoid shutdown();\n\t};\n\tprocess.stdin.on(\"end\", onInputEnd);\n\n\tdetachInput = (() => {\n\t\tconst detachJsonl = attachJsonlLineReader(\n\t\t\tprocess.stdin,\n\t\t\t(line) => {\n\t\t\t\tvoid handleInputLine(line);\n\t\t\t},\n\t\t\t{\n\t\t\t\tmaxLineLength: MAX_RPC_LINE_CHARACTERS,\n\t\t\t\tonOversizedLine: () => {\n\t\t\t\t\twriteRawStdout(\n\t\t\t\t\t\tserializeJsonLine({\n\t\t\t\t\t\t\ttype: \"response\",\n\t\t\t\t\t\t\tcommand: \"parse\",\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: `RPC input line exceeds ${MAX_RPC_LINE_CHARACTERS} characters.`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tvoid waitForRawStdoutBackpressure();\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t\treturn () => {\n\t\t\tdetachJsonl();\n\t\t\tprocess.stdin.off(\"end\", onInputEnd);\n\t\t};\n\t})();\n\n\t// Keep process alive forever\n\treturn new Promise(() => {});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"rpc-mode.js","sourceRoot":"","sources":["../../../src/modes/rpc/rpc-mode.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * RPC mode: Headless operation with JSON stdin/stdout protocol.\n *\n * Used for embedding the agent in other applications.\n * Receives commands as JSON on stdin, outputs events and responses as JSON on stdout.\n *\n * Protocol:\n * - Commands: JSON objects with `type` field, optional `id` for correlation\n * - Responses: JSON objects with `type: \"response\"`, `command`, `success`, and optional `data`/`error`\n * - Events: AgentSessionEvent objects streamed as they occur\n * - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response\n *\n * This is the single-connection stdio host. The RPC command loop, extension-UI\n * bridge, and event subscription live in `connection-handler.ts` so the same\n * logic can also serve a caller-owned RPC transport. This file owns exactly the\n * process-level concerns that a per-connection handler must NOT\n * touch: stdout takeover, stdin wiring, signal handlers, and process exit.\n *\n * ── Multi-session mode (`--multi-session`) ─────────────────────────────────\n *\n * Startup: `senpi --mode rpc --multi-session` → NO default session is constructed\n * (no default `AgentSessionRuntime`, no default extension/watcher load). Classic\n * `senpi --mode rpc` is byte-identical to today. Mode is fixed at process start;\n * there is no runtime transition.\n *\n * D1 normative table (multi-session mode):\n *\n * | Command | Params | Success data | Notes |\n * | ---------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------- | ----- |\n * | `get_protocol_info` | - | `{ protocolVersion: 1, serverVersion, capabilities, mode: \"classic\"|\"multi\", instanceId, generation, engineVersion, engineOrdinal, launch_profile }` | Answered in BOTH modes; side-effect-free capability probe. The identity fields name the host process (`instanceId`), its daemon generation, its build (`engineVersion`, and `engineOrdinal` = `[y, m, d, postRelease, buildEpoch]`) and what it was launched with (`launch_profile { profile_id, core }`). Compatibility and upgrade decisions use `protocolVersion` + `capabilities` + `engineOrdinal`; `serverVersion` is informational and is NEVER compared for compatibility. |\n * | `open_session` | `sessionPath?`, `cwd?`, `provider?`, `modelId?`, `thinkingLevel?`, `permissionPreset?`, `retain_on_disconnect?`, `kind?`, `context?`, `auto_title?`, `durableSessionId?` (all optional; paths MUST be absolute) | `{ sessionId, state: RpcSessionState, attached?: boolean }` | `sessionPath` = today's `--session` semantics (open-if-exists else create persisting there); `provider`/`modelId` applied only on create (resume restores the session's model); params form the immutable launch profile (D8). `retain_on_disconnect: true` (default false, host capability `retain_on_disconnect`) makes a dropped connection detach instead of closing the session. `kind: \"interactive\"|\"worker\"` (default `interactive`, host capability `session_kind`) sets the session's visibility class. `context` (host capability `session_context`) is an opaque `Record<string,string>` the host never interprets: at most 32 keys matching `^[a-z][a-z0-9_]*$`, each value <= 16 KiB, <= 32 KiB of JSON in total; it reaches that session's extensions as `pi.sessionContext` and nothing else. `auto_title` (host capability `auto_title_per_session`) opts this session into or out of engine-side titling; omitted keeps the host `--auto-title-sessions` / appMode default. |\n * | `close_session` | `sessionId` | `{}` | `unknown_session` when the requesting connection never attached to that handle (a close releases the CALLER's attachment). Otherwise aborts active work and awaits teardown for the host grace window, then force-releases; the first closer's response is the LAST record tagged with that handle, while concurrent closes join and receive targeted success responses. |\n * | `list_sessions` | `include_workers?` (default false) | `{ sessions: [{ sessionId, durableSessionId, sessionPath, cwd, name, status, attachments, kind, context? }] }` | Includes `opening`/`closing` entries with their status; `attachments` is the live client count (`0` = retained, detached). `kind: \"worker\"` rows are omitted unless `include_workers: true`, and `context` is published ONLY on that listing. |\n * | `get_steering_messages` / `get_follow_up_messages` / `clear_queue` | queue read or clear parameters | host-authoritative queue values | Interactive attach clients must not read bootstrap queues. |\n * | `abort_branch_summary` / `record_bash_result` / `set_label` | narrow mutation payloads | `{}` | Routes interactive runtime mutations to the owning host session. |\n * | every existing command | + `sessionId` (REQUIRED in multi mode) | unchanged | Routed to that session. |\n *\n * Identities (D6): response-level `sessionId` = opaque routing handle, unique per\n * process epoch, ephemeral (dies with the child). `state.sessionId` = durable\n * JSONL session identity (what a resume cursor stores today). `list_sessions`\n * exposes both. Clients store both, discard routing handles on child exit, verify\n * only durable ids against cursors.\n *\n * A client that already owns a stable record id for the conversation may CHOOSE the\n * durable id instead of mapping to a host-minted one: `open_session.durableSessionId`\n * (host capability `durable_session_id`) creates the session under that id. It is named\n * `durableSessionId` and not `sessionId` precisely because the routing envelope carries\n * `sessionId` on every established command and this value is not a routing handle. It\n * applies to CREATE only - re-opening an existing `sessionPath` keeps that file's header\n * id, so identity is never rewritten by a resume.\n *\n * Stable error codes (in the response `error` field, machine-matchable):\n * `unknown_session`, `session_closing`, `session_path_in_use`, `missing_session_id`\n * (session-scoped command without `sessionId` in multi mode), `multi_session_disabled`\n * (`open_session` in classic mode), `invalid_path` (relative `sessionPath`/`cwd`),\n * `open_failed: <detail>`, `invalid_session_context: <detail>` (a `context` past a\n * documented cap), `invalid_session_kind: <detail>` (a `kind` that is neither\n * `interactive` nor `worker`), `invalid_launch_profile: <detail>` (a non-boolean\n * `auto_title`), `invalid_session_id: <detail>` (a `durableSessionId` that is not a\n * legal session id), `session_id_in_use` (a `durableSessionId` a LIVE session already\n * holds).\n *\n * Tagging: every response/event/`extension_ui_request` belonging to a session\n * carries top-level `sessionId` (routing handle). `get_protocol_info`/\n * `list_sessions` responses are untagged. Classic mode: nothing tagged\n * (byte-identical).\n *\n * Lifecycle visibility: content-free lifecycle records are broadcast to every\n * connection, EXCEPT `session_closed` and `session_parked` for a `kind: \"worker\"`\n * session, which are delivered only to the connections attached to that session.\n * `session_parked { sessionId, sessionPath }` replaces `session_closed` when the\n * idle sweep parks a session opened with `retain_on_disconnect`: the routing handle\n * is released, the session itself reopens by `sessionPath`.\n *\n * Ordering guarantee (D9): strict FIFO per session; one total stdout order;\n * cross-session order unspecified; fair round-robin between sessions' queued\n * complete records; NO cross-session batch coalescing (per-session event buffers;\n * the process-wide single-array coalescer in `event-output-buffer.ts` must not\n * merge records of different sessions into one write). Starvation freedom is NOT\n * promised (single pipe); a giant tool record delays others — bounded only by\n * record completion.\n *\n * Duplicate/idempotency: duplicate `open_session` while a path reservation is\n * held → `session_path_in_use`; a reservation held by a session whose teardown is\n * already in flight is WAITED OUT on the in-process runtime (bounded by the close\n * grace window) and the path then opens fresh. `close_session` on\n * unknown/already-closed, or from a connection that never attached to that handle →\n * `unknown_session` error. Request `id`s are client-owned; the server echoes them\n * without dedup.\n *\n * Full prose docs: `packages/coding-agent/docs/rpc.md` (Multi-session mode).\n */\n\nimport type { AgentSessionEvent } from \"../../core/agent-session.ts\";\nimport type { AgentSessionRuntime } from \"../../core/agent-session-runtime.ts\";\nimport { envValue } from \"../../core/brand.ts\";\nimport {\n\tflushRawStdout,\n\ttakeOverStdout,\n\twaitForRawStdoutBackpressure,\n\twriteRawStdout,\n} from \"../../core/output-guard.ts\";\nimport { killTrackedDetachedChildren } from \"../../utils/shell.ts\";\nimport { toJsonEvent } from \"../json-event.ts\";\nimport { createRpcConnectionHandler, type RpcConnectionSink } from \"./connection-handler.ts\";\nimport { parseClientCapabilities } from \"./custom-capability.ts\";\nimport { attachJsonlLineReader, MAX_RPC_LINE_CHARACTERS, serializeJsonLine } from \"./jsonl.ts\";\nimport { createRpcShutdown } from \"./shutdown.ts\";\n\n// Re-export types for consumers\nexport type {\n\tRpcCommand,\n\tRpcExtensionUIRequest,\n\tRpcExtensionUIResponse,\n\tRpcResponse,\n\tRpcSessionState,\n} from \"./rpc-types.ts\";\n\n/**\n * Run in RPC mode.\n * Listens for JSON commands on stdin, outputs events and responses on stdout.\n */\nexport async function runRpcMode(runtimeHost: AgentSessionRuntime): Promise<never> {\n\ttakeOverStdout();\n\n\tconst sink: RpcConnectionSink = {\n\t\twriteRaw: (chunk) => {\n\t\t\tconst linearized = chunk\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.filter((line) => line.length > 0)\n\t\t\t\t.map((line) => {\n\t\t\t\t\tconst value = JSON.parse(line) as { type?: string };\n\t\t\t\t\treturn JSON.stringify(value.type === \"message_update\" ? toJsonEvent(value as AgentSessionEvent) : value);\n\t\t\t\t})\n\t\t\t\t.join(\"\\n\");\n\t\t\twriteRawStdout(linearized.length > 0 ? `${linearized}\\n` : \"\");\n\t\t},\n\t\twaitForBackpressure: waitForRawStdoutBackpressure,\n\t};\n\n\t// Client capability flags reach this single-connection stdio host via the\n\t// SENPI_RPC_CLIENT_CAPABILITIES env var (comma-separated). A launcher can set\n\t// it from a client handshake; a plain stdio client that sets nothing gets\n\t// byte-identical default behavior.\n\tconst capabilities = parseClientCapabilities(envValue(\"RPC_CLIENT_CAPABILITIES\"));\n\tconst handler = createRpcConnectionHandler(runtimeHost, sink, { capabilities });\n\n\tconst signalCleanupHandlers: Array<() => void> = [];\n\n\tconst registerSignalHandlers = (): void => {\n\t\tconst signals: NodeJS.Signals[] = [\"SIGTERM\"];\n\t\tif (process.platform !== \"win32\") {\n\t\t\tsignals.push(\"SIGHUP\");\n\t\t}\n\n\t\tfor (const signal of signals) {\n\t\t\tconst handler = () => {\n\t\t\t\tkillTrackedDetachedChildren();\n\t\t\t\tvoid shutdown(signal === \"SIGHUP\" ? 129 : 143, signal);\n\t\t\t};\n\t\t\tprocess.on(signal, handler);\n\t\t\tsignalCleanupHandlers.push(() => process.off(signal, handler));\n\t\t}\n\t};\n\n\tregisterSignalHandlers();\n\n\tlet detachInput = () => {};\n\n\tconst shutdown = createRpcShutdown(\n\t\tasync (signal) => {\n\t\t\tfor (const cleanup of signalCleanupHandlers) {\n\t\t\t\tcleanup();\n\t\t\t}\n\t\t\tawait handler.dispose();\n\t\t\tdetachInput();\n\t\t\tprocess.stdin.pause();\n\t\t\tif (signal !== \"SIGTERM\") {\n\t\t\t\tawait flushRawStdout();\n\t\t\t}\n\t\t},\n\t\t(exitCode) => process.exit(exitCode),\n\t);\n\n\tconst handleInputLine = async (line: string): Promise<void> => {\n\t\tawait handler.handleInputLine(line);\n\t\tif (handler.isShutdownRequested()) {\n\t\t\tawait shutdown();\n\t\t}\n\t};\n\n\tconst onInputEnd = () => {\n\t\tvoid shutdown();\n\t};\n\tprocess.stdin.on(\"end\", onInputEnd);\n\n\tdetachInput = (() => {\n\t\tconst detachJsonl = attachJsonlLineReader(\n\t\t\tprocess.stdin,\n\t\t\t(line) => {\n\t\t\t\tvoid handleInputLine(line);\n\t\t\t},\n\t\t\t{\n\t\t\t\tmaxLineLength: MAX_RPC_LINE_CHARACTERS,\n\t\t\t\tonOversizedLine: () => {\n\t\t\t\t\twriteRawStdout(\n\t\t\t\t\t\tserializeJsonLine({\n\t\t\t\t\t\t\ttype: \"response\",\n\t\t\t\t\t\t\tcommand: \"parse\",\n\t\t\t\t\t\t\tsuccess: false,\n\t\t\t\t\t\t\terror: `RPC input line exceeds ${MAX_RPC_LINE_CHARACTERS} characters.`,\n\t\t\t\t\t\t}),\n\t\t\t\t\t);\n\t\t\t\t\tvoid waitForRawStdoutBackpressure();\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t\treturn () => {\n\t\t\tdetachJsonl();\n\t\t\tprocess.stdin.off(\"end\", onInputEnd);\n\t\t};\n\t})();\n\n\t// Keep process alive forever\n\treturn new Promise(() => {});\n}\n"]}
|