@code-yeongyu/senpi 2026.8.28 → 2026.8.30-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 +196 -0
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +2 -0
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +50 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +330 -29
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +1 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +3 -0
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +155 -37
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/compaction/compaction-execution.d.ts +14 -0
- package/dist/core/compaction/compaction-execution.d.ts.map +1 -0
- package/dist/core/compaction/compaction-execution.js +121 -0
- package/dist/core/compaction/compaction-execution.js.map +1 -0
- package/dist/core/compaction/compaction-settings.d.ts +17 -0
- package/dist/core/compaction/compaction-settings.d.ts.map +1 -0
- package/dist/core/compaction/compaction-settings.js +17 -0
- package/dist/core/compaction/compaction-settings.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +13 -26
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +7 -136
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/ideal-compaction-settings.d.ts +9 -0
- package/dist/core/compaction/ideal-compaction-settings.d.ts.map +1 -0
- package/dist/core/compaction/ideal-compaction-settings.js +7 -0
- package/dist/core/compaction/ideal-compaction-settings.js.map +1 -0
- package/dist/core/compaction-settings-access.d.ts +19 -0
- package/dist/core/compaction-settings-access.d.ts.map +1 -0
- package/dist/core/compaction-settings-access.js +10 -0
- package/dist/core/compaction-settings-access.js.map +1 -0
- package/dist/core/compaction-settings-resolver.d.ts +22 -0
- package/dist/core/compaction-settings-resolver.d.ts.map +1 -0
- package/dist/core/compaction-settings-resolver.js +46 -0
- package/dist/core/compaction-settings-resolver.js.map +1 -0
- package/dist/core/credential-accounts.d.ts.map +1 -1
- package/dist/core/credential-accounts.js +45 -21
- package/dist/core/credential-accounts.js.map +1 -1
- package/dist/core/credential-pool/classify.d.ts +6 -1
- package/dist/core/credential-pool/classify.d.ts.map +1 -1
- package/dist/core/credential-pool/classify.js +22 -5
- package/dist/core/credential-pool/classify.js.map +1 -1
- package/dist/core/credential-pool/env-slots.d.ts.map +1 -1
- package/dist/core/credential-pool/env-slots.js +2 -0
- package/dist/core/credential-pool/env-slots.js.map +1 -1
- package/dist/core/credential-pool/failover.d.ts +8 -0
- package/dist/core/credential-pool/failover.d.ts.map +1 -1
- package/dist/core/credential-pool/failover.js +24 -6
- package/dist/core/credential-pool/failover.js.map +1 -1
- package/dist/core/credential-pool/rotation-stream.d.ts +13 -2
- package/dist/core/credential-pool/rotation-stream.d.ts.map +1 -1
- package/dist/core/credential-pool/rotation-stream.js +97 -10
- package/dist/core/credential-pool/rotation-stream.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts +17 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.js +28 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +8 -2
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +163 -48
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts +17 -0
- package/dist/core/extensions/builtin/compaction/emergency-prune.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/emergency-prune.js +64 -0
- package/dist/core/extensions/builtin/compaction/emergency-prune.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts +27 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.js +84 -0
- package/dist/core/extensions/builtin/compaction/extension-wiring.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/idle.d.ts +1 -0
- package/dist/core/extensions/builtin/compaction/idle.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/idle.js +13 -2
- package/dist/core/extensions/builtin/compaction/idle.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +121 -147
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
- package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/log.js +2 -0
- package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/model-selection.d.ts +14 -0
- package/dist/core/extensions/builtin/compaction/model-selection.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/model-selection.js +37 -0
- package/dist/core/extensions/builtin/compaction/model-selection.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts +9 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js +2 -0
- package/dist/core/extensions/builtin/compaction/openai-remote-dependencies.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +3 -6
- package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/orchestration.d.ts +56 -0
- package/dist/core/extensions/builtin/compaction/orchestration.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/orchestration.js +136 -0
- package/dist/core/extensions/builtin/compaction/orchestration.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/policy.d.ts +5 -2
- package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/policy.js +43 -26
- package/dist/core/extensions/builtin/compaction/policy.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 +31 -8
- package/dist/core/extensions/builtin/compaction/retained-message-safety.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts +8 -0
- package/dist/core/extensions/builtin/compaction/speculation-lead.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculation-lead.js +17 -0
- package/dist/core/extensions/builtin/compaction/speculation-lead.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.d.ts +22 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.js +16 -0
- package/dist/core/extensions/builtin/compaction/speculative-job.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts +22 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.js +131 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative.d.ts +8 -13
- package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.js +7 -196
- package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts +18 -0
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.js +18 -0
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.d.ts +36 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.js +92 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +8 -28
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -1
- package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/index.js +7 -0
- package/dist/core/extensions/builtin/goal/index.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 +15 -5
- 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 +16 -1
- package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
- package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
- package/dist/core/extensions/builtin/service-tier.js +13 -0
- package/dist/core/extensions/builtin/service-tier.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/extension.js +4 -2
- package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.d.ts +5 -0
- package/dist/core/extensions/builtin/terminal/manager.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/manager.js +31 -3
- package/dist/core/extensions/builtin/terminal/manager.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts +8 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.js +16 -0
- package/dist/core/extensions/builtin/terminal/monitor-permission.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +13 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +403 -3
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/session-bundle.js +1 -0
- package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js +5 -2
- package/dist/core/extensions/builtin/terminal/tools/kill-bash.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts +4 -0
- package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/monitor.js +40 -2
- package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
- package/dist/core/model-config-schema.d.ts +4 -4
- package/dist/core/model-resolver.js +2 -2
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +74 -37
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/retry-fallback/chains.d.ts +23 -1
- package/dist/core/retry-fallback/chains.d.ts.map +1 -1
- package/dist/core/retry-fallback/chains.js +59 -3
- package/dist/core/retry-fallback/chains.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +9 -2
- package/dist/core/retry-fallback/controller.js.map +1 -1
- package/dist/core/retry-fallback/settings.d.ts.map +1 -1
- package/dist/core/retry-fallback/settings.js +9 -0
- package/dist/core/retry-fallback/settings.js.map +1 -1
- package/dist/core/retry-fallback/validate.js +1 -1
- package/dist/core/retry-fallback/validate.js.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +9 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +40 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +15 -85
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +15 -17
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-public-types.d.ts +11 -0
- package/dist/core/settings-public-types.d.ts.map +1 -0
- package/dist/core/settings-public-types.js +2 -0
- package/dist/core/settings-public-types.js.map +1 -0
- package/dist/core/settings-shapes.d.ts +36 -0
- package/dist/core/settings-shapes.d.ts.map +1 -0
- package/dist/core/settings-shapes.js +2 -0
- package/dist/core/settings-shapes.js.map +1 -0
- package/dist/core/shared-host-policy.d.ts +6 -0
- package/dist/core/shared-host-policy.d.ts.map +1 -0
- package/dist/core/shared-host-policy.js +7 -0
- package/dist/core/shared-host-policy.js.map +1 -0
- package/dist/core/terminal-settings.d.ts +22 -0
- package/dist/core/terminal-settings.d.ts.map +1 -0
- package/dist/core/terminal-settings.js +2 -0
- package/dist/core/terminal-settings.js.map +1 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +97 -15
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +8 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +1 -1
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +76 -34
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/main.d.ts +7 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +27 -6
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/favorite-models-selector.js +2 -2
- package/dist/modes/interactive/components/favorite-models-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +7 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +34 -8
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/grok/chrome.d.ts +2 -0
- package/dist/modes/interactive/grok/chrome.d.ts.map +1 -1
- package/dist/modes/interactive/grok/chrome.js.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts +121 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.js +606 -45
- package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +7 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +390 -239
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts +8 -1
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +268 -63
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/host-ensure.d.ts +19 -0
- package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
- package/dist/modes/rpc/host-ensure.js +79 -15
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts +29 -0
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +96 -25
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/host-watchdog.d.ts +2 -0
- package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
- package/dist/modes/rpc/host-watchdog.js +23 -12
- package/dist/modes/rpc/host-watchdog.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +20 -8
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +91 -16
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +131 -37
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-input-validation.d.ts +1 -0
- package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-input-validation.js +66 -0
- package/dist/modes/rpc/rpc-input-validation.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +3 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +248 -3
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-binding.d.ts +1 -0
- package/dist/modes/rpc/session-binding.d.ts.map +1 -1
- package/dist/modes/rpc/session-binding.js +21 -1
- package/dist/modes/rpc/session-binding.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts +22 -0
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +148 -16
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +4 -0
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +58 -7
- package/dist/modes/rpc/session-event-writer.js.map +1 -1
- package/dist/modes/rpc/session-extension-ui-requests.d.ts +1 -0
- package/dist/modes/rpc/session-extension-ui-requests.d.ts.map +1 -1
- package/dist/modes/rpc/session-extension-ui-requests.js +5 -0
- package/dist/modes/rpc/session-extension-ui-requests.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +17 -2
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +67 -4
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/dist/modes/rpc/socket-event-fanout.d.ts +22 -0
- package/dist/modes/rpc/socket-event-fanout.d.ts.map +1 -0
- package/dist/modes/rpc/socket-event-fanout.js +84 -0
- package/dist/modes/rpc/socket-event-fanout.js.map +1 -0
- package/docs/rpc.md +55 -2
- package/docs/settings.md +34 -0
- package/docs/windows.md +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +74 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +139 -90
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +4 -4
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +6 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +40 -20
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts +3 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.js +28 -9
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/shell-output.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/anthropic-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +67 -38
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +20 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +443 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.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/baseten.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts +7 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +5 -0
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/mux.js +2 -2
- package/node_modules/@earendil-works/pi-tui/dist/mux.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +19 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -4,6 +4,7 @@ import type { RpcSessionEntry } from "./session-registry.ts";
|
|
|
4
4
|
/** A session-owned adapter around the classic command and extension-UI wiring. */
|
|
5
5
|
export interface RpcSessionBinding {
|
|
6
6
|
handle(command: object): Promise<void>;
|
|
7
|
+
cancelPendingExtensionUiRequests?(): void;
|
|
7
8
|
dispose(): Promise<void>;
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-binding.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-binding.ts"],"names":[],"mappings":"AAEA,OAAO,EAGN,KAAK,oBAAoB,EAEzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IACjC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAQD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC5C,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,kBAAkB,EAC1B,YAAY,EAAE,MAAM,IAAI,EACxB,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,cAAc,CAAM,GACtD,OAAO,CAAC,iBAAiB,CAAC,
|
|
1
|
+
{"version":3,"file":"session-binding.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-binding.ts"],"names":[],"mappings":"AAEA,OAAO,EAGN,KAAK,oBAAoB,EAEzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IACjC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,gCAAgC,CAAC,IAAI,IAAI,CAAC;IAC1C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAQD;;;GAGG;AACH,wBAAsB,uBAAuB,CAC5C,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,eAAe,EACtB,MAAM,EAAE,kBAAkB,EAC1B,YAAY,EAAE,MAAM,IAAI,EACxB,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,cAAc,CAAM,GACtD,OAAO,CAAC,iBAAiB,CAAC,CAsC5B"}
|
|
@@ -13,12 +13,31 @@ function enqueueRecords(writer, sessionId, chunk) {
|
|
|
13
13
|
export async function createRpcSessionBinding(sessionId, entry, writer, requestClose, options = {}) {
|
|
14
14
|
if (!entry.runtime)
|
|
15
15
|
throw new Error("Session runtime was not created");
|
|
16
|
+
// Attachments share one entry, so resolve the host from the live runtime. In
|
|
17
|
+
// particular, switch_session must use the entry's replacement-aware method
|
|
18
|
+
// instead of a runtime captured during open_session.
|
|
19
|
+
const runtimeHost = new Proxy({}, {
|
|
20
|
+
get(_target, property) {
|
|
21
|
+
if (property === "switchSession" && entry.switchSession)
|
|
22
|
+
return entry.switchSession;
|
|
23
|
+
if (property === "setRebindSession")
|
|
24
|
+
return (callback) => {
|
|
25
|
+
entry.rebindSession = callback;
|
|
26
|
+
entry.runtime?.setRebindSession(callback);
|
|
27
|
+
};
|
|
28
|
+
const runtime = entry.runtime;
|
|
29
|
+
if (!runtime)
|
|
30
|
+
throw new Error("Session runtime was not created");
|
|
31
|
+
const value = Reflect.get(runtime, property, runtime);
|
|
32
|
+
return typeof value === "function" ? value.bind(runtime) : value;
|
|
33
|
+
},
|
|
34
|
+
});
|
|
16
35
|
const handler = await runWithProviderScope(entry.scope, async () => {
|
|
17
36
|
const taggedSink = {
|
|
18
37
|
writeRaw: bindToProviderScope((chunk) => enqueueRecords(writer, sessionId, chunk)),
|
|
19
38
|
waitForBackpressure: bindToProviderScope(async () => { }),
|
|
20
39
|
};
|
|
21
|
-
return createRpcConnectionHandler(
|
|
40
|
+
return createRpcConnectionHandler(runtimeHost, taggedSink, {
|
|
22
41
|
sessionId,
|
|
23
42
|
shutdownHandler: bindToProviderScope(requestClose),
|
|
24
43
|
disposeRuntime: false,
|
|
@@ -28,6 +47,7 @@ export async function createRpcSessionBinding(sessionId, entry, writer, requestC
|
|
|
28
47
|
await handler.ready;
|
|
29
48
|
return {
|
|
30
49
|
handle: (command) => runWithProviderScope(entry.scope, () => handler.handleInputLine(JSON.stringify(command))),
|
|
50
|
+
cancelPendingExtensionUiRequests: () => runWithProviderScope(entry.scope, () => handler.cancelPendingExtensionUiRequests()),
|
|
31
51
|
dispose: () => runWithProviderScope(entry.scope, () => handler.dispose()),
|
|
32
52
|
};
|
|
33
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-binding.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-binding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEtG,OAAO,EACN,0BAA0B,GAI1B,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"session-binding.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-binding.ts"],"names":[],"mappings":"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","sourcesContent":["import { bindToProviderScope, runWithProviderScope } from \"@earendil-works/pi-ai/node/provider-scope\";\nimport type { AgentSessionRuntime } from \"../../core/agent-session-runtime.ts\";\nimport {\n\tcreateRpcConnectionHandler,\n\ttype RpcConnectionHandler,\n\ttype RpcConnectionOptions,\n\ttype RpcConnectionSink,\n} from \"./connection-handler.ts\";\nimport type { SessionEventWriter } from \"./session-event-writer.ts\";\nimport type { RpcSessionEntry } from \"./session-registry.ts\";\n\n/** A session-owned adapter around the classic command and extension-UI wiring. */\nexport interface RpcSessionBinding {\n\thandle(command: object): Promise<void>;\n\tcancelPendingExtensionUiRequests?(): void;\n\tdispose(): Promise<void>;\n}\n\nfunction enqueueRecords(writer: SessionEventWriter, sessionId: string, chunk: string): void {\n\tfor (const line of chunk.split(\"\\n\")) {\n\t\tif (line) writer.enqueue(sessionId, JSON.parse(line) as object);\n\t}\n}\n\n/**\n * Creates the extension UI bridge and subscriptions within the entry's provider\n * scope. The classic handler remains the single source of command semantics.\n */\nexport async function createRpcSessionBinding(\n\tsessionId: string,\n\tentry: RpcSessionEntry,\n\twriter: SessionEventWriter,\n\trequestClose: () => void,\n\toptions: Pick<RpcConnectionOptions, \"capabilities\"> = {},\n): Promise<RpcSessionBinding> {\n\tif (!entry.runtime) throw new Error(\"Session runtime was not created\");\n\t// Attachments share one entry, so resolve the host from the live runtime. In\n\t// particular, switch_session must use the entry's replacement-aware method\n\t// instead of a runtime captured during open_session.\n\tconst runtimeHost = new Proxy({} as AgentSessionRuntime, {\n\t\tget(_target, property) {\n\t\t\tif (property === \"switchSession\" && entry.switchSession) return entry.switchSession;\n\t\t\tif (property === \"setRebindSession\")\n\t\t\t\treturn (callback?: Parameters<AgentSessionRuntime[\"setRebindSession\"]>[0]) => {\n\t\t\t\t\tentry.rebindSession = callback;\n\t\t\t\t\tentry.runtime?.setRebindSession(callback);\n\t\t\t\t};\n\t\t\tconst runtime = entry.runtime;\n\t\t\tif (!runtime) throw new Error(\"Session runtime was not created\");\n\t\t\tconst value = Reflect.get(runtime, property, runtime);\n\t\t\treturn typeof value === \"function\" ? value.bind(runtime) : value;\n\t\t},\n\t});\n\tconst handler: RpcConnectionHandler = await runWithProviderScope(entry.scope, async () => {\n\t\tconst taggedSink: RpcConnectionSink = {\n\t\t\twriteRaw: bindToProviderScope((chunk: string) => enqueueRecords(writer, sessionId, chunk)),\n\t\t\twaitForBackpressure: bindToProviderScope(async () => {}),\n\t\t};\n\t\treturn createRpcConnectionHandler(runtimeHost, taggedSink, {\n\t\t\tsessionId,\n\t\t\tshutdownHandler: bindToProviderScope(requestClose),\n\t\t\tdisposeRuntime: false,\n\t\t\t...options,\n\t\t});\n\t});\n\tawait handler.ready;\n\treturn {\n\t\thandle: (command) => runWithProviderScope(entry.scope, () => handler.handleInputLine(JSON.stringify(command))),\n\t\tcancelPendingExtensionUiRequests: () =>\n\t\t\trunWithProviderScope(entry.scope, () => handler.cancelPendingExtensionUiRequests()),\n\t\tdispose: () => runWithProviderScope(entry.scope, () => handler.dispose()),\n\t};\n}\n"]}
|
|
@@ -5,6 +5,12 @@ import type { RpcSessionLaunchProfile, RpcSessionRegistry } from "./session-regi
|
|
|
5
5
|
/** Routes control messages and enforces a routing handle for every session command. */
|
|
6
6
|
export declare class SessionCommandRouter {
|
|
7
7
|
private readonly bindings;
|
|
8
|
+
/** Session handles opened by each connection, with one count per attachment. */
|
|
9
|
+
private readonly sessionsByConnection;
|
|
10
|
+
/** Opens currently awaiting runtime/binding setup, keyed by connection. */
|
|
11
|
+
private readonly opensByConnection;
|
|
12
|
+
/** Connections whose socket has already been detached. */
|
|
13
|
+
private readonly releasedConnections;
|
|
8
14
|
private readonly registry;
|
|
9
15
|
private readonly writer;
|
|
10
16
|
private readonly defaults;
|
|
@@ -13,7 +19,23 @@ export declare class SessionCommandRouter {
|
|
|
13
19
|
constructor(registry: RpcSessionRegistry, writer: SessionEventWriter, defaults: Pick<RpcSessionLaunchProfile, "cwd" | "permissionPreset" | "creationModel" | "initialThinkingLevel">, createBinding?: typeof createRpcSessionBinding, connectionOptions?: Parameters<typeof createRpcSessionBinding>[4]);
|
|
14
20
|
handle(command: RpcCommand): Promise<RpcResponse | undefined>;
|
|
15
21
|
dispose(): Promise<void>;
|
|
22
|
+
private openWithBarrier;
|
|
16
23
|
private open;
|
|
24
|
+
/**
|
|
25
|
+
* Releases every session a dropped connection still owned. Each handle goes through
|
|
26
|
+
* the same refcounted close as an explicit close_session, so a session another
|
|
27
|
+
* connection is still attached to keeps running and only the last owner tears it down.
|
|
28
|
+
*/
|
|
29
|
+
releaseConnection(connectionId: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* One owned handle's refcounted close: the same sequence as an explicit
|
|
32
|
+
* close_session, tolerant of races with other lifecycle paths (an entry
|
|
33
|
+
* already closed or claimed elsewhere is simply skipped). Disposal and
|
|
34
|
+
* binding removal happen only when this was the LAST attachment (the entry
|
|
35
|
+
* transitioned to "closing"): surviving attachments keep the shared binding
|
|
36
|
+
* and their event stream.
|
|
37
|
+
*/
|
|
38
|
+
private releaseOwnedSession;
|
|
17
39
|
private close;
|
|
18
40
|
private code;
|
|
19
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-command-router.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-command-router.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAO9D,OAAO,EAAE,uBAAuB,EAA0B,MAAM,sBAAsB,CAAC;AACvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAGX,uBAAuB,EACvB,kBAAkB,EAClB,MAAM,uBAAuB,CAAC;AAS/B,uFAAuF;AACvF,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwC;IACjE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAGvB;IACF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAC/D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAgD;gBAGjF,QAAQ,EAAE,kBAAkB,EAC5B,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,IAAI,CAAC,uBAAuB,EAAE,KAAK,GAAG,kBAAkB,GAAG,eAAe,GAAG,sBAAsB,CAAC,EAC9G,aAAa,GAAE,OAAO,uBAAiD,EACvE,iBAAiB,GAAE,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC,CAAC,CAAM;IAShE,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAuC7D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"session-command-router.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-command-router.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAO9D,OAAO,EAAE,uBAAuB,EAA0B,MAAM,sBAAsB,CAAC;AACvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAGX,uBAAuB,EACvB,kBAAkB,EAClB,MAAM,uBAAuB,CAAC;AAS/B,uFAAuF;AACvF,qBAAa,oBAAoB;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwC;IACjE,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAA0C;IAC/E,2EAA2E;IAC3E,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyC;IAC3E,0DAA0D;IAC1D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAGvB;IACF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAC/D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAgD;gBAGjF,QAAQ,EAAE,kBAAkB,EAC5B,MAAM,EAAE,kBAAkB,EAC1B,QAAQ,EAAE,IAAI,CAAC,uBAAuB,EAAE,KAAK,GAAG,kBAAkB,GAAG,eAAe,GAAG,sBAAsB,CAAC,EAC9G,aAAa,GAAE,OAAO,uBAAiD,EACvE,iBAAiB,GAAE,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC,CAAC,CAAM;IAShE,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAuC7D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB9B,OAAO,CAAC,eAAe;YAiBT,IAAI;IAkElB;;;;OAIG;IACG,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2C5D;;;;;;;OAOG;YACW,mBAAmB;YAkBnB,KAAK;IAuCnB,OAAO,CAAC,IAAI;CAWZ"}
|
|
@@ -11,6 +11,12 @@ function error(id, command, code) {
|
|
|
11
11
|
export class SessionCommandRouter {
|
|
12
12
|
constructor(registry, writer, defaults, createBinding = createRpcSessionBinding, connectionOptions = {}) {
|
|
13
13
|
this.bindings = new Map();
|
|
14
|
+
/** Session handles opened by each connection, with one count per attachment. */
|
|
15
|
+
this.sessionsByConnection = new Map();
|
|
16
|
+
/** Opens currently awaiting runtime/binding setup, keyed by connection. */
|
|
17
|
+
this.opensByConnection = new Map();
|
|
18
|
+
/** Connections whose socket has already been detached. */
|
|
19
|
+
this.releasedConnections = new Set();
|
|
14
20
|
this.registry = registry;
|
|
15
21
|
this.writer = writer;
|
|
16
22
|
this.defaults = defaults;
|
|
@@ -42,7 +48,7 @@ export class SessionCommandRouter {
|
|
|
42
48
|
data: { sessions: this.registry.list() },
|
|
43
49
|
};
|
|
44
50
|
if (command.type === "open_session")
|
|
45
|
-
return this.
|
|
51
|
+
return this.openWithBarrier(command);
|
|
46
52
|
if (command.type === "close_session")
|
|
47
53
|
return this.close(command);
|
|
48
54
|
if (controls.has(command.type))
|
|
@@ -71,19 +77,39 @@ export class SessionCommandRouter {
|
|
|
71
77
|
return;
|
|
72
78
|
}
|
|
73
79
|
try {
|
|
80
|
+
if (entry.state === "open") {
|
|
81
|
+
while (entry.state === "open")
|
|
82
|
+
entry = this.registry.beginClose(sessionId);
|
|
83
|
+
}
|
|
74
84
|
await binding.dispose();
|
|
75
85
|
}
|
|
76
86
|
finally {
|
|
77
87
|
this.bindings.delete(sessionId);
|
|
78
|
-
// Attached sessions outlive this connection: only the last
|
|
79
|
-
// attachment's close transitions the entry to "closing".
|
|
80
88
|
if (entry.state === "closing")
|
|
81
89
|
await this.registry.closeMarked(sessionId);
|
|
82
90
|
}
|
|
83
91
|
}));
|
|
84
92
|
this.bindings.clear();
|
|
85
93
|
}
|
|
86
|
-
|
|
94
|
+
openWithBarrier(command) {
|
|
95
|
+
const owner = this.writer.currentConnection();
|
|
96
|
+
if (owner === undefined)
|
|
97
|
+
return this.open(command);
|
|
98
|
+
let resolveBarrier;
|
|
99
|
+
const barrier = new Promise((resolve) => {
|
|
100
|
+
resolveBarrier = resolve;
|
|
101
|
+
});
|
|
102
|
+
const opens = this.opensByConnection.get(owner) ?? new Set();
|
|
103
|
+
opens.add(barrier);
|
|
104
|
+
this.opensByConnection.set(owner, opens);
|
|
105
|
+
return this.open(command, owner).finally(() => {
|
|
106
|
+
resolveBarrier();
|
|
107
|
+
opens.delete(barrier);
|
|
108
|
+
if (opens.size === 0)
|
|
109
|
+
this.opensByConnection.delete(owner);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async open(command, owner) {
|
|
87
113
|
let opened;
|
|
88
114
|
try {
|
|
89
115
|
opened = await this.registry.openSession({
|
|
@@ -97,7 +123,20 @@ export class SessionCommandRouter {
|
|
|
97
123
|
});
|
|
98
124
|
const openedSession = opened;
|
|
99
125
|
const entry = this.registry.getForCommand(openedSession.sessionId, "open_session");
|
|
100
|
-
|
|
126
|
+
// A client that dies without close_session (terminal closed, SIGKILL, dropped
|
|
127
|
+
// SSH) still holds this handle's attachment and its path reservation. Remember
|
|
128
|
+
// which connection owns it so releaseConnection() can close exactly that.
|
|
129
|
+
if (owner !== undefined) {
|
|
130
|
+
const owned = this.sessionsByConnection.get(owner) ?? new Map();
|
|
131
|
+
owned.set(openedSession.sessionId, (owned.get(openedSession.sessionId) ?? 0) + 1);
|
|
132
|
+
this.sessionsByConnection.set(owner, owned);
|
|
133
|
+
}
|
|
134
|
+
if (!this.bindings.has(openedSession.sessionId)) {
|
|
135
|
+
this.bindings.set(openedSession.sessionId, await this.createBinding(openedSession.sessionId, entry, this.writer, () => void this.close({ type: "close_session", sessionId: openedSession.sessionId }), this.connectionOptions));
|
|
136
|
+
}
|
|
137
|
+
if (owner !== undefined && this.releasedConnections.has(owner)) {
|
|
138
|
+
await this.releaseOwnedSession(openedSession.sessionId);
|
|
139
|
+
}
|
|
101
140
|
const state = entry.runtime.session;
|
|
102
141
|
this.writer.enqueue(opened.sessionId, {
|
|
103
142
|
id: command.id,
|
|
@@ -124,28 +163,121 @@ export class SessionCommandRouter {
|
|
|
124
163
|
return error(command.id, "open_session", this.code(cause));
|
|
125
164
|
}
|
|
126
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Releases every session a dropped connection still owned. Each handle goes through
|
|
168
|
+
* the same refcounted close as an explicit close_session, so a session another
|
|
169
|
+
* connection is still attached to keeps running and only the last owner tears it down.
|
|
170
|
+
*/
|
|
171
|
+
async releaseConnection(connectionId) {
|
|
172
|
+
this.releasedConnections.add(connectionId);
|
|
173
|
+
const opens = this.opensByConnection.get(connectionId);
|
|
174
|
+
if (opens)
|
|
175
|
+
await Promise.all([...opens]);
|
|
176
|
+
const owned = this.sessionsByConnection.get(connectionId);
|
|
177
|
+
this.sessionsByConnection.delete(connectionId);
|
|
178
|
+
if (owned === undefined) {
|
|
179
|
+
this.releasedConnections.delete(connectionId);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
for (const [sessionId, count] of owned) {
|
|
183
|
+
this.bindings.get(sessionId)?.cancelPendingExtensionUiRequests?.();
|
|
184
|
+
for (let attachment = 0; attachment < count; attachment++) {
|
|
185
|
+
// Headless completion contract: a turn survives its client's death and
|
|
186
|
+
// runs to settlement (the host lifecycle keeps the process alive on
|
|
187
|
+
// active turns even with zero connections). Releasing mid-turn aborts
|
|
188
|
+
// the run and seals the session before agent_settled reaches the
|
|
189
|
+
// lifecycle observer, leaking the busy counter so the host never
|
|
190
|
+
// idle-exits. Defer - never skip - the release until the turn settles,
|
|
191
|
+
// so the dropped owner's reservation still frees afterwards.
|
|
192
|
+
const live = this.registry.peek(sessionId);
|
|
193
|
+
const session = live?.state === "open" ? live.runtime?.session : undefined;
|
|
194
|
+
if (session?.isStreaming) {
|
|
195
|
+
let released = false;
|
|
196
|
+
const unsubscribe = session.subscribe((event) => {
|
|
197
|
+
if (event.type !== "agent_settled" && event.type !== "agent_idle")
|
|
198
|
+
return;
|
|
199
|
+
if (released)
|
|
200
|
+
return;
|
|
201
|
+
released = true;
|
|
202
|
+
unsubscribe();
|
|
203
|
+
void this.releaseOwnedSession(sessionId).catch((cause) => {
|
|
204
|
+
process.stderr.write(`senpi rpc deferred release for session ${sessionId} failed: ${String(cause)}\n`);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
await this.releaseOwnedSession(sessionId);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
this.releasedConnections.delete(connectionId);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* One owned handle's refcounted close: the same sequence as an explicit
|
|
216
|
+
* close_session, tolerant of races with other lifecycle paths (an entry
|
|
217
|
+
* already closed or claimed elsewhere is simply skipped). Disposal and
|
|
218
|
+
* binding removal happen only when this was the LAST attachment (the entry
|
|
219
|
+
* transitioned to "closing"): surviving attachments keep the shared binding
|
|
220
|
+
* and their event stream.
|
|
221
|
+
*/
|
|
222
|
+
async releaseOwnedSession(sessionId) {
|
|
223
|
+
let entry;
|
|
224
|
+
try {
|
|
225
|
+
entry = this.registry.beginClose(sessionId);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Already closed or claimed by another lifecycle path; nothing to release.
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
if (entry.state === "closing")
|
|
233
|
+
await this.bindings.get(sessionId)?.dispose();
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
if (entry.state === "closing") {
|
|
237
|
+
this.bindings.delete(sessionId);
|
|
238
|
+
await this.registry.closeMarked(sessionId);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
127
242
|
async close(command) {
|
|
128
243
|
try {
|
|
129
244
|
// This must be the first operation: binding.dispose() awaits teardown and
|
|
130
245
|
// otherwise leaves a window where commands can enter the old handler.
|
|
131
246
|
const entry = this.registry.beginClose(command.sessionId);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
247
|
+
const owner = this.writer.currentConnection();
|
|
248
|
+
if (owner !== undefined) {
|
|
249
|
+
const owned = this.sessionsByConnection.get(owner);
|
|
250
|
+
const count = owned?.get(command.sessionId);
|
|
251
|
+
if (owned && count !== undefined) {
|
|
252
|
+
if (count === 1)
|
|
253
|
+
owned.delete(command.sessionId);
|
|
254
|
+
else
|
|
255
|
+
owned.set(command.sessionId, count - 1);
|
|
256
|
+
if (owned.size === 0)
|
|
257
|
+
this.sessionsByConnection.delete(owner);
|
|
258
|
+
}
|
|
141
259
|
}
|
|
142
|
-
|
|
260
|
+
const response = {
|
|
143
261
|
id: command.id,
|
|
144
262
|
type: "response",
|
|
145
263
|
command: "close_session",
|
|
146
264
|
success: true,
|
|
147
265
|
data: {},
|
|
148
|
-
}
|
|
266
|
+
};
|
|
267
|
+
try {
|
|
268
|
+
if (entry.state === "closing")
|
|
269
|
+
await this.bindings.get(command.sessionId)?.dispose();
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
if (entry.state === "closing") {
|
|
273
|
+
this.bindings.delete(command.sessionId);
|
|
274
|
+
await this.registry.closeMarked(command.sessionId);
|
|
275
|
+
this.writer.closeSession(command.sessionId, response);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
this.writer.enqueue(command.sessionId, response);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
149
281
|
return undefined;
|
|
150
282
|
}
|
|
151
283
|
catch (cause) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-command-router.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-command-router.ts"],"names":[],"mappings":"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","sourcesContent":["import { VERSION } from \"../../config.ts\";\nimport { buildRpcSessionState } from \"./connection-handler.ts\";\nimport type { RpcCommand, RpcResponse } from \"./rpc-types.ts\";\nimport {\n\tRPC_ERROR_MISSING_SESSION_ID,\n\tRPC_ERROR_OPEN_FAILED,\n\tRPC_ERROR_SESSION_CLOSING,\n\tRPC_ERROR_UNKNOWN_SESSION,\n} from \"./rpc-types.ts\";\nimport { createRpcSessionBinding, type RpcSessionBinding } from \"./session-binding.ts\";\nimport type { SessionEventWriter } from \"./session-event-writer.ts\";\nimport type {\n\tOpenRpcSession,\n\tRpcSessionEntry,\n\tRpcSessionLaunchProfile,\n\tRpcSessionRegistry,\n} from \"./session-registry.ts\";\nimport { RpcSessionRegistryError } from \"./session-registry.ts\";\n\nconst controls = new Set([\"get_protocol_info\", \"open_session\", \"close_session\", \"list_sessions\"]);\n\nfunction error(id: string | undefined, command: string, code: string): RpcResponse {\n\treturn { id, type: \"response\", command, success: false, error: code };\n}\n\n/** Routes control messages and enforces a routing handle for every session command. */\nexport class SessionCommandRouter {\n\tprivate readonly bindings = new Map<string, RpcSessionBinding>();\n\tprivate readonly registry: RpcSessionRegistry;\n\tprivate readonly writer: SessionEventWriter;\n\tprivate readonly defaults: Pick<\n\t\tRpcSessionLaunchProfile,\n\t\t\"cwd\" | \"permissionPreset\" | \"creationModel\" | \"initialThinkingLevel\"\n\t>;\n\tprivate readonly createBinding: typeof createRpcSessionBinding;\n\tprivate readonly connectionOptions: Parameters<typeof createRpcSessionBinding>[4];\n\n\tconstructor(\n\t\tregistry: RpcSessionRegistry,\n\t\twriter: SessionEventWriter,\n\t\tdefaults: Pick<RpcSessionLaunchProfile, \"cwd\" | \"permissionPreset\" | \"creationModel\" | \"initialThinkingLevel\">,\n\t\tcreateBinding: typeof createRpcSessionBinding = createRpcSessionBinding,\n\t\tconnectionOptions: Parameters<typeof createRpcSessionBinding>[4] = {},\n\t) {\n\t\tthis.registry = registry;\n\t\tthis.writer = writer;\n\t\tthis.defaults = defaults;\n\t\tthis.createBinding = createBinding;\n\t\tthis.connectionOptions = connectionOptions;\n\t}\n\n\tasync handle(command: RpcCommand): Promise<RpcResponse | undefined> {\n\t\tif (command.type === \"get_protocol_info\") {\n\t\t\tconst capabilities = new Set([\"multi_session\", ...(this.connectionOptions?.capabilities ?? [])]);\n\t\t\treturn {\n\t\t\t\tid: command.id,\n\t\t\t\ttype: \"response\",\n\t\t\t\tcommand: \"get_protocol_info\",\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: {\n\t\t\t\t\tprotocolVersion: 1,\n\t\t\t\t\tserverVersion: VERSION,\n\t\t\t\t\tcapabilities: [...capabilities],\n\t\t\t\t\tmode: \"multi\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (command.type === \"list_sessions\")\n\t\t\treturn {\n\t\t\t\tid: command.id,\n\t\t\t\ttype: \"response\",\n\t\t\t\tcommand: \"list_sessions\",\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: { sessions: this.registry.list() },\n\t\t\t};\n\t\tif (command.type === \"open_session\") return this.open(command);\n\t\tif (command.type === \"close_session\") return this.close(command);\n\t\tif (controls.has(command.type)) return undefined;\n\t\tif (!command.sessionId) return error(command.id, command.type, RPC_ERROR_MISSING_SESSION_ID);\n\t\ttry {\n\t\t\tthis.registry.getForCommand(command.sessionId, command.type);\n\t\t\tconst binding = this.bindings.get(command.sessionId);\n\t\t\tif (!binding) return error(command.id, command.type, RPC_ERROR_UNKNOWN_SESSION);\n\t\t\tawait binding.handle(command);\n\t\t\treturn undefined;\n\t\t} catch (cause) {\n\t\t\treturn error(command.id, command.type, this.code(cause));\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tawait Promise.all(\n\t\t\t[...this.bindings.entries()].map(async ([sessionId, binding]) => {\n\t\t\t\tlet entry: RpcSessionEntry | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tentry = this.registry.beginClose(sessionId);\n\t\t\t\t} catch {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tawait binding.dispose();\n\t\t\t\t} finally {\n\t\t\t\t\tthis.bindings.delete(sessionId);\n\t\t\t\t\t// Attached sessions outlive this connection: only the last\n\t\t\t\t\t// attachment's close transitions the entry to \"closing\".\n\t\t\t\t\tif (entry.state === \"closing\") await this.registry.closeMarked(sessionId);\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t\tthis.bindings.clear();\n\t}\n\n\tprivate async open(command: Extract<RpcCommand, { type: \"open_session\" }>): Promise<RpcResponse | undefined> {\n\t\tlet opened: OpenRpcSession | undefined;\n\t\ttry {\n\t\t\topened = await this.registry.openSession({\n\t\t\t\tcwd: command.cwd ?? this.defaults.cwd,\n\t\t\t\tsessionPath: command.sessionPath,\n\t\t\t\tpermissionPreset: command.permissionPreset ?? this.defaults.permissionPreset,\n\t\t\t\tcreationModel:\n\t\t\t\t\tcommand.provider && command.modelId\n\t\t\t\t\t\t? { provider: command.provider, modelId: command.modelId }\n\t\t\t\t\t\t: this.defaults.creationModel,\n\t\t\t\tinitialThinkingLevel: command.thinkingLevel ?? this.defaults.initialThinkingLevel,\n\t\t\t});\n\t\t\tconst openedSession = opened;\n\t\t\tconst entry = this.registry.getForCommand(openedSession.sessionId, \"open_session\");\n\t\t\tthis.bindings.set(\n\t\t\t\topenedSession.sessionId,\n\t\t\t\tawait this.createBinding(\n\t\t\t\t\topenedSession.sessionId,\n\t\t\t\t\tentry,\n\t\t\t\t\tthis.writer,\n\t\t\t\t\t() => void this.close({ type: \"close_session\", sessionId: openedSession.sessionId }),\n\t\t\t\t\tthis.connectionOptions,\n\t\t\t\t),\n\t\t\t);\n\t\t\tconst state = entry.runtime!.session;\n\t\t\tthis.writer.enqueue(opened.sessionId, {\n\t\t\t\tid: command.id,\n\t\t\t\ttype: \"response\",\n\t\t\t\tcommand: \"open_session\",\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: {\n\t\t\t\t\tsessionId: opened.sessionId,\n\t\t\t\t\tstate: buildRpcSessionState(state),\n\t\t\t\t\t...(opened.attached ? { attached: true } : {}),\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn undefined;\n\t\t} catch (cause) {\n\t\t\tif (opened) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.registry.close(opened.sessionId);\n\t\t\t\t} catch {\n\t\t\t\t\t/* The open rollback has already removed the entry. */\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn error(command.id, \"open_session\", this.code(cause));\n\t\t}\n\t}\n\n\tprivate async close(command: Extract<RpcCommand, { type: \"close_session\" }>): Promise<RpcResponse | undefined> {\n\t\ttry {\n\t\t\t// This must be the first operation: binding.dispose() awaits teardown and\n\t\t\t// otherwise leaves a window where commands can enter the old handler.\n\t\t\tconst entry = this.registry.beginClose(command.sessionId);\n\t\t\ttry {\n\t\t\t\tawait this.bindings.get(command.sessionId)?.dispose();\n\t\t\t} finally {\n\t\t\t\tthis.bindings.delete(command.sessionId);\n\t\t\t\t// Other attachments may still own the live session; only the last\n\t\t\t\t// close finalizes the runtime teardown.\n\t\t\t\tif (entry.state === \"closing\") await this.registry.closeMarked(command.sessionId);\n\t\t\t}\n\t\t\tthis.writer.closeSession(command.sessionId, {\n\t\t\t\tid: command.id,\n\t\t\t\ttype: \"response\",\n\t\t\t\tcommand: \"close_session\",\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: {},\n\t\t\t});\n\t\t\treturn undefined;\n\t\t} catch (cause) {\n\t\t\treturn error(command.id, \"close_session\", this.code(cause));\n\t\t}\n\t}\n\n\tprivate code(cause: unknown): string {\n\t\tif (cause instanceof RpcSessionRegistryError) {\n\t\t\treturn cause.code === RPC_ERROR_OPEN_FAILED ? cause.message : cause.code;\n\t\t}\n\t\tif (cause instanceof Error && [RPC_ERROR_UNKNOWN_SESSION, RPC_ERROR_SESSION_CLOSING].includes(cause.message)) {\n\t\t\treturn cause.message;\n\t\t}\n\t\treturn cause instanceof Error && cause.message\n\t\t\t? `${RPC_ERROR_OPEN_FAILED}: ${cause.message}`\n\t\t\t: RPC_ERROR_UNKNOWN_SESSION;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-command-router.js","sourceRoot":"","sources":["../../../src/modes/rpc/session-command-router.ts"],"names":[],"mappings":"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","sourcesContent":["import { VERSION } from \"../../config.ts\";\nimport { buildRpcSessionState } from \"./connection-handler.ts\";\nimport type { RpcCommand, RpcResponse } from \"./rpc-types.ts\";\nimport {\n\tRPC_ERROR_MISSING_SESSION_ID,\n\tRPC_ERROR_OPEN_FAILED,\n\tRPC_ERROR_SESSION_CLOSING,\n\tRPC_ERROR_UNKNOWN_SESSION,\n} from \"./rpc-types.ts\";\nimport { createRpcSessionBinding, type RpcSessionBinding } from \"./session-binding.ts\";\nimport type { SessionEventWriter } from \"./session-event-writer.ts\";\nimport type {\n\tOpenRpcSession,\n\tRpcSessionEntry,\n\tRpcSessionLaunchProfile,\n\tRpcSessionRegistry,\n} from \"./session-registry.ts\";\nimport { RpcSessionRegistryError } from \"./session-registry.ts\";\n\nconst controls = new Set([\"get_protocol_info\", \"open_session\", \"close_session\", \"list_sessions\"]);\n\nfunction error(id: string | undefined, command: string, code: string): RpcResponse {\n\treturn { id, type: \"response\", command, success: false, error: code };\n}\n\n/** Routes control messages and enforces a routing handle for every session command. */\nexport class SessionCommandRouter {\n\tprivate readonly bindings = new Map<string, RpcSessionBinding>();\n\t/** Session handles opened by each connection, with one count per attachment. */\n\tprivate readonly sessionsByConnection = new Map<string, Map<string, number>>();\n\t/** Opens currently awaiting runtime/binding setup, keyed by connection. */\n\tprivate readonly opensByConnection = new Map<string, Set<Promise<void>>>();\n\t/** Connections whose socket has already been detached. */\n\tprivate readonly releasedConnections = new Set<string>();\n\tprivate readonly registry: RpcSessionRegistry;\n\tprivate readonly writer: SessionEventWriter;\n\tprivate readonly defaults: Pick<\n\t\tRpcSessionLaunchProfile,\n\t\t\"cwd\" | \"permissionPreset\" | \"creationModel\" | \"initialThinkingLevel\"\n\t>;\n\tprivate readonly createBinding: typeof createRpcSessionBinding;\n\tprivate readonly connectionOptions: Parameters<typeof createRpcSessionBinding>[4];\n\n\tconstructor(\n\t\tregistry: RpcSessionRegistry,\n\t\twriter: SessionEventWriter,\n\t\tdefaults: Pick<RpcSessionLaunchProfile, \"cwd\" | \"permissionPreset\" | \"creationModel\" | \"initialThinkingLevel\">,\n\t\tcreateBinding: typeof createRpcSessionBinding = createRpcSessionBinding,\n\t\tconnectionOptions: Parameters<typeof createRpcSessionBinding>[4] = {},\n\t) {\n\t\tthis.registry = registry;\n\t\tthis.writer = writer;\n\t\tthis.defaults = defaults;\n\t\tthis.createBinding = createBinding;\n\t\tthis.connectionOptions = connectionOptions;\n\t}\n\n\tasync handle(command: RpcCommand): Promise<RpcResponse | undefined> {\n\t\tif (command.type === \"get_protocol_info\") {\n\t\t\tconst capabilities = new Set([\"multi_session\", ...(this.connectionOptions?.capabilities ?? [])]);\n\t\t\treturn {\n\t\t\t\tid: command.id,\n\t\t\t\ttype: \"response\",\n\t\t\t\tcommand: \"get_protocol_info\",\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: {\n\t\t\t\t\tprotocolVersion: 1,\n\t\t\t\t\tserverVersion: VERSION,\n\t\t\t\t\tcapabilities: [...capabilities],\n\t\t\t\t\tmode: \"multi\",\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tif (command.type === \"list_sessions\")\n\t\t\treturn {\n\t\t\t\tid: command.id,\n\t\t\t\ttype: \"response\",\n\t\t\t\tcommand: \"list_sessions\",\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: { sessions: this.registry.list() },\n\t\t\t};\n\t\tif (command.type === \"open_session\") return this.openWithBarrier(command);\n\t\tif (command.type === \"close_session\") return this.close(command);\n\t\tif (controls.has(command.type)) return undefined;\n\t\tif (!command.sessionId) return error(command.id, command.type, RPC_ERROR_MISSING_SESSION_ID);\n\t\ttry {\n\t\t\tthis.registry.getForCommand(command.sessionId, command.type);\n\t\t\tconst binding = this.bindings.get(command.sessionId);\n\t\t\tif (!binding) return error(command.id, command.type, RPC_ERROR_UNKNOWN_SESSION);\n\t\t\tawait binding.handle(command);\n\t\t\treturn undefined;\n\t\t} catch (cause) {\n\t\t\treturn error(command.id, command.type, this.code(cause));\n\t\t}\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tawait Promise.all(\n\t\t\t[...this.bindings.entries()].map(async ([sessionId, binding]) => {\n\t\t\t\tlet entry: RpcSessionEntry | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tentry = this.registry.beginClose(sessionId);\n\t\t\t\t} catch {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tif (entry.state === \"open\") {\n\t\t\t\t\t\twhile (entry.state === \"open\") entry = this.registry.beginClose(sessionId);\n\t\t\t\t\t}\n\t\t\t\t\tawait binding.dispose();\n\t\t\t\t} finally {\n\t\t\t\t\tthis.bindings.delete(sessionId);\n\t\t\t\t\tif (entry.state === \"closing\") await this.registry.closeMarked(sessionId);\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\t\tthis.bindings.clear();\n\t}\n\n\tprivate openWithBarrier(command: Extract<RpcCommand, { type: \"open_session\" }>): Promise<RpcResponse | undefined> {\n\t\tconst owner = this.writer.currentConnection();\n\t\tif (owner === undefined) return this.open(command);\n\t\tlet resolveBarrier!: () => void;\n\t\tconst barrier = new Promise<void>((resolve) => {\n\t\t\tresolveBarrier = resolve;\n\t\t});\n\t\tconst opens = this.opensByConnection.get(owner) ?? new Set<Promise<void>>();\n\t\topens.add(barrier);\n\t\tthis.opensByConnection.set(owner, opens);\n\t\treturn this.open(command, owner).finally(() => {\n\t\t\tresolveBarrier();\n\t\t\topens.delete(barrier);\n\t\t\tif (opens.size === 0) this.opensByConnection.delete(owner);\n\t\t});\n\t}\n\n\tprivate async open(\n\t\tcommand: Extract<RpcCommand, { type: \"open_session\" }>,\n\t\towner?: string,\n\t): Promise<RpcResponse | undefined> {\n\t\tlet opened: OpenRpcSession | undefined;\n\t\ttry {\n\t\t\topened = await this.registry.openSession({\n\t\t\t\tcwd: command.cwd ?? this.defaults.cwd,\n\t\t\t\tsessionPath: command.sessionPath,\n\t\t\t\tpermissionPreset: command.permissionPreset ?? this.defaults.permissionPreset,\n\t\t\t\tcreationModel:\n\t\t\t\t\tcommand.provider && command.modelId\n\t\t\t\t\t\t? { provider: command.provider, modelId: command.modelId }\n\t\t\t\t\t\t: this.defaults.creationModel,\n\t\t\t\tinitialThinkingLevel: command.thinkingLevel ?? this.defaults.initialThinkingLevel,\n\t\t\t});\n\t\t\tconst openedSession = opened;\n\t\t\tconst entry = this.registry.getForCommand(openedSession.sessionId, \"open_session\");\n\t\t\t// A client that dies without close_session (terminal closed, SIGKILL, dropped\n\t\t\t// SSH) still holds this handle's attachment and its path reservation. Remember\n\t\t\t// which connection owns it so releaseConnection() can close exactly that.\n\t\t\tif (owner !== undefined) {\n\t\t\t\tconst owned = this.sessionsByConnection.get(owner) ?? new Map<string, number>();\n\t\t\t\towned.set(openedSession.sessionId, (owned.get(openedSession.sessionId) ?? 0) + 1);\n\t\t\t\tthis.sessionsByConnection.set(owner, owned);\n\t\t\t}\n\t\t\tif (!this.bindings.has(openedSession.sessionId)) {\n\t\t\t\tthis.bindings.set(\n\t\t\t\t\topenedSession.sessionId,\n\t\t\t\t\tawait this.createBinding(\n\t\t\t\t\t\topenedSession.sessionId,\n\t\t\t\t\t\tentry,\n\t\t\t\t\t\tthis.writer,\n\t\t\t\t\t\t() => void this.close({ type: \"close_session\", sessionId: openedSession.sessionId }),\n\t\t\t\t\t\tthis.connectionOptions,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (owner !== undefined && this.releasedConnections.has(owner)) {\n\t\t\t\tawait this.releaseOwnedSession(openedSession.sessionId);\n\t\t\t}\n\t\t\tconst state = entry.runtime!.session;\n\t\t\tthis.writer.enqueue(opened.sessionId, {\n\t\t\t\tid: command.id,\n\t\t\t\ttype: \"response\",\n\t\t\t\tcommand: \"open_session\",\n\t\t\t\tsuccess: true,\n\t\t\t\tdata: {\n\t\t\t\t\tsessionId: opened.sessionId,\n\t\t\t\t\tstate: buildRpcSessionState(state),\n\t\t\t\t\t...(opened.attached ? { attached: true } : {}),\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn undefined;\n\t\t} catch (cause) {\n\t\t\tif (opened) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.registry.close(opened.sessionId);\n\t\t\t\t} catch {\n\t\t\t\t\t/* The open rollback has already removed the entry. */\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn error(command.id, \"open_session\", this.code(cause));\n\t\t}\n\t}\n\n\t/**\n\t * Releases every session a dropped connection still owned. Each handle goes through\n\t * the same refcounted close as an explicit close_session, so a session another\n\t * connection is still attached to keeps running and only the last owner tears it down.\n\t */\n\tasync releaseConnection(connectionId: string): Promise<void> {\n\t\tthis.releasedConnections.add(connectionId);\n\t\tconst opens = this.opensByConnection.get(connectionId);\n\t\tif (opens) await Promise.all([...opens]);\n\t\tconst owned = this.sessionsByConnection.get(connectionId);\n\t\tthis.sessionsByConnection.delete(connectionId);\n\t\tif (owned === undefined) {\n\t\t\tthis.releasedConnections.delete(connectionId);\n\t\t\treturn;\n\t\t}\n\t\tfor (const [sessionId, count] of owned) {\n\t\t\tthis.bindings.get(sessionId)?.cancelPendingExtensionUiRequests?.();\n\t\t\tfor (let attachment = 0; attachment < count; attachment++) {\n\t\t\t\t// Headless completion contract: a turn survives its client's death and\n\t\t\t\t// runs to settlement (the host lifecycle keeps the process alive on\n\t\t\t\t// active turns even with zero connections). Releasing mid-turn aborts\n\t\t\t\t// the run and seals the session before agent_settled reaches the\n\t\t\t\t// lifecycle observer, leaking the busy counter so the host never\n\t\t\t\t// idle-exits. Defer - never skip - the release until the turn settles,\n\t\t\t\t// so the dropped owner's reservation still frees afterwards.\n\t\t\t\tconst live = this.registry.peek(sessionId);\n\t\t\t\tconst session = live?.state === \"open\" ? live.runtime?.session : undefined;\n\t\t\t\tif (session?.isStreaming) {\n\t\t\t\t\tlet released = false;\n\t\t\t\t\tconst unsubscribe = session.subscribe((event) => {\n\t\t\t\t\t\tif (event.type !== \"agent_settled\" && event.type !== \"agent_idle\") return;\n\t\t\t\t\t\tif (released) return;\n\t\t\t\t\t\treleased = true;\n\t\t\t\t\t\tunsubscribe();\n\t\t\t\t\t\tvoid this.releaseOwnedSession(sessionId).catch((cause) => {\n\t\t\t\t\t\t\tprocess.stderr.write(\n\t\t\t\t\t\t\t\t`senpi rpc deferred release for session ${sessionId} failed: ${String(cause)}\\n`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tawait this.releaseOwnedSession(sessionId);\n\t\t\t}\n\t\t}\n\t\tthis.releasedConnections.delete(connectionId);\n\t}\n\n\t/**\n\t * One owned handle's refcounted close: the same sequence as an explicit\n\t * close_session, tolerant of races with other lifecycle paths (an entry\n\t * already closed or claimed elsewhere is simply skipped). Disposal and\n\t * binding removal happen only when this was the LAST attachment (the entry\n\t * transitioned to \"closing\"): surviving attachments keep the shared binding\n\t * and their event stream.\n\t */\n\tprivate async releaseOwnedSession(sessionId: string): Promise<void> {\n\t\tlet entry: RpcSessionEntry | undefined;\n\t\ttry {\n\t\t\tentry = this.registry.beginClose(sessionId);\n\t\t} catch {\n\t\t\t// Already closed or claimed by another lifecycle path; nothing to release.\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tif (entry.state === \"closing\") await this.bindings.get(sessionId)?.dispose();\n\t\t} finally {\n\t\t\tif (entry.state === \"closing\") {\n\t\t\t\tthis.bindings.delete(sessionId);\n\t\t\t\tawait this.registry.closeMarked(sessionId);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async close(command: Extract<RpcCommand, { type: \"close_session\" }>): Promise<RpcResponse | undefined> {\n\t\ttry {\n\t\t\t// This must be the first operation: binding.dispose() awaits teardown and\n\t\t\t// otherwise leaves a window where commands can enter the old handler.\n\t\t\tconst entry = this.registry.beginClose(command.sessionId);\n\t\t\tconst owner = this.writer.currentConnection();\n\t\t\tif (owner !== undefined) {\n\t\t\t\tconst owned = this.sessionsByConnection.get(owner);\n\t\t\t\tconst count = owned?.get(command.sessionId);\n\t\t\t\tif (owned && count !== undefined) {\n\t\t\t\t\tif (count === 1) owned.delete(command.sessionId);\n\t\t\t\t\telse owned.set(command.sessionId, count - 1);\n\t\t\t\t\tif (owned.size === 0) this.sessionsByConnection.delete(owner);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst response = {\n\t\t\t\tid: command.id,\n\t\t\t\ttype: \"response\" as const,\n\t\t\t\tcommand: \"close_session\" as const,\n\t\t\t\tsuccess: true as const,\n\t\t\t\tdata: {},\n\t\t\t};\n\t\t\ttry {\n\t\t\t\tif (entry.state === \"closing\") await this.bindings.get(command.sessionId)?.dispose();\n\t\t\t} finally {\n\t\t\t\tif (entry.state === \"closing\") {\n\t\t\t\t\tthis.bindings.delete(command.sessionId);\n\t\t\t\t\tawait this.registry.closeMarked(command.sessionId);\n\t\t\t\t\tthis.writer.closeSession(command.sessionId, response);\n\t\t\t\t} else {\n\t\t\t\t\tthis.writer.enqueue(command.sessionId, response);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn undefined;\n\t\t} catch (cause) {\n\t\t\treturn error(command.id, \"close_session\", this.code(cause));\n\t\t}\n\t}\n\n\tprivate code(cause: unknown): string {\n\t\tif (cause instanceof RpcSessionRegistryError) {\n\t\t\treturn cause.code === RPC_ERROR_OPEN_FAILED ? cause.message : cause.code;\n\t\t}\n\t\tif (cause instanceof Error && [RPC_ERROR_UNKNOWN_SESSION, RPC_ERROR_SESSION_CLOSING].includes(cause.message)) {\n\t\t\treturn cause.message;\n\t\t}\n\t\treturn cause instanceof Error && cause.message\n\t\t\t? `${RPC_ERROR_OPEN_FAILED}: ${cause.message}`\n\t\t\t: RPC_ERROR_UNKNOWN_SESSION;\n\t}\n}\n"]}
|
|
@@ -17,6 +17,7 @@ export interface SessionEventWriterConnection {
|
|
|
17
17
|
export declare class SessionEventWriter {
|
|
18
18
|
private readonly queues;
|
|
19
19
|
private readonly connections;
|
|
20
|
+
private readonly sessionSnapshots;
|
|
20
21
|
private readonly connectionContext;
|
|
21
22
|
private readonly controlQueue;
|
|
22
23
|
private readonly readyQueues;
|
|
@@ -36,6 +37,8 @@ export declare class SessionEventWriter {
|
|
|
36
37
|
unregisterConnection(id: string): void;
|
|
37
38
|
/** Execute a connection's command with its response destination in context. */
|
|
38
39
|
withConnection<T>(id: string, task: () => T): T;
|
|
40
|
+
/** Connection id of the command currently being dispatched, when one owns it. */
|
|
41
|
+
currentConnection(): string | undefined;
|
|
39
42
|
/** Queue a session record. Lifecycle/events are broadcast; responses/UI are targeted. */
|
|
40
43
|
enqueue(sessionId: string, value: object): boolean;
|
|
41
44
|
/** Queue one untagged host-control response for the current connection. */
|
|
@@ -50,6 +53,7 @@ export declare class SessionEventWriter {
|
|
|
50
53
|
flush(): Promise<void>;
|
|
51
54
|
private drainUntilEmpty;
|
|
52
55
|
private drainReadyQueues;
|
|
56
|
+
private rememberSnapshot;
|
|
53
57
|
private connectionQueue;
|
|
54
58
|
private appendSessionRecord;
|
|
55
59
|
private demoteAndMerge;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-event-writer.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-event-writer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session-event-writer.d.ts","sourceRoot":"","sources":["../../../src/modes/rpc/session-event-writer.ts"],"names":[],"mappings":"AAIA,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AACzC,KAAK,kBAAkB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAC9C,KAAK,cAAc,GAAG,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;AAmD3D;;;;;;;;GAQG;AACH,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,mBAAmB,EAAE,kBAAkB,CAAC;CACjD;AAED,qBAAa,kBAAkB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAGxB;IACJ,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA+B;IAChE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAmC;IACrE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAyD;IACtF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAY;IACrC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAqB;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiB;IAC/C,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,YAAY,CAAC,CAAgB;IACrC,OAAO,CAAC,QAAQ,CAAC,CAA0C;IAC3D,OAAO,CAAC,OAAO,CAAC,CAAU;gBAEd,QAAQ,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE,cAAc;gBACnD,QAAQ,EAAE,SAAS,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,aAAa,CAAC,EAAE,cAAc;IAexG,IAAI,mBAAmB,IAAI,MAAM,CAMhC;IAED,IAAI,kBAAkB,IAAI,MAAM,CAQ/B;IAED,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,4BAA4B,GAAG,IAAI;IAQ9E,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAOtC,+EAA+E;IAC/E,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC;IAI/C,iFAAiF;IACjF,iBAAiB,IAAI,MAAM,GAAG,SAAS;IAIvC,yFAAyF;IACzF,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAwBlD,2EAA2E;IAC3E,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB5C;;;;OAIG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAevD,kEAAkE;IAClE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YA2BR,eAAe;YAOf,gBAAgB;IAuC9B,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,mBAAmB;IAoC3B,OAAO,CAAC,cAAc;IA2BtB,OAAO,CAAC,MAAM;IAcd,OAAO,CAAC,MAAM;IAUd,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAE,SAAS;CAIlB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { serializeJsonLine } from "./jsonl.js";
|
|
3
|
+
import { SocketEventSinkActor } from "./socket-event-fanout.js";
|
|
3
4
|
const MESSAGE_KEY = "message";
|
|
4
5
|
const COMPACT_DELTA_TYPES = new Set(["text_delta", "thinking_delta", "toolcall_delta"]);
|
|
5
6
|
function compactDelta(value) {
|
|
@@ -30,6 +31,7 @@ export class SessionEventWriter {
|
|
|
30
31
|
constructor(writeRaw, waitOrSchedule, scheduleFlush) {
|
|
31
32
|
this.queues = new Map();
|
|
32
33
|
this.connections = new Map();
|
|
34
|
+
this.sessionSnapshots = new Map();
|
|
33
35
|
this.connectionContext = new AsyncLocalStorage();
|
|
34
36
|
this.controlQueue = { latestByKey: new Map(), ready: false };
|
|
35
37
|
this.readyQueues = [];
|
|
@@ -62,27 +64,52 @@ export class SessionEventWriter {
|
|
|
62
64
|
return bytes;
|
|
63
65
|
}
|
|
64
66
|
registerConnection(id, connection) {
|
|
65
|
-
|
|
67
|
+
const actor = new SocketEventSinkActor(connection, () => {
|
|
68
|
+
if (this.connections.get(id)?.actor === actor)
|
|
69
|
+
this.connections.delete(id);
|
|
70
|
+
});
|
|
71
|
+
this.connections.set(id, { connection, actor });
|
|
72
|
+
for (const snapshot of this.sessionSnapshots.values())
|
|
73
|
+
for (const line of snapshot)
|
|
74
|
+
actor.enqueue(line);
|
|
66
75
|
}
|
|
67
76
|
unregisterConnection(id) {
|
|
77
|
+
const registered = this.connections.get(id);
|
|
78
|
+
if (!registered)
|
|
79
|
+
return;
|
|
80
|
+
registered.actor.close();
|
|
68
81
|
this.connections.delete(id);
|
|
69
82
|
}
|
|
70
83
|
/** Execute a connection's command with its response destination in context. */
|
|
71
84
|
withConnection(id, task) {
|
|
72
85
|
return this.connectionContext.run(id, task);
|
|
73
86
|
}
|
|
87
|
+
/** Connection id of the command currently being dispatched, when one owns it. */
|
|
88
|
+
currentConnection() {
|
|
89
|
+
return this.connectionContext.getStore();
|
|
90
|
+
}
|
|
74
91
|
/** Queue a session record. Lifecycle/events are broadcast; responses/UI are targeted. */
|
|
75
92
|
enqueue(sessionId, value) {
|
|
76
93
|
if (this.sealedSessions.has(sessionId))
|
|
77
94
|
return false;
|
|
78
95
|
const targetId = this.connectionContext.getStore();
|
|
79
96
|
const record = value;
|
|
80
|
-
const isTargeted = record.type === "response" ||
|
|
97
|
+
const isTargeted = record.type === "response" ||
|
|
98
|
+
record.type === "bash_execution_update" ||
|
|
99
|
+
(record.type === "extension_ui_request" &&
|
|
100
|
+
["select", "confirm", "input", "editor"].includes(String(record.method)));
|
|
101
|
+
const tagged = { ...value, sessionId };
|
|
102
|
+
const line = serializeJsonLine(tagged);
|
|
103
|
+
this.rememberSnapshot(sessionId, tagged, line);
|
|
81
104
|
const targets = isTargeted ? [targetId] : this.connections.size > 0 ? [...this.connections.keys()] : [undefined];
|
|
82
105
|
for (const target of targets) {
|
|
83
106
|
if (target !== undefined && !this.connections.has(target))
|
|
84
107
|
continue;
|
|
85
|
-
|
|
108
|
+
const registered = target === undefined ? undefined : this.connections.get(target);
|
|
109
|
+
if (registered)
|
|
110
|
+
registered.actor.enqueue(line, compactDelta(tagged) && tagged.message !== null ? MESSAGE_KEY : undefined);
|
|
111
|
+
else
|
|
112
|
+
this.appendSessionRecord(sessionId, tagged, target);
|
|
86
113
|
}
|
|
87
114
|
this.requestFlush();
|
|
88
115
|
return true;
|
|
@@ -92,6 +119,11 @@ export class SessionEventWriter {
|
|
|
92
119
|
if (this.failure !== undefined)
|
|
93
120
|
return Promise.reject(this.failure);
|
|
94
121
|
const targetId = this.connectionContext.getStore();
|
|
122
|
+
const registered = targetId === undefined ? undefined : this.connections.get(targetId);
|
|
123
|
+
if (registered) {
|
|
124
|
+
registered.actor.enqueue(serializeJsonLine(value));
|
|
125
|
+
return Promise.resolve();
|
|
126
|
+
}
|
|
95
127
|
const queue = targetId === undefined ? this.controlQueue : this.connectionQueue(targetId);
|
|
96
128
|
const completion = new Promise((resolve, reject) => {
|
|
97
129
|
this.append(queue, { ...value }, undefined, resolve, reject);
|
|
@@ -112,9 +144,14 @@ export class SessionEventWriter {
|
|
|
112
144
|
const targetId = this.connectionContext.getStore();
|
|
113
145
|
const lifecycle = { type: "session_closed", sessionId };
|
|
114
146
|
for (const connectionId of this.connections.keys()) {
|
|
115
|
-
this.
|
|
147
|
+
this.connections.get(connectionId).actor.enqueue(serializeJsonLine(lifecycle));
|
|
116
148
|
}
|
|
117
|
-
|
|
149
|
+
const taggedResponse = { ...response, sessionId };
|
|
150
|
+
const registered = targetId === undefined ? undefined : this.connections.get(targetId);
|
|
151
|
+
if (registered)
|
|
152
|
+
registered.actor.enqueue(serializeJsonLine(taggedResponse));
|
|
153
|
+
else
|
|
154
|
+
this.appendSessionRecord(sessionId, taggedResponse, targetId);
|
|
118
155
|
this.requestFlush();
|
|
119
156
|
}
|
|
120
157
|
/** Drain every retained lane and the current in-flight record. */
|
|
@@ -125,7 +162,7 @@ export class SessionEventWriter {
|
|
|
125
162
|
if (this.drainPromise)
|
|
126
163
|
return this.drainPromise;
|
|
127
164
|
if (this.readyQueues.length === 0)
|
|
128
|
-
return Promise.
|
|
165
|
+
return Promise.all([...this.connections.values()].map(({ actor }) => actor.flush())).then(() => undefined);
|
|
129
166
|
let resolveDrain;
|
|
130
167
|
let rejectDrain;
|
|
131
168
|
const drain = new Promise((resolve, reject) => {
|
|
@@ -137,6 +174,8 @@ export class SessionEventWriter {
|
|
|
137
174
|
void drain.then(() => {
|
|
138
175
|
if (this.drainPromise === drain)
|
|
139
176
|
this.drainPromise = undefined;
|
|
177
|
+
if (this.readyQueues.length > 0)
|
|
178
|
+
this.requestFlush();
|
|
140
179
|
}, (cause) => {
|
|
141
180
|
if (this.drainPromise === drain)
|
|
142
181
|
this.drainPromise = undefined;
|
|
@@ -148,6 +187,7 @@ export class SessionEventWriter {
|
|
|
148
187
|
do {
|
|
149
188
|
await this.drainReadyQueues();
|
|
150
189
|
} while (this.readyQueues.length > 0);
|
|
190
|
+
await Promise.all([...this.connections.values()].map(({ actor }) => actor.flush()));
|
|
151
191
|
}
|
|
152
192
|
async drainReadyQueues() {
|
|
153
193
|
while (this.readyQueues.length > 0) {
|
|
@@ -161,7 +201,7 @@ export class SessionEventWriter {
|
|
|
161
201
|
try {
|
|
162
202
|
// D9: exactly one complete record per raw write. The next lane is not
|
|
163
203
|
// selected until this record has cleared stdout backpressure.
|
|
164
|
-
const connection = queue.targetId ? this.connections.get(queue.targetId) : undefined;
|
|
204
|
+
const connection = queue.targetId ? this.connections.get(queue.targetId)?.connection : undefined;
|
|
165
205
|
const writeRaw = connection?.writeRaw ?? this.writeRaw;
|
|
166
206
|
const waitForBackpressure = connection?.waitForBackpressure ?? this.waitForBackpressure;
|
|
167
207
|
if (!connection && queue.targetId) {
|
|
@@ -192,6 +232,17 @@ export class SessionEventWriter {
|
|
|
192
232
|
}
|
|
193
233
|
}
|
|
194
234
|
}
|
|
235
|
+
rememberSnapshot(sessionId, value, line) {
|
|
236
|
+
const event = value.assistantMessageEvent;
|
|
237
|
+
if (value.type === "message_start" || (value.type === "message_update" && event?.type === "text_start")) {
|
|
238
|
+
this.sessionSnapshots.set(sessionId, [line]);
|
|
239
|
+
}
|
|
240
|
+
else if (this.sessionSnapshots.has(sessionId)) {
|
|
241
|
+
this.sessionSnapshots.get(sessionId).push(line);
|
|
242
|
+
}
|
|
243
|
+
if (value.type === "message_end")
|
|
244
|
+
this.sessionSnapshots.delete(sessionId);
|
|
245
|
+
}
|
|
195
246
|
connectionQueue(targetId) {
|
|
196
247
|
const key = `connection:${targetId}`;
|
|
197
248
|
let queue = this.queues.get(key);
|