@code-yeongyu/senpi 2026.9.5 → 2026.9.7-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +104 -0
- package/dist/core/agent-session.d.ts +60 -5
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +262 -78
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +6 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +6 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/stuck-overflow.d.ts +11 -0
- package/dist/core/compaction/stuck-overflow.d.ts.map +1 -0
- package/dist/core/compaction/stuck-overflow.js +17 -0
- package/dist/core/compaction/stuck-overflow.js.map +1 -0
- package/dist/core/extensions/builtin/cache-keepalive/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cache-keepalive/index.js +3 -21
- package/dist/core/extensions/builtin/cache-keepalive/index.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/prompt-bridge.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/prompt-bridge.js +2 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/prompt-bridge.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js +36 -19
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js +21 -10
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts +4 -17
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js +23 -33
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-pump.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-pump.js +15 -2
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-pump.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js +5 -3
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js +8 -3
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.d.ts +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.js +2 -3
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-claim.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/tools.d.ts +6 -5
- package/dist/core/extensions/builtin/claude-sdk-oauth/tools.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/tools.js +6 -5
- package/dist/core/extensions/builtin/claude-sdk-oauth/tools.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts +8 -0
- package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/circuit-breaker.js +8 -0
- package/dist/core/extensions/builtin/compaction/circuit-breaker.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +4 -0
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +40 -20
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.d.ts +18 -0
- package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.js +20 -0
- package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/index.js +2 -2
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/lane-policy.d.ts +12 -0
- package/dist/core/extensions/builtin/compaction/lane-policy.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/lane-policy.js +47 -18
- package/dist/core/extensions/builtin/compaction/lane-policy.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote-convert.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote-convert.js +2 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-convert.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js +14 -1
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +6 -4
- package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm.d.ts +19 -1
- package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/cache-warm.js +23 -10
- package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
- package/dist/core/extensions/builtin/goal/continuation-recovery.d.ts +1 -0
- package/dist/core/extensions/builtin/goal/continuation-recovery.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/continuation-recovery.js +2 -0
- package/dist/core/extensions/builtin/goal/continuation-recovery.js.map +1 -1
- package/dist/core/extensions/builtin/goal/continuation.d.ts +3 -1
- package/dist/core/extensions/builtin/goal/continuation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/continuation.js +4 -0
- package/dist/core/extensions/builtin/goal/continuation.js.map +1 -1
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +3 -0
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +14 -3
- package/dist/core/extensions/builtin/goal/lifecycle-helpers.js.map +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +7 -3
- package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
- package/dist/core/extensions/builtin/hooks/plugin-manifest.js +2 -2
- package/dist/core/extensions/builtin/hooks/plugin-manifest.js.map +1 -1
- package/dist/core/extensions/builtin/hooks/safety.js +2 -2
- package/dist/core/extensions/builtin/hooks/safety.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/external-dir.js +5 -22
- package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js +5 -7
- package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +2 -2
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +8 -2
- package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +14 -7
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
- package/dist/core/messages.d.ts +7 -0
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js +2 -0
- package/dist/core/messages.js.map +1 -1
- package/dist/core/package-identity.d.ts +12 -0
- package/dist/core/package-identity.d.ts.map +1 -0
- package/dist/core/package-identity.js +57 -0
- package/dist/core/package-identity.js.map +1 -0
- package/dist/core/resource-loader.d.ts +0 -2
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +8 -60
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +5 -4
- package/dist/core/retry-fallback/controller.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +5 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +12 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +29 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +6 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +4 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/bounded-realpath.d.ts +28 -0
- package/dist/core/tools/bounded-realpath.d.ts.map +1 -0
- package/dist/core/tools/bounded-realpath.js +53 -0
- package/dist/core/tools/bounded-realpath.js.map +1 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
- package/dist/core/tools/file-mutation-queue.js +15 -13
- package/dist/core/tools/file-mutation-queue.js.map +1 -1
- package/dist/core/tools/filesystem-policy.d.ts +7 -0
- package/dist/core/tools/filesystem-policy.d.ts.map +1 -1
- package/dist/core/tools/filesystem-policy.js +25 -15
- package/dist/core/tools/filesystem-policy.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +13 -2
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +5 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/tips/catalog/memory-tips.d.ts.map +1 -1
- package/dist/modes/interactive/tips/catalog/memory-tips.js +6 -0
- package/dist/modes/interactive/tips/catalog/memory-tips.js.map +1 -1
- package/dist/modes/interactive/tips/catalog/subagent-tips.d.ts.map +1 -1
- package/dist/modes/interactive/tips/catalog/subagent-tips.js +6 -0
- package/dist/modes/interactive/tips/catalog/subagent-tips.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +31 -5
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/host-watchdog.d.ts +11 -0
- package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
- package/dist/modes/rpc/host-watchdog.js +16 -2
- package/dist/modes/rpc/host-watchdog.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.d.ts +3 -7
- package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +57 -29
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +1 -2
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +0 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +1 -4
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +0 -15
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/dist/modes/rpc/socket-ownership.d.ts +46 -0
- package/dist/modes/rpc/socket-ownership.d.ts.map +1 -0
- package/dist/modes/rpc/socket-ownership.js +178 -0
- package/dist/modes/rpc/socket-ownership.js.map +1 -0
- package/dist/utils/fs-watch.d.ts +17 -0
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js +38 -10
- package/dist/utils/fs-watch.js.map +1 -1
- package/dist/utils/paths.d.ts +33 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +109 -3
- package/dist/utils/paths.js.map +1 -1
- package/docs/compaction-guide.md +2 -2
- package/docs/compaction.md +2 -2
- package/docs/development.md +3 -2
- package/docs/providers.md +1 -1
- package/docs/rpc.md +6 -7
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +74 -0
- package/node_modules/@code-yeongyu/senpi-codemode/README.md +34 -5
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +1 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/reserved.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/runtime-factory.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +7 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/AGENTS.md +7 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/kernel.ts +6 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +73 -129
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/inline-worker.ts +2 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/interrupt-bounds.ts +66 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/run-queue.ts +18 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +59 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +10 -6
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +16 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-shell-capture.d.ts +25 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-shell-capture.js +70 -8
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-slot.ts +106 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-startup.ts +70 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel-contract.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/transport.ts +11 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/kernel.ts +6 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/session-env.ts +56 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/runtime-asset.ts +40 -14
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-contract.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +9 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/output/output-meta.ts +26 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +10 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-execution.ts +9 -11
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-contract.ts +45 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +43 -71
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-notification.ts +4 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-snapshot.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-status.ts +30 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-eval-result.ts +1 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-notification-queue.ts +2 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image.ts +12 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/interrupt-note.ts +29 -15
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +3 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js +17 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/agent-harness.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/branch-summarization.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/reducer.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/reducer.js +6 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/reducer.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.d.ts +2 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.js +5 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/context.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.d.ts +7 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +15 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +4 -4
- package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/azure-openai-responses.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cerebras.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/moonshotai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai-codex.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/faux.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/faux.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +8 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/estimate.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +14 -3
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-types.js","sourceRoot":"","sources":["../../../src/modes/rpc/rpc-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA8JH,iDAAiD;AACjD,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAC;AAC3D,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAC;AAC3D,MAAM,CAAC,MAAM,6BAA6B,GAAG,qBAAqB,CAAC;AACnE,MAAM,CAAC,MAAM,4BAA4B,GAAG,oBAAoB,CAAC;AACjE,MAAM,CAAC,MAAM,gCAAgC,GAAG,wBAAwB,CAAC;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,cAAc,CAAC;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AACnD,MAAM,CAAC,MAAM,2BAA2B,GAAG,mBAAmB,CAAC;AAC/D,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAC","sourcesContent":["/**\n * RPC protocol types for headless operation.\n *\n * Commands are sent as JSON lines on stdin.\n * Responses and events are emitted as JSON lines on stdout.\n */\n\nimport type { AgentMessage, ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, Model, ThinkingSelection } from \"@earendil-works/pi-ai\";\nimport type { AgentAbortSource } from \"../../core/agent-abort-provenance.ts\";\nimport type { PromptDisposition, SessionStats } from \"../../core/agent-session.ts\";\nimport type { BashResult } from \"../../core/bash-executor.ts\";\nimport type { CompactionResult } from \"../../core/compaction/index.ts\";\nimport type { ServiceTier } from \"../../core/extensions/builtin/service-tier.ts\";\nimport type { ContextUsage } from \"../../core/extensions/types.ts\";\nimport type { SessionEntry, SessionTreeNode, UsageTotals } from \"../../core/session-manager.ts\";\nimport type { SourceInfo } from \"../../core/source-info.ts\";\nimport type { RpcSlashCommand } from \"./rpc-command-surface.ts\";\n\nexport type { RpcCommandInvocationEvent } from \"./rpc-command-invocation.ts\";\nexport type { RpcCommandsChangedEvent, RpcSlashCommand } from \"./rpc-command-surface.ts\";\n\n// ============================================================================\n// RPC Commands (stdin)\n// ============================================================================\n\ntype RpcSessionCommand =\n\t// Prompting\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"prompt\";\n\t\t\tmessage: string;\n\t\t\timages?: ImageContent[];\n\t\t\tstreamingBehavior?: \"steer\" | \"followUp\";\n\t\t\tthinkingLevel?: ThinkingLevel;\n\t\t\tsessionTitlePrompt?: string | false;\n\t\t\texpandPromptTemplates?: boolean;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"send_custom_message\";\n\t\t\tcustomType: string;\n\t\t\tcontent: unknown;\n\t\t\tdisplay: boolean;\n\t\t\tdetails?: unknown;\n\t\t\ttriggerTurn?: boolean;\n\t\t\tdeliverAs?: \"steer\" | \"followUp\" | \"nextTurn\";\n\t }\n\t| { id?: string; type: \"append_user_message\"; content: unknown }\n\t| { id?: string; type: \"append_session_entry\"; entry: SessionEntry }\n\t| { id?: string; type: \"steer\"; message: string; images?: ImageContent[]; enqueueOrder?: number }\n\t| { id?: string; type: \"follow_up\"; message: string; images?: ImageContent[]; enqueueOrder?: number }\n\t| { id?: string; type: \"abort\" }\n\t| { id?: string; type: \"abort_compaction\" }\n\t| { id?: string; type: \"reload\" }\n\t| { id?: string; type: \"check_reload_veto\" }\n\t| { id?: string; type: \"clear_queue\"; abortWillFollow?: boolean }\n\t| { id?: string; type: \"get_steering_messages\" }\n\t| { id?: string; type: \"get_follow_up_messages\" }\n\t| { id?: string; type: \"abort_branch_summary\" }\n\t| { id?: string; type: \"new_session\"; parentSession?: string }\n\n\t// State\n\t| { id?: string; type: \"get_state\" }\n\n\t// Model\n\t| { id?: string; type: \"set_model\"; provider: string; modelId: string }\n\t| { id?: string; type: \"set_favorite_models\"; models: RpcSessionModelEntry[] }\n\t| { id?: string; type: \"set_scoped_models\"; models: RpcSessionModelEntry[] }\n\t| { id?: string; type: \"cycle_model\"; direction?: \"forward\" | \"backward\" }\n\t| { id?: string; type: \"get_available_models\" }\n\n\t// Thinking\n\t| { id?: string; type: \"set_thinking_level\"; level: ThinkingLevel; scope?: \"turn\" }\n\t| { id?: string; type: \"cycle_thinking_level\" }\n\t| { id?: string; type: \"get_available_thinking_levels\" }\n\n\t// Fast mode (OpenAI Codex priority service tier)\n\t| { id?: string; type: \"set_fast_mode\"; enabled: boolean }\n\t| { id?: string; type: \"get_fast_mode\" }\n\n\t// Queue modes\n\t| { id?: string; type: \"set_steering_mode\"; mode: \"all\" | \"one-at-a-time\" }\n\t| { id?: string; type: \"set_follow_up_mode\"; mode: \"all\" | \"one-at-a-time\" }\n\n\t// Compaction\n\t| { id?: string; type: \"compact\"; customInstructions?: string }\n\t| { id?: string; type: \"set_auto_compaction\"; enabled: boolean }\n\n\t// Retry\n\t| { id?: string; type: \"set_auto_retry\"; enabled: boolean }\n\t| { id?: string; type: \"abort_retry\" }\n\n\t// Bash\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"bash\";\n\t\t\tcommand: string;\n\t\t\t/** Identifies output chunks to the requesting client. */\n\t\t\tbashId?: string;\n\t\t\texcludeFromContext?: boolean;\n\t\t\texecutionId?: string;\n\t\t\toperations?: Record<string, unknown>;\n\t }\n\t| { id?: string; type: \"record_bash_result\"; command: string; result: BashResult; excludeFromContext?: boolean }\n\t| { id?: string; type: \"abort_bash\" }\n\t| { id?: string; type: \"cleanup_bash_output\"; path: string }\n\t| { id?: string; type: \"set_label\"; entryId: string; label?: string }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"navigate_tree\";\n\t\t\ttargetId: string;\n\t\t\tsummarize?: boolean;\n\t\t\tcustomInstructions?: string;\n\t\t\treplaceInstructions?: boolean;\n\t\t\tlabel?: string;\n\t }\n\n\t// Session\n\t| { id?: string; type: \"get_session_stats\" }\n\t| { id?: string; type: \"export_html\"; outputPath?: string; themeName?: string }\n\t| { id?: string; type: \"export_jsonl\"; outputPath?: string }\n\t| { id?: string; type: \"switch_session\"; sessionPath: string; cwdOverride?: string }\n\t| { id?: string; type: \"fork\"; entryId: string; position?: \"before\" | \"at\" }\n\t| { id?: string; type: \"clone\" }\n\t| { id?: string; type: \"get_fork_messages\" }\n\t| { id?: string; type: \"get_entries\"; since?: string }\n\t| { id?: string; type: \"get_tree\" }\n\t| { id?: string; type: \"get_last_assistant_text\" }\n\t| { id?: string; type: \"set_session_name\"; name: string }\n\t| { id?: string; type: \"import_jsonl\"; inputPath: string; cwdOverride?: string }\n\n\t// Messages\n\t| { id?: string; type: \"get_messages\" }\n\t/**\n\t * Fetch one media block a `media_placeholders` client received as an `image_ref`\n\t * stub. `contentIndex` indexes the toolResult's `content` array and must point at\n\t * an image block; anything else answers `media_not_found`.\n\t */\n\t| { id?: string; type: \"get_media\"; toolCallId: string; contentIndex: number }\n\n\t// Commands and loaded runtime surfaces\n\t| { id?: string; type: \"get_commands\" }\n\t| { id?: string; type: \"get_loaded_surfaces\" }\n\t| { id?: string; type: \"extension_request\"; name: string; data?: unknown }\n\n\t// Auth (task 13) is additive. get_auth_providers, login_api_key and logout\n\t// answer synchronously. login_start responds immediately (flow-started) and\n\t// completion is delivered via auth_login_url / auth_login_end EVENTS, because\n\t// an interactive OAuth round-trip cannot fit the 30s request timeout.\n\t| { id?: string; type: \"get_auth_providers\" }\n\t| { id?: string; type: \"login_start\"; provider: string }\n\t| { id?: string; type: \"login_cancel\"; provider: string }\n\t| { id?: string; type: \"login_api_key\"; provider: string; key: string }\n\t| { id?: string; type: \"logout\"; provider: string }\n\n\t// Provider accounts (task 13) are additive. The desktop consumer contract\n\t// lives in ../omo-desktop-app/packages/contracts/src/rpc.ts and is updated separately.\n\t| { id?: string; type: \"get_provider_accounts\"; provider: string }\n\t| { id?: string; type: \"account_pin\"; provider: string; name: string | null }\n\t| { id?: string; type: \"account_remove\"; provider: string; name: string }\n\t| { id?: string; type: \"set_client_info\"; width: number; capabilities?: string[] };\n\n/** Stable multi-session protocol error codes. */\nexport const RPC_ERROR_UNKNOWN_SESSION = \"unknown_session\";\nexport const RPC_ERROR_SESSION_CLOSING = \"session_closing\";\nexport const RPC_ERROR_SESSION_PATH_IN_USE = \"session_path_in_use\";\nexport const RPC_ERROR_MISSING_SESSION_ID = \"missing_session_id\";\nexport const RPC_ERROR_MULTI_SESSION_DISABLED = \"multi_session_disabled\";\nexport const RPC_ERROR_INVALID_PATH = \"invalid_path\";\nexport const RPC_ERROR_OPEN_FAILED = \"open_failed\";\nexport const RPC_ERROR_TOO_MANY_SESSIONS = \"too_many_sessions\";\nexport const RPC_ERROR_MEDIA_NOT_FOUND = \"media_not_found\";\n\nexport type RpcErrorCode =\n\t| typeof RPC_ERROR_UNKNOWN_SESSION\n\t| typeof RPC_ERROR_SESSION_CLOSING\n\t| typeof RPC_ERROR_SESSION_PATH_IN_USE\n\t| typeof RPC_ERROR_MISSING_SESSION_ID\n\t| typeof RPC_ERROR_MULTI_SESSION_DISABLED\n\t| typeof RPC_ERROR_INVALID_PATH\n\t| typeof RPC_ERROR_OPEN_FAILED\n\t| typeof RPC_ERROR_TOO_MANY_SESSIONS\n\t| typeof RPC_ERROR_MEDIA_NOT_FOUND;\n\n/** Every established command accepts an additive routing envelope. */\nexport type RpcCommand =\n\t| (RpcSessionCommand & { sessionId?: string })\n\t| { id?: string; type: \"get_protocol_info\" }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"open_session\";\n\t\t\tsessionPath?: string;\n\t\t\tcwd?: string;\n\t\t\tprovider?: string;\n\t\t\tmodelId?: string;\n\t\t\tthinkingLevel?: ThinkingLevel;\n\t\t\tpermissionPreset?: string;\n\t }\n\t| { id?: string; type: \"close_session\"; sessionId: string }\n\t| { id?: string; type: \"list_sessions\" };\n\n// ============================================================================\n// Auth provider info (get_auth_providers response)\n// ============================================================================\n\n/** One provider row for the /login and /logout selectors. */\nexport interface RpcAuthProvider {\n\t/** Provider id (e.g. \"anthropic\", \"openai\"). */\n\tid: string;\n\t/** Human-readable display name. */\n\tname: string;\n\t/** How this provider authenticates. */\n\tauthType: \"oauth\" | \"api_key\";\n\t/** Auth status without exposing or refreshing any credential. */\n\tstatus: RpcAuthStatus;\n}\n\n/** Auth status mirror (no credential values), from getProviderAuthStatus. */\nexport interface RpcAuthStatus {\n\tconfigured: boolean;\n\tsource?:\n\t\t| \"stored\"\n\t\t| \"runtime\"\n\t\t| \"environment\"\n\t\t| \"fallback\"\n\t\t| \"models_json_key\"\n\t\t| \"models_json_command\"\n\t\t| \"models_json_headers\"\n\t\t| \"extension_headers\";\n\tlabel?: string;\n}\n\n/** Account-slot metadata safe to send to desktop clients. */\nexport interface RpcProviderAccount {\n\tname: string;\n\tsource: \"login\" | \"import\" | \"env\";\n\tblocked: boolean;\n\tpinned: boolean;\n}\n\n// ============================================================================\n// RPC Slash Command (for get_commands response)\n// ============================================================================\n\n/** One extension module loaded by the session resource loader. */\nexport interface RpcLoadedExtension {\n\tname: string;\n\tpath: string;\n\tsourceInfo: SourceInfo;\n\tenabled: boolean;\n}\n\nexport type RpcMcpServerStatus =\n\t| \"enabled\"\n\t| \"disabled\"\n\t| \"untrusted\"\n\t| \"idle\"\n\t| \"connecting\"\n\t| \"connected\"\n\t| \"degraded\"\n\t| \"suspended\"\n\t| \"needs_auth\"\n\t| \"needs_client_registration\";\n\n/** Runtime MCP server state projected from the session-owned MCP service. */\nexport interface RpcLoadedMcpServer {\n\tname: string;\n\ttoolCount: number;\n\tstatus: RpcMcpServerStatus;\n\tauthStatus: \"unsupported\" | \"notLoggedIn\" | \"bearerToken\" | \"oAuth\";\n}\n\n// ============================================================================\n// RPC State\n// ============================================================================\n\nexport interface RpcSessionModelEntry {\n\tmodel: Model<any>;\n\tthinkingLevel?: ThinkingLevel;\n\tthinkingSelection?: ThinkingSelection;\n\tserviceTier?: ServiceTier;\n}\n\nexport interface RpcSessionState {\n\tmodel?: Model<any>;\n\tthinkingLevel: ThinkingLevel;\n\t/**\n\t * Explicit selector provenance for `thinkingLevel`, absent for SDK-defaulted\n\t * effective levels. An attached client cannot distinguish \"the user chose high\"\n\t * from \"high is simply the effective level\" without it.\n\t */\n\tthinkingSelection?: ThinkingSelection;\n\t/**\n\t * Abort owner of the most recent aborted turn, or the in-flight one while it is\n\t * still settling. Retained after settle: the live session getter is transient, so a\n\t * client that snapshots state after the turn ends would otherwise see nothing and\n\t * fall back to generic wording instead of \"Operation aborted\".\n\t */\n\tlastAbortSource?: AgentAbortSource;\n\t/** Service tier the session resolved for the active model, if any. */\n\tserviceTier?: ServiceTier;\n\t/** True when the active model is served at the priority (\"fast\") tier. */\n\tfastMode: boolean;\n\tisStreaming: boolean;\n\tisCompacting: boolean;\n\tsteeringMode: \"all\" | \"one-at-a-time\";\n\tfollowUpMode: \"all\" | \"one-at-a-time\";\n\tsessionFile?: string;\n\tsessionId: string;\n\tsessionName?: string;\n\tcwd: string;\n\t/** Whether project-scoped settings and resources are trusted by the host. */\n\tprojectTrusted: boolean;\n\t/** Authoritative entries for setup-only sessions whose deferred file does not exist yet. */\n\tentries?: SessionEntry[];\n\tfavoriteModels: RpcSessionModelEntry[];\n\tscopedModels: RpcSessionModelEntry[];\n\tsteering: string[];\n\tfollowUp: string[];\n\tordered: Array<{ text: string; mode: \"steer\" | \"followUp\"; enqueueOrder: number }>;\n\tautoCompactionEnabled: boolean;\n\tmessageCount: number;\n\tpendingMessageCount: number;\n\tusageTotals: UsageTotals;\n\tcontextUsage?: ContextUsage;\n\tretryAttempt: number;\n\tisBashRunning: boolean;\n}\n\n// ============================================================================\n// RPC Responses (stdout)\n// ============================================================================\n\n// Success responses with data\nexport type RpcResponse =\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_protocol_info\";\n\t\t\tsuccess: true;\n\t\t\tdata: {\n\t\t\t\tprotocolVersion: 1;\n\t\t\t\tserverVersion: string;\n\t\t\t\tcapabilities: string[];\n\t\t\t\tmode: \"classic\" | \"multi\";\n\t\t\t};\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"open_session\";\n\t\t\tsuccess: true;\n\t\t\tdata: { sessionId: string; state: RpcSessionState; attached?: boolean };\n\t }\n\t| { id?: string; type: \"response\"; command: \"close_session\"; success: true; data: Record<string, never> }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"list_sessions\";\n\t\t\tsuccess: true;\n\t\t\tdata: {\n\t\t\t\tsessions: Array<{\n\t\t\t\t\tsessionId: string;\n\t\t\t\t\tdurableSessionId?: string;\n\t\t\t\t\tsessionPath?: string;\n\t\t\t\t\tcwd: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\tstatus: \"opening\" | \"open\" | \"closing\" | \"closed\";\n\t\t\t\t}>;\n\t\t\t};\n\t }\n\t// Prompting (async - events follow)\n\t// data.disposition reports how the host disposed the prompt (started/queued/handled)\n\t// so proxied optimistic-echo contracts resolve exactly like the local path; older\n\t// hosts omit it and clients must degrade to canonical-only rendering.\n\t| { id?: string; type: \"response\"; command: \"prompt\"; success: true; data?: { disposition?: PromptDisposition } }\n\t| { id?: string; type: \"response\"; command: \"send_custom_message\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"append_user_message\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"append_session_entry\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"steer\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"follow_up\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"abort\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"abort_compaction\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"reload\"; success: true; data: { cancelled: boolean; reason?: string } }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"check_reload_veto\";\n\t\t\tsuccess: true;\n\t\t\tdata: { cancelled: boolean; reason?: string };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"clear_queue\";\n\t\t\tsuccess: true;\n\t\t\tdata: {\n\t\t\t\tsteering: string[];\n\t\t\t\tfollowUp: string[];\n\t\t\t\tordered: Array<{ text: string; mode: \"steer\" | \"followUp\"; enqueueOrder: number }>;\n\t\t\t};\n\t }\n\t| { id?: string; type: \"response\"; command: \"new_session\"; success: true; data: { cancelled: boolean } }\n\n\t// State\n\t| { id?: string; type: \"response\"; command: \"get_state\"; success: true; data: RpcSessionState }\n\n\t// Model\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"set_model\";\n\t\t\tsuccess: true;\n\t\t\tdata: Model<any> & { systemPromptName?: string };\n\t }\n\t| { id?: string; type: \"response\"; command: \"set_favorite_models\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"set_scoped_models\"; success: true }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"cycle_model\";\n\t\t\tsuccess: true;\n\t\t\tdata: { model: Model<any>; thinkingLevel: ThinkingLevel; isScoped: boolean } | null;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_available_models\";\n\t\t\tsuccess: true;\n\t\t\tdata: { models: Array<Model<any> & { supportedThinkingLevels: ThinkingLevel[] }> };\n\t }\n\n\t// Thinking\n\t| { id?: string; type: \"response\"; command: \"set_thinking_level\"; success: true }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"cycle_thinking_level\";\n\t\t\tsuccess: true;\n\t\t\tdata: { level: ThinkingLevel } | null;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_available_thinking_levels\";\n\t\t\tsuccess: true;\n\t\t\tdata: { levels: ThinkingLevel[] };\n\t }\n\n\t// Fast mode\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"set_fast_mode\";\n\t\t\tsuccess: true;\n\t\t\tdata: { enabled: boolean; serviceTier: ServiceTier; provider: string; modelId: string };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_fast_mode\";\n\t\t\tsuccess: true;\n\t\t\tdata: { enabled: boolean; serviceTier: ServiceTier | null };\n\t }\n\n\t// Queue modes\n\t| { id?: string; type: \"response\"; command: \"set_steering_mode\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"set_follow_up_mode\"; success: true }\n\n\t// Compaction\n\t| { id?: string; type: \"response\"; command: \"compact\"; success: true; data: CompactionResult }\n\t| { id?: string; type: \"response\"; command: \"set_auto_compaction\"; success: true }\n\n\t// Retry\n\t| { id?: string; type: \"response\"; command: \"set_auto_retry\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"abort_retry\"; success: true }\n\n\t// Bash\n\t| { id?: string; type: \"response\"; command: \"bash\"; success: true; data: BashResult }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"navigate_tree\";\n\t\t\tsuccess: true;\n\t\t\tdata: { cancelled: boolean; editorText?: string; aborted?: boolean; summaryEntry?: unknown };\n\t }\n\t| { id?: string; type: \"response\"; command: \"abort_bash\"; success: true }\n\n\t// Session\n\t| { id?: string; type: \"response\"; command: \"get_session_stats\"; success: true; data: SessionStats }\n\t| { id?: string; type: \"response\"; command: \"export_html\"; success: true; data: { path: string } }\n\t| { id?: string; type: \"response\"; command: \"export_jsonl\"; success: true; data: { path: string } }\n\t| { id?: string; type: \"response\"; command: \"switch_session\"; success: true; data: { cancelled: boolean } }\n\t| { id?: string; type: \"response\"; command: \"fork\"; success: true; data: { text: string; cancelled: boolean } }\n\t| { id?: string; type: \"response\"; command: \"clone\"; success: true; data: { cancelled: boolean } }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_fork_messages\";\n\t\t\tsuccess: true;\n\t\t\tdata: { messages: Array<{ entryId: string; text: string }> };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_entries\";\n\t\t\tsuccess: true;\n\t\t\tdata: { entries: SessionEntry[]; leafId: string | null };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_tree\";\n\t\t\tsuccess: true;\n\t\t\tdata: { tree: SessionTreeNode[]; leafId: string | null };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_last_assistant_text\";\n\t\t\tsuccess: true;\n\t\t\tdata: { text: string | null };\n\t }\n\t| { id?: string; type: \"response\"; command: \"set_session_name\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"import_jsonl\"; success: true; data: { cancelled: boolean } }\n\n\t// Messages\n\t| { id?: string; type: \"response\"; command: \"get_messages\"; success: true; data: { messages: AgentMessage[] } }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_media\";\n\t\t\tsuccess: true;\n\t\t\tdata: { toolCallId: string; contentIndex: number; content: ImageContent };\n\t }\n\n\t// Commands and loaded runtime surfaces\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_commands\";\n\t\t\tsuccess: true;\n\t\t\tdata: { commands: RpcSlashCommand[] };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_loaded_surfaces\";\n\t\t\tsuccess: true;\n\t\t\tdata: { extensions: RpcLoadedExtension[]; mcpServers: RpcLoadedMcpServer[] };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"extension_request\";\n\t\t\tsuccess: true;\n\t\t\tdata: unknown;\n\t }\n\n\t// Auth (task 13)\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_auth_providers\";\n\t\t\tsuccess: true;\n\t\t\tdata: { providers: RpcAuthProvider[] };\n\t }\n\t// login_start returns immediately: success:true means the flow has started.\n\t// The URL and completion arrive as auth_login_url / auth_login_end events.\n\t| { id?: string; type: \"response\"; command: \"login_start\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"login_cancel\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"login_api_key\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"logout\"; success: true }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_provider_accounts\";\n\t\t\tsuccess: true;\n\t\t\tdata: { accounts: RpcProviderAccount[] };\n\t }\n\t| { id?: string; type: \"response\"; command: \"account_pin\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"account_remove\"; success: true }\n\n\t// Error response (any command can fail)\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: string;\n\t\t\tsuccess: false;\n\t\t\terror: string;\n\t\t\terrorCode?: string;\n\t\t\terrorData?: unknown;\n\t };\n\n// ============================================================================\n// Extension UI Events (stdout)\n// ============================================================================\n\n/** Emitted when an extension needs user input */\nexport type RpcExtensionUIRequest =\n\t| { type: \"extension_ui_request\"; id: string; method: \"select\"; title: string; options: string[]; timeout?: number }\n\t| { type: \"extension_ui_request\"; id: string; method: \"confirm\"; title: string; message: string; timeout?: number }\n\t| {\n\t\t\ttype: \"extension_ui_request\";\n\t\t\tid: string;\n\t\t\tmethod: \"input\";\n\t\t\ttitle: string;\n\t\t\tplaceholder?: string;\n\t\t\ttimeout?: number;\n\t }\n\t| { type: \"extension_ui_request\"; id: string; method: \"editor\"; title: string; prefill?: string }\n\t| {\n\t\t\ttype: \"extension_ui_request\";\n\t\t\tid: string;\n\t\t\tmethod: \"notify\";\n\t\t\tmessage: string;\n\t\t\tnotifyType?: \"info\" | \"warning\" | \"error\";\n\t }\n\t| {\n\t\t\ttype: \"extension_ui_request\";\n\t\t\tid: string;\n\t\t\tmethod: \"setStatus\";\n\t\t\tstatusKey: string;\n\t\t\tstatusText: string | undefined;\n\t }\n\t| {\n\t\t\ttype: \"extension_ui_request\";\n\t\t\tid: string;\n\t\t\tmethod: \"setWidget\";\n\t\t\twidgetKey: string;\n\t\t\twidgetLines: string[] | undefined;\n\t\t\twidgetPlacement?: \"aboveEditor\" | \"belowEditor\";\n\t }\n\t| {\n\t\t\ttype: \"extension_ui_request\";\n\t\t\tid: string;\n\t\t\tmethod: \"setHeader\" | \"setFooter\";\n\t\t\twidgetLines: string[] | undefined;\n\t }\n\t| { type: \"extension_ui_request\"; id: string; method: \"setTitle\"; title: string }\n\t| { type: \"extension_ui_request\"; id: string; method: \"set_editor_text\"; text: string }\n\t// Additive (task 13/14): emitted ONLY when the client advertised the\n\t// \"custom_unsupported\" capability. ctx.ui.custom cannot render a third-party\n\t// component in RPC mode, so a flagged client gets this notice before custom()\n\t// returns undefined. Default clients never see it (byte-identical behavior).\n\t| { type: \"extension_ui_request\"; id: string; method: \"custom_unsupported\"; extensionName: string };\n\nexport type RpcExtensionEvent = {\n\ttype: \"extension_event\";\n\tname: string;\n\tdata: unknown;\n};\n\n// ============================================================================\n// Extension UI Commands (stdin)\n// ============================================================================\n\n/** Response to an extension UI request */\nexport type RpcExtensionUIResponse =\n\t| { type: \"extension_ui_response\"; id: string; value: string }\n\t| { type: \"extension_ui_response\"; id: string; confirmed: boolean }\n\t| { type: \"extension_ui_response\"; id: string; cancelled: true };\n\n/** Emitted when the effective session thinking level changes. */\nexport interface RpcThinkingLevelChangedEvent {\n\ttype: \"thinking_level_changed\";\n\tlevel: ThinkingLevel;\n\t/**\n\t * Selector provenance in force after the change; absent when the level is an\n\t * SDK-defaulted effective level rather than an explicit choice. Additive: an old\n\t * client that does not know the field ignores it.\n\t */\n\tthinkingSelection?: ThinkingSelection;\n}\n\nexport interface RpcHighReasoningWarningEvent {\n\ttype: \"high_reasoning_warning\";\n\tmodelId: string;\n\tprovider: string;\n\tthinkingLevel: ThinkingLevel;\n}\n\n/** Emitted after explicit skill tokens are expanded for a user-authored request. */\nexport interface RpcSkillInvocationEvent {\n\ttype: \"skill_invocation\";\n\tskills: readonly {\n\t\tname: string;\n\t\tpath: string;\n\t\tsyntax: \"dollar\" | \"slash\";\n\t}[];\n}\n\n/** Emitted when startup or reload selects an existing settings file. */\nexport interface RpcSettingsSourceSelectedEvent {\n\ttype: \"settings_source_selected\";\n\tpath: string;\n\tformat: \"jsonc\" | \"json\";\n\treason: \"explicit-jsonc\" | \"json-only\";\n\tscope: \"global\" | \"project\";\n}\n\n/**\n * Emitted after the session's active model changed, with the thinking level in force AFTER\n * the switch (per-model memory, a favorite's pinned level, or the clamped previous level).\n *\n * Clients that tracked the model by inferring it from `entry_appended` can consume this\n * instead. Additive: an old client that does not know the type filters it out.\n */\nexport interface RpcModelChangedEvent {\n\ttype: \"model_changed\";\n\tmodel: Model<any>;\n\tthinkingLevel: ThinkingLevel;\n\t/** Why the model changed: \"set\", \"cycle\", \"restore\", \"fallback\", or \"fallback-revert\". */\n\tsource: string;\n\t/** Selector provenance for `thinkingLevel` after the switch, when one was explicit. */\n\tthinkingSelection?: ThinkingSelection;\n}\n\n/** Emitted when the effective service tier or fast-mode state of the session changes. */\nexport interface RpcServiceTierChangedEvent {\n\ttype: \"service_tier_changed\";\n\ttier?: ServiceTier;\n\tfastMode: boolean;\n}\n/**\n * Emitted after the host swapped the live session behind this connection (new session,\n * fork, or switch), carrying the new authoritative identity.\n *\n * A replacement can be initiated by ANY attached client. Without this event the other\n * attached clients keep their stale identity and keep routing replacement-dependent\n * actions at the session that no longer exists. Additive: an old client that does not\n * know the type filters it out.\n */\nexport interface RpcSessionReplacedEvent {\n\ttype: \"session_replaced\";\n\t/**\n\t * Durable session id of the session now bound to this connection.\n\t *\n\t * Deliberately NOT `sessionId`: top-level `sessionId` is reserved for the\n\t * per-connection routing handle that multi-session hosts tag every record\n\t * with, and that tag is applied last - it would overwrite this value and\n\t * leave the event carrying no identity at all.\n\t */\n\tdurableSessionId: string;\n\t/** Session file backing the new session, absent for a deferred setup-only session. */\n\tsessionFile?: string;\n\tcwd: string;\n\tsessionName?: string;\n}\n\n/** Emitted after the loaded skill, extension, or MCP inventory changes. */\nexport interface RpcLoadedSurfacesChangedEvent {\n\ttype: \"loaded_surfaces_changed\";\n}\n\n/** Emitted after an account is added, removed, pinned, or blocked by refresh failure. */\nexport interface RpcAuthAccountsChangedEvent {\n\ttype: \"auth_accounts_changed\";\n\tprovider: string;\n}\n\n/** Emitted when the SDK failover engine advances to a different account slot. */\nexport interface RpcAccountFailoverEvent {\n\ttype: \"account_failover\";\n\tprovider: string;\n\tfrom: string;\n\tto: string;\n\treason: string;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"rpc-types.js","sourceRoot":"","sources":["../../../src/modes/rpc/rpc-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA8JH,iDAAiD;AACjD,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAC;AAC3D,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAC;AAC3D,MAAM,CAAC,MAAM,6BAA6B,GAAG,qBAAqB,CAAC;AACnE,MAAM,CAAC,MAAM,4BAA4B,GAAG,oBAAoB,CAAC;AACjE,MAAM,CAAC,MAAM,gCAAgC,GAAG,wBAAwB,CAAC;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,cAAc,CAAC;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;AACnD,MAAM,CAAC,MAAM,yBAAyB,GAAG,iBAAiB,CAAC","sourcesContent":["/**\n * RPC protocol types for headless operation.\n *\n * Commands are sent as JSON lines on stdin.\n * Responses and events are emitted as JSON lines on stdout.\n */\n\nimport type { AgentMessage, ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, Model, ThinkingSelection } from \"@earendil-works/pi-ai\";\nimport type { AgentAbortSource } from \"../../core/agent-abort-provenance.ts\";\nimport type { PromptDisposition, SessionStats } from \"../../core/agent-session.ts\";\nimport type { BashResult } from \"../../core/bash-executor.ts\";\nimport type { CompactionResult } from \"../../core/compaction/index.ts\";\nimport type { ServiceTier } from \"../../core/extensions/builtin/service-tier.ts\";\nimport type { ContextUsage } from \"../../core/extensions/types.ts\";\nimport type { SessionEntry, SessionTreeNode, UsageTotals } from \"../../core/session-manager.ts\";\nimport type { SourceInfo } from \"../../core/source-info.ts\";\nimport type { RpcSlashCommand } from \"./rpc-command-surface.ts\";\n\nexport type { RpcCommandInvocationEvent } from \"./rpc-command-invocation.ts\";\nexport type { RpcCommandsChangedEvent, RpcSlashCommand } from \"./rpc-command-surface.ts\";\n\n// ============================================================================\n// RPC Commands (stdin)\n// ============================================================================\n\ntype RpcSessionCommand =\n\t// Prompting\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"prompt\";\n\t\t\tmessage: string;\n\t\t\timages?: ImageContent[];\n\t\t\tstreamingBehavior?: \"steer\" | \"followUp\";\n\t\t\tthinkingLevel?: ThinkingLevel;\n\t\t\tsessionTitlePrompt?: string | false;\n\t\t\texpandPromptTemplates?: boolean;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"send_custom_message\";\n\t\t\tcustomType: string;\n\t\t\tcontent: unknown;\n\t\t\tdisplay: boolean;\n\t\t\tdetails?: unknown;\n\t\t\ttriggerTurn?: boolean;\n\t\t\tdeliverAs?: \"steer\" | \"followUp\" | \"nextTurn\";\n\t }\n\t| { id?: string; type: \"append_user_message\"; content: unknown }\n\t| { id?: string; type: \"append_session_entry\"; entry: SessionEntry }\n\t| { id?: string; type: \"steer\"; message: string; images?: ImageContent[]; enqueueOrder?: number }\n\t| { id?: string; type: \"follow_up\"; message: string; images?: ImageContent[]; enqueueOrder?: number }\n\t| { id?: string; type: \"abort\" }\n\t| { id?: string; type: \"abort_compaction\" }\n\t| { id?: string; type: \"reload\" }\n\t| { id?: string; type: \"check_reload_veto\" }\n\t| { id?: string; type: \"clear_queue\"; abortWillFollow?: boolean }\n\t| { id?: string; type: \"get_steering_messages\" }\n\t| { id?: string; type: \"get_follow_up_messages\" }\n\t| { id?: string; type: \"abort_branch_summary\" }\n\t| { id?: string; type: \"new_session\"; parentSession?: string }\n\n\t// State\n\t| { id?: string; type: \"get_state\" }\n\n\t// Model\n\t| { id?: string; type: \"set_model\"; provider: string; modelId: string }\n\t| { id?: string; type: \"set_favorite_models\"; models: RpcSessionModelEntry[] }\n\t| { id?: string; type: \"set_scoped_models\"; models: RpcSessionModelEntry[] }\n\t| { id?: string; type: \"cycle_model\"; direction?: \"forward\" | \"backward\" }\n\t| { id?: string; type: \"get_available_models\" }\n\n\t// Thinking\n\t| { id?: string; type: \"set_thinking_level\"; level: ThinkingLevel; scope?: \"turn\" }\n\t| { id?: string; type: \"cycle_thinking_level\" }\n\t| { id?: string; type: \"get_available_thinking_levels\" }\n\n\t// Fast mode (OpenAI Codex priority service tier)\n\t| { id?: string; type: \"set_fast_mode\"; enabled: boolean }\n\t| { id?: string; type: \"get_fast_mode\" }\n\n\t// Queue modes\n\t| { id?: string; type: \"set_steering_mode\"; mode: \"all\" | \"one-at-a-time\" }\n\t| { id?: string; type: \"set_follow_up_mode\"; mode: \"all\" | \"one-at-a-time\" }\n\n\t// Compaction\n\t| { id?: string; type: \"compact\"; customInstructions?: string }\n\t| { id?: string; type: \"set_auto_compaction\"; enabled: boolean }\n\n\t// Retry\n\t| { id?: string; type: \"set_auto_retry\"; enabled: boolean }\n\t| { id?: string; type: \"abort_retry\" }\n\n\t// Bash\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"bash\";\n\t\t\tcommand: string;\n\t\t\t/** Identifies output chunks to the requesting client. */\n\t\t\tbashId?: string;\n\t\t\texcludeFromContext?: boolean;\n\t\t\texecutionId?: string;\n\t\t\toperations?: Record<string, unknown>;\n\t }\n\t| { id?: string; type: \"record_bash_result\"; command: string; result: BashResult; excludeFromContext?: boolean }\n\t| { id?: string; type: \"abort_bash\" }\n\t| { id?: string; type: \"cleanup_bash_output\"; path: string }\n\t| { id?: string; type: \"set_label\"; entryId: string; label?: string }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"navigate_tree\";\n\t\t\ttargetId: string;\n\t\t\tsummarize?: boolean;\n\t\t\tcustomInstructions?: string;\n\t\t\treplaceInstructions?: boolean;\n\t\t\tlabel?: string;\n\t }\n\n\t// Session\n\t| { id?: string; type: \"get_session_stats\" }\n\t| { id?: string; type: \"export_html\"; outputPath?: string; themeName?: string }\n\t| { id?: string; type: \"export_jsonl\"; outputPath?: string }\n\t| { id?: string; type: \"switch_session\"; sessionPath: string; cwdOverride?: string }\n\t| { id?: string; type: \"fork\"; entryId: string; position?: \"before\" | \"at\" }\n\t| { id?: string; type: \"clone\" }\n\t| { id?: string; type: \"get_fork_messages\" }\n\t| { id?: string; type: \"get_entries\"; since?: string }\n\t| { id?: string; type: \"get_tree\" }\n\t| { id?: string; type: \"get_last_assistant_text\" }\n\t| { id?: string; type: \"set_session_name\"; name: string }\n\t| { id?: string; type: \"import_jsonl\"; inputPath: string; cwdOverride?: string }\n\n\t// Messages\n\t| { id?: string; type: \"get_messages\" }\n\t/**\n\t * Fetch one media block a `media_placeholders` client received as an `image_ref`\n\t * stub. `contentIndex` indexes the toolResult's `content` array and must point at\n\t * an image block; anything else answers `media_not_found`.\n\t */\n\t| { id?: string; type: \"get_media\"; toolCallId: string; contentIndex: number }\n\n\t// Commands and loaded runtime surfaces\n\t| { id?: string; type: \"get_commands\" }\n\t| { id?: string; type: \"get_loaded_surfaces\" }\n\t| { id?: string; type: \"extension_request\"; name: string; data?: unknown }\n\n\t// Auth (task 13) is additive. get_auth_providers, login_api_key and logout\n\t// answer synchronously. login_start responds immediately (flow-started) and\n\t// completion is delivered via auth_login_url / auth_login_end EVENTS, because\n\t// an interactive OAuth round-trip cannot fit the 30s request timeout.\n\t| { id?: string; type: \"get_auth_providers\" }\n\t| { id?: string; type: \"login_start\"; provider: string }\n\t| { id?: string; type: \"login_cancel\"; provider: string }\n\t| { id?: string; type: \"login_api_key\"; provider: string; key: string }\n\t| { id?: string; type: \"logout\"; provider: string }\n\n\t// Provider accounts (task 13) are additive. The desktop consumer contract\n\t// lives in ../omo-desktop-app/packages/contracts/src/rpc.ts and is updated separately.\n\t| { id?: string; type: \"get_provider_accounts\"; provider: string }\n\t| { id?: string; type: \"account_pin\"; provider: string; name: string | null }\n\t| { id?: string; type: \"account_remove\"; provider: string; name: string }\n\t| { id?: string; type: \"set_client_info\"; width: number; capabilities?: string[] };\n\n/** Stable multi-session protocol error codes. */\nexport const RPC_ERROR_UNKNOWN_SESSION = \"unknown_session\";\nexport const RPC_ERROR_SESSION_CLOSING = \"session_closing\";\nexport const RPC_ERROR_SESSION_PATH_IN_USE = \"session_path_in_use\";\nexport const RPC_ERROR_MISSING_SESSION_ID = \"missing_session_id\";\nexport const RPC_ERROR_MULTI_SESSION_DISABLED = \"multi_session_disabled\";\nexport const RPC_ERROR_INVALID_PATH = \"invalid_path\";\nexport const RPC_ERROR_OPEN_FAILED = \"open_failed\";\nexport const RPC_ERROR_MEDIA_NOT_FOUND = \"media_not_found\";\n\nexport type RpcErrorCode =\n\t| typeof RPC_ERROR_UNKNOWN_SESSION\n\t| typeof RPC_ERROR_SESSION_CLOSING\n\t| typeof RPC_ERROR_SESSION_PATH_IN_USE\n\t| typeof RPC_ERROR_MISSING_SESSION_ID\n\t| typeof RPC_ERROR_MULTI_SESSION_DISABLED\n\t| typeof RPC_ERROR_INVALID_PATH\n\t| typeof RPC_ERROR_OPEN_FAILED\n\t| typeof RPC_ERROR_MEDIA_NOT_FOUND;\n\n/** Every established command accepts an additive routing envelope. */\nexport type RpcCommand =\n\t| (RpcSessionCommand & { sessionId?: string })\n\t| { id?: string; type: \"get_protocol_info\" }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"open_session\";\n\t\t\tsessionPath?: string;\n\t\t\tcwd?: string;\n\t\t\tprovider?: string;\n\t\t\tmodelId?: string;\n\t\t\tthinkingLevel?: ThinkingLevel;\n\t\t\tpermissionPreset?: string;\n\t }\n\t| { id?: string; type: \"close_session\"; sessionId: string }\n\t| { id?: string; type: \"list_sessions\" };\n\n// ============================================================================\n// Auth provider info (get_auth_providers response)\n// ============================================================================\n\n/** One provider row for the /login and /logout selectors. */\nexport interface RpcAuthProvider {\n\t/** Provider id (e.g. \"anthropic\", \"openai\"). */\n\tid: string;\n\t/** Human-readable display name. */\n\tname: string;\n\t/** How this provider authenticates. */\n\tauthType: \"oauth\" | \"api_key\";\n\t/** Auth status without exposing or refreshing any credential. */\n\tstatus: RpcAuthStatus;\n}\n\n/** Auth status mirror (no credential values), from getProviderAuthStatus. */\nexport interface RpcAuthStatus {\n\tconfigured: boolean;\n\tsource?:\n\t\t| \"stored\"\n\t\t| \"runtime\"\n\t\t| \"environment\"\n\t\t| \"fallback\"\n\t\t| \"models_json_key\"\n\t\t| \"models_json_command\"\n\t\t| \"models_json_headers\"\n\t\t| \"extension_headers\";\n\tlabel?: string;\n}\n\n/** Account-slot metadata safe to send to desktop clients. */\nexport interface RpcProviderAccount {\n\tname: string;\n\tsource: \"login\" | \"import\" | \"env\";\n\tblocked: boolean;\n\tpinned: boolean;\n}\n\n// ============================================================================\n// RPC Slash Command (for get_commands response)\n// ============================================================================\n\n/** One extension module loaded by the session resource loader. */\nexport interface RpcLoadedExtension {\n\tname: string;\n\tpath: string;\n\tsourceInfo: SourceInfo;\n\tenabled: boolean;\n}\n\nexport type RpcMcpServerStatus =\n\t| \"enabled\"\n\t| \"disabled\"\n\t| \"untrusted\"\n\t| \"idle\"\n\t| \"connecting\"\n\t| \"connected\"\n\t| \"degraded\"\n\t| \"suspended\"\n\t| \"needs_auth\"\n\t| \"needs_client_registration\";\n\n/** Runtime MCP server state projected from the session-owned MCP service. */\nexport interface RpcLoadedMcpServer {\n\tname: string;\n\ttoolCount: number;\n\tstatus: RpcMcpServerStatus;\n\tauthStatus: \"unsupported\" | \"notLoggedIn\" | \"bearerToken\" | \"oAuth\";\n}\n\n// ============================================================================\n// RPC State\n// ============================================================================\n\nexport interface RpcSessionModelEntry {\n\tmodel: Model<any>;\n\tthinkingLevel?: ThinkingLevel;\n\tthinkingSelection?: ThinkingSelection;\n\tserviceTier?: ServiceTier;\n}\n\nexport interface RpcSessionState {\n\tmodel?: Model<any>;\n\tthinkingLevel: ThinkingLevel;\n\t/**\n\t * Explicit selector provenance for `thinkingLevel`, absent for SDK-defaulted\n\t * effective levels. An attached client cannot distinguish \"the user chose high\"\n\t * from \"high is simply the effective level\" without it.\n\t */\n\tthinkingSelection?: ThinkingSelection;\n\t/**\n\t * Abort owner of the most recent aborted turn, or the in-flight one while it is\n\t * still settling. Retained after settle: the live session getter is transient, so a\n\t * client that snapshots state after the turn ends would otherwise see nothing and\n\t * fall back to generic wording instead of \"Operation aborted\".\n\t */\n\tlastAbortSource?: AgentAbortSource;\n\t/** Service tier the session resolved for the active model, if any. */\n\tserviceTier?: ServiceTier;\n\t/** True when the active model is served at the priority (\"fast\") tier. */\n\tfastMode: boolean;\n\tisStreaming: boolean;\n\tisCompacting: boolean;\n\tsteeringMode: \"all\" | \"one-at-a-time\";\n\tfollowUpMode: \"all\" | \"one-at-a-time\";\n\tsessionFile?: string;\n\tsessionId: string;\n\tsessionName?: string;\n\tcwd: string;\n\t/** Whether project-scoped settings and resources are trusted by the host. */\n\tprojectTrusted: boolean;\n\t/** Authoritative entries for setup-only sessions whose deferred file does not exist yet. */\n\tentries?: SessionEntry[];\n\tfavoriteModels: RpcSessionModelEntry[];\n\tscopedModels: RpcSessionModelEntry[];\n\tsteering: string[];\n\tfollowUp: string[];\n\tordered: Array<{ text: string; mode: \"steer\" | \"followUp\"; enqueueOrder: number }>;\n\tautoCompactionEnabled: boolean;\n\tmessageCount: number;\n\tpendingMessageCount: number;\n\tusageTotals: UsageTotals;\n\tcontextUsage?: ContextUsage;\n\tretryAttempt: number;\n\tisBashRunning: boolean;\n}\n\n// ============================================================================\n// RPC Responses (stdout)\n// ============================================================================\n\n// Success responses with data\nexport type RpcResponse =\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_protocol_info\";\n\t\t\tsuccess: true;\n\t\t\tdata: {\n\t\t\t\tprotocolVersion: 1;\n\t\t\t\tserverVersion: string;\n\t\t\t\tcapabilities: string[];\n\t\t\t\tmode: \"classic\" | \"multi\";\n\t\t\t};\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"open_session\";\n\t\t\tsuccess: true;\n\t\t\tdata: { sessionId: string; state: RpcSessionState; attached?: boolean };\n\t }\n\t| { id?: string; type: \"response\"; command: \"close_session\"; success: true; data: Record<string, never> }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"list_sessions\";\n\t\t\tsuccess: true;\n\t\t\tdata: {\n\t\t\t\tsessions: Array<{\n\t\t\t\t\tsessionId: string;\n\t\t\t\t\tdurableSessionId?: string;\n\t\t\t\t\tsessionPath?: string;\n\t\t\t\t\tcwd: string;\n\t\t\t\t\tname?: string;\n\t\t\t\t\tstatus: \"opening\" | \"open\" | \"closing\" | \"closed\";\n\t\t\t\t}>;\n\t\t\t};\n\t }\n\t// Prompting (async - events follow)\n\t// data.disposition reports how the host disposed the prompt (started/queued/handled)\n\t// so proxied optimistic-echo contracts resolve exactly like the local path; older\n\t// hosts omit it and clients must degrade to canonical-only rendering.\n\t| { id?: string; type: \"response\"; command: \"prompt\"; success: true; data?: { disposition?: PromptDisposition } }\n\t| { id?: string; type: \"response\"; command: \"send_custom_message\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"append_user_message\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"append_session_entry\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"steer\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"follow_up\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"abort\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"abort_compaction\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"reload\"; success: true; data: { cancelled: boolean; reason?: string } }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"check_reload_veto\";\n\t\t\tsuccess: true;\n\t\t\tdata: { cancelled: boolean; reason?: string };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"clear_queue\";\n\t\t\tsuccess: true;\n\t\t\tdata: {\n\t\t\t\tsteering: string[];\n\t\t\t\tfollowUp: string[];\n\t\t\t\tordered: Array<{ text: string; mode: \"steer\" | \"followUp\"; enqueueOrder: number }>;\n\t\t\t};\n\t }\n\t| { id?: string; type: \"response\"; command: \"new_session\"; success: true; data: { cancelled: boolean } }\n\n\t// State\n\t| { id?: string; type: \"response\"; command: \"get_state\"; success: true; data: RpcSessionState }\n\n\t// Model\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"set_model\";\n\t\t\tsuccess: true;\n\t\t\tdata: Model<any> & { systemPromptName?: string };\n\t }\n\t| { id?: string; type: \"response\"; command: \"set_favorite_models\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"set_scoped_models\"; success: true }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"cycle_model\";\n\t\t\tsuccess: true;\n\t\t\tdata: { model: Model<any>; thinkingLevel: ThinkingLevel; isScoped: boolean } | null;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_available_models\";\n\t\t\tsuccess: true;\n\t\t\tdata: { models: Array<Model<any> & { supportedThinkingLevels: ThinkingLevel[] }> };\n\t }\n\n\t// Thinking\n\t| { id?: string; type: \"response\"; command: \"set_thinking_level\"; success: true }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"cycle_thinking_level\";\n\t\t\tsuccess: true;\n\t\t\tdata: { level: ThinkingLevel } | null;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_available_thinking_levels\";\n\t\t\tsuccess: true;\n\t\t\tdata: { levels: ThinkingLevel[] };\n\t }\n\n\t// Fast mode\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"set_fast_mode\";\n\t\t\tsuccess: true;\n\t\t\tdata: { enabled: boolean; serviceTier: ServiceTier; provider: string; modelId: string };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_fast_mode\";\n\t\t\tsuccess: true;\n\t\t\tdata: { enabled: boolean; serviceTier: ServiceTier | null };\n\t }\n\n\t// Queue modes\n\t| { id?: string; type: \"response\"; command: \"set_steering_mode\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"set_follow_up_mode\"; success: true }\n\n\t// Compaction\n\t| { id?: string; type: \"response\"; command: \"compact\"; success: true; data: CompactionResult }\n\t| { id?: string; type: \"response\"; command: \"set_auto_compaction\"; success: true }\n\n\t// Retry\n\t| { id?: string; type: \"response\"; command: \"set_auto_retry\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"abort_retry\"; success: true }\n\n\t// Bash\n\t| { id?: string; type: \"response\"; command: \"bash\"; success: true; data: BashResult }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"navigate_tree\";\n\t\t\tsuccess: true;\n\t\t\tdata: { cancelled: boolean; editorText?: string; aborted?: boolean; summaryEntry?: unknown };\n\t }\n\t| { id?: string; type: \"response\"; command: \"abort_bash\"; success: true }\n\n\t// Session\n\t| { id?: string; type: \"response\"; command: \"get_session_stats\"; success: true; data: SessionStats }\n\t| { id?: string; type: \"response\"; command: \"export_html\"; success: true; data: { path: string } }\n\t| { id?: string; type: \"response\"; command: \"export_jsonl\"; success: true; data: { path: string } }\n\t| { id?: string; type: \"response\"; command: \"switch_session\"; success: true; data: { cancelled: boolean } }\n\t| { id?: string; type: \"response\"; command: \"fork\"; success: true; data: { text: string; cancelled: boolean } }\n\t| { id?: string; type: \"response\"; command: \"clone\"; success: true; data: { cancelled: boolean } }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_fork_messages\";\n\t\t\tsuccess: true;\n\t\t\tdata: { messages: Array<{ entryId: string; text: string }> };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_entries\";\n\t\t\tsuccess: true;\n\t\t\tdata: { entries: SessionEntry[]; leafId: string | null };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_tree\";\n\t\t\tsuccess: true;\n\t\t\tdata: { tree: SessionTreeNode[]; leafId: string | null };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_last_assistant_text\";\n\t\t\tsuccess: true;\n\t\t\tdata: { text: string | null };\n\t }\n\t| { id?: string; type: \"response\"; command: \"set_session_name\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"import_jsonl\"; success: true; data: { cancelled: boolean } }\n\n\t// Messages\n\t| { id?: string; type: \"response\"; command: \"get_messages\"; success: true; data: { messages: AgentMessage[] } }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_media\";\n\t\t\tsuccess: true;\n\t\t\tdata: { toolCallId: string; contentIndex: number; content: ImageContent };\n\t }\n\n\t// Commands and loaded runtime surfaces\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_commands\";\n\t\t\tsuccess: true;\n\t\t\tdata: { commands: RpcSlashCommand[] };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_loaded_surfaces\";\n\t\t\tsuccess: true;\n\t\t\tdata: { extensions: RpcLoadedExtension[]; mcpServers: RpcLoadedMcpServer[] };\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"extension_request\";\n\t\t\tsuccess: true;\n\t\t\tdata: unknown;\n\t }\n\n\t// Auth (task 13)\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_auth_providers\";\n\t\t\tsuccess: true;\n\t\t\tdata: { providers: RpcAuthProvider[] };\n\t }\n\t// login_start returns immediately: success:true means the flow has started.\n\t// The URL and completion arrive as auth_login_url / auth_login_end events.\n\t| { id?: string; type: \"response\"; command: \"login_start\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"login_cancel\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"login_api_key\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"logout\"; success: true }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: \"get_provider_accounts\";\n\t\t\tsuccess: true;\n\t\t\tdata: { accounts: RpcProviderAccount[] };\n\t }\n\t| { id?: string; type: \"response\"; command: \"account_pin\"; success: true }\n\t| { id?: string; type: \"response\"; command: \"account_remove\"; success: true }\n\n\t// Error response (any command can fail)\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"response\";\n\t\t\tcommand: string;\n\t\t\tsuccess: false;\n\t\t\terror: string;\n\t\t\terrorCode?: string;\n\t\t\terrorData?: unknown;\n\t };\n\n// ============================================================================\n// Extension UI Events (stdout)\n// ============================================================================\n\n/** Emitted when an extension needs user input */\nexport type RpcExtensionUIRequest =\n\t| { type: \"extension_ui_request\"; id: string; method: \"select\"; title: string; options: string[]; timeout?: number }\n\t| { type: \"extension_ui_request\"; id: string; method: \"confirm\"; title: string; message: string; timeout?: number }\n\t| {\n\t\t\ttype: \"extension_ui_request\";\n\t\t\tid: string;\n\t\t\tmethod: \"input\";\n\t\t\ttitle: string;\n\t\t\tplaceholder?: string;\n\t\t\ttimeout?: number;\n\t }\n\t| { type: \"extension_ui_request\"; id: string; method: \"editor\"; title: string; prefill?: string }\n\t| {\n\t\t\ttype: \"extension_ui_request\";\n\t\t\tid: string;\n\t\t\tmethod: \"notify\";\n\t\t\tmessage: string;\n\t\t\tnotifyType?: \"info\" | \"warning\" | \"error\";\n\t }\n\t| {\n\t\t\ttype: \"extension_ui_request\";\n\t\t\tid: string;\n\t\t\tmethod: \"setStatus\";\n\t\t\tstatusKey: string;\n\t\t\tstatusText: string | undefined;\n\t }\n\t| {\n\t\t\ttype: \"extension_ui_request\";\n\t\t\tid: string;\n\t\t\tmethod: \"setWidget\";\n\t\t\twidgetKey: string;\n\t\t\twidgetLines: string[] | undefined;\n\t\t\twidgetPlacement?: \"aboveEditor\" | \"belowEditor\";\n\t }\n\t| {\n\t\t\ttype: \"extension_ui_request\";\n\t\t\tid: string;\n\t\t\tmethod: \"setHeader\" | \"setFooter\";\n\t\t\twidgetLines: string[] | undefined;\n\t }\n\t| { type: \"extension_ui_request\"; id: string; method: \"setTitle\"; title: string }\n\t| { type: \"extension_ui_request\"; id: string; method: \"set_editor_text\"; text: string }\n\t// Additive (task 13/14): emitted ONLY when the client advertised the\n\t// \"custom_unsupported\" capability. ctx.ui.custom cannot render a third-party\n\t// component in RPC mode, so a flagged client gets this notice before custom()\n\t// returns undefined. Default clients never see it (byte-identical behavior).\n\t| { type: \"extension_ui_request\"; id: string; method: \"custom_unsupported\"; extensionName: string };\n\nexport type RpcExtensionEvent = {\n\ttype: \"extension_event\";\n\tname: string;\n\tdata: unknown;\n};\n\n// ============================================================================\n// Extension UI Commands (stdin)\n// ============================================================================\n\n/** Response to an extension UI request */\nexport type RpcExtensionUIResponse =\n\t| { type: \"extension_ui_response\"; id: string; value: string }\n\t| { type: \"extension_ui_response\"; id: string; confirmed: boolean }\n\t| { type: \"extension_ui_response\"; id: string; cancelled: true };\n\n/** Emitted when the effective session thinking level changes. */\nexport interface RpcThinkingLevelChangedEvent {\n\ttype: \"thinking_level_changed\";\n\tlevel: ThinkingLevel;\n\t/**\n\t * Selector provenance in force after the change; absent when the level is an\n\t * SDK-defaulted effective level rather than an explicit choice. Additive: an old\n\t * client that does not know the field ignores it.\n\t */\n\tthinkingSelection?: ThinkingSelection;\n}\n\nexport interface RpcHighReasoningWarningEvent {\n\ttype: \"high_reasoning_warning\";\n\tmodelId: string;\n\tprovider: string;\n\tthinkingLevel: ThinkingLevel;\n}\n\n/** Emitted after explicit skill tokens are expanded for a user-authored request. */\nexport interface RpcSkillInvocationEvent {\n\ttype: \"skill_invocation\";\n\tskills: readonly {\n\t\tname: string;\n\t\tpath: string;\n\t\tsyntax: \"dollar\" | \"slash\";\n\t}[];\n}\n\n/** Emitted when startup or reload selects an existing settings file. */\nexport interface RpcSettingsSourceSelectedEvent {\n\ttype: \"settings_source_selected\";\n\tpath: string;\n\tformat: \"jsonc\" | \"json\";\n\treason: \"explicit-jsonc\" | \"json-only\";\n\tscope: \"global\" | \"project\";\n}\n\n/**\n * Emitted after the session's active model changed, with the thinking level in force AFTER\n * the switch (per-model memory, a favorite's pinned level, or the clamped previous level).\n *\n * Clients that tracked the model by inferring it from `entry_appended` can consume this\n * instead. Additive: an old client that does not know the type filters it out.\n */\nexport interface RpcModelChangedEvent {\n\ttype: \"model_changed\";\n\tmodel: Model<any>;\n\tthinkingLevel: ThinkingLevel;\n\t/** Why the model changed: \"set\", \"cycle\", \"restore\", \"fallback\", or \"fallback-revert\". */\n\tsource: string;\n\t/** Selector provenance for `thinkingLevel` after the switch, when one was explicit. */\n\tthinkingSelection?: ThinkingSelection;\n}\n\n/** Emitted when the effective service tier or fast-mode state of the session changes. */\nexport interface RpcServiceTierChangedEvent {\n\ttype: \"service_tier_changed\";\n\ttier?: ServiceTier;\n\tfastMode: boolean;\n}\n/**\n * Emitted after the host swapped the live session behind this connection (new session,\n * fork, or switch), carrying the new authoritative identity.\n *\n * A replacement can be initiated by ANY attached client. Without this event the other\n * attached clients keep their stale identity and keep routing replacement-dependent\n * actions at the session that no longer exists. Additive: an old client that does not\n * know the type filters it out.\n */\nexport interface RpcSessionReplacedEvent {\n\ttype: \"session_replaced\";\n\t/**\n\t * Durable session id of the session now bound to this connection.\n\t *\n\t * Deliberately NOT `sessionId`: top-level `sessionId` is reserved for the\n\t * per-connection routing handle that multi-session hosts tag every record\n\t * with, and that tag is applied last - it would overwrite this value and\n\t * leave the event carrying no identity at all.\n\t */\n\tdurableSessionId: string;\n\t/** Session file backing the new session, absent for a deferred setup-only session. */\n\tsessionFile?: string;\n\tcwd: string;\n\tsessionName?: string;\n}\n\n/** Emitted after the loaded skill, extension, or MCP inventory changes. */\nexport interface RpcLoadedSurfacesChangedEvent {\n\ttype: \"loaded_surfaces_changed\";\n}\n\n/** Emitted after an account is added, removed, pinned, or blocked by refresh failure. */\nexport interface RpcAuthAccountsChangedEvent {\n\ttype: \"auth_accounts_changed\";\n\tprovider: string;\n}\n\n/** Emitted when the SDK failover engine advances to a different account slot. */\nexport interface RpcAccountFailoverEvent {\n\ttype: \"account_failover\";\n\tprovider: string;\n\tfrom: string;\n\tto: string;\n\treason: string;\n}\n"]}
|
|
@@ -31,7 +31,7 @@ export interface RpcSessionEntry {
|
|
|
31
31
|
closeStarted?: boolean;
|
|
32
32
|
}
|
|
33
33
|
export declare class RpcSessionRegistryError extends Error {
|
|
34
|
-
readonly code: "unknown_session" | "session_closing" | "session_path_in_use" | "invalid_path" | "open_failed"
|
|
34
|
+
readonly code: "unknown_session" | "session_closing" | "session_path_in_use" | "invalid_path" | "open_failed";
|
|
35
35
|
constructor(code: RpcSessionRegistryError["code"], reason?: string);
|
|
36
36
|
}
|
|
37
37
|
export interface RpcSessionRegistryOptions {
|
|
@@ -39,8 +39,6 @@ export interface RpcSessionRegistryOptions {
|
|
|
39
39
|
createRuntime: CreateAgentSessionRuntimeFactory;
|
|
40
40
|
/** Injectable clock (defaults to Date.now) so idle bookkeeping is testable. */
|
|
41
41
|
now?: () => number;
|
|
42
|
-
/** Cap on concurrently opening/open sessions; attach-on-open is exempt. */
|
|
43
|
-
maxSessions?: number;
|
|
44
42
|
/** Maximum time to wait for graceful runtime teardown before forced release. */
|
|
45
43
|
closeGraceMs?: number;
|
|
46
44
|
}
|
|
@@ -88,6 +86,5 @@ export declare class RpcSessionRegistry {
|
|
|
88
86
|
/** Reconcile path and durable identity after runtime replacement. */
|
|
89
87
|
private syncRuntimeMetadata;
|
|
90
88
|
private validateProfile;
|
|
91
|
-
private countActiveSessions;
|
|
92
89
|
}
|
|
93
90
|
//# sourceMappingURL=session-registry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-registry.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-registry.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAwB,MAAM,2CAA2C,CAAC;AAChG,OAAO,EACN,KAAK,yBAAyB,EAC9B,mBAAmB,EACnB,KAAK,gCAAgC,EAErC,MAAM,qCAAqC,CAAC;AAI7C,qEAAqE;AACrE,MAAM,WAAW,uBAAwB,SAAQ,yBAAyB;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,cAAc,GAAG,mBAAmB,CAAC;AACjD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAExE,MAAM,WAAW,eAAe;IAC/B,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC;IAChD,sEAAsE;IACtE,aAAa,CAAC,EAAE,UAAU,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC3C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wFAAwF;IACxF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,uGAAuG;IACvG,WAAW,EAAE,MAAM,CAAC;IACpB,sFAAsF;IACtF,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,eAAe,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,
|
|
1
|
+
{"version":3,"file":"session-registry.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -68,13 +68,6 @@ export class RpcSessionRegistry {
|
|
|
68
68
|
attached: true,
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
-
const maxSessions = this.options.maxSessions;
|
|
72
|
-
if (maxSessions !== undefined && this.countActiveSessions() >= maxSessions) {
|
|
73
|
-
// Reconnect-churn backstop: every open_session owns a complete runtime
|
|
74
|
-
// (hundreds of MB measured), so admission is bounded. Attachments to a
|
|
75
|
-
// live session add none and stay allowed via the branch above.
|
|
76
|
-
throw new RpcSessionRegistryError("too_many_sessions");
|
|
77
|
-
}
|
|
78
71
|
if (sessionPath)
|
|
79
72
|
this.reservations.add(sessionPath);
|
|
80
73
|
// Resume vs create parity (D1 + omo SenpiSessionRuntime.ts:198-200):
|
|
@@ -238,13 +231,5 @@ export class RpcSessionRegistry {
|
|
|
238
231
|
throw new RpcSessionRegistryError("invalid_path");
|
|
239
232
|
}
|
|
240
233
|
}
|
|
241
|
-
countActiveSessions() {
|
|
242
|
-
let count = 0;
|
|
243
|
-
for (const entry of this.entries.values()) {
|
|
244
|
-
if (entry.state === "opening" || entry.state === "open")
|
|
245
|
-
count += 1;
|
|
246
|
-
}
|
|
247
|
-
return count;
|
|
248
|
-
}
|
|
249
234
|
}
|
|
250
235
|
//# sourceMappingURL=session-registry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, realpathSync } from \"node:fs\";\nimport { basename, dirname, isAbsolute, resolve } from \"node:path\";\nimport { ProviderScope, runWithProviderScope } from \"@earendil-works/pi-ai/node/provider-scope\";\nimport {\n\ttype AgentSessionLaunchProfile,\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\tcreateAgentSessionRuntime,\n} from \"../../core/agent-session-runtime.ts\";\nimport { SessionManager } from \"../../core/session-manager.ts\";\nimport { beginSessionClose, closeMarkedSession, closeSession, type SessionTeardownHost } from \"./session-teardown.ts\";\n\n/** The immutable flags selected when a routing session is opened. */\nexport interface RpcSessionLaunchProfile extends AgentSessionLaunchProfile {\n\tsessionPath?: string;\n}\n\nexport type SessionRuntime = AgentSessionRuntime;\nexport type RpcSessionState = \"opening\" | \"open\" | \"closing\" | \"closed\";\n\nexport interface RpcSessionEntry {\n\tstate: RpcSessionState;\n\truntime?: SessionRuntime;\n\t/** Resolves replacement against the runtime currently owned by this entry. */\n\tswitchSession?: SessionRuntime[\"switchSession\"];\n\t/** Rebind callback installed by the shared RPC connection handler. */\n\trebindSession?: Parameters<SessionRuntime[\"setRebindSession\"]>[0];\n\tscope: ProviderScope;\n\tprofile: Readonly<RpcSessionLaunchProfile>;\n\tdurableSessionId?: string;\n\tsessionPath?: string;\n\t/** Canonical reservation key for path-opened sessions; matches the reservations set. */\n\treservationKey?: string;\n\t/** Current runtime cwd, which can change when a session is replaced. */\n\tcwd: string;\n\t/** Live attachments (open + later attaches). The runtime is disposed only when the last one closes. */\n\tattachments: number;\n\t/** Timestamp of the last routed command / observed activity; drives idle eviction. */\n\tlastCommandAt: number;\n\tlifecycleMutex: Promise<void>;\n\tcloseCompletion?: Promise<void>;\n\tcloseResolve?: () => void;\n\tcloseStarted?: boolean;\n}\n\nexport class RpcSessionRegistryError extends Error {\n\treadonly code:\n\t\t| \"unknown_session\"\n\t\t| \"session_closing\"\n\t\t| \"session_path_in_use\"\n\t\t| \"invalid_path\"\n\t\t| \"open_failed\"\n\t\t| \"too_many_sessions\";\n\n\tconstructor(code: RpcSessionRegistryError[\"code\"], reason?: string) {\n\t\tsuper(code === \"open_failed\" && reason ? `${code}: ${reason}` : code);\n\t\tthis.code = code;\n\t\tthis.name = \"RpcSessionRegistryError\";\n\t}\n}\n\nexport interface RpcSessionRegistryOptions {\n\tagentDir: string;\n\tcreateRuntime: CreateAgentSessionRuntimeFactory;\n\t/** Injectable clock (defaults to Date.now) so idle bookkeeping is testable. */\n\tnow?: () => number;\n\t/** Cap on concurrently opening/open sessions; attach-on-open is exempt. */\n\tmaxSessions?: number;\n\t/** Maximum time to wait for graceful runtime teardown before forced release. */\n\tcloseGraceMs?: number;\n}\n\nexport interface OpenRpcSession {\n\tsessionId: string;\n\tdurableSessionId: string;\n\tsessionPath?: string;\n\t/** True when this open attached to an already-open session instead of creating one. */\n\tattached?: boolean;\n}\n\nfunction canonicalPath(path: string): string {\n\tconst absolutePath = resolve(path);\n\tif (existsSync(absolutePath)) return realpathSync(absolutePath);\n\treturn `${realpathSync(dirname(absolutePath))}/${basename(absolutePath)}`;\n}\n\nfunction frozenProfile(profile: RpcSessionLaunchProfile): Readonly<RpcSessionLaunchProfile> {\n\treturn Object.freeze({\n\t\t...profile,\n\t\t...(profile.creationModel ? { creationModel: Object.freeze({ ...profile.creationModel }) } : {}),\n\t});\n}\n\n/** Process-local lifecycle owner for multi-session RPC runtimes. */\nexport class RpcSessionRegistry {\n\tprivate readonly entries = new Map<string, RpcSessionEntry>();\n\tprivate readonly reservations = new Set<string>();\n\tprivate readonly teardownHost: SessionTeardownHost;\n\tprivate nextHandle = 0;\n\tprivate readonly options: RpcSessionRegistryOptions;\n\tprivate readonly now: () => number;\n\treadonly closeGraceMs: number;\n\n\tconstructor(options: RpcSessionRegistryOptions) {\n\t\tthis.options = options;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.closeGraceMs = options.closeGraceMs ?? 10_000;\n\t\tthis.teardownHost = {\n\t\t\tcloseGraceMs: this.closeGraceMs,\n\t\t\tget: (handle) => this.entries.get(handle),\n\t\t\tdelete: (handle) => this.entries.delete(handle),\n\t\t\treleaseReservation: (key) => this.reservations.delete(key),\n\t\t\tsync: () => this.syncRuntimeMetadata(),\n\t\t};\n\t}\n\n\t/** Number of live entries, including ones still opening or closing. */\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\tasync openSession(profile: RpcSessionLaunchProfile): Promise<OpenRpcSession> {\n\t\tthis.validateProfile(profile);\n\t\tthis.syncRuntimeMetadata();\n\t\tconst sessionPath = profile.sessionPath ? canonicalPath(profile.sessionPath) : undefined;\n\t\tif (sessionPath && this.reservations.has(sessionPath)) {\n\t\t\t// Attach-on-open: a live session outlives individual client attachments, so a\n\t\t\t// resume (or a second surface) for an already-hosted path joins the existing\n\t\t\t// runtime instead of failing. Entries still opening or closing keep the\n\t\t\t// exclusive reservation and reject as before.\n\t\t\tconst existing = [...this.entries].find(\n\t\t\t\t([, entry]) => entry.reservationKey === sessionPath && entry.state === \"open\",\n\t\t\t);\n\t\t\tif (!existing) throw new RpcSessionRegistryError(\"session_path_in_use\");\n\t\t\tconst [handle, entry] = existing;\n\t\t\tentry.attachments += 1;\n\t\t\tif (!entry.durableSessionId) throw new RpcSessionRegistryError(\"session_path_in_use\");\n\t\t\tentry.lastCommandAt = this.now();\n\t\t\treturn {\n\t\t\t\tsessionId: handle,\n\t\t\t\tdurableSessionId: entry.durableSessionId,\n\t\t\t\tsessionPath: entry.sessionPath,\n\t\t\t\tattached: true,\n\t\t\t};\n\t\t}\n\t\tconst maxSessions = this.options.maxSessions;\n\t\tif (maxSessions !== undefined && this.countActiveSessions() >= maxSessions) {\n\t\t\t// Reconnect-churn backstop: every open_session owns a complete runtime\n\t\t\t// (hundreds of MB measured), so admission is bounded. Attachments to a\n\t\t\t// live session add none and stay allowed via the branch above.\n\t\t\tthrow new RpcSessionRegistryError(\"too_many_sessions\");\n\t\t}\n\t\tif (sessionPath) this.reservations.add(sessionPath);\n\n\t\t// Resume vs create parity (D1 + omo SenpiSessionRuntime.ts:198-200):\n\t\t// Create-only launch semantics mirror classic startup flags. A resumed\n\t\t// session restores its persisted model and thinking level instead of being\n\t\t// overridden by the new open_session request.\n\t\tconst isResume = sessionPath !== undefined && existsSync(sessionPath);\n\t\tconst storedProfile = frozenProfile({ ...profile, ...(sessionPath ? { sessionPath } : {}) });\n\t\tconst runtimeProfile = isResume\n\t\t\t? frozenProfile({ ...storedProfile, creationModel: undefined, initialThinkingLevel: undefined })\n\t\t\t: storedProfile;\n\n\t\tconst handle = `rpc-${++this.nextHandle}`;\n\t\tconst entry: RpcSessionEntry = {\n\t\t\tstate: \"opening\",\n\t\t\tscope: new ProviderScope(),\n\t\t\tprofile: storedProfile,\n\t\t\tsessionPath,\n\t\t\treservationKey: sessionPath,\n\t\t\tcwd: storedProfile.cwd,\n\t\t\tattachments: 1,\n\t\t\tlastCommandAt: this.now(),\n\t\t\tlifecycleMutex: Promise.resolve(),\n\t\t};\n\t\tentry.switchSession = (sessionPath, options) => {\n\t\t\tconst operation = entry.lifecycleMutex.then(async () => {\n\t\t\t\tif (entry.state !== \"open\" || !entry.runtime) {\n\t\t\t\t\tthrow new RpcSessionRegistryError(\"unknown_session\");\n\t\t\t\t}\n\t\t\t\tconst runtime = entry.runtime;\n\t\t\t\tconst cwdOverride = options?.cwdOverride;\n\t\t\t\tconst cwdChanged = cwdOverride !== undefined && runtime.session.sessionManager.getCwd() !== cwdOverride;\n\t\t\t\tconst result = await runtime.switchSession(sessionPath, options);\n\t\t\t\tif (result.cancelled || !cwdChanged) return result;\n\n\t\t\t\t// A multi-session binding outlives an individual replacement. Keep the\n\t\t\t\t// entry's runtime object aligned with the replacement so every attached\n\t\t\t\t// client resolves getters and future commands against the new cwd-bound\n\t\t\t\t// runtime, not the object created during open_session.\n\t\t\t\tconst replacement = new AgentSessionRuntime(\n\t\t\t\t\truntime.session,\n\t\t\t\t\truntime.services,\n\t\t\t\t\tthis.options.createRuntime,\n\t\t\t\t\t[...runtime.diagnostics],\n\t\t\t\t\truntime.modelFallbackMessage,\n\t\t\t\t\truntime.launchProfile,\n\t\t\t\t);\n\t\t\t\treplacement.setRebindSession(entry.rebindSession);\n\t\t\t\tentry.runtime = replacement;\n\t\t\t\tthis.syncRuntimeMetadata();\n\t\t\t\treturn result;\n\t\t\t});\n\t\t\tentry.lifecycleMutex = operation.then(\n\t\t\t\t() => undefined,\n\t\t\t\t() => undefined,\n\t\t\t);\n\t\t\treturn operation;\n\t\t};\n\t\tthis.entries.set(handle, entry);\n\t\ttry {\n\t\t\tconst manager = sessionPath\n\t\t\t\t? SessionManager.open(sessionPath, undefined, storedProfile.cwd)\n\t\t\t\t: SessionManager.create(storedProfile.cwd);\n\t\t\tentry.runtime = await runWithProviderScope(entry.scope, () =>\n\t\t\t\tcreateAgentSessionRuntime(this.options.createRuntime, {\n\t\t\t\t\tcwd: manager.getCwd(),\n\t\t\t\t\tagentDir: this.options.agentDir,\n\t\t\t\t\tsessionManager: manager,\n\t\t\t\t\tlaunchProfile: runtimeProfile,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tentry.durableSessionId = manager.getSessionId();\n\t\t\tentry.sessionPath ??= manager.getSessionFile();\n\t\t\tentry.state = \"open\";\n\t\t\treturn { sessionId: handle, durableSessionId: entry.durableSessionId, sessionPath: entry.sessionPath };\n\t\t} catch (error) {\n\t\t\t// Runtime construction may have started extensions, watchers, and provider\n\t\t\t// registrations before it rejects. Keep the reservation and entry private\n\t\t\t// until all of those resources have been torn down, then release them as\n\t\t\t// one rollback so the path can immediately be opened again.\n\t\t\ttry {\n\t\t\t\tawait entry.runtime?.dispose();\n\t\t\t} catch {\n\t\t\t\t// The original construction error remains the externally visible cause.\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tawait entry.scope.close?.();\n\t\t\t\t} finally {\n\t\t\t\t\tthis.entries.delete(handle);\n\t\t\t\t\tif (sessionPath) this.reservations.delete(sessionPath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (error instanceof RpcSessionRegistryError) throw error;\n\t\t\tthrow new RpcSessionRegistryError(\"open_failed\", error instanceof Error ? error.message : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Read-only lookup with no state transitions or attachment accounting.\n\t * Exists so lifecycle decisions (e.g. deferring a dropped connection's\n\t * release while a turn is still streaming) can inspect the live entry\n\t * without claiming it.\n\t */\n\tpeek(handle: string): RpcSessionEntry | undefined {\n\t\treturn this.entries.get(handle);\n\t}\n\n\tgetForCommand(handle: string, command: string): RpcSessionEntry {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (!entry) throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tif (entry.state === \"closing\" && ![\"abort\", \"abort_bash\", \"extension_ui_response\"].includes(command)) {\n\t\t\tthrow new RpcSessionRegistryError(\"session_closing\");\n\t\t}\n\t\tif (entry.state !== \"open\" && entry.state !== \"closing\") throw new RpcSessionRegistryError(\"unknown_session\");\n\t\t// Every routed command counts as activity for idle eviction. Lookups that\n\t\t// must not refresh idleness (sweeps, listing) use peek()/list() instead.\n\t\tentry.lastCommandAt = this.now();\n\t\treturn entry;\n\t}\n\n\t/** Starts a close synchronously and returns the live entry for routing decisions. */\n\tbeginClose(handle: string, onRole?: (finalizer: boolean) => void): RpcSessionEntry {\n\t\treturn beginSessionClose(this.teardownHost, handle, onRole);\n\t}\n\n\tasync close(handle: string): Promise<void> {\n\t\treturn closeSession(this.teardownHost, handle);\n\t}\n\n\t/** Completes a close previously made visible by beginClose(). */\n\tasync closeMarked(handle: string): Promise<void> {\n\t\treturn closeMarkedSession(this.teardownHost, handle);\n\t}\n\n\tlist(): Array<{\n\t\tsessionId: string;\n\t\tdurableSessionId?: string;\n\t\tsessionPath?: string;\n\t\tcwd: string;\n\t\tname?: string;\n\t\tstatus: RpcSessionState;\n\t}> {\n\t\tthis.syncRuntimeMetadata();\n\t\treturn [...this.entries].map(([sessionId, entry]) => ({\n\t\t\tsessionId,\n\t\t\tdurableSessionId: entry.durableSessionId,\n\t\t\tsessionPath: entry.sessionPath,\n\t\t\tcwd: entry.cwd,\n\t\t\tname: entry.runtime?.session.sessionManager.getSessionName(),\n\t\t\tstatus: entry.state,\n\t\t}));\n\t}\n\n\t/** Reconcile path and durable identity after runtime replacement. */\n\tprivate syncRuntimeMetadata(): void {\n\t\tfor (const entry of this.entries.values()) {\n\t\t\tconst manager = entry.runtime?.session.sessionManager;\n\t\t\tif (!manager) continue;\n\t\t\tconst currentPath = manager.getSessionFile();\n\t\t\t// Preserve the originally canonicalized key while the runtime still points at\n\t\t\t// the same path. SessionManager may expose a symlink-resolved spelling after\n\t\t\t// opening a file that did not exist yet; treating that as replacement would\n\t\t\t// break ordinary attach-on-open aliases.\n\t\t\tif (currentPath !== entry.sessionPath) {\n\t\t\t\tconst currentKey = currentPath ? canonicalPath(currentPath) : undefined;\n\t\t\t\tif (entry.reservationKey) this.reservations.delete(entry.reservationKey);\n\t\t\t\tif (currentKey) this.reservations.add(currentKey);\n\t\t\t\tentry.reservationKey = currentKey;\n\t\t\t\tentry.sessionPath = currentPath;\n\t\t\t}\n\t\t\tentry.durableSessionId = manager.getSessionId();\n\t\t\tentry.cwd = manager.getCwd();\n\t\t}\n\t}\n\n\tprivate validateProfile(profile: RpcSessionLaunchProfile): void {\n\t\tif (!isAbsolute(profile.cwd) || (profile.sessionPath !== undefined && !isAbsolute(profile.sessionPath))) {\n\t\t\tthrow new RpcSessionRegistryError(\"invalid_path\");\n\t\t}\n\t}\n\n\tprivate countActiveSessions(): number {\n\t\tlet count = 0;\n\t\tfor (const entry of this.entries.values()) {\n\t\t\tif (entry.state === \"opening\" || entry.state === \"open\") count += 1;\n\t\t}\n\t\treturn count;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAChG,OAAO,EAEN,mBAAmB,EAEnB,yBAAyB,GACzB,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,YAAY,EAA4B,MAAM,uBAAuB,CAAC;AAmCtH,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAGjD,YAAY,IAAqC,EAAE,MAAe;QACjE,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACvC,CAAC;CACD;AAmBD,SAAS,aAAa,CAAC,IAAY;IAClC,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,YAAY,CAAC,YAAY,CAAC,CAAC;IAChE,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,IAAI,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED,SAAS,aAAa,CAAC,OAAgC;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC;QACpB,GAAG,OAAO;QACV,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChG,CAAC,CAAC;AACJ,CAAC;AAED,oEAAoE;AACpE,MAAM,OAAO,kBAAkB;IAS9B,YAAY,OAAkC;QAR7B,YAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;QAC7C,iBAAY,GAAG,IAAI,GAAG,EAAU,CAAC;QAE1C,eAAU,GAAG,CAAC,CAAC;QAMtB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,MAAM,CAAC;QACnD,IAAI,CAAC,YAAY,GAAG;YACnB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YACzC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC;YAC/C,kBAAkB,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC;YAC1D,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE;SACtC,CAAC;IACH,CAAC;IAED,uEAAuE;IACvE,IAAI,IAAI;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAgC;QACjD,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACzF,IAAI,WAAW,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YACvD,8EAA8E;YAC9E,6EAA6E;YAC7E,wEAAwE;YACxE,8CAA8C;YAC9C,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CACtC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,KAAK,WAAW,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,CAC7E,CAAC;YACF,IAAI,CAAC,QAAQ;gBAAE,MAAM,IAAI,uBAAuB,CAAC,qBAAqB,CAAC,CAAC;YACxE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,QAAQ,CAAC;YACjC,KAAK,CAAC,WAAW,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,gBAAgB;gBAAE,MAAM,IAAI,uBAAuB,CAAC,qBAAqB,CAAC,CAAC;YACtF,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACjC,OAAO;gBACN,SAAS,EAAE,MAAM;gBACjB,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;gBACxC,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,QAAQ,EAAE,IAAI;aACd,CAAC;QACH,CAAC;QACD,IAAI,WAAW;YAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAEpD,qEAAqE;QACrE,uEAAuE;QACvE,2EAA2E;QAC3E,8CAA8C;QAC9C,MAAM,QAAQ,GAAG,WAAW,KAAK,SAAS,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;QACtE,MAAM,aAAa,GAAG,aAAa,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC7F,MAAM,cAAc,GAAG,QAAQ;YAC9B,CAAC,CAAC,aAAa,CAAC,EAAE,GAAG,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,oBAAoB,EAAE,SAAS,EAAE,CAAC;YAChG,CAAC,CAAC,aAAa,CAAC;QAEjB,MAAM,MAAM,GAAG,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAoB;YAC9B,KAAK,EAAE,SAAS;YAChB,KAAK,EAAE,IAAI,aAAa,EAAE;YAC1B,OAAO,EAAE,aAAa;YACtB,WAAW;YACX,cAAc,EAAE,WAAW;YAC3B,GAAG,EAAE,aAAa,CAAC,GAAG;YACtB,WAAW,EAAE,CAAC;YACd,aAAa,EAAE,IAAI,CAAC,GAAG,EAAE;YACzB,cAAc,EAAE,OAAO,CAAC,OAAO,EAAE;SACjC,CAAC;QACF,KAAK,CAAC,aAAa,GAAG,CAAC,WAAW,EAAE,OAAO,EAAE,EAAE;YAC9C,MAAM,SAAS,GAAG,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;gBACtD,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;oBAC9C,MAAM,IAAI,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;gBACtD,CAAC;gBACD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;gBAC9B,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,CAAC;gBACzC,MAAM,UAAU,GAAG,WAAW,KAAK,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,MAAM,EAAE,KAAK,WAAW,CAAC;gBACxG,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;gBACjE,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC,UAAU;oBAAE,OAAO,MAAM,CAAC;gBAEnD,uEAAuE;gBACvE,wEAAwE;gBACxE,wEAAwE;gBACxE,uDAAuD;gBACvD,MAAM,WAAW,GAAG,IAAI,mBAAmB,CAC1C,OAAO,CAAC,OAAO,EACf,OAAO,CAAC,QAAQ,EAChB,IAAI,CAAC,OAAO,CAAC,aAAa,EAC1B,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,EACxB,OAAO,CAAC,oBAAoB,EAC5B,OAAO,CAAC,aAAa,CACrB,CAAC;gBACF,WAAW,CAAC,gBAAgB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;gBAClD,KAAK,CAAC,OAAO,GAAG,WAAW,CAAC;gBAC5B,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC3B,OAAO,MAAM,CAAC;YACf,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,cAAc,GAAG,SAAS,CAAC,IAAI,CACpC,GAAG,EAAE,CAAC,SAAS,EACf,GAAG,EAAE,CAAC,SAAS,CACf,CAAC;YACF,OAAO,SAAS,CAAC;QAClB,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,WAAW;gBAC1B,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,aAAa,CAAC,GAAG,CAAC;gBAChE,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;YAC5C,KAAK,CAAC,OAAO,GAAG,MAAM,oBAAoB,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAC5D,yBAAyB,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE;gBACrD,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE;gBACrB,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;gBAC/B,cAAc,EAAE,OAAO;gBACvB,aAAa,EAAE,cAAc;aAC7B,CAAC,CACF,CAAC;YACF,KAAK,CAAC,gBAAgB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YAChD,KAAK,CAAC,WAAW,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC;YAC/C,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC;YACrB,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC;QACxG,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,2EAA2E;YAC3E,0EAA0E;YAC1E,yEAAyE;YACzE,4DAA4D;YAC5D,IAAI,CAAC;gBACJ,MAAM,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACR,wEAAwE;YACzE,CAAC;oBAAS,CAAC;gBACV,IAAI,CAAC;oBACJ,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;gBAC7B,CAAC;wBAAS,CAAC;oBACV,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBAC5B,IAAI,WAAW;wBAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;gBACxD,CAAC;YACF,CAAC;YACD,IAAI,KAAK,YAAY,uBAAuB;gBAAE,MAAM,KAAK,CAAC;YAC1D,MAAM,IAAI,uBAAuB,CAAC,aAAa,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACtG,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAAC,MAAc;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,aAAa,CAAC,MAAc,EAAE,OAAe;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;QACjE,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,CAAC,OAAO,EAAE,YAAY,EAAE,uBAAuB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACtG,MAAM,IAAI,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;QACtD,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,MAAM,IAAI,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;QAC9G,0EAA0E;QAC1E,yEAAyE;QACzE,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACd,CAAC;IAED,qFAAqF;IACrF,UAAU,CAAC,MAAc,EAAE,MAAqC;QAC/D,OAAO,iBAAiB,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,MAAc;QACzB,OAAO,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAChD,CAAC;IAED,iEAAiE;IACjE,KAAK,CAAC,WAAW,CAAC,MAAc;QAC/B,OAAO,kBAAkB,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;IAED,IAAI;QAQH,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;YACrD,SAAS;YACT,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;YACxC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE;YAC5D,MAAM,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,qEAAqE;IAC7D,mBAAmB;QAC1B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,cAAc,CAAC;YACtD,IAAI,CAAC,OAAO;gBAAE,SAAS;YACvB,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;YAC7C,8EAA8E;YAC9E,6EAA6E;YAC7E,4EAA4E;YAC5E,yCAAyC;YACzC,IAAI,WAAW,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC;gBACvC,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACxE,IAAI,KAAK,CAAC,cAAc;oBAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;gBACzE,IAAI,UAAU;oBAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAClD,KAAK,CAAC,cAAc,GAAG,UAAU,CAAC;gBAClC,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;YACjC,CAAC;YACD,KAAK,CAAC,gBAAgB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;YAChD,KAAK,CAAC,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;QAC9B,CAAC;IACF,CAAC;IAEO,eAAe,CAAC,OAAgC;QACvD,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;YACzG,MAAM,IAAI,uBAAuB,CAAC,cAAc,CAAC,CAAC;QACnD,CAAC;IACF,CAAC;CACD","sourcesContent":["import { existsSync, realpathSync } from \"node:fs\";\nimport { basename, dirname, isAbsolute, resolve } from \"node:path\";\nimport { ProviderScope, runWithProviderScope } from \"@earendil-works/pi-ai/node/provider-scope\";\nimport {\n\ttype AgentSessionLaunchProfile,\n\tAgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\tcreateAgentSessionRuntime,\n} from \"../../core/agent-session-runtime.ts\";\nimport { SessionManager } from \"../../core/session-manager.ts\";\nimport { beginSessionClose, closeMarkedSession, closeSession, type SessionTeardownHost } from \"./session-teardown.ts\";\n\n/** The immutable flags selected when a routing session is opened. */\nexport interface RpcSessionLaunchProfile extends AgentSessionLaunchProfile {\n\tsessionPath?: string;\n}\n\nexport type SessionRuntime = AgentSessionRuntime;\nexport type RpcSessionState = \"opening\" | \"open\" | \"closing\" | \"closed\";\n\nexport interface RpcSessionEntry {\n\tstate: RpcSessionState;\n\truntime?: SessionRuntime;\n\t/** Resolves replacement against the runtime currently owned by this entry. */\n\tswitchSession?: SessionRuntime[\"switchSession\"];\n\t/** Rebind callback installed by the shared RPC connection handler. */\n\trebindSession?: Parameters<SessionRuntime[\"setRebindSession\"]>[0];\n\tscope: ProviderScope;\n\tprofile: Readonly<RpcSessionLaunchProfile>;\n\tdurableSessionId?: string;\n\tsessionPath?: string;\n\t/** Canonical reservation key for path-opened sessions; matches the reservations set. */\n\treservationKey?: string;\n\t/** Current runtime cwd, which can change when a session is replaced. */\n\tcwd: string;\n\t/** Live attachments (open + later attaches). The runtime is disposed only when the last one closes. */\n\tattachments: number;\n\t/** Timestamp of the last routed command / observed activity; drives idle eviction. */\n\tlastCommandAt: number;\n\tlifecycleMutex: Promise<void>;\n\tcloseCompletion?: Promise<void>;\n\tcloseResolve?: () => void;\n\tcloseStarted?: boolean;\n}\n\nexport class RpcSessionRegistryError extends Error {\n\treadonly code: \"unknown_session\" | \"session_closing\" | \"session_path_in_use\" | \"invalid_path\" | \"open_failed\";\n\n\tconstructor(code: RpcSessionRegistryError[\"code\"], reason?: string) {\n\t\tsuper(code === \"open_failed\" && reason ? `${code}: ${reason}` : code);\n\t\tthis.code = code;\n\t\tthis.name = \"RpcSessionRegistryError\";\n\t}\n}\n\nexport interface RpcSessionRegistryOptions {\n\tagentDir: string;\n\tcreateRuntime: CreateAgentSessionRuntimeFactory;\n\t/** Injectable clock (defaults to Date.now) so idle bookkeeping is testable. */\n\tnow?: () => number;\n\t/** Maximum time to wait for graceful runtime teardown before forced release. */\n\tcloseGraceMs?: number;\n}\n\nexport interface OpenRpcSession {\n\tsessionId: string;\n\tdurableSessionId: string;\n\tsessionPath?: string;\n\t/** True when this open attached to an already-open session instead of creating one. */\n\tattached?: boolean;\n}\n\nfunction canonicalPath(path: string): string {\n\tconst absolutePath = resolve(path);\n\tif (existsSync(absolutePath)) return realpathSync(absolutePath);\n\treturn `${realpathSync(dirname(absolutePath))}/${basename(absolutePath)}`;\n}\n\nfunction frozenProfile(profile: RpcSessionLaunchProfile): Readonly<RpcSessionLaunchProfile> {\n\treturn Object.freeze({\n\t\t...profile,\n\t\t...(profile.creationModel ? { creationModel: Object.freeze({ ...profile.creationModel }) } : {}),\n\t});\n}\n\n/** Process-local lifecycle owner for multi-session RPC runtimes. */\nexport class RpcSessionRegistry {\n\tprivate readonly entries = new Map<string, RpcSessionEntry>();\n\tprivate readonly reservations = new Set<string>();\n\tprivate readonly teardownHost: SessionTeardownHost;\n\tprivate nextHandle = 0;\n\tprivate readonly options: RpcSessionRegistryOptions;\n\tprivate readonly now: () => number;\n\treadonly closeGraceMs: number;\n\n\tconstructor(options: RpcSessionRegistryOptions) {\n\t\tthis.options = options;\n\t\tthis.now = options.now ?? Date.now;\n\t\tthis.closeGraceMs = options.closeGraceMs ?? 10_000;\n\t\tthis.teardownHost = {\n\t\t\tcloseGraceMs: this.closeGraceMs,\n\t\t\tget: (handle) => this.entries.get(handle),\n\t\t\tdelete: (handle) => this.entries.delete(handle),\n\t\t\treleaseReservation: (key) => this.reservations.delete(key),\n\t\t\tsync: () => this.syncRuntimeMetadata(),\n\t\t};\n\t}\n\n\t/** Number of live entries, including ones still opening or closing. */\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n\n\tasync openSession(profile: RpcSessionLaunchProfile): Promise<OpenRpcSession> {\n\t\tthis.validateProfile(profile);\n\t\tthis.syncRuntimeMetadata();\n\t\tconst sessionPath = profile.sessionPath ? canonicalPath(profile.sessionPath) : undefined;\n\t\tif (sessionPath && this.reservations.has(sessionPath)) {\n\t\t\t// Attach-on-open: a live session outlives individual client attachments, so a\n\t\t\t// resume (or a second surface) for an already-hosted path joins the existing\n\t\t\t// runtime instead of failing. Entries still opening or closing keep the\n\t\t\t// exclusive reservation and reject as before.\n\t\t\tconst existing = [...this.entries].find(\n\t\t\t\t([, entry]) => entry.reservationKey === sessionPath && entry.state === \"open\",\n\t\t\t);\n\t\t\tif (!existing) throw new RpcSessionRegistryError(\"session_path_in_use\");\n\t\t\tconst [handle, entry] = existing;\n\t\t\tentry.attachments += 1;\n\t\t\tif (!entry.durableSessionId) throw new RpcSessionRegistryError(\"session_path_in_use\");\n\t\t\tentry.lastCommandAt = this.now();\n\t\t\treturn {\n\t\t\t\tsessionId: handle,\n\t\t\t\tdurableSessionId: entry.durableSessionId,\n\t\t\t\tsessionPath: entry.sessionPath,\n\t\t\t\tattached: true,\n\t\t\t};\n\t\t}\n\t\tif (sessionPath) this.reservations.add(sessionPath);\n\n\t\t// Resume vs create parity (D1 + omo SenpiSessionRuntime.ts:198-200):\n\t\t// Create-only launch semantics mirror classic startup flags. A resumed\n\t\t// session restores its persisted model and thinking level instead of being\n\t\t// overridden by the new open_session request.\n\t\tconst isResume = sessionPath !== undefined && existsSync(sessionPath);\n\t\tconst storedProfile = frozenProfile({ ...profile, ...(sessionPath ? { sessionPath } : {}) });\n\t\tconst runtimeProfile = isResume\n\t\t\t? frozenProfile({ ...storedProfile, creationModel: undefined, initialThinkingLevel: undefined })\n\t\t\t: storedProfile;\n\n\t\tconst handle = `rpc-${++this.nextHandle}`;\n\t\tconst entry: RpcSessionEntry = {\n\t\t\tstate: \"opening\",\n\t\t\tscope: new ProviderScope(),\n\t\t\tprofile: storedProfile,\n\t\t\tsessionPath,\n\t\t\treservationKey: sessionPath,\n\t\t\tcwd: storedProfile.cwd,\n\t\t\tattachments: 1,\n\t\t\tlastCommandAt: this.now(),\n\t\t\tlifecycleMutex: Promise.resolve(),\n\t\t};\n\t\tentry.switchSession = (sessionPath, options) => {\n\t\t\tconst operation = entry.lifecycleMutex.then(async () => {\n\t\t\t\tif (entry.state !== \"open\" || !entry.runtime) {\n\t\t\t\t\tthrow new RpcSessionRegistryError(\"unknown_session\");\n\t\t\t\t}\n\t\t\t\tconst runtime = entry.runtime;\n\t\t\t\tconst cwdOverride = options?.cwdOverride;\n\t\t\t\tconst cwdChanged = cwdOverride !== undefined && runtime.session.sessionManager.getCwd() !== cwdOverride;\n\t\t\t\tconst result = await runtime.switchSession(sessionPath, options);\n\t\t\t\tif (result.cancelled || !cwdChanged) return result;\n\n\t\t\t\t// A multi-session binding outlives an individual replacement. Keep the\n\t\t\t\t// entry's runtime object aligned with the replacement so every attached\n\t\t\t\t// client resolves getters and future commands against the new cwd-bound\n\t\t\t\t// runtime, not the object created during open_session.\n\t\t\t\tconst replacement = new AgentSessionRuntime(\n\t\t\t\t\truntime.session,\n\t\t\t\t\truntime.services,\n\t\t\t\t\tthis.options.createRuntime,\n\t\t\t\t\t[...runtime.diagnostics],\n\t\t\t\t\truntime.modelFallbackMessage,\n\t\t\t\t\truntime.launchProfile,\n\t\t\t\t);\n\t\t\t\treplacement.setRebindSession(entry.rebindSession);\n\t\t\t\tentry.runtime = replacement;\n\t\t\t\tthis.syncRuntimeMetadata();\n\t\t\t\treturn result;\n\t\t\t});\n\t\t\tentry.lifecycleMutex = operation.then(\n\t\t\t\t() => undefined,\n\t\t\t\t() => undefined,\n\t\t\t);\n\t\t\treturn operation;\n\t\t};\n\t\tthis.entries.set(handle, entry);\n\t\ttry {\n\t\t\tconst manager = sessionPath\n\t\t\t\t? SessionManager.open(sessionPath, undefined, storedProfile.cwd)\n\t\t\t\t: SessionManager.create(storedProfile.cwd);\n\t\t\tentry.runtime = await runWithProviderScope(entry.scope, () =>\n\t\t\t\tcreateAgentSessionRuntime(this.options.createRuntime, {\n\t\t\t\t\tcwd: manager.getCwd(),\n\t\t\t\t\tagentDir: this.options.agentDir,\n\t\t\t\t\tsessionManager: manager,\n\t\t\t\t\tlaunchProfile: runtimeProfile,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tentry.durableSessionId = manager.getSessionId();\n\t\t\tentry.sessionPath ??= manager.getSessionFile();\n\t\t\tentry.state = \"open\";\n\t\t\treturn { sessionId: handle, durableSessionId: entry.durableSessionId, sessionPath: entry.sessionPath };\n\t\t} catch (error) {\n\t\t\t// Runtime construction may have started extensions, watchers, and provider\n\t\t\t// registrations before it rejects. Keep the reservation and entry private\n\t\t\t// until all of those resources have been torn down, then release them as\n\t\t\t// one rollback so the path can immediately be opened again.\n\t\t\ttry {\n\t\t\t\tawait entry.runtime?.dispose();\n\t\t\t} catch {\n\t\t\t\t// The original construction error remains the externally visible cause.\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tawait entry.scope.close?.();\n\t\t\t\t} finally {\n\t\t\t\t\tthis.entries.delete(handle);\n\t\t\t\t\tif (sessionPath) this.reservations.delete(sessionPath);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (error instanceof RpcSessionRegistryError) throw error;\n\t\t\tthrow new RpcSessionRegistryError(\"open_failed\", error instanceof Error ? error.message : undefined);\n\t\t}\n\t}\n\n\t/**\n\t * Read-only lookup with no state transitions or attachment accounting.\n\t * Exists so lifecycle decisions (e.g. deferring a dropped connection's\n\t * release while a turn is still streaming) can inspect the live entry\n\t * without claiming it.\n\t */\n\tpeek(handle: string): RpcSessionEntry | undefined {\n\t\treturn this.entries.get(handle);\n\t}\n\n\tgetForCommand(handle: string, command: string): RpcSessionEntry {\n\t\tconst entry = this.entries.get(handle);\n\t\tif (!entry) throw new RpcSessionRegistryError(\"unknown_session\");\n\t\tif (entry.state === \"closing\" && ![\"abort\", \"abort_bash\", \"extension_ui_response\"].includes(command)) {\n\t\t\tthrow new RpcSessionRegistryError(\"session_closing\");\n\t\t}\n\t\tif (entry.state !== \"open\" && entry.state !== \"closing\") throw new RpcSessionRegistryError(\"unknown_session\");\n\t\t// Every routed command counts as activity for idle eviction. Lookups that\n\t\t// must not refresh idleness (sweeps, listing) use peek()/list() instead.\n\t\tentry.lastCommandAt = this.now();\n\t\treturn entry;\n\t}\n\n\t/** Starts a close synchronously and returns the live entry for routing decisions. */\n\tbeginClose(handle: string, onRole?: (finalizer: boolean) => void): RpcSessionEntry {\n\t\treturn beginSessionClose(this.teardownHost, handle, onRole);\n\t}\n\n\tasync close(handle: string): Promise<void> {\n\t\treturn closeSession(this.teardownHost, handle);\n\t}\n\n\t/** Completes a close previously made visible by beginClose(). */\n\tasync closeMarked(handle: string): Promise<void> {\n\t\treturn closeMarkedSession(this.teardownHost, handle);\n\t}\n\n\tlist(): Array<{\n\t\tsessionId: string;\n\t\tdurableSessionId?: string;\n\t\tsessionPath?: string;\n\t\tcwd: string;\n\t\tname?: string;\n\t\tstatus: RpcSessionState;\n\t}> {\n\t\tthis.syncRuntimeMetadata();\n\t\treturn [...this.entries].map(([sessionId, entry]) => ({\n\t\t\tsessionId,\n\t\t\tdurableSessionId: entry.durableSessionId,\n\t\t\tsessionPath: entry.sessionPath,\n\t\t\tcwd: entry.cwd,\n\t\t\tname: entry.runtime?.session.sessionManager.getSessionName(),\n\t\t\tstatus: entry.state,\n\t\t}));\n\t}\n\n\t/** Reconcile path and durable identity after runtime replacement. */\n\tprivate syncRuntimeMetadata(): void {\n\t\tfor (const entry of this.entries.values()) {\n\t\t\tconst manager = entry.runtime?.session.sessionManager;\n\t\t\tif (!manager) continue;\n\t\t\tconst currentPath = manager.getSessionFile();\n\t\t\t// Preserve the originally canonicalized key while the runtime still points at\n\t\t\t// the same path. SessionManager may expose a symlink-resolved spelling after\n\t\t\t// opening a file that did not exist yet; treating that as replacement would\n\t\t\t// break ordinary attach-on-open aliases.\n\t\t\tif (currentPath !== entry.sessionPath) {\n\t\t\t\tconst currentKey = currentPath ? canonicalPath(currentPath) : undefined;\n\t\t\t\tif (entry.reservationKey) this.reservations.delete(entry.reservationKey);\n\t\t\t\tif (currentKey) this.reservations.add(currentKey);\n\t\t\t\tentry.reservationKey = currentKey;\n\t\t\t\tentry.sessionPath = currentPath;\n\t\t\t}\n\t\t\tentry.durableSessionId = manager.getSessionId();\n\t\t\tentry.cwd = manager.getCwd();\n\t\t}\n\t}\n\n\tprivate validateProfile(profile: RpcSessionLaunchProfile): void {\n\t\tif (!isAbsolute(profile.cwd) || (profile.sessionPath !== undefined && !isAbsolute(profile.sessionPath))) {\n\t\t\tthrow new RpcSessionRegistryError(\"invalid_path\");\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Filesystem identity of one bound socket path entry. */
|
|
2
|
+
export interface SocketFileIdentity {
|
|
3
|
+
readonly dev: number;
|
|
4
|
+
readonly ino: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Name of the sidecar, inside the lifecycle supervisor's private scratch
|
|
8
|
+
* directory, that records which public socket entry that supervisor bound. The
|
|
9
|
+
* directory is per-supervisor, so a replacement supervisor's token can never be
|
|
10
|
+
* mistaken for the old one's.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PUBLIC_SOCKET_IDENTITY_FILE = "public-socket.owner";
|
|
13
|
+
/** Default bound on waiting for a supervisor to publish its ownership token. */
|
|
14
|
+
export declare const SOCKET_IDENTITY_WAIT_MS = 30000;
|
|
15
|
+
/** Reads the current identity of a socket path entry; `undefined` once absent. */
|
|
16
|
+
export declare function statSocketIdentity(socketPath: string): Promise<SocketFileIdentity | undefined>;
|
|
17
|
+
/** Records a bound socket identity for teardown paths that cannot hold it in memory. */
|
|
18
|
+
export declare function writeSocketIdentityFile(path: string, identity: SocketFileIdentity): Promise<void>;
|
|
19
|
+
export declare function readSocketIdentityFile(path: string): Promise<SocketFileIdentity | undefined>;
|
|
20
|
+
/**
|
|
21
|
+
* Waits, bounded, for an identity file to appear. The lifecycle supervisor
|
|
22
|
+
* publishes its public-socket token after this host's internal listener is
|
|
23
|
+
* ready, so supervised hosts poll briefly instead of assuming ordering.
|
|
24
|
+
*/
|
|
25
|
+
export declare function waitForSocketIdentityFile(path: string, timeoutMs?: number, intervalMs?: number): Promise<SocketFileIdentity | undefined>;
|
|
26
|
+
/**
|
|
27
|
+
* Unlinks a socket path only while it still refers to the entry `identity`
|
|
28
|
+
* describes. An absent path is nothing to do; a mismatched identity means a
|
|
29
|
+
* newer host owns the name now and is left alone; an unknown identity can
|
|
30
|
+
* never be proven ours, so the path is left and the decision is logged.
|
|
31
|
+
*/
|
|
32
|
+
export declare function unlinkOwnedSocket(socketPath: string, identity: SocketFileIdentity | undefined, log: (message: string) => void): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Runs `close` with the socket path's current entry shielded from libuv's
|
|
35
|
+
* close-time unlink.
|
|
36
|
+
*
|
|
37
|
+
* libuv unlinks the NAME a pipe was bound to when its listening handle
|
|
38
|
+
* closes - even when a takeover has renamed a DIFFERENT socket over that
|
|
39
|
+
* name, so an ordinary `server.close()` silently deletes the newer host's
|
|
40
|
+
* freshly published entry. Moving the current entry aside for the duration
|
|
41
|
+
* of the close (libuv's unlink then hits ENOENT) and restoring it afterwards
|
|
42
|
+
* keeps the filesystem exactly as it was; ownership-checked removal via
|
|
43
|
+
* `unlinkOwnedSocket` decides afterwards what may actually be deleted.
|
|
44
|
+
*/
|
|
45
|
+
export declare function shieldSocketDuringClose(socketPath: string, close: () => Promise<void>): Promise<void>;
|
|
46
|
+
//# sourceMappingURL=socket-ownership.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-ownership.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/socket-ownership.ts"],"names":[],"mappings":"AAeA,0DAA0D;AAC1D,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,wBAAwB,CAAC;AAEjE,gFAAgF;AAChF,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAM9C,kFAAkF;AAClF,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAQpG;AAED,wFAAwF;AACxF,wBAAsB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKvG;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAwBlG;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC9C,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,MAAgC,EAC3C,UAAU,SAAK,GACb,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAQzC;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,kBAAkB,GAAG,SAAS,EACxC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC5B,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3G"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ownership-checked removal for Unix domain socket path entries.
|
|
3
|
+
*
|
|
4
|
+
* A socket path is a rendezvous name, not a capability: once a host unlinks
|
|
5
|
+
* its entry, any other process may bind the same path. Teardown that removes
|
|
6
|
+
* "its" socket by path alone can therefore delete a NEWER host's freshly
|
|
7
|
+
* published entry after a takeover renamed a replacement over that path. The
|
|
8
|
+
* startup path already refuses to touch a socket owned by a live server
|
|
9
|
+
* (`prepareSocketPath`); these helpers give every teardown path the same rule:
|
|
10
|
+
* capture the bound entry's filesystem identity (dev + ino) at listen time and
|
|
11
|
+
* unlink only while the path still stat-matches that identity.
|
|
12
|
+
*/
|
|
13
|
+
import { mkdir, readFile, rename, stat, unlink, writeFile } from "node:fs/promises";
|
|
14
|
+
import { dirname } from "node:path";
|
|
15
|
+
/**
|
|
16
|
+
* Name of the sidecar, inside the lifecycle supervisor's private scratch
|
|
17
|
+
* directory, that records which public socket entry that supervisor bound. The
|
|
18
|
+
* directory is per-supervisor, so a replacement supervisor's token can never be
|
|
19
|
+
* mistaken for the old one's.
|
|
20
|
+
*/
|
|
21
|
+
export const PUBLIC_SOCKET_IDENTITY_FILE = "public-socket.owner";
|
|
22
|
+
/** Default bound on waiting for a supervisor to publish its ownership token. */
|
|
23
|
+
export const SOCKET_IDENTITY_WAIT_MS = 30_000;
|
|
24
|
+
function sameSocketIdentity(a, b) {
|
|
25
|
+
return a.dev === b.dev && a.ino === b.ino;
|
|
26
|
+
}
|
|
27
|
+
/** Reads the current identity of a socket path entry; `undefined` once absent. */
|
|
28
|
+
export async function statSocketIdentity(socketPath) {
|
|
29
|
+
try {
|
|
30
|
+
const { dev, ino } = await stat(socketPath);
|
|
31
|
+
return { dev, ino };
|
|
32
|
+
}
|
|
33
|
+
catch (cause) {
|
|
34
|
+
if (isNodeErrorCode(cause, "ENOENT"))
|
|
35
|
+
return undefined;
|
|
36
|
+
throw cause;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Records a bound socket identity for teardown paths that cannot hold it in memory. */
|
|
40
|
+
export async function writeSocketIdentityFile(path, identity) {
|
|
41
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
42
|
+
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
43
|
+
await writeFile(tmpPath, `${JSON.stringify(identity)}\n`, { mode: 0o600 });
|
|
44
|
+
await rename(tmpPath, path);
|
|
45
|
+
}
|
|
46
|
+
export async function readSocketIdentityFile(path) {
|
|
47
|
+
let raw;
|
|
48
|
+
try {
|
|
49
|
+
raw = await readFile(path, "utf8");
|
|
50
|
+
}
|
|
51
|
+
catch (cause) {
|
|
52
|
+
if (isNodeErrorCode(cause, "ENOENT"))
|
|
53
|
+
return undefined;
|
|
54
|
+
throw cause;
|
|
55
|
+
}
|
|
56
|
+
let parsed;
|
|
57
|
+
try {
|
|
58
|
+
parsed = JSON.parse(raw);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Truncated or in-progress JSON is not a token: callers wait or leave the path.
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
if (typeof parsed !== "object" ||
|
|
65
|
+
parsed === null ||
|
|
66
|
+
typeof parsed.dev !== "number" ||
|
|
67
|
+
typeof parsed.ino !== "number") {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
return { dev: parsed.dev, ino: parsed.ino };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Waits, bounded, for an identity file to appear. The lifecycle supervisor
|
|
74
|
+
* publishes its public-socket token after this host's internal listener is
|
|
75
|
+
* ready, so supervised hosts poll briefly instead of assuming ordering.
|
|
76
|
+
*/
|
|
77
|
+
export async function waitForSocketIdentityFile(path, timeoutMs = SOCKET_IDENTITY_WAIT_MS, intervalMs = 25) {
|
|
78
|
+
const deadline = Date.now() + timeoutMs;
|
|
79
|
+
for (;;) {
|
|
80
|
+
const identity = await readSocketIdentityFile(path);
|
|
81
|
+
if (identity !== undefined)
|
|
82
|
+
return identity;
|
|
83
|
+
if (Date.now() >= deadline)
|
|
84
|
+
return undefined;
|
|
85
|
+
await delayUnrefed(intervalMs);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Unlinks a socket path only while it still refers to the entry `identity`
|
|
90
|
+
* describes. An absent path is nothing to do; a mismatched identity means a
|
|
91
|
+
* newer host owns the name now and is left alone; an unknown identity can
|
|
92
|
+
* never be proven ours, so the path is left and the decision is logged.
|
|
93
|
+
*/
|
|
94
|
+
export async function unlinkOwnedSocket(socketPath, identity, log) {
|
|
95
|
+
if (process.platform === "win32" || socketPath.startsWith("\0"))
|
|
96
|
+
return;
|
|
97
|
+
let current;
|
|
98
|
+
try {
|
|
99
|
+
current = await statSocketIdentity(socketPath);
|
|
100
|
+
}
|
|
101
|
+
catch (cause) {
|
|
102
|
+
log(`socket ${socketPath} ownership could not be verified (${errorMessage(cause)}); leaving it`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (current === undefined)
|
|
106
|
+
return;
|
|
107
|
+
if (identity === undefined) {
|
|
108
|
+
log(`socket path ${socketPath} ownership unknown; leaving it`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (!sameSocketIdentity(current, identity)) {
|
|
112
|
+
log(`socket path ${socketPath} now owned by another host; leaving it`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
await unlink(socketPath);
|
|
117
|
+
}
|
|
118
|
+
catch (cause) {
|
|
119
|
+
if (!isNodeErrorCode(cause, "ENOENT")) {
|
|
120
|
+
log(`socket ${socketPath} removal failed (${errorMessage(cause)})`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Runs `close` with the socket path's current entry shielded from libuv's
|
|
126
|
+
* close-time unlink.
|
|
127
|
+
*
|
|
128
|
+
* libuv unlinks the NAME a pipe was bound to when its listening handle
|
|
129
|
+
* closes - even when a takeover has renamed a DIFFERENT socket over that
|
|
130
|
+
* name, so an ordinary `server.close()` silently deletes the newer host's
|
|
131
|
+
* freshly published entry. Moving the current entry aside for the duration
|
|
132
|
+
* of the close (libuv's unlink then hits ENOENT) and restoring it afterwards
|
|
133
|
+
* keeps the filesystem exactly as it was; ownership-checked removal via
|
|
134
|
+
* `unlinkOwnedSocket` decides afterwards what may actually be deleted.
|
|
135
|
+
*/
|
|
136
|
+
export async function shieldSocketDuringClose(socketPath, close) {
|
|
137
|
+
if (process.platform === "win32" || socketPath.startsWith("\0"))
|
|
138
|
+
return close();
|
|
139
|
+
const shieldPath = `${socketPath}.shield-${process.pid}`;
|
|
140
|
+
let shielded = false;
|
|
141
|
+
try {
|
|
142
|
+
if ((await statSocketIdentity(socketPath)) !== undefined) {
|
|
143
|
+
await rename(socketPath, shieldPath);
|
|
144
|
+
shielded = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// Without a shield the close degrades to today's behavior; the
|
|
149
|
+
// ownership-checked unlink still runs afterwards.
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
await close();
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
if (shielded) {
|
|
156
|
+
try {
|
|
157
|
+
await rename(shieldPath, socketPath);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// Best effort: a failed restore leaves the entry under the shield
|
|
161
|
+
// name; startup probes treat a dead entry as replaceable.
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function delayUnrefed(ms) {
|
|
167
|
+
return new Promise((resolve) => {
|
|
168
|
+
const timer = setTimeout(resolve, ms);
|
|
169
|
+
timer.unref?.();
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
function errorMessage(cause) {
|
|
173
|
+
return cause instanceof Error ? cause.message : String(cause);
|
|
174
|
+
}
|
|
175
|
+
function isNodeErrorCode(cause, code) {
|
|
176
|
+
return cause instanceof Error && "code" in cause && cause.code === code;
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=socket-ownership.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-ownership.js","sourceRoot":"","sources":["../../../src/modes/rpc/socket-ownership.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Ownership-checked removal for Unix domain socket path entries.\n *\n * A socket path is a rendezvous name, not a capability: once a host unlinks\n * its entry, any other process may bind the same path. Teardown that removes\n * \"its\" socket by path alone can therefore delete a NEWER host's freshly\n * published entry after a takeover renamed a replacement over that path. The\n * startup path already refuses to touch a socket owned by a live server\n * (`prepareSocketPath`); these helpers give every teardown path the same rule:\n * capture the bound entry's filesystem identity (dev + ino) at listen time and\n * unlink only while the path still stat-matches that identity.\n */\nimport { mkdir, readFile, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\n\n/** Filesystem identity of one bound socket path entry. */\nexport interface SocketFileIdentity {\n\treadonly dev: number;\n\treadonly ino: number;\n}\n\n/**\n * Name of the sidecar, inside the lifecycle supervisor's private scratch\n * directory, that records which public socket entry that supervisor bound. The\n * directory is per-supervisor, so a replacement supervisor's token can never be\n * mistaken for the old one's.\n */\nexport const PUBLIC_SOCKET_IDENTITY_FILE = \"public-socket.owner\";\n\n/** Default bound on waiting for a supervisor to publish its ownership token. */\nexport const SOCKET_IDENTITY_WAIT_MS = 30_000;\n\nfunction sameSocketIdentity(a: SocketFileIdentity, b: SocketFileIdentity): boolean {\n\treturn a.dev === b.dev && a.ino === b.ino;\n}\n\n/** Reads the current identity of a socket path entry; `undefined` once absent. */\nexport async function statSocketIdentity(socketPath: string): Promise<SocketFileIdentity | undefined> {\n\ttry {\n\t\tconst { dev, ino } = await stat(socketPath);\n\t\treturn { dev, ino };\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return undefined;\n\t\tthrow cause;\n\t}\n}\n\n/** Records a bound socket identity for teardown paths that cannot hold it in memory. */\nexport async function writeSocketIdentityFile(path: string, identity: SocketFileIdentity): Promise<void> {\n\tawait mkdir(dirname(path), { recursive: true, mode: 0o700 });\n\tconst tmpPath = `${path}.${process.pid}.tmp`;\n\tawait writeFile(tmpPath, `${JSON.stringify(identity)}\\n`, { mode: 0o600 });\n\tawait rename(tmpPath, path);\n}\n\nexport async function readSocketIdentityFile(path: string): Promise<SocketFileIdentity | undefined> {\n\tlet raw: string;\n\ttry {\n\t\traw = await readFile(path, \"utf8\");\n\t} catch (cause) {\n\t\tif (isNodeErrorCode(cause, \"ENOENT\")) return undefined;\n\t\tthrow cause;\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(raw);\n\t} catch {\n\t\t// Truncated or in-progress JSON is not a token: callers wait or leave the path.\n\t\treturn undefined;\n\t}\n\tif (\n\t\ttypeof parsed !== \"object\" ||\n\t\tparsed === null ||\n\t\ttypeof (parsed as { dev?: unknown }).dev !== \"number\" ||\n\t\ttypeof (parsed as { ino?: unknown }).ino !== \"number\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn { dev: (parsed as { dev: number }).dev, ino: (parsed as { ino: number }).ino };\n}\n\n/**\n * Waits, bounded, for an identity file to appear. The lifecycle supervisor\n * publishes its public-socket token after this host's internal listener is\n * ready, so supervised hosts poll briefly instead of assuming ordering.\n */\nexport async function waitForSocketIdentityFile(\n\tpath: string,\n\ttimeoutMs: number = SOCKET_IDENTITY_WAIT_MS,\n\tintervalMs = 25,\n): Promise<SocketFileIdentity | undefined> {\n\tconst deadline = Date.now() + timeoutMs;\n\tfor (;;) {\n\t\tconst identity = await readSocketIdentityFile(path);\n\t\tif (identity !== undefined) return identity;\n\t\tif (Date.now() >= deadline) return undefined;\n\t\tawait delayUnrefed(intervalMs);\n\t}\n}\n\n/**\n * Unlinks a socket path only while it still refers to the entry `identity`\n * describes. An absent path is nothing to do; a mismatched identity means a\n * newer host owns the name now and is left alone; an unknown identity can\n * never be proven ours, so the path is left and the decision is logged.\n */\nexport async function unlinkOwnedSocket(\n\tsocketPath: string,\n\tidentity: SocketFileIdentity | undefined,\n\tlog: (message: string) => void,\n): Promise<void> {\n\tif (process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return;\n\tlet current: SocketFileIdentity | undefined;\n\ttry {\n\t\tcurrent = await statSocketIdentity(socketPath);\n\t} catch (cause) {\n\t\tlog(`socket ${socketPath} ownership could not be verified (${errorMessage(cause)}); leaving it`);\n\t\treturn;\n\t}\n\tif (current === undefined) return;\n\tif (identity === undefined) {\n\t\tlog(`socket path ${socketPath} ownership unknown; leaving it`);\n\t\treturn;\n\t}\n\tif (!sameSocketIdentity(current, identity)) {\n\t\tlog(`socket path ${socketPath} now owned by another host; leaving it`);\n\t\treturn;\n\t}\n\ttry {\n\t\tawait unlink(socketPath);\n\t} catch (cause) {\n\t\tif (!isNodeErrorCode(cause, \"ENOENT\")) {\n\t\t\tlog(`socket ${socketPath} removal failed (${errorMessage(cause)})`);\n\t\t}\n\t}\n}\n\n/**\n * Runs `close` with the socket path's current entry shielded from libuv's\n * close-time unlink.\n *\n * libuv unlinks the NAME a pipe was bound to when its listening handle\n * closes - even when a takeover has renamed a DIFFERENT socket over that\n * name, so an ordinary `server.close()` silently deletes the newer host's\n * freshly published entry. Moving the current entry aside for the duration\n * of the close (libuv's unlink then hits ENOENT) and restoring it afterwards\n * keeps the filesystem exactly as it was; ownership-checked removal via\n * `unlinkOwnedSocket` decides afterwards what may actually be deleted.\n */\nexport async function shieldSocketDuringClose(socketPath: string, close: () => Promise<void>): Promise<void> {\n\tif (process.platform === \"win32\" || socketPath.startsWith(\"\\0\")) return close();\n\tconst shieldPath = `${socketPath}.shield-${process.pid}`;\n\tlet shielded = false;\n\ttry {\n\t\tif ((await statSocketIdentity(socketPath)) !== undefined) {\n\t\t\tawait rename(socketPath, shieldPath);\n\t\t\tshielded = true;\n\t\t}\n\t} catch {\n\t\t// Without a shield the close degrades to today's behavior; the\n\t\t// ownership-checked unlink still runs afterwards.\n\t}\n\ttry {\n\t\tawait close();\n\t} finally {\n\t\tif (shielded) {\n\t\t\ttry {\n\t\t\t\tawait rename(shieldPath, socketPath);\n\t\t\t} catch {\n\t\t\t\t// Best effort: a failed restore leaves the entry under the shield\n\t\t\t\t// name; startup probes treat a dead entry as replaceable.\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction delayUnrefed(ms: number): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tconst timer = setTimeout(resolve, ms);\n\t\ttimer.unref?.();\n\t});\n}\n\nfunction errorMessage(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\nfunction isNodeErrorCode(cause: unknown, code: string): boolean {\n\treturn cause instanceof Error && \"code\" in cause && cause.code === code;\n}\n"]}
|
package/dist/utils/fs-watch.d.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { type FSWatcher, type WatchListener, type WatchOptions } from "node:fs";
|
|
2
2
|
export declare const FS_WATCH_RETRY_DELAY_MS = 5000;
|
|
3
3
|
export declare function closeWatcher(watcher: FSWatcher | null | undefined): void;
|
|
4
|
+
/**
|
|
5
|
+
* The path to hand to `fs.watch`. On Windows libuv aborts the whole process when a directory
|
|
6
|
+
* watch is created through a non-canonical (8.3 short-name) path (issue #1229), so the canonical
|
|
7
|
+
* form is looked up there. Elsewhere the caller's path is used as-is: POSIX realpath opens every
|
|
8
|
+
* directory it resolves, which blocks forever on a wedged autofs trigger, and fs.watch does not
|
|
9
|
+
* need the canonical form on those platforms.
|
|
10
|
+
*/
|
|
11
|
+
export declare function canonicalWatchPath(path: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Prove that a directory can be opened before a synchronous `fs.watch` touches it. The watcher
|
|
14
|
+
* opens the directory on the calling thread; on a wedged mount (a macOS autofs trigger whose
|
|
15
|
+
* automounter never answers) that open never returns and would block the host main thread. This
|
|
16
|
+
* probe performs the same open on the async pool, so a caller can bound it with a deadline. It
|
|
17
|
+
* rejects with the open error (for example EACCES on an execute-only directory, which fs.watch
|
|
18
|
+
* would refuse as well).
|
|
19
|
+
*/
|
|
20
|
+
export declare function probeDirectoryOpenable(directory: string): Promise<void>;
|
|
4
21
|
export declare function watchWithErrorHandler(path: string, listener: WatchListener<string>, onError: () => void, options?: WatchOptions): FSWatcher | null;
|
|
5
22
|
//# sourceMappingURL=fs-watch.d.ts.map
|