@code-yeongyu/senpi 2026.8.29 → 2026.8.30-3
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 +172 -0
- package/dist/core/agent-session.d.ts +47 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +406 -39
- package/dist/core/agent-session.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 +23 -9
- package/dist/core/credential-accounts.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/extensions/builtin/compaction/context-pipeline.d.ts +18 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/context-pipeline.js +32 -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 +164 -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-retry.d.ts +1 -1
- package/dist/core/extensions/builtin/compaction/idle-retry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/idle-retry.js +1 -1
- package/dist/core/extensions/builtin/compaction/idle-retry.js.map +1 -1
- 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 +148 -150
- 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/model-usability-budget.d.ts +30 -0
- package/dist/core/extensions/builtin/compaction/model-usability-budget.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/model-usability-budget.js +74 -0
- package/dist/core/extensions/builtin/compaction/model-usability-budget.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 +46 -0
- package/dist/core/extensions/builtin/compaction/orchestration.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/orchestration.js +103 -0
- package/dist/core/extensions/builtin/compaction/orchestration.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/policy.d.ts +12 -2
- package/dist/core/extensions/builtin/compaction/policy.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/policy.js +52 -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 +24 -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 +25 -0
- package/dist/core/extensions/builtin/compaction/token-budget-reminder.js.map +1 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.d.ts +27 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/tool-admission.js +56 -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/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/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/runtime-session.d.ts +11 -0
- package/dist/core/extensions/builtin/terminal/runtime-session.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/runtime-session.js +18 -2
- package/dist/core/extensions/builtin/terminal/runtime-session.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-resolver.js +2 -2
- package/dist/core/model-resolver.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.map +1 -1
- package/dist/core/session-manager.js +10 -1
- 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/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +17 -5
- package/dist/main.js.map +1 -1
- package/dist/modes/index.d.ts +1 -1
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +1 -1
- package/dist/modes/index.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 +132 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.js +633 -117
- 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 +114 -2
- 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 +158 -49
- 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 +6 -4
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +20 -4
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +76 -17
- 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-types.d.ts +86 -1
- 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.map +1 -1
- package/dist/modes/rpc/session-command-router.js +1 -0
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +2 -0
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +54 -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 +10 -2
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +58 -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 +22 -2
- package/docs/settings.md +34 -0
- package/docs/windows.md +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +62 -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/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/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/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/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/dist/screen-operations.d.ts +42 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-operations.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-operations.js +34 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-operations.js.map +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-text.d.ts +8 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-text.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-text.js +51 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen-text.js.map +1 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen.d.ts +45 -0
- package/node_modules/@earendil-works/pi-pty/dist/screen.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-pty/dist/screen.js +178 -55
- package/node_modules/@earendil-works/pi-pty/dist/screen.js.map +1 -1
- 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
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { rm } from "node:fs/promises";
|
|
1
3
|
import { executeBashWithOperations } from "../../core/bash-executor.js";
|
|
2
4
|
import { SessionManager } from "../../core/session-manager.js";
|
|
3
5
|
import { SettingsManager } from "../../core/settings-manager.js";
|
|
4
6
|
import { ensureHost } from "../rpc/host-ensure.js";
|
|
5
|
-
import { RpcClient } from "../rpc/rpc-client.js";
|
|
7
|
+
import { isTransportGoneError, RpcClient } from "../rpc/rpc-client.js";
|
|
6
8
|
export const INTERACTIVE_HOST_FALLBACK_WARNING = "Warning: shared interactive host unavailable; continuing locally";
|
|
9
|
+
export const INTERACTIVE_HOST_RECONNECTING_WARNING = "Warning: shared interactive host connection lost; reconnecting";
|
|
7
10
|
/**
|
|
8
11
|
* Replace only the transport-facing session operations. The object returned is
|
|
9
12
|
* deliberately still an AgentSessionRuntime: InteractiveMode and extensions
|
|
@@ -15,7 +18,82 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
|
|
|
15
18
|
if (!sessionPath)
|
|
16
19
|
return localRuntime;
|
|
17
20
|
const startHost = options.ensureHost ?? ((hostOptions) => ensureHost(hostOptions));
|
|
18
|
-
|
|
21
|
+
let remoteSession;
|
|
22
|
+
let remoteRuntime;
|
|
23
|
+
let reconnecting;
|
|
24
|
+
let disconnectQueued = false;
|
|
25
|
+
let disposed = false;
|
|
26
|
+
let fallbackWarned = false;
|
|
27
|
+
let reconnectWarningShown = false;
|
|
28
|
+
const warnReconnect = (cause) => {
|
|
29
|
+
if (reconnectWarningShown)
|
|
30
|
+
return;
|
|
31
|
+
reconnectWarningShown = true;
|
|
32
|
+
options.onWarning?.({
|
|
33
|
+
type: "interactive_host_action_failed",
|
|
34
|
+
message: INTERACTIVE_HOST_RECONNECTING_WARNING,
|
|
35
|
+
cause,
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
const warnFallback = (cause) => {
|
|
39
|
+
if (fallbackWarned)
|
|
40
|
+
return;
|
|
41
|
+
fallbackWarned = true;
|
|
42
|
+
options.onWarning?.({ type: "interactive_host_fallback", message: INTERACTIVE_HOST_FALLBACK_WARNING, cause });
|
|
43
|
+
};
|
|
44
|
+
let scheduleReconnect = () => { };
|
|
45
|
+
const client = new RpcClient({
|
|
46
|
+
socketPath: options.socket,
|
|
47
|
+
onDisconnect: () => {
|
|
48
|
+
disconnectQueued = true;
|
|
49
|
+
scheduleReconnect();
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
scheduleReconnect = () => {
|
|
53
|
+
if (!remoteSession || reconnecting || disposed || remoteRuntime?.isFallback)
|
|
54
|
+
return;
|
|
55
|
+
remoteRuntime?.enterReconnecting();
|
|
56
|
+
reconnecting = (async () => {
|
|
57
|
+
let cause;
|
|
58
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
59
|
+
if (disposed)
|
|
60
|
+
return;
|
|
61
|
+
disconnectQueued = false;
|
|
62
|
+
try {
|
|
63
|
+
await startHost({ socket: options.socket, agentDir: options.agentDir });
|
|
64
|
+
if (disposed)
|
|
65
|
+
return;
|
|
66
|
+
await client.start();
|
|
67
|
+
if (disposed)
|
|
68
|
+
return;
|
|
69
|
+
await client.openSession({ sessionPath, cwd: localRuntime.cwd });
|
|
70
|
+
if (disposed)
|
|
71
|
+
return;
|
|
72
|
+
await remoteSession.refresh();
|
|
73
|
+
if (disposed)
|
|
74
|
+
return;
|
|
75
|
+
fallbackWarned = false;
|
|
76
|
+
reconnectWarningShown = false;
|
|
77
|
+
remoteRuntime?.enterConnected();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
cause = error;
|
|
82
|
+
await client.stop().catch(() => { });
|
|
83
|
+
if (disposed)
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!disposed) {
|
|
88
|
+
await remoteRuntime?.enterFallback();
|
|
89
|
+
warnFallback(cause);
|
|
90
|
+
}
|
|
91
|
+
})().finally(() => {
|
|
92
|
+
reconnecting = undefined;
|
|
93
|
+
if (disconnectQueued && !disposed && !remoteRuntime?.isFallback)
|
|
94
|
+
scheduleReconnect();
|
|
95
|
+
});
|
|
96
|
+
};
|
|
19
97
|
try {
|
|
20
98
|
await startHost({ socket: options.socket, agentDir: options.agentDir });
|
|
21
99
|
await client.start();
|
|
@@ -26,38 +104,108 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
|
|
|
26
104
|
modelId: localRuntime.session.model?.id,
|
|
27
105
|
thinkingLevel: localRuntime.session.thinkingLevel,
|
|
28
106
|
});
|
|
29
|
-
|
|
30
|
-
|
|
107
|
+
remoteSession = createRemoteSessionProxy(localRuntime.session, localRuntime.services.agentDir, client, opened.state, options.onWarning, (cause) => {
|
|
108
|
+
if (remoteRuntime?.isReconnecting)
|
|
109
|
+
warnReconnect(cause);
|
|
110
|
+
else
|
|
111
|
+
warnFallback(cause);
|
|
112
|
+
});
|
|
113
|
+
if (opened.state.isBashRunning && !opened.attached) {
|
|
114
|
+
// Only a newly opened session can have an execution orphaned by a
|
|
115
|
+
// previous connection. An attach is an observer of the same live runtime;
|
|
116
|
+
// aborting it would cancel work owned by the surviving attachment.
|
|
117
|
+
await client.abortBash().catch(() => { });
|
|
118
|
+
}
|
|
119
|
+
if (opened.attached)
|
|
120
|
+
await remoteSession.refresh();
|
|
121
|
+
remoteRuntime = new RemoteInteractiveRuntime(localRuntime, remoteSession, client, {
|
|
122
|
+
onTransportGone: (cause) => {
|
|
123
|
+
if (remoteRuntime?.isReconnecting)
|
|
124
|
+
warnReconnect(cause);
|
|
125
|
+
else
|
|
126
|
+
warnFallback(cause);
|
|
127
|
+
},
|
|
128
|
+
get reconnecting() {
|
|
129
|
+
return reconnecting;
|
|
130
|
+
},
|
|
131
|
+
dispose: () => {
|
|
132
|
+
disposed = true;
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
return remoteRuntime;
|
|
31
136
|
}
|
|
32
137
|
catch (cause) {
|
|
33
138
|
await client.stop().catch(() => { });
|
|
34
|
-
|
|
35
|
-
type: "interactive_host_fallback",
|
|
36
|
-
message: `${INTERACTIVE_HOST_FALLBACK_WARNING}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
37
|
-
cause,
|
|
38
|
-
});
|
|
139
|
+
warnFallback(cause);
|
|
39
140
|
return localRuntime;
|
|
40
141
|
}
|
|
41
142
|
}
|
|
42
|
-
class RemoteInteractiveRuntime {
|
|
143
|
+
export class RemoteInteractiveRuntime {
|
|
43
144
|
#local;
|
|
44
145
|
#remoteSession;
|
|
45
146
|
#client;
|
|
147
|
+
#lifecycle;
|
|
148
|
+
#onTransportGone;
|
|
46
149
|
#rebindSession;
|
|
47
150
|
#beforeSessionInvalidate;
|
|
48
|
-
|
|
151
|
+
#state = "connected";
|
|
152
|
+
#fallbackHandoff;
|
|
153
|
+
get isReconnecting() {
|
|
154
|
+
return this.#state === "reconnecting";
|
|
155
|
+
}
|
|
156
|
+
get isFallback() {
|
|
157
|
+
return this.#state === "fallback";
|
|
158
|
+
}
|
|
159
|
+
constructor(local, remoteSession, client, lifecycle) {
|
|
49
160
|
this.#local = local;
|
|
50
161
|
this.#remoteSession = remoteSession;
|
|
51
162
|
this.#client = client;
|
|
163
|
+
this.#lifecycle = lifecycle;
|
|
164
|
+
this.#onTransportGone = lifecycle?.onTransportGone ?? (() => { });
|
|
165
|
+
}
|
|
166
|
+
async #call(call) {
|
|
167
|
+
try {
|
|
168
|
+
return await call();
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
if (isTransportGoneError(error)) {
|
|
172
|
+
this.#onTransportGone(error);
|
|
173
|
+
return { cancelled: true };
|
|
174
|
+
}
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
enterConnected() {
|
|
179
|
+
if (this.#state !== "disposed" && this.#state !== "fallback")
|
|
180
|
+
this.#state = "connected";
|
|
181
|
+
}
|
|
182
|
+
enterReconnecting() {
|
|
183
|
+
if (this.#state !== "disposed" && this.#state !== "fallback")
|
|
184
|
+
this.#state = "reconnecting";
|
|
185
|
+
}
|
|
186
|
+
async enterFallback() {
|
|
187
|
+
if (this.#state === "disposed" || this.#state === "fallback")
|
|
188
|
+
return;
|
|
189
|
+
this.#state = "fallback";
|
|
190
|
+
this.#beforeSessionInvalidate?.();
|
|
191
|
+
const rebind = this.#rebindSession?.();
|
|
192
|
+
if (rebind) {
|
|
193
|
+
this.#fallbackHandoff = rebind.catch((error) => {
|
|
194
|
+
this.#onTransportGone(error);
|
|
195
|
+
});
|
|
196
|
+
await this.#fallbackHandoff;
|
|
197
|
+
}
|
|
52
198
|
}
|
|
53
199
|
get session() {
|
|
54
|
-
return this.#
|
|
200
|
+
return this.#state === "fallback" || this.#state === "disposed"
|
|
201
|
+
? this.#local.session
|
|
202
|
+
: this.#remoteSession.session;
|
|
55
203
|
}
|
|
56
204
|
get services() {
|
|
57
205
|
return this.#local.services;
|
|
58
206
|
}
|
|
59
207
|
get cwd() {
|
|
60
|
-
return this
|
|
208
|
+
return this.session.sessionManager.getCwd();
|
|
61
209
|
}
|
|
62
210
|
get diagnostics() {
|
|
63
211
|
return this.#local.diagnostics;
|
|
@@ -70,90 +218,238 @@ class RemoteInteractiveRuntime {
|
|
|
70
218
|
}
|
|
71
219
|
setBeforeSessionInvalidate(callback) {
|
|
72
220
|
this.#beforeSessionInvalidate = callback;
|
|
221
|
+
this.#local.setBeforeSessionInvalidate(callback);
|
|
73
222
|
}
|
|
74
223
|
setRebindSession(callback) {
|
|
75
224
|
this.#rebindSession = callback;
|
|
225
|
+
this.#local.setRebindSession(callback);
|
|
226
|
+
if (callback && this.#state === "fallback" && !this.#fallbackHandoff) {
|
|
227
|
+
this.#fallbackHandoff = callback().catch((error) => {
|
|
228
|
+
this.#onTransportGone(error);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
setHostUiHandler(callback) {
|
|
233
|
+
this.#remoteSession.setHostUiHandler(callback);
|
|
76
234
|
}
|
|
77
235
|
async dispose() {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
236
|
+
if (this.#state === "disposed")
|
|
237
|
+
return;
|
|
238
|
+
this.#state = "disposed";
|
|
239
|
+
this.#lifecycle?.dispose();
|
|
240
|
+
await this.#lifecycle?.reconnecting?.catch(() => { });
|
|
241
|
+
await this.#fallbackHandoff?.catch(() => { });
|
|
242
|
+
const errors = [];
|
|
243
|
+
for (const cleanup of [
|
|
244
|
+
() => this.#remoteSession.abortLocalBash(),
|
|
245
|
+
() => this.#client.closeSession(),
|
|
246
|
+
() => this.#client.stop(),
|
|
247
|
+
() => this.#local.dispose(),
|
|
248
|
+
]) {
|
|
249
|
+
try {
|
|
250
|
+
await cleanup();
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
errors.push(error);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (errors.length > 0)
|
|
257
|
+
throw errors[0];
|
|
81
258
|
}
|
|
82
259
|
async newSession(options) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
260
|
+
if (this.#state === "fallback")
|
|
261
|
+
return this.#local.newSession(options);
|
|
262
|
+
// Suppresses for the ENTIRE command, not just the refresh tail: a
|
|
263
|
+
// multi-session host emits session_replaced while completing it, so the echo
|
|
264
|
+
// can arrive before the refresh/rebind sequence below starts and race it -
|
|
265
|
+
// newSession transports setup entries between two refreshes.
|
|
266
|
+
return this.#remoteSession
|
|
267
|
+
.aroundLocalReplacement(async () => {
|
|
268
|
+
const result = await this.#call(() => this.#client.newSession(options?.parentSession));
|
|
269
|
+
if (!result.cancelled) {
|
|
270
|
+
this.#beforeSessionInvalidate?.();
|
|
271
|
+
this.#remoteSession.abortLocalBash();
|
|
93
272
|
await this.#remoteSession.refresh();
|
|
273
|
+
if (options?.setup) {
|
|
274
|
+
const capture = SessionManager.inMemory(this.#remoteSession.session.sessionManager.getCwd());
|
|
275
|
+
await options.setup(capture);
|
|
276
|
+
for (const entry of capture.getEntries())
|
|
277
|
+
await this.#client.appendSessionEntry(entry);
|
|
278
|
+
await this.#remoteSession.refresh();
|
|
279
|
+
}
|
|
280
|
+
await this.#rebindSession?.();
|
|
281
|
+
if (options?.withSession)
|
|
282
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
94
283
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
284
|
+
return result;
|
|
285
|
+
})
|
|
286
|
+
.catch((error) => {
|
|
287
|
+
if (isTransportGoneError(error))
|
|
288
|
+
return { cancelled: true };
|
|
289
|
+
throw error;
|
|
290
|
+
});
|
|
100
291
|
}
|
|
101
292
|
async switchSession(sessionPath, options) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
await
|
|
113
|
-
|
|
114
|
-
|
|
293
|
+
if (this.#state === "fallback")
|
|
294
|
+
return this.#local.switchSession(sessionPath, options);
|
|
295
|
+
// See newSession: the suppression must cover the command itself, since the
|
|
296
|
+
// host emits session_replaced while completing it.
|
|
297
|
+
return this.#remoteSession
|
|
298
|
+
.aroundLocalReplacement(async () => {
|
|
299
|
+
const result = await this.#call(() => this.#client.switchSession(sessionPath, options?.cwdOverride === undefined ? undefined : { cwdOverride: options.cwdOverride }));
|
|
300
|
+
if (!result.cancelled) {
|
|
301
|
+
this.#beforeSessionInvalidate?.();
|
|
302
|
+
this.#remoteSession.abortLocalBash();
|
|
303
|
+
await this.#remoteSession.refresh();
|
|
304
|
+
await this.#rebindSession?.();
|
|
305
|
+
// The shared host already resolved trust; this callback is retained for
|
|
306
|
+
// compatibility, but its result cannot override host-authoritative state.
|
|
307
|
+
options?.projectTrustContextFactory?.(this.#remoteSession.session.sessionManager.getCwd());
|
|
308
|
+
if (options?.withSession)
|
|
309
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
310
|
+
}
|
|
311
|
+
return result;
|
|
312
|
+
})
|
|
313
|
+
.catch((error) => {
|
|
314
|
+
if (isTransportGoneError(error))
|
|
315
|
+
return { cancelled: true };
|
|
316
|
+
throw error;
|
|
317
|
+
});
|
|
115
318
|
}
|
|
116
319
|
async fork(entryId, options) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
320
|
+
if (this.#state === "fallback")
|
|
321
|
+
return this.#local.fork(entryId, options);
|
|
322
|
+
// See newSession: the suppression must cover the command itself.
|
|
323
|
+
return this.#remoteSession
|
|
324
|
+
.aroundLocalReplacement(async () => {
|
|
325
|
+
const result = await this.#call(() => this.#client.fork(entryId, options));
|
|
326
|
+
if (!result.cancelled) {
|
|
327
|
+
this.#beforeSessionInvalidate?.();
|
|
328
|
+
this.#remoteSession.abortLocalBash();
|
|
329
|
+
await this.#refreshAndRebind();
|
|
330
|
+
if (options?.withSession)
|
|
331
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
332
|
+
}
|
|
333
|
+
return { cancelled: result.cancelled, selectedText: result.text };
|
|
334
|
+
})
|
|
335
|
+
.catch((error) => {
|
|
336
|
+
if (isTransportGoneError(error))
|
|
337
|
+
return { cancelled: true };
|
|
338
|
+
throw error;
|
|
339
|
+
});
|
|
126
340
|
}
|
|
127
341
|
async importFromJsonl(inputPath, cwdOverride) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
342
|
+
if (this.#state === "fallback")
|
|
343
|
+
return this.#local.importFromJsonl(inputPath, cwdOverride);
|
|
344
|
+
// See newSession: the suppression must cover the command itself.
|
|
345
|
+
return this.#remoteSession
|
|
346
|
+
.aroundLocalReplacement(async () => {
|
|
347
|
+
const result = await this.#call(() => this.#client.importJsonl(inputPath, cwdOverride));
|
|
348
|
+
if (!result.cancelled) {
|
|
349
|
+
this.#beforeSessionInvalidate?.();
|
|
350
|
+
this.#remoteSession.abortLocalBash();
|
|
351
|
+
await this.#refreshAndRebind();
|
|
352
|
+
}
|
|
353
|
+
return result;
|
|
354
|
+
})
|
|
355
|
+
.catch((error) => {
|
|
356
|
+
if (isTransportGoneError(error))
|
|
357
|
+
return { cancelled: true };
|
|
358
|
+
throw error;
|
|
359
|
+
});
|
|
135
360
|
}
|
|
136
361
|
async #refreshAndRebind() {
|
|
137
362
|
await this.#remoteSession.refresh();
|
|
138
363
|
await this.#rebindSession?.();
|
|
139
364
|
}
|
|
140
365
|
}
|
|
141
|
-
|
|
366
|
+
/** Exported for direct coverage of the wire-event handling below. */
|
|
367
|
+
export function createRemoteSessionProxy(local, agentDir, client, initialState, onWarning, onTransportGone) {
|
|
142
368
|
// Fire-and-forget setters keep the sync AgentSession signature, but their RPC
|
|
143
369
|
// failures must not vanish: the matching *_changed wire event confirms success,
|
|
144
370
|
// and a rejection here is the only signal of failure.
|
|
145
371
|
const reportActionFailure = (action) => (error) => {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
372
|
+
const transportGone = isTransportGoneError(error);
|
|
373
|
+
if (transportGone)
|
|
374
|
+
onTransportGone?.(error);
|
|
375
|
+
else
|
|
376
|
+
onWarning?.({
|
|
377
|
+
type: "interactive_host_action_failed",
|
|
378
|
+
message: `Warning: shared interactive host ${action} failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
379
|
+
cause: error,
|
|
380
|
+
});
|
|
381
|
+
};
|
|
382
|
+
const transportCall = async (action, call, cancelledValue) => {
|
|
383
|
+
try {
|
|
384
|
+
return await call();
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
if (!isTransportGoneError(error))
|
|
388
|
+
throw error;
|
|
389
|
+
reportActionFailure(action)(error);
|
|
390
|
+
return cancelledValue;
|
|
391
|
+
}
|
|
151
392
|
};
|
|
152
393
|
let state = { ...initialState };
|
|
153
|
-
|
|
394
|
+
const waitForBashCallbacks = async (promises, allowAbandonment) => {
|
|
395
|
+
if (promises.size === 0)
|
|
396
|
+
return true;
|
|
397
|
+
const settled = Promise.allSettled([...promises]).then(() => true);
|
|
398
|
+
let timeout;
|
|
399
|
+
const bounded = new Promise((resolve) => {
|
|
400
|
+
timeout = setTimeout(resolve, allowAbandonment ? 100 : 5_000, false);
|
|
401
|
+
timeout.unref?.();
|
|
402
|
+
});
|
|
403
|
+
try {
|
|
404
|
+
return await Promise.race([settled, bounded]);
|
|
405
|
+
}
|
|
406
|
+
finally {
|
|
407
|
+
if (timeout)
|
|
408
|
+
clearTimeout(timeout);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
const executionNamespace = randomUUID();
|
|
412
|
+
const cleanupRemoteBashOutput = async (path) => {
|
|
413
|
+
let cleanupTimeout;
|
|
414
|
+
try {
|
|
415
|
+
await Promise.race([
|
|
416
|
+
client.cleanupBashOutput(path),
|
|
417
|
+
new Promise((_, reject) => {
|
|
418
|
+
cleanupTimeout = setTimeout(() => reject(new Error("RPC cleanup timed out")), 1_000);
|
|
419
|
+
}),
|
|
420
|
+
]);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
catch (transportError) {
|
|
424
|
+
// Spill paths are shared host filesystem paths. If the RPC transport is
|
|
425
|
+
// already unavailable, remove locally as a bounded best-effort fallback;
|
|
426
|
+
// a later reconnect is not required to prevent this callback failure from
|
|
427
|
+
// permanently leaking the host-owned artifact.
|
|
428
|
+
try {
|
|
429
|
+
await rm(path, { force: true });
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
catch (localError) {
|
|
433
|
+
onWarning?.({
|
|
434
|
+
type: "interactive_host_action_failed",
|
|
435
|
+
message: `Warning: failed to clean up shared bash output spill: ${localError instanceof Error ? localError.message : String(localError)}`,
|
|
436
|
+
cause: new AggregateError([transportError, localError]),
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
finally {
|
|
441
|
+
if (cleanupTimeout)
|
|
442
|
+
clearTimeout(cleanupTimeout);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
let nextBashExecutionId = 0;
|
|
446
|
+
const bashExecutions = new Map();
|
|
447
|
+
let hostUiHandler;
|
|
448
|
+
const pendingUiRequests = [];
|
|
154
449
|
let localBashAbortController;
|
|
155
450
|
let localBashRunning = false;
|
|
156
451
|
let hostBashRunning = initialState.isBashRunning;
|
|
452
|
+
let nextQueuedInputOrder = Math.max(0, ...initialState.ordered.map((item) => item.enqueueOrder));
|
|
157
453
|
let sessionManager = local.sessionManager;
|
|
158
454
|
let settingsManager = SettingsManager.create(initialState.cwd, agentDir, {
|
|
159
455
|
projectTrusted: initialState.projectTrusted,
|
|
@@ -164,8 +460,10 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
164
460
|
const remoteSessionManager = new Proxy({}, {
|
|
165
461
|
get(_target, property, _receiver) {
|
|
166
462
|
if (property === "appendLabelChange") {
|
|
167
|
-
return (entryId, label) => void client.setLabel(entryId, label);
|
|
463
|
+
return (entryId, label) => void client.setLabel(entryId, label).catch(reportActionFailure("appendLabelChange"));
|
|
168
464
|
}
|
|
465
|
+
if (property === "getCwd")
|
|
466
|
+
return () => state.cwd;
|
|
169
467
|
if (property === "getSessionName")
|
|
170
468
|
return () => state.sessionName;
|
|
171
469
|
if (property === "getUsageTotals")
|
|
@@ -176,8 +474,38 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
176
474
|
});
|
|
177
475
|
let streamingAssistant;
|
|
178
476
|
let mirroredCurrentAssistantUsage = false;
|
|
477
|
+
/** Non-zero while this runtime is driving its own replacement sequence. */
|
|
478
|
+
let localReplacementDepth = 0;
|
|
179
479
|
const listeners = new Set();
|
|
180
480
|
client.onEvent((wireEvent) => {
|
|
481
|
+
if (wireEvent.type === "extension_ui_request") {
|
|
482
|
+
const request = wireEvent;
|
|
483
|
+
if (hostUiHandler)
|
|
484
|
+
void Promise.resolve(hostUiHandler(request))
|
|
485
|
+
.then((response) => response && client.sendExtensionUIResponse(response))
|
|
486
|
+
.catch(reportActionFailure("host UI response"));
|
|
487
|
+
else
|
|
488
|
+
pendingUiRequests.push(request);
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
if (wireEvent.type === "session_replaced") {
|
|
492
|
+
// The host swapped the live session behind this connection - another
|
|
493
|
+
// attached client issued the replacement, or an extension drove one that no
|
|
494
|
+
// client issued. Nothing else re-reads the binding, so without this the
|
|
495
|
+
// proxy keeps serving the previous session's manager, settings and message
|
|
496
|
+
// mirror while the host has already moved on. The command response carries
|
|
497
|
+
// only `{ cancelled }`, so this event is the only signal available here.
|
|
498
|
+
//
|
|
499
|
+
// A multi-session host broadcasts the event to every connection including
|
|
500
|
+
// the one that issued the replacement, and this runtime's own replacement
|
|
501
|
+
// methods already run an ordered refresh/rebind - newSession additionally
|
|
502
|
+
// transports setup entries between two refreshes. Refreshing again from
|
|
503
|
+
// here would race that sequence, so self-driven replacements are skipped.
|
|
504
|
+
if (localReplacementDepth === 0) {
|
|
505
|
+
void refresh().catch(reportActionFailure("session refresh after replacement"));
|
|
506
|
+
}
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
181
509
|
if (wireEvent.type === "agent_settled")
|
|
182
510
|
state = { ...state, isStreaming: false, retryAttempt: 0 };
|
|
183
511
|
if (wireEvent.type === "bash_start") {
|
|
@@ -188,8 +516,30 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
188
516
|
hostBashRunning = false;
|
|
189
517
|
updateBashState();
|
|
190
518
|
}
|
|
191
|
-
if (wireEvent.type === "bash_execution_update")
|
|
192
|
-
|
|
519
|
+
if (wireEvent.type === "bash_execution_update" && wireEvent.id) {
|
|
520
|
+
const execution = bashExecutions.get(wireEvent.id);
|
|
521
|
+
if (execution?.chunk && !execution.hasError) {
|
|
522
|
+
try {
|
|
523
|
+
const callbackPromise = Promise.resolve(execution.chunk(wireEvent.delta)).catch((error) => {
|
|
524
|
+
if (!execution.hasError) {
|
|
525
|
+
execution.error = error;
|
|
526
|
+
execution.hasError = true;
|
|
527
|
+
void client.abortBash().catch(() => { });
|
|
528
|
+
}
|
|
529
|
+
throw error;
|
|
530
|
+
});
|
|
531
|
+
execution.promises.add(callbackPromise);
|
|
532
|
+
void callbackPromise.catch(() => { }).finally(() => execution.promises.delete(callbackPromise));
|
|
533
|
+
}
|
|
534
|
+
catch (error) {
|
|
535
|
+
if (!execution.hasError) {
|
|
536
|
+
execution.error = error;
|
|
537
|
+
execution.hasError = true;
|
|
538
|
+
void client.abortBash().catch(() => { });
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
193
543
|
if (wireEvent.type === "agent_start")
|
|
194
544
|
state = { ...state, isStreaming: true };
|
|
195
545
|
if (wireEvent.type === "compaction_start")
|
|
@@ -201,6 +551,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
201
551
|
if (wireEvent.type === "auto_retry_end")
|
|
202
552
|
state = { ...state, retryAttempt: 0 };
|
|
203
553
|
if (wireEvent.type === "queue_update") {
|
|
554
|
+
nextQueuedInputOrder = Math.max(nextQueuedInputOrder, ...wireEvent.ordered.map((item) => item.enqueueOrder));
|
|
204
555
|
state = {
|
|
205
556
|
...state,
|
|
206
557
|
steering: [...wireEvent.steering],
|
|
@@ -217,6 +568,23 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
217
568
|
if (wireEvent.type === "service_tier_changed") {
|
|
218
569
|
state = { ...state, serviceTier: wireEvent.tier, fastMode: wireEvent.fastMode };
|
|
219
570
|
}
|
|
571
|
+
if (wireEvent.type === "session_settings_changed") {
|
|
572
|
+
state = {
|
|
573
|
+
...state,
|
|
574
|
+
steeringMode: wireEvent.steeringMode,
|
|
575
|
+
followUpMode: wireEvent.followUpMode,
|
|
576
|
+
autoCompactionEnabled: wireEvent.autoCompactionEnabled,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
if (wireEvent.type === "entry_appended") {
|
|
580
|
+
try {
|
|
581
|
+
sessionManager.appendEntry(wireEvent.entry);
|
|
582
|
+
local.agent.state.messages = sessionManager.buildSessionContext().messages;
|
|
583
|
+
}
|
|
584
|
+
catch {
|
|
585
|
+
// Non-fatal if the local snapshot cannot accept a concurrent entry.
|
|
586
|
+
}
|
|
587
|
+
}
|
|
220
588
|
if (wireEvent.type === "session_info_changed")
|
|
221
589
|
state = { ...state, sessionName: wireEvent.name };
|
|
222
590
|
if (wireEvent.type === "message_start") {
|
|
@@ -233,8 +601,13 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
233
601
|
if (usage && !mirroredCurrentAssistantUsage) {
|
|
234
602
|
mirroredCurrentAssistantUsage = true;
|
|
235
603
|
const latestPromptTokens = usage.input + usage.cacheRead + usage.cacheWrite;
|
|
604
|
+
const contextWindow = state.model?.contextWindow ?? 0;
|
|
605
|
+
const contextTokens = usage.input + usage.cacheRead + usage.cacheWrite;
|
|
236
606
|
state = {
|
|
237
607
|
...state,
|
|
608
|
+
contextUsage: contextWindow > 0
|
|
609
|
+
? { tokens: contextTokens, contextWindow, percent: (contextTokens / contextWindow) * 100 }
|
|
610
|
+
: undefined,
|
|
238
611
|
usageTotals: {
|
|
239
612
|
...state.usageTotals,
|
|
240
613
|
input: state.usageTotals.input + usage.input,
|
|
@@ -254,7 +627,9 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
254
627
|
}
|
|
255
628
|
if (wireEvent.type === "compaction_end" && wireEvent.accepted && !wireEvent.aborted) {
|
|
256
629
|
try {
|
|
257
|
-
sessionManager.reloadFromDisk?.();
|
|
630
|
+
local.sessionManager.reloadFromDisk?.();
|
|
631
|
+
if (sessionManager !== local.sessionManager)
|
|
632
|
+
sessionManager.reloadFromDisk?.();
|
|
258
633
|
local.agent.state.messages = sessionManager.buildSessionContext().messages;
|
|
259
634
|
}
|
|
260
635
|
catch {
|
|
@@ -265,43 +640,109 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
265
640
|
for (const listener of listeners)
|
|
266
641
|
listener(event);
|
|
267
642
|
});
|
|
268
|
-
const
|
|
269
|
-
|
|
643
|
+
const performRefresh = async () => {
|
|
644
|
+
// Captured before the first await: entries that arrive via `entry_appended`
|
|
645
|
+
// while this refresh is in flight are newer than the snapshot it reconciles
|
|
646
|
+
// against, and must survive the rebuild below.
|
|
647
|
+
const idsAtRefreshStart = new Set(sessionManager.getEntries().map((entry) => entry.id));
|
|
648
|
+
const nextState = await transportCall("refresh", () => client.getState());
|
|
649
|
+
if (!nextState)
|
|
650
|
+
return;
|
|
270
651
|
state = { ...stateFromRpc(nextState) };
|
|
652
|
+
nextQueuedInputOrder = Math.max(0, ...nextState.ordered.map((item) => item.enqueueOrder));
|
|
271
653
|
let messages;
|
|
272
654
|
settingsManager = SettingsManager.create(nextState.cwd, agentDir, {
|
|
273
655
|
projectTrusted: nextState.projectTrusted,
|
|
274
656
|
});
|
|
275
657
|
if (nextState.sessionFile) {
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
if (
|
|
280
|
-
|
|
281
|
-
|
|
658
|
+
// Keep the caller-owned manager identity when refreshing the same session.
|
|
659
|
+
// Navigation and host-side mutations must update every existing mirror, not
|
|
660
|
+
// leave the interactive runtime holding the pre-refresh snapshot.
|
|
661
|
+
if (sessionManager.getSessionFile() === nextState.sessionFile) {
|
|
662
|
+
sessionManager.reloadFromDisk?.();
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
// SessionManager.open retains the explicit path even when the host has
|
|
666
|
+
// deferred creating the file for a setup-only session.
|
|
667
|
+
sessionManager = SessionManager.open(nextState.sessionFile, undefined, nextState.cwd);
|
|
668
|
+
}
|
|
669
|
+
// The host ships its complete entry list while the session file is still
|
|
670
|
+
// deferred (no assistant message yet, so nothing has been written), which
|
|
671
|
+
// makes that list the only authoritative view of the session. Reconcile the
|
|
672
|
+
// mirror to it exactly: `entry_appended` notifications that cross a
|
|
673
|
+
// concurrent refresh land on whichever manager is current, so the mirror
|
|
674
|
+
// can hold an arbitrary partial set rather than a prefix, and a snapshot
|
|
675
|
+
// taken mid-bind can simply be short. Backfilling only an empty mirror
|
|
676
|
+
// wedged it at whatever partial set it happened to hold.
|
|
677
|
+
const authoritative = nextState.entries;
|
|
678
|
+
if (authoritative?.length) {
|
|
679
|
+
const mirror = sessionManager.getEntries();
|
|
680
|
+
const matches = mirror.length === authoritative.length &&
|
|
681
|
+
mirror.every((entry, index) => entry.id === authoritative[index]?.id);
|
|
682
|
+
if (!matches) {
|
|
683
|
+
const rebuilt = SessionManager.open(nextState.sessionFile, undefined, nextState.cwd);
|
|
684
|
+
for (const entry of authoritative)
|
|
685
|
+
rebuilt.appendEntry(entry);
|
|
686
|
+
// A notification that crossed this refresh refers to an entry the
|
|
687
|
+
// snapshot predates, so it is newer than the snapshot; dropping it
|
|
688
|
+
// would strand the entry until an unrelated refresh happened to
|
|
689
|
+
// run. Keep it, in arrival order, after the authoritative list.
|
|
690
|
+
const authoritativeIds = new Set(authoritative.map((entry) => entry.id));
|
|
691
|
+
for (const entry of mirror) {
|
|
692
|
+
if (!idsAtRefreshStart.has(entry.id) && !authoritativeIds.has(entry.id)) {
|
|
693
|
+
rebuilt.appendEntry(entry);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
sessionManager = rebuilt;
|
|
697
|
+
}
|
|
282
698
|
}
|
|
283
699
|
messages = sessionManager.buildSessionContext().messages;
|
|
284
700
|
}
|
|
285
701
|
else {
|
|
286
|
-
messages = await client.getMessages();
|
|
702
|
+
messages = await transportCall("refresh", () => client.getMessages(), []);
|
|
287
703
|
}
|
|
288
704
|
local.agent.state.messages.splice(0, local.agent.state.messages.length, ...structuredClone(messages));
|
|
289
705
|
streamingAssistant = undefined;
|
|
290
706
|
};
|
|
707
|
+
// Refreshes must not interleave: each one reassigns sessionManager,
|
|
708
|
+
// settingsManager and the mirrored message list, so two in flight can commit a
|
|
709
|
+
// mixed snapshot of two different sessions. A replacement-driven refresh races
|
|
710
|
+
// exactly that way against a caller-driven one, so run them in order.
|
|
711
|
+
let refreshChain = Promise.resolve();
|
|
712
|
+
const refresh = () => {
|
|
713
|
+
const next = refreshChain.then(performRefresh, performRefresh);
|
|
714
|
+
refreshChain = next.then(() => { }, () => { });
|
|
715
|
+
return next;
|
|
716
|
+
};
|
|
291
717
|
const session = new Proxy(local, {
|
|
292
718
|
get(target, property, receiver) {
|
|
293
719
|
if (property === "prompt")
|
|
294
|
-
return (message, options) =>
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
720
|
+
return async (message, options) => {
|
|
721
|
+
try {
|
|
722
|
+
await client.prompt(message, {
|
|
723
|
+
...(options?.images ? { images: options.images } : {}),
|
|
724
|
+
...(options?.streamingBehavior ? { streamingBehavior: options.streamingBehavior } : {}),
|
|
725
|
+
...(options?.thinkingLevel ? { thinkingLevel: options.thinkingLevel } : {}),
|
|
726
|
+
...(options?.sessionTitlePrompt !== undefined
|
|
727
|
+
? { sessionTitlePrompt: options.sessionTitlePrompt }
|
|
728
|
+
: {}),
|
|
729
|
+
...(options?.expandPromptTemplates !== undefined
|
|
730
|
+
? { expandPromptTemplates: options.expandPromptTemplates }
|
|
731
|
+
: {}),
|
|
732
|
+
...(options?.promptDisposition ? { promptDisposition: options.promptDisposition } : {}),
|
|
733
|
+
...(options?.preflightResult ? { preflightResult: options.preflightResult } : {}),
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
catch (error) {
|
|
737
|
+
if (!isTransportGoneError(error))
|
|
738
|
+
throw error;
|
|
739
|
+
reportActionFailure("prompt")(error);
|
|
740
|
+
}
|
|
741
|
+
};
|
|
301
742
|
if (property === "abort")
|
|
302
743
|
return () => client.abort();
|
|
303
744
|
if (property === "abortCompaction")
|
|
304
|
-
return () => void client.abortCompaction();
|
|
745
|
+
return () => void client.abortCompaction().catch(reportActionFailure("abortCompaction"));
|
|
305
746
|
if (property === "steer")
|
|
306
747
|
return (message, images, recovery) => {
|
|
307
748
|
const enqueueOrder = recovery?.enqueueOrder ?? Math.max(0, ...state.ordered.map((item) => item.enqueueOrder)) + 1;
|
|
@@ -311,7 +752,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
311
752
|
ordered: [...state.ordered, { text: message, mode: "steer", enqueueOrder }],
|
|
312
753
|
pendingMessageCount: state.pendingMessageCount + 1,
|
|
313
754
|
};
|
|
314
|
-
return client.steer(message, images, { ...recovery, enqueueOrder });
|
|
755
|
+
return transportCall("steer", () => client.steer(message, images, { ...recovery, enqueueOrder }));
|
|
315
756
|
};
|
|
316
757
|
if (property === "followUp")
|
|
317
758
|
return (message, images, recovery) => {
|
|
@@ -322,31 +763,53 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
322
763
|
ordered: [...state.ordered, { text: message, mode: "followUp", enqueueOrder }],
|
|
323
764
|
pendingMessageCount: state.pendingMessageCount + 1,
|
|
324
765
|
};
|
|
325
|
-
return client.followUp(message, images, { ...recovery, enqueueOrder });
|
|
766
|
+
return transportCall("followUp", () => client.followUp(message, images, { ...recovery, enqueueOrder }));
|
|
326
767
|
};
|
|
327
768
|
if (property === "waitForIdle")
|
|
328
769
|
return () => client.waitForIdle();
|
|
329
770
|
if (property === "getLastAssistantText")
|
|
330
771
|
return () => target.getLastAssistantText();
|
|
331
|
-
if (property === "setModel")
|
|
772
|
+
if (property === "setModel" || property === "setSessionModel")
|
|
332
773
|
return async (model) => {
|
|
333
|
-
const next = await client.setModel(model.provider, model.id);
|
|
334
|
-
return { systemPromptName: next.systemPromptName, model: next };
|
|
774
|
+
const next = await transportCall("setModel", () => client.setModel(model.provider, model.id), undefined);
|
|
775
|
+
return next ? { systemPromptName: next.systemPromptName, model: next } : undefined;
|
|
776
|
+
};
|
|
777
|
+
if (property === "setSessionThinkingLevel")
|
|
778
|
+
return (level) => void client
|
|
779
|
+
.setThinkingLevel(level, { scope: "turn" })
|
|
780
|
+
.catch(reportActionFailure("setSessionThinkingLevel"));
|
|
781
|
+
if (property === "setSessionFastMode")
|
|
782
|
+
return (enabled) => void client.setFastMode(enabled).catch(reportActionFailure("setSessionFastMode"));
|
|
783
|
+
if (property === "abortRetry")
|
|
784
|
+
return () => void client.abortRetry().catch(reportActionFailure("abortRetry"));
|
|
785
|
+
if (property === "setAutoRetryEnabled")
|
|
786
|
+
return (enabled) => void client.setAutoRetry(enabled).catch(reportActionFailure("setAutoRetryEnabled"));
|
|
787
|
+
if (property === "reserveQueuedInputOrder")
|
|
788
|
+
return () => ++nextQueuedInputOrder;
|
|
789
|
+
if (property === "setFavoriteModels")
|
|
790
|
+
return (models) => {
|
|
791
|
+
state = { ...state, favoriteModels: [...models] };
|
|
792
|
+
void client.setFavoriteModels(models).catch(reportActionFailure("setFavoriteModels"));
|
|
793
|
+
};
|
|
794
|
+
if (property === "setScopedModels")
|
|
795
|
+
return (models) => {
|
|
796
|
+
state = { ...state, scopedModels: [...models] };
|
|
797
|
+
void client.setScopedModels(models).catch(reportActionFailure("setScopedModels"));
|
|
335
798
|
};
|
|
336
799
|
if (property === "cycleModel")
|
|
337
|
-
return () => client.cycleModel();
|
|
800
|
+
return (direction) => transportCall("cycleModel", () => client.cycleModel(direction), undefined);
|
|
338
801
|
if (property === "setThinkingLevel")
|
|
339
802
|
return (level) => void client.setThinkingLevel(level).catch(reportActionFailure("setThinkingLevel"));
|
|
340
803
|
if (property === "cycleThinkingLevel")
|
|
341
|
-
return () => client.cycleThinkingLevel().then((result) => result?.level);
|
|
804
|
+
return () => transportCall("cycleThinkingLevel", () => client.cycleThinkingLevel(), undefined).then((result) => result?.level);
|
|
342
805
|
if (property === "getAvailableThinkingLevels")
|
|
343
|
-
return () => client.getAvailableThinkingLevels();
|
|
806
|
+
return () => transportCall("getAvailableThinkingLevels", () => client.getAvailableThinkingLevels(), []);
|
|
344
807
|
if (property === "setSteeringMode")
|
|
345
808
|
return (mode) => void client.setSteeringMode(mode).catch(reportActionFailure("setSteeringMode"));
|
|
346
809
|
if (property === "setFollowUpMode")
|
|
347
810
|
return (mode) => void client.setFollowUpMode(mode).catch(reportActionFailure("setFollowUpMode"));
|
|
348
811
|
if (property === "compact")
|
|
349
|
-
return (instructions) => client.compact(instructions);
|
|
812
|
+
return (instructions) => transportCall("compact", () => client.compact(instructions));
|
|
350
813
|
if (property === "setAutoCompactionEnabled")
|
|
351
814
|
return (enabled) => void client.setAutoCompaction(enabled).catch(reportActionFailure("setAutoCompaction"));
|
|
352
815
|
if (property === "executeBash")
|
|
@@ -362,7 +825,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
362
825
|
try {
|
|
363
826
|
const result = await executeBashWithOperations(resolvedCommand, state.cwd, options.operations, { onChunk, signal: abortController.signal });
|
|
364
827
|
if (state.sessionId === sessionAtStart) {
|
|
365
|
-
await client.recordBashResult(command, result, options.excludeFromContext);
|
|
828
|
+
await transportCall("recordBashResult", () => client.recordBashResult(command, result, options.excludeFromContext));
|
|
366
829
|
}
|
|
367
830
|
return result;
|
|
368
831
|
}
|
|
@@ -372,15 +835,35 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
372
835
|
updateBashState();
|
|
373
836
|
}
|
|
374
837
|
}
|
|
375
|
-
|
|
838
|
+
const executionId = `bash-${executionNamespace}-${++nextBashExecutionId}`;
|
|
839
|
+
const execution = {
|
|
840
|
+
chunk: onChunk,
|
|
841
|
+
promises: new Set(),
|
|
842
|
+
error: undefined,
|
|
843
|
+
hasError: false,
|
|
844
|
+
};
|
|
845
|
+
bashExecutions.set(executionId, execution);
|
|
376
846
|
try {
|
|
377
|
-
|
|
847
|
+
const result = await transportCall("bash", () => client.bash(command, {
|
|
378
848
|
excludeFromContext: options?.excludeFromContext,
|
|
849
|
+
executionId,
|
|
379
850
|
operations: options?.operations,
|
|
380
|
-
});
|
|
851
|
+
}), { output: "", exitCode: undefined, cancelled: true, truncated: false });
|
|
852
|
+
const callbacksSettled = await waitForBashCallbacks(execution.promises, false);
|
|
853
|
+
if (!callbacksSettled) {
|
|
854
|
+
if (result.fullOutputPath)
|
|
855
|
+
await cleanupRemoteBashOutput(result.fullOutputPath);
|
|
856
|
+
throw new Error("Bash output callback did not settle within 5000ms");
|
|
857
|
+
}
|
|
858
|
+
if (execution.hasError) {
|
|
859
|
+
if (result.fullOutputPath)
|
|
860
|
+
await cleanupRemoteBashOutput(result.fullOutputPath);
|
|
861
|
+
throw execution.error;
|
|
862
|
+
}
|
|
863
|
+
return result;
|
|
381
864
|
}
|
|
382
865
|
finally {
|
|
383
|
-
|
|
866
|
+
bashExecutions.delete(executionId);
|
|
384
867
|
}
|
|
385
868
|
};
|
|
386
869
|
if (property === "abortBash")
|
|
@@ -390,21 +873,25 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
390
873
|
else
|
|
391
874
|
void client.abortBash().catch(reportActionFailure("abortBash"));
|
|
392
875
|
};
|
|
876
|
+
if (property === "getContextUsage")
|
|
877
|
+
return () => state.contextUsage;
|
|
393
878
|
if (property === "getSessionStats")
|
|
394
|
-
return () => client.getSessionStats();
|
|
879
|
+
return () => transportCall("getSessionStats", () => client.getSessionStats(), local.getSessionStats());
|
|
395
880
|
if (property === "exportToHtml")
|
|
396
|
-
return (outputPath) => client.exportHtml(outputPath).then((result) => result
|
|
881
|
+
return (outputPath, options) => transportCall("exportToHtml", () => client.exportHtml(outputPath, options?.themeName)).then((result) => result?.path);
|
|
397
882
|
if (property === "setSessionName")
|
|
398
883
|
return (name) => client.setSessionName(name).catch(reportActionFailure("setSessionName"));
|
|
399
884
|
if (property === "navigateTree")
|
|
400
885
|
return async (targetId, options) => {
|
|
401
|
-
const result = await client.navigateTree(targetId, options)
|
|
886
|
+
const result = await transportCall("navigateTree", () => client.navigateTree(targetId, options), {
|
|
887
|
+
cancelled: true,
|
|
888
|
+
});
|
|
402
889
|
if (!result.cancelled)
|
|
403
890
|
await refresh();
|
|
404
891
|
return result;
|
|
405
892
|
};
|
|
406
893
|
if (property === "getUserMessagesForForking")
|
|
407
|
-
return () => client.getForkMessages();
|
|
894
|
+
return () => transportCall("getUserMessagesForForking", () => client.getForkMessages(), []);
|
|
408
895
|
if (property === "subscribe")
|
|
409
896
|
return (listener) => {
|
|
410
897
|
listeners.add(listener);
|
|
@@ -416,10 +903,10 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
416
903
|
};
|
|
417
904
|
if (property === "isStreaming")
|
|
418
905
|
return state.isStreaming;
|
|
906
|
+
if (property === "isIdle")
|
|
907
|
+
return !state.isStreaming;
|
|
419
908
|
if (property === "isCompacting")
|
|
420
909
|
return state.isCompacting;
|
|
421
|
-
if (property === "isFastModeActive")
|
|
422
|
-
return () => state.fastMode;
|
|
423
910
|
if (property === "pendingMessageCount")
|
|
424
911
|
return state.pendingMessageCount;
|
|
425
912
|
if (property === "getSteeringMessages")
|
|
@@ -450,11 +937,11 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
450
937
|
if (property === "isBashRunning")
|
|
451
938
|
return state.isBashRunning;
|
|
452
939
|
if (property === "reload")
|
|
453
|
-
return (_options) => client.reload();
|
|
940
|
+
return (_options) => transportCall("reload", () => client.reload(), { cancelled: true });
|
|
454
941
|
if (property === "checkReloadVeto")
|
|
455
|
-
return () => client.checkReloadVeto();
|
|
942
|
+
return () => transportCall("checkReloadVeto", () => client.checkReloadVeto(), { cancelled: true });
|
|
456
943
|
if (property === "exportToJsonl")
|
|
457
|
-
return (outputPath) => client.exportJsonl(outputPath).then((result) => result
|
|
944
|
+
return (outputPath) => transportCall("exportToJsonl", () => client.exportJsonl(outputPath)).then((result) => result?.path);
|
|
458
945
|
// The footer and other renderers read session.state.*; surface the
|
|
459
946
|
// host-authoritative fields there too, not only via the direct getters.
|
|
460
947
|
if (property === "state") {
|
|
@@ -475,12 +962,24 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
475
962
|
return state.sessionName;
|
|
476
963
|
if (property === "serviceTier")
|
|
477
964
|
return state.serviceTier;
|
|
965
|
+
if (property === "steeringMode")
|
|
966
|
+
return state.steeringMode;
|
|
967
|
+
if (property === "followUpMode")
|
|
968
|
+
return state.followUpMode;
|
|
969
|
+
if (property === "autoCompactionEnabled")
|
|
970
|
+
return state.autoCompactionEnabled;
|
|
971
|
+
if (property === "isFastModeActive")
|
|
972
|
+
return () => state.fastMode;
|
|
478
973
|
if (property === "sessionManager")
|
|
479
974
|
return remoteSessionManager;
|
|
480
975
|
if (property === "settingsManager")
|
|
481
976
|
return settingsManager;
|
|
482
977
|
if (property === "messages")
|
|
483
978
|
return target.messages;
|
|
979
|
+
if (property === "favoriteModels")
|
|
980
|
+
return state.favoriteModels;
|
|
981
|
+
if (property === "scopedModels")
|
|
982
|
+
return state.scopedModels;
|
|
484
983
|
if (property === "model")
|
|
485
984
|
return state.model ?? target.model;
|
|
486
985
|
if (property === "thinkingLevel")
|
|
@@ -490,32 +989,49 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
490
989
|
});
|
|
491
990
|
return {
|
|
492
991
|
session,
|
|
992
|
+
setHostUiHandler: (callback) => {
|
|
993
|
+
hostUiHandler = callback;
|
|
994
|
+
if (callback)
|
|
995
|
+
for (const request of pendingUiRequests.splice(0))
|
|
996
|
+
void Promise.resolve(callback(request))
|
|
997
|
+
.then((response) => response && client.sendExtensionUIResponse(response))
|
|
998
|
+
.catch(reportActionFailure("host UI response"));
|
|
999
|
+
},
|
|
493
1000
|
refresh,
|
|
1001
|
+
aroundLocalReplacement: async (body) => {
|
|
1002
|
+
localReplacementDepth++;
|
|
1003
|
+
try {
|
|
1004
|
+
return await body();
|
|
1005
|
+
}
|
|
1006
|
+
finally {
|
|
1007
|
+
localReplacementDepth--;
|
|
1008
|
+
}
|
|
1009
|
+
},
|
|
494
1010
|
abortLocalBash: () => localBashAbortController?.abort(),
|
|
495
1011
|
createReplacedSessionContext: () => {
|
|
496
1012
|
const context = local.createReplacedSessionContext();
|
|
497
1013
|
Object.defineProperty(context, "cwd", { value: state.cwd });
|
|
498
1014
|
Object.defineProperty(context, "sessionManager", { value: remoteSessionManager });
|
|
499
|
-
context.sendMessage = (message, options) => client.sendCustomMessage(message, {
|
|
1015
|
+
context.sendMessage = (message, options) => transportCall("sendMessage", () => client.sendCustomMessage(message, {
|
|
500
1016
|
triggerTurn: options?.triggerTurn,
|
|
501
1017
|
deliverAs: options?.deliverAs,
|
|
502
|
-
});
|
|
1018
|
+
}));
|
|
503
1019
|
context.sendUserMessage = (content, options) => {
|
|
504
1020
|
if (typeof content === "string")
|
|
505
|
-
return client.prompt(content, {
|
|
1021
|
+
return transportCall("sendUserMessage", () => client.prompt(content, {
|
|
506
1022
|
streamingBehavior: options?.deliverAs,
|
|
507
1023
|
expandPromptTemplates: options?.expandPromptTemplates,
|
|
508
|
-
});
|
|
1024
|
+
}));
|
|
509
1025
|
const text = content
|
|
510
1026
|
.filter((part) => part.type === "text")
|
|
511
1027
|
.map((part) => part.text)
|
|
512
1028
|
.join("\n");
|
|
513
1029
|
const images = content.filter((part) => part.type === "image");
|
|
514
|
-
return client.prompt(text, {
|
|
1030
|
+
return transportCall("sendUserMessage", () => client.prompt(text, {
|
|
515
1031
|
images,
|
|
516
1032
|
streamingBehavior: options?.deliverAs,
|
|
517
1033
|
expandPromptTemplates: options?.expandPromptTemplates,
|
|
518
|
-
});
|
|
1034
|
+
}));
|
|
519
1035
|
};
|
|
520
1036
|
return context;
|
|
521
1037
|
},
|