@code-yeongyu/senpi 2026.8.30 → 2026.8.31
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 +142 -2
- package/README.md +3 -0
- package/dist/core/agent-session.d.ts +28 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +161 -14
- package/dist/core/agent-session.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/extensions/builtin/claude-sdk-oauth/session-registry.d.ts +9 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry.js +10 -3
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry.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 +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +8 -2
- 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/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 +23 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/speculative-summary.js +132 -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 +21 -197
- 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/hooks/trust-state-json.d.ts +5 -0
- package/dist/core/extensions/builtin/hooks/trust-state-json.d.ts.map +1 -0
- package/dist/core/extensions/builtin/hooks/trust-state-json.js +76 -0
- package/dist/core/extensions/builtin/hooks/trust-state-json.js.map +1 -0
- package/dist/core/extensions/builtin/hooks/trust-storage.d.ts +7 -1
- package/dist/core/extensions/builtin/hooks/trust-storage.d.ts.map +1 -1
- package/dist/core/extensions/builtin/hooks/trust-storage.js +60 -8
- package/dist/core/extensions/builtin/hooks/trust-storage.js.map +1 -1
- package/dist/core/extensions/builtin/hooks/trust.d.ts +1 -2
- package/dist/core/extensions/builtin/hooks/trust.d.ts.map +1 -1
- package/dist/core/extensions/builtin/hooks/trust.js +1 -72
- package/dist/core/extensions/builtin/hooks/trust.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/ttsr/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/ttsr/index.js +4 -0
- package/dist/core/extensions/builtin/ttsr/index.js.map +1 -1
- package/dist/core/extensions/builtin/ttsr/manager.d.ts +2 -0
- package/dist/core/extensions/builtin/ttsr/manager.d.ts.map +1 -1
- package/dist/core/extensions/builtin/ttsr/manager.js +28 -1
- package/dist/core/extensions/builtin/ttsr/manager.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +7 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts +14 -0
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +33 -3
- package/dist/core/retry-fallback/controller.js.map +1 -1
- package/dist/core/retry-fallback/log.js +1 -1
- package/dist/core/retry-fallback/log.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-activity.d.ts +47 -0
- package/dist/core/session-activity.d.ts.map +1 -0
- package/dist/core/session-activity.js +58 -0
- package/dist/core/session-activity.js.map +1 -0
- package/dist/core/session-manager.d.ts +8 -12
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +118 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-resident-store.d.ts +8 -1
- package/dist/core/session-resident-store.d.ts.map +1 -1
- package/dist/core/session-resident-store.js +12 -5
- package/dist/core/session-resident-store.js.map +1 -1
- package/dist/core/settings-manager.d.ts +8 -85
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +9 -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/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/app-server/daemon.js +3 -3
- package/dist/modes/app-server/daemon.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/interactive-host-runtime.d.ts +84 -1
- package/dist/modes/interactive/interactive-host-runtime.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-host-runtime.js +435 -112
- package/dist/modes/interactive/interactive-host-runtime.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +24 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts +12 -5
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +173 -23
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/custom-capability.d.ts +1 -0
- package/dist/modes/rpc/custom-capability.d.ts.map +1 -1
- package/dist/modes/rpc/custom-capability.js +1 -0
- package/dist/modes/rpc/custom-capability.js.map +1 -1
- package/dist/modes/rpc/host-ensure.d.ts +17 -0
- package/dist/modes/rpc/host-ensure.d.ts.map +1 -1
- package/dist/modes/rpc/host-ensure.js +67 -16
- package/dist/modes/rpc/host-ensure.js.map +1 -1
- package/dist/modes/rpc/host-lifecycle.d.ts +39 -0
- package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
- package/dist/modes/rpc/host-lifecycle.js +80 -15
- package/dist/modes/rpc/host-lifecycle.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.d.ts +38 -0
- package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +55 -5
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/ownership-safe-lock.d.ts +14 -0
- package/dist/modes/rpc/ownership-safe-lock.d.ts.map +1 -0
- package/dist/modes/rpc/ownership-safe-lock.js +87 -0
- package/dist/modes/rpc/ownership-safe-lock.js.map +1 -0
- package/dist/modes/rpc/rpc-client.d.ts +13 -2
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +38 -11
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +21 -3
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +1 -0
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-binding.d.ts +2 -1
- package/dist/modes/rpc/session-binding.d.ts.map +1 -1
- package/dist/modes/rpc/session-binding.js +1 -0
- package/dist/modes/rpc/session-binding.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts +54 -1
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +174 -2
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +18 -0
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +88 -8
- package/dist/modes/rpc/session-event-writer.js.map +1 -1
- package/dist/modes/rpc/session-registry.d.ts +11 -1
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js +25 -0
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/dist/modes/rpc/widget-line-renderer.d.ts +17 -0
- package/dist/modes/rpc/widget-line-renderer.d.ts.map +1 -0
- package/dist/modes/rpc/widget-line-renderer.js +73 -0
- package/dist/modes/rpc/widget-line-renderer.js.map +1 -0
- package/dist/utils/fs-watch.d.ts.map +1 -1
- package/dist/utils/fs-watch.js +11 -2
- package/dist/utils/fs-watch.js.map +1 -1
- package/docs/app-server.md +2 -2
- package/docs/containerization.md +2 -2
- package/docs/development.md +7 -7
- package/docs/extensions.md +4 -4
- package/docs/grok-neo.md +1 -1
- package/docs/index.md +1 -1
- package/docs/packages.md +2 -2
- package/docs/quickstart.md +4 -4
- package/docs/release-guide.md +21 -21
- package/docs/rpc.md +78 -5
- package/docs/sdk.md +1 -1
- package/docs/skills.md +2 -2
- package/docs/terminal-tools.md +1 -1
- package/docs/termux.md +1 -1
- package/docs/tui.md +1 -1
- package/examples/AGENTS.md +1 -1
- package/examples/sdk/README.md +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +40 -0
- package/node_modules/@code-yeongyu/senpi-codemode/README.md +2 -2
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/skill-contribution.ts +71 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +3 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/SKILL.md +44 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/breaking-changes.md +75 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/build.md +114 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/dev-tooling.md +34 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/http-networking.md +113 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/install.md +93 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/node-compat-platforms.md +36 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/performance.md +33 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/runtime-apis.md +156 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/security.md +52 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/skill/bun-1-4/references/test.md +103 -0
- package/node_modules/@earendil-works/pi-agent-core/README.md +3 -0
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/README.md +6 -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 +24 -7
- 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.d.ts +7 -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 +316 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.d.ts +4 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.js +23 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts +24 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +20 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.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/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/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/README.md +6 -3
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/README.md +5 -4
- package/node_modules/@earendil-works/pi-tui/native/darwin/README.md +2 -2
- package/node_modules/@earendil-works/pi-tui/native/win32/README.md +2 -2
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -4,8 +4,9 @@ import { executeBashWithOperations } from "../../core/bash-executor.js";
|
|
|
4
4
|
import { SessionManager } from "../../core/session-manager.js";
|
|
5
5
|
import { SettingsManager } from "../../core/settings-manager.js";
|
|
6
6
|
import { ensureHost } from "../rpc/host-ensure.js";
|
|
7
|
-
import { RpcClient } from "../rpc/rpc-client.js";
|
|
7
|
+
import { isTransportGoneError, RpcClient } from "../rpc/rpc-client.js";
|
|
8
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";
|
|
9
10
|
/**
|
|
10
11
|
* Replace only the transport-facing session operations. The object returned is
|
|
11
12
|
* deliberately still an AgentSessionRuntime: InteractiveMode and extensions
|
|
@@ -17,10 +18,92 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
|
|
|
17
18
|
if (!sessionPath)
|
|
18
19
|
return localRuntime;
|
|
19
20
|
const startHost = options.ensureHost ?? ((hostOptions) => ensureHost(hostOptions));
|
|
20
|
-
|
|
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
|
+
if (remoteRuntime)
|
|
73
|
+
await remoteRuntime.reRegisterClientInfo();
|
|
74
|
+
if (disposed)
|
|
75
|
+
return;
|
|
76
|
+
await remoteSession.refresh();
|
|
77
|
+
if (disposed)
|
|
78
|
+
return;
|
|
79
|
+
fallbackWarned = false;
|
|
80
|
+
reconnectWarningShown = false;
|
|
81
|
+
remoteRuntime?.enterConnected();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
cause = error;
|
|
86
|
+
await client.stop().catch(() => { });
|
|
87
|
+
if (disposed)
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (!disposed) {
|
|
92
|
+
await remoteRuntime?.enterFallback();
|
|
93
|
+
warnFallback(cause);
|
|
94
|
+
}
|
|
95
|
+
})().finally(() => {
|
|
96
|
+
reconnecting = undefined;
|
|
97
|
+
if (disconnectQueued && !disposed && !remoteRuntime?.isFallback)
|
|
98
|
+
scheduleReconnect();
|
|
99
|
+
});
|
|
100
|
+
};
|
|
21
101
|
try {
|
|
22
102
|
await startHost({ socket: options.socket, agentDir: options.agentDir });
|
|
23
103
|
await client.start();
|
|
104
|
+
await client.setClientInfo(80, ["rendered_components"]);
|
|
105
|
+
const startupEvents = [];
|
|
106
|
+
const stopBuffering = client.onEvent((event) => startupEvents.push(event));
|
|
24
107
|
const opened = await client.openSession({
|
|
25
108
|
sessionPath,
|
|
26
109
|
cwd: localRuntime.cwd,
|
|
@@ -28,7 +111,13 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
|
|
|
28
111
|
modelId: localRuntime.session.model?.id,
|
|
29
112
|
thinkingLevel: localRuntime.session.thinkingLevel,
|
|
30
113
|
});
|
|
31
|
-
|
|
114
|
+
remoteSession = createRemoteSessionProxy(localRuntime.session, localRuntime.services.agentDir, client, opened.state, options.onWarning, (cause) => {
|
|
115
|
+
if (remoteRuntime?.isReconnecting)
|
|
116
|
+
warnReconnect(cause);
|
|
117
|
+
else
|
|
118
|
+
warnFallback(cause);
|
|
119
|
+
}, startupEvents.filter((event) => !("sessionId" in event) || event.sessionId === opened.sessionId));
|
|
120
|
+
stopBuffering();
|
|
32
121
|
if (opened.state.isBashRunning && !opened.attached) {
|
|
33
122
|
// Only a newly opened session can have an execution orphaned by a
|
|
34
123
|
// previous connection. An attach is an observer of the same live runtime;
|
|
@@ -37,15 +126,25 @@ export async function createInteractiveHostRuntime(localRuntime, options) {
|
|
|
37
126
|
}
|
|
38
127
|
if (opened.attached)
|
|
39
128
|
await remoteSession.refresh();
|
|
40
|
-
|
|
129
|
+
remoteRuntime = new RemoteInteractiveRuntime(localRuntime, remoteSession, client, {
|
|
130
|
+
onTransportGone: (cause) => {
|
|
131
|
+
if (remoteRuntime?.isReconnecting)
|
|
132
|
+
warnReconnect(cause);
|
|
133
|
+
else
|
|
134
|
+
warnFallback(cause);
|
|
135
|
+
},
|
|
136
|
+
get reconnecting() {
|
|
137
|
+
return reconnecting;
|
|
138
|
+
},
|
|
139
|
+
dispose: () => {
|
|
140
|
+
disposed = true;
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
return remoteRuntime;
|
|
41
144
|
}
|
|
42
145
|
catch (cause) {
|
|
43
146
|
await client.stop().catch(() => { });
|
|
44
|
-
|
|
45
|
-
type: "interactive_host_fallback",
|
|
46
|
-
message: `${INTERACTIVE_HOST_FALLBACK_WARNING}: ${cause instanceof Error ? cause.message : String(cause)}`,
|
|
47
|
-
cause,
|
|
48
|
-
});
|
|
147
|
+
warnFallback(cause);
|
|
49
148
|
return localRuntime;
|
|
50
149
|
}
|
|
51
150
|
}
|
|
@@ -53,21 +152,68 @@ export class RemoteInteractiveRuntime {
|
|
|
53
152
|
#local;
|
|
54
153
|
#remoteSession;
|
|
55
154
|
#client;
|
|
155
|
+
#lifecycle;
|
|
156
|
+
#onTransportGone;
|
|
56
157
|
#rebindSession;
|
|
57
158
|
#beforeSessionInvalidate;
|
|
58
|
-
|
|
159
|
+
#state = "connected";
|
|
160
|
+
#fallbackHandoff;
|
|
161
|
+
get isReconnecting() {
|
|
162
|
+
return this.#state === "reconnecting";
|
|
163
|
+
}
|
|
164
|
+
get isFallback() {
|
|
165
|
+
return this.#state === "fallback";
|
|
166
|
+
}
|
|
167
|
+
constructor(local, remoteSession, client, lifecycle) {
|
|
59
168
|
this.#local = local;
|
|
60
169
|
this.#remoteSession = remoteSession;
|
|
61
170
|
this.#client = client;
|
|
171
|
+
this.#lifecycle = lifecycle;
|
|
172
|
+
this.#onTransportGone = lifecycle?.onTransportGone ?? (() => { });
|
|
173
|
+
}
|
|
174
|
+
async #call(call) {
|
|
175
|
+
try {
|
|
176
|
+
return await call();
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (isTransportGoneError(error)) {
|
|
180
|
+
this.#onTransportGone(error);
|
|
181
|
+
return { cancelled: true };
|
|
182
|
+
}
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
enterConnected() {
|
|
187
|
+
if (this.#state !== "disposed" && this.#state !== "fallback")
|
|
188
|
+
this.#state = "connected";
|
|
189
|
+
}
|
|
190
|
+
enterReconnecting() {
|
|
191
|
+
if (this.#state !== "disposed" && this.#state !== "fallback")
|
|
192
|
+
this.#state = "reconnecting";
|
|
193
|
+
}
|
|
194
|
+
async enterFallback() {
|
|
195
|
+
if (this.#state === "disposed" || this.#state === "fallback")
|
|
196
|
+
return;
|
|
197
|
+
this.#state = "fallback";
|
|
198
|
+
this.#beforeSessionInvalidate?.();
|
|
199
|
+
const rebind = this.#rebindSession?.();
|
|
200
|
+
if (rebind) {
|
|
201
|
+
this.#fallbackHandoff = rebind.catch((error) => {
|
|
202
|
+
this.#onTransportGone(error);
|
|
203
|
+
});
|
|
204
|
+
await this.#fallbackHandoff;
|
|
205
|
+
}
|
|
62
206
|
}
|
|
63
207
|
get session() {
|
|
64
|
-
return this.#
|
|
208
|
+
return this.#state === "fallback" || this.#state === "disposed"
|
|
209
|
+
? this.#local.session
|
|
210
|
+
: this.#remoteSession.session;
|
|
65
211
|
}
|
|
66
212
|
get services() {
|
|
67
213
|
return this.#local.services;
|
|
68
214
|
}
|
|
69
215
|
get cwd() {
|
|
70
|
-
return this
|
|
216
|
+
return this.session.sessionManager.getCwd();
|
|
71
217
|
}
|
|
72
218
|
get diagnostics() {
|
|
73
219
|
return this.#local.diagnostics;
|
|
@@ -80,17 +226,41 @@ export class RemoteInteractiveRuntime {
|
|
|
80
226
|
}
|
|
81
227
|
setBeforeSessionInvalidate(callback) {
|
|
82
228
|
this.#beforeSessionInvalidate = callback;
|
|
229
|
+
this.#local.setBeforeSessionInvalidate(callback);
|
|
83
230
|
}
|
|
84
231
|
setRebindSession(callback) {
|
|
85
232
|
this.#rebindSession = callback;
|
|
233
|
+
this.#local.setRebindSession(callback);
|
|
234
|
+
if (callback && this.#state === "fallback" && !this.#fallbackHandoff) {
|
|
235
|
+
this.#fallbackHandoff = callback().catch((error) => {
|
|
236
|
+
this.#onTransportGone(error);
|
|
237
|
+
});
|
|
238
|
+
}
|
|
86
239
|
}
|
|
87
240
|
setHostUiHandler(callback) {
|
|
88
241
|
this.#remoteSession.setHostUiHandler(callback);
|
|
89
242
|
}
|
|
243
|
+
#clientInfoSent = false;
|
|
244
|
+
#lastClientWidth = 80;
|
|
245
|
+
setClientInfo(width) {
|
|
246
|
+
this.#lastClientWidth = width;
|
|
247
|
+
const capabilities = this.#clientInfoSent ? undefined : ["rendered_components"];
|
|
248
|
+
this.#clientInfoSent = true;
|
|
249
|
+
void this.#client.setClientInfo(width, capabilities).catch(() => { });
|
|
250
|
+
}
|
|
251
|
+
async reRegisterClientInfo() {
|
|
252
|
+
await this.#client.setClientInfo(this.#lastClientWidth, ["rendered_components"]);
|
|
253
|
+
}
|
|
90
254
|
async dispose() {
|
|
255
|
+
if (this.#state === "disposed")
|
|
256
|
+
return;
|
|
257
|
+
this.#state = "disposed";
|
|
258
|
+
this.#lifecycle?.dispose();
|
|
259
|
+
await this.#lifecycle?.reconnecting?.catch(() => { });
|
|
260
|
+
await this.#fallbackHandoff?.catch(() => { });
|
|
91
261
|
const errors = [];
|
|
92
262
|
for (const cleanup of [
|
|
93
|
-
() => this.#remoteSession.abortLocalBash
|
|
263
|
+
() => this.#remoteSession.abortLocalBash(),
|
|
94
264
|
() => this.#client.closeSession(),
|
|
95
265
|
() => this.#client.stop(),
|
|
96
266
|
() => this.#local.dispose(),
|
|
@@ -106,74 +276,138 @@ export class RemoteInteractiveRuntime {
|
|
|
106
276
|
throw errors[0];
|
|
107
277
|
}
|
|
108
278
|
async newSession(options) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
279
|
+
if (this.#state === "fallback")
|
|
280
|
+
return this.#local.newSession(options);
|
|
281
|
+
// Suppresses for the ENTIRE command, not just the refresh tail: a
|
|
282
|
+
// multi-session host emits session_replaced while completing it, so the echo
|
|
283
|
+
// can arrive before the refresh/rebind sequence below starts and race it -
|
|
284
|
+
// newSession transports setup entries between two refreshes.
|
|
285
|
+
return this.#remoteSession
|
|
286
|
+
.aroundLocalReplacement(async () => {
|
|
287
|
+
const result = await this.#call(() => this.#client.newSession(options?.parentSession));
|
|
288
|
+
if (!result.cancelled) {
|
|
289
|
+
this.#beforeSessionInvalidate?.();
|
|
290
|
+
this.#remoteSession.abortLocalBash();
|
|
119
291
|
await this.#remoteSession.refresh();
|
|
292
|
+
if (options?.setup) {
|
|
293
|
+
const capture = SessionManager.inMemory(this.#remoteSession.session.sessionManager.getCwd());
|
|
294
|
+
await options.setup(capture);
|
|
295
|
+
for (const entry of capture.getEntries())
|
|
296
|
+
await this.#client.appendSessionEntry(entry);
|
|
297
|
+
await this.#remoteSession.refresh();
|
|
298
|
+
}
|
|
299
|
+
await this.#rebindSession?.();
|
|
300
|
+
if (options?.withSession)
|
|
301
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
120
302
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
303
|
+
return result;
|
|
304
|
+
})
|
|
305
|
+
.catch((error) => {
|
|
306
|
+
if (isTransportGoneError(error))
|
|
307
|
+
return { cancelled: true };
|
|
308
|
+
throw error;
|
|
309
|
+
});
|
|
126
310
|
}
|
|
127
311
|
async switchSession(sessionPath, options) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
await
|
|
139
|
-
|
|
140
|
-
|
|
312
|
+
if (this.#state === "fallback")
|
|
313
|
+
return this.#local.switchSession(sessionPath, options);
|
|
314
|
+
// See newSession: the suppression must cover the command itself, since the
|
|
315
|
+
// host emits session_replaced while completing it.
|
|
316
|
+
return this.#remoteSession
|
|
317
|
+
.aroundLocalReplacement(async () => {
|
|
318
|
+
const result = await this.#call(() => this.#client.switchSession(sessionPath, options?.cwdOverride === undefined ? undefined : { cwdOverride: options.cwdOverride }));
|
|
319
|
+
if (!result.cancelled) {
|
|
320
|
+
this.#beforeSessionInvalidate?.();
|
|
321
|
+
this.#remoteSession.abortLocalBash();
|
|
322
|
+
await this.#remoteSession.refresh();
|
|
323
|
+
await this.#rebindSession?.();
|
|
324
|
+
// The shared host already resolved trust; this callback is retained for
|
|
325
|
+
// compatibility, but its result cannot override host-authoritative state.
|
|
326
|
+
options?.projectTrustContextFactory?.(this.#remoteSession.session.sessionManager.getCwd());
|
|
327
|
+
if (options?.withSession)
|
|
328
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
329
|
+
}
|
|
330
|
+
return result;
|
|
331
|
+
})
|
|
332
|
+
.catch((error) => {
|
|
333
|
+
if (isTransportGoneError(error))
|
|
334
|
+
return { cancelled: true };
|
|
335
|
+
throw error;
|
|
336
|
+
});
|
|
141
337
|
}
|
|
142
338
|
async fork(entryId, options) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
339
|
+
if (this.#state === "fallback")
|
|
340
|
+
return this.#local.fork(entryId, options);
|
|
341
|
+
// See newSession: the suppression must cover the command itself.
|
|
342
|
+
return this.#remoteSession
|
|
343
|
+
.aroundLocalReplacement(async () => {
|
|
344
|
+
const result = await this.#call(() => this.#client.fork(entryId, options));
|
|
345
|
+
if (!result.cancelled) {
|
|
346
|
+
this.#beforeSessionInvalidate?.();
|
|
347
|
+
this.#remoteSession.abortLocalBash();
|
|
348
|
+
await this.#refreshAndRebind();
|
|
349
|
+
if (options?.withSession)
|
|
350
|
+
await options.withSession(this.#remoteSession.createReplacedSessionContext());
|
|
351
|
+
}
|
|
352
|
+
return { cancelled: result.cancelled, selectedText: result.text };
|
|
353
|
+
})
|
|
354
|
+
.catch((error) => {
|
|
355
|
+
if (isTransportGoneError(error))
|
|
356
|
+
return { cancelled: true };
|
|
357
|
+
throw error;
|
|
358
|
+
});
|
|
152
359
|
}
|
|
153
360
|
async importFromJsonl(inputPath, cwdOverride) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
361
|
+
if (this.#state === "fallback")
|
|
362
|
+
return this.#local.importFromJsonl(inputPath, cwdOverride);
|
|
363
|
+
// See newSession: the suppression must cover the command itself.
|
|
364
|
+
return this.#remoteSession
|
|
365
|
+
.aroundLocalReplacement(async () => {
|
|
366
|
+
const result = await this.#call(() => this.#client.importJsonl(inputPath, cwdOverride));
|
|
367
|
+
if (!result.cancelled) {
|
|
368
|
+
this.#beforeSessionInvalidate?.();
|
|
369
|
+
this.#remoteSession.abortLocalBash();
|
|
370
|
+
await this.#refreshAndRebind();
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
})
|
|
374
|
+
.catch((error) => {
|
|
375
|
+
if (isTransportGoneError(error))
|
|
376
|
+
return { cancelled: true };
|
|
377
|
+
throw error;
|
|
378
|
+
});
|
|
161
379
|
}
|
|
162
380
|
async #refreshAndRebind() {
|
|
163
381
|
await this.#remoteSession.refresh();
|
|
164
382
|
await this.#rebindSession?.();
|
|
165
383
|
}
|
|
166
384
|
}
|
|
167
|
-
|
|
385
|
+
/** Exported for direct coverage of the wire-event handling below. */
|
|
386
|
+
export function createRemoteSessionProxy(local, agentDir, client, initialState, onWarning, onTransportGone, startupEvents = []) {
|
|
168
387
|
// Fire-and-forget setters keep the sync AgentSession signature, but their RPC
|
|
169
388
|
// failures must not vanish: the matching *_changed wire event confirms success,
|
|
170
389
|
// and a rejection here is the only signal of failure.
|
|
171
390
|
const reportActionFailure = (action) => (error) => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
391
|
+
const transportGone = isTransportGoneError(error);
|
|
392
|
+
if (transportGone)
|
|
393
|
+
onTransportGone?.(error);
|
|
394
|
+
else
|
|
395
|
+
onWarning?.({
|
|
396
|
+
type: "interactive_host_action_failed",
|
|
397
|
+
message: `Warning: shared interactive host ${action} failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
398
|
+
cause: error,
|
|
399
|
+
});
|
|
400
|
+
};
|
|
401
|
+
const transportCall = async (action, call, cancelledValue) => {
|
|
402
|
+
try {
|
|
403
|
+
return await call();
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
if (!isTransportGoneError(error))
|
|
407
|
+
throw error;
|
|
408
|
+
reportActionFailure(action)(error);
|
|
409
|
+
return cancelledValue;
|
|
410
|
+
}
|
|
177
411
|
};
|
|
178
412
|
let state = { ...initialState };
|
|
179
413
|
const waitForBashCallbacks = async (promises, allowAbandonment) => {
|
|
@@ -245,7 +479,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
245
479
|
const remoteSessionManager = new Proxy({}, {
|
|
246
480
|
get(_target, property, _receiver) {
|
|
247
481
|
if (property === "appendLabelChange") {
|
|
248
|
-
return (entryId, label) => void client.setLabel(entryId, label);
|
|
482
|
+
return (entryId, label) => void client.setLabel(entryId, label).catch(reportActionFailure("appendLabelChange"));
|
|
249
483
|
}
|
|
250
484
|
if (property === "getCwd")
|
|
251
485
|
return () => state.cwd;
|
|
@@ -259,16 +493,38 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
259
493
|
});
|
|
260
494
|
let streamingAssistant;
|
|
261
495
|
let mirroredCurrentAssistantUsage = false;
|
|
496
|
+
/** Non-zero while this runtime is driving its own replacement sequence. */
|
|
497
|
+
let localReplacementDepth = 0;
|
|
262
498
|
const listeners = new Set();
|
|
263
|
-
|
|
499
|
+
const handleWireEvent = (wireEvent) => {
|
|
264
500
|
if (wireEvent.type === "extension_ui_request") {
|
|
265
501
|
const request = wireEvent;
|
|
266
502
|
if (hostUiHandler)
|
|
267
|
-
void Promise.resolve(hostUiHandler(request))
|
|
503
|
+
void Promise.resolve(hostUiHandler(request))
|
|
504
|
+
.then((response) => response && client.sendExtensionUIResponse(response))
|
|
505
|
+
.catch(reportActionFailure("host UI response"));
|
|
268
506
|
else
|
|
269
507
|
pendingUiRequests.push(request);
|
|
270
508
|
return;
|
|
271
509
|
}
|
|
510
|
+
if (wireEvent.type === "session_replaced") {
|
|
511
|
+
// The host swapped the live session behind this connection - another
|
|
512
|
+
// attached client issued the replacement, or an extension drove one that no
|
|
513
|
+
// client issued. Nothing else re-reads the binding, so without this the
|
|
514
|
+
// proxy keeps serving the previous session's manager, settings and message
|
|
515
|
+
// mirror while the host has already moved on. The command response carries
|
|
516
|
+
// only `{ cancelled }`, so this event is the only signal available here.
|
|
517
|
+
//
|
|
518
|
+
// A multi-session host broadcasts the event to every connection including
|
|
519
|
+
// the one that issued the replacement, and this runtime's own replacement
|
|
520
|
+
// methods already run an ordered refresh/rebind - newSession additionally
|
|
521
|
+
// transports setup entries between two refreshes. Refreshing again from
|
|
522
|
+
// here would race that sequence, so self-driven replacements are skipped.
|
|
523
|
+
if (localReplacementDepth === 0) {
|
|
524
|
+
void refresh().catch(reportActionFailure("session refresh after replacement"));
|
|
525
|
+
}
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
272
528
|
if (wireEvent.type === "agent_settled")
|
|
273
529
|
state = { ...state, isStreaming: false, retryAttempt: 0 };
|
|
274
530
|
if (wireEvent.type === "bash_start") {
|
|
@@ -402,9 +658,18 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
402
658
|
const event = hydrateMessageUpdate(wireEvent, streamingAssistant);
|
|
403
659
|
for (const listener of listeners)
|
|
404
660
|
listener(event);
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
|
|
661
|
+
};
|
|
662
|
+
client.onEvent(handleWireEvent);
|
|
663
|
+
for (const event of startupEvents)
|
|
664
|
+
handleWireEvent(event);
|
|
665
|
+
const performRefresh = async () => {
|
|
666
|
+
// Captured before the first await: entries that arrive via `entry_appended`
|
|
667
|
+
// while this refresh is in flight are newer than the snapshot it reconciles
|
|
668
|
+
// against, and must survive the rebuild below.
|
|
669
|
+
const idsAtRefreshStart = new Set(sessionManager.getEntries().map((entry) => entry.id));
|
|
670
|
+
const nextState = await transportCall("refresh", () => client.getState());
|
|
671
|
+
if (!nextState)
|
|
672
|
+
return;
|
|
408
673
|
state = { ...stateFromRpc(nextState) };
|
|
409
674
|
nextQueuedInputOrder = Math.max(0, ...nextState.ordered.map((item) => item.enqueueOrder));
|
|
410
675
|
let messages;
|
|
@@ -423,38 +688,83 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
423
688
|
// deferred creating the file for a setup-only session.
|
|
424
689
|
sessionManager = SessionManager.open(nextState.sessionFile, undefined, nextState.cwd);
|
|
425
690
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
691
|
+
// The host ships its complete entry list while the session file is still
|
|
692
|
+
// deferred (no assistant message yet, so nothing has been written), which
|
|
693
|
+
// makes that list the only authoritative view of the session. Reconcile the
|
|
694
|
+
// mirror to it exactly: `entry_appended` notifications that cross a
|
|
695
|
+
// concurrent refresh land on whichever manager is current, so the mirror
|
|
696
|
+
// can hold an arbitrary partial set rather than a prefix, and a snapshot
|
|
697
|
+
// taken mid-bind can simply be short. Backfilling only an empty mirror
|
|
698
|
+
// wedged it at whatever partial set it happened to hold.
|
|
699
|
+
const authoritative = nextState.entries;
|
|
700
|
+
if (authoritative?.length) {
|
|
701
|
+
const mirror = sessionManager.getEntries();
|
|
702
|
+
const matches = mirror.length === authoritative.length &&
|
|
703
|
+
mirror.every((entry, index) => entry.id === authoritative[index]?.id);
|
|
704
|
+
if (!matches) {
|
|
705
|
+
const rebuilt = SessionManager.open(nextState.sessionFile, undefined, nextState.cwd);
|
|
706
|
+
for (const entry of authoritative)
|
|
707
|
+
rebuilt.appendEntry(entry);
|
|
708
|
+
// A notification that crossed this refresh refers to an entry the
|
|
709
|
+
// snapshot predates, so it is newer than the snapshot; dropping it
|
|
710
|
+
// would strand the entry until an unrelated refresh happened to
|
|
711
|
+
// run. Keep it, in arrival order, after the authoritative list.
|
|
712
|
+
const authoritativeIds = new Set(authoritative.map((entry) => entry.id));
|
|
713
|
+
for (const entry of mirror) {
|
|
714
|
+
if (!idsAtRefreshStart.has(entry.id) && !authoritativeIds.has(entry.id)) {
|
|
715
|
+
rebuilt.appendEntry(entry);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
sessionManager = rebuilt;
|
|
719
|
+
}
|
|
429
720
|
}
|
|
430
721
|
messages = sessionManager.buildSessionContext().messages;
|
|
431
722
|
}
|
|
432
723
|
else {
|
|
433
|
-
messages = await client.getMessages();
|
|
724
|
+
messages = await transportCall("refresh", () => client.getMessages(), []);
|
|
434
725
|
}
|
|
435
726
|
local.agent.state.messages.splice(0, local.agent.state.messages.length, ...structuredClone(messages));
|
|
436
727
|
streamingAssistant = undefined;
|
|
437
728
|
};
|
|
729
|
+
// Refreshes must not interleave: each one reassigns sessionManager,
|
|
730
|
+
// settingsManager and the mirrored message list, so two in flight can commit a
|
|
731
|
+
// mixed snapshot of two different sessions. A replacement-driven refresh races
|
|
732
|
+
// exactly that way against a caller-driven one, so run them in order.
|
|
733
|
+
let refreshChain = Promise.resolve();
|
|
734
|
+
const refresh = () => {
|
|
735
|
+
const next = refreshChain.then(performRefresh, performRefresh);
|
|
736
|
+
refreshChain = next.then(() => { }, () => { });
|
|
737
|
+
return next;
|
|
738
|
+
};
|
|
438
739
|
const session = new Proxy(local, {
|
|
439
740
|
get(target, property, receiver) {
|
|
440
741
|
if (property === "prompt")
|
|
441
|
-
return (message, options) =>
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
742
|
+
return async (message, options) => {
|
|
743
|
+
try {
|
|
744
|
+
await client.prompt(message, {
|
|
745
|
+
...(options?.images ? { images: options.images } : {}),
|
|
746
|
+
...(options?.streamingBehavior ? { streamingBehavior: options.streamingBehavior } : {}),
|
|
747
|
+
...(options?.thinkingLevel ? { thinkingLevel: options.thinkingLevel } : {}),
|
|
748
|
+
...(options?.sessionTitlePrompt !== undefined
|
|
749
|
+
? { sessionTitlePrompt: options.sessionTitlePrompt }
|
|
750
|
+
: {}),
|
|
751
|
+
...(options?.expandPromptTemplates !== undefined
|
|
752
|
+
? { expandPromptTemplates: options.expandPromptTemplates }
|
|
753
|
+
: {}),
|
|
754
|
+
...(options?.promptDisposition ? { promptDisposition: options.promptDisposition } : {}),
|
|
755
|
+
...(options?.preflightResult ? { preflightResult: options.preflightResult } : {}),
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
catch (error) {
|
|
759
|
+
if (!isTransportGoneError(error))
|
|
760
|
+
throw error;
|
|
761
|
+
reportActionFailure("prompt")(error);
|
|
762
|
+
}
|
|
763
|
+
};
|
|
454
764
|
if (property === "abort")
|
|
455
765
|
return () => client.abort();
|
|
456
766
|
if (property === "abortCompaction")
|
|
457
|
-
return () => void client.abortCompaction();
|
|
767
|
+
return () => void client.abortCompaction().catch(reportActionFailure("abortCompaction"));
|
|
458
768
|
if (property === "steer")
|
|
459
769
|
return (message, images, recovery) => {
|
|
460
770
|
const enqueueOrder = recovery?.enqueueOrder ?? Math.max(0, ...state.ordered.map((item) => item.enqueueOrder)) + 1;
|
|
@@ -464,7 +774,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
464
774
|
ordered: [...state.ordered, { text: message, mode: "steer", enqueueOrder }],
|
|
465
775
|
pendingMessageCount: state.pendingMessageCount + 1,
|
|
466
776
|
};
|
|
467
|
-
return client.steer(message, images, { ...recovery, enqueueOrder });
|
|
777
|
+
return transportCall("steer", () => client.steer(message, images, { ...recovery, enqueueOrder }));
|
|
468
778
|
};
|
|
469
779
|
if (property === "followUp")
|
|
470
780
|
return (message, images, recovery) => {
|
|
@@ -475,7 +785,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
475
785
|
ordered: [...state.ordered, { text: message, mode: "followUp", enqueueOrder }],
|
|
476
786
|
pendingMessageCount: state.pendingMessageCount + 1,
|
|
477
787
|
};
|
|
478
|
-
return client.followUp(message, images, { ...recovery, enqueueOrder });
|
|
788
|
+
return transportCall("followUp", () => client.followUp(message, images, { ...recovery, enqueueOrder }));
|
|
479
789
|
};
|
|
480
790
|
if (property === "waitForIdle")
|
|
481
791
|
return () => client.waitForIdle();
|
|
@@ -483,8 +793,8 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
483
793
|
return () => target.getLastAssistantText();
|
|
484
794
|
if (property === "setModel" || property === "setSessionModel")
|
|
485
795
|
return async (model) => {
|
|
486
|
-
const next = await client.setModel(model.provider, model.id);
|
|
487
|
-
return { systemPromptName: next.systemPromptName, model: next };
|
|
796
|
+
const next = await transportCall("setModel", () => client.setModel(model.provider, model.id), undefined);
|
|
797
|
+
return next ? { systemPromptName: next.systemPromptName, model: next } : undefined;
|
|
488
798
|
};
|
|
489
799
|
if (property === "setSessionThinkingLevel")
|
|
490
800
|
return (level) => void client
|
|
@@ -509,19 +819,19 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
509
819
|
void client.setScopedModels(models).catch(reportActionFailure("setScopedModels"));
|
|
510
820
|
};
|
|
511
821
|
if (property === "cycleModel")
|
|
512
|
-
return (direction) => client.cycleModel(direction);
|
|
822
|
+
return (direction) => transportCall("cycleModel", () => client.cycleModel(direction), undefined);
|
|
513
823
|
if (property === "setThinkingLevel")
|
|
514
824
|
return (level) => void client.setThinkingLevel(level).catch(reportActionFailure("setThinkingLevel"));
|
|
515
825
|
if (property === "cycleThinkingLevel")
|
|
516
|
-
return () => client.cycleThinkingLevel().then((result) => result?.level);
|
|
826
|
+
return () => transportCall("cycleThinkingLevel", () => client.cycleThinkingLevel(), undefined).then((result) => result?.level);
|
|
517
827
|
if (property === "getAvailableThinkingLevels")
|
|
518
|
-
return () => client.getAvailableThinkingLevels();
|
|
828
|
+
return () => transportCall("getAvailableThinkingLevels", () => client.getAvailableThinkingLevels(), []);
|
|
519
829
|
if (property === "setSteeringMode")
|
|
520
830
|
return (mode) => void client.setSteeringMode(mode).catch(reportActionFailure("setSteeringMode"));
|
|
521
831
|
if (property === "setFollowUpMode")
|
|
522
832
|
return (mode) => void client.setFollowUpMode(mode).catch(reportActionFailure("setFollowUpMode"));
|
|
523
833
|
if (property === "compact")
|
|
524
|
-
return (instructions) => client.compact(instructions);
|
|
834
|
+
return (instructions) => transportCall("compact", () => client.compact(instructions));
|
|
525
835
|
if (property === "setAutoCompactionEnabled")
|
|
526
836
|
return (enabled) => void client.setAutoCompaction(enabled).catch(reportActionFailure("setAutoCompaction"));
|
|
527
837
|
if (property === "executeBash")
|
|
@@ -537,7 +847,7 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
537
847
|
try {
|
|
538
848
|
const result = await executeBashWithOperations(resolvedCommand, state.cwd, options.operations, { onChunk, signal: abortController.signal });
|
|
539
849
|
if (state.sessionId === sessionAtStart) {
|
|
540
|
-
await client.recordBashResult(command, result, options.excludeFromContext);
|
|
850
|
+
await transportCall("recordBashResult", () => client.recordBashResult(command, result, options.excludeFromContext));
|
|
541
851
|
}
|
|
542
852
|
return result;
|
|
543
853
|
}
|
|
@@ -556,11 +866,11 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
556
866
|
};
|
|
557
867
|
bashExecutions.set(executionId, execution);
|
|
558
868
|
try {
|
|
559
|
-
const result = await client.bash(command, {
|
|
869
|
+
const result = await transportCall("bash", () => client.bash(command, {
|
|
560
870
|
excludeFromContext: options?.excludeFromContext,
|
|
561
871
|
executionId,
|
|
562
872
|
operations: options?.operations,
|
|
563
|
-
});
|
|
873
|
+
}), { output: "", exitCode: undefined, cancelled: true, truncated: false });
|
|
564
874
|
const callbacksSettled = await waitForBashCallbacks(execution.promises, false);
|
|
565
875
|
if (!callbacksSettled) {
|
|
566
876
|
if (result.fullOutputPath)
|
|
@@ -588,20 +898,22 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
588
898
|
if (property === "getContextUsage")
|
|
589
899
|
return () => state.contextUsage;
|
|
590
900
|
if (property === "getSessionStats")
|
|
591
|
-
return () => client.getSessionStats();
|
|
901
|
+
return () => transportCall("getSessionStats", () => client.getSessionStats(), local.getSessionStats());
|
|
592
902
|
if (property === "exportToHtml")
|
|
593
|
-
return (outputPath, options) => client.exportHtml(outputPath, options?.themeName).then((result) => result
|
|
903
|
+
return (outputPath, options) => transportCall("exportToHtml", () => client.exportHtml(outputPath, options?.themeName)).then((result) => result?.path);
|
|
594
904
|
if (property === "setSessionName")
|
|
595
905
|
return (name) => client.setSessionName(name).catch(reportActionFailure("setSessionName"));
|
|
596
906
|
if (property === "navigateTree")
|
|
597
907
|
return async (targetId, options) => {
|
|
598
|
-
const result = await client.navigateTree(targetId, options)
|
|
908
|
+
const result = await transportCall("navigateTree", () => client.navigateTree(targetId, options), {
|
|
909
|
+
cancelled: true,
|
|
910
|
+
});
|
|
599
911
|
if (!result.cancelled)
|
|
600
912
|
await refresh();
|
|
601
913
|
return result;
|
|
602
914
|
};
|
|
603
915
|
if (property === "getUserMessagesForForking")
|
|
604
|
-
return () => client.getForkMessages();
|
|
916
|
+
return () => transportCall("getUserMessagesForForking", () => client.getForkMessages(), []);
|
|
605
917
|
if (property === "subscribe")
|
|
606
918
|
return (listener) => {
|
|
607
919
|
listeners.add(listener);
|
|
@@ -647,11 +959,11 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
647
959
|
if (property === "isBashRunning")
|
|
648
960
|
return state.isBashRunning;
|
|
649
961
|
if (property === "reload")
|
|
650
|
-
return (_options) => client.reload();
|
|
962
|
+
return (_options) => transportCall("reload", () => client.reload(), { cancelled: true });
|
|
651
963
|
if (property === "checkReloadVeto")
|
|
652
|
-
return () => client.checkReloadVeto();
|
|
964
|
+
return () => transportCall("checkReloadVeto", () => client.checkReloadVeto(), { cancelled: true });
|
|
653
965
|
if (property === "exportToJsonl")
|
|
654
|
-
return (outputPath) => client.exportJsonl(outputPath).then((result) => result
|
|
966
|
+
return (outputPath) => transportCall("exportToJsonl", () => client.exportJsonl(outputPath)).then((result) => result?.path);
|
|
655
967
|
// The footer and other renderers read session.state.*; surface the
|
|
656
968
|
// host-authoritative fields there too, not only via the direct getters.
|
|
657
969
|
if (property === "state") {
|
|
@@ -703,34 +1015,45 @@ function createRemoteSessionProxy(local, agentDir, client, initialState, onWarni
|
|
|
703
1015
|
hostUiHandler = callback;
|
|
704
1016
|
if (callback)
|
|
705
1017
|
for (const request of pendingUiRequests.splice(0))
|
|
706
|
-
void Promise.resolve(callback(request))
|
|
1018
|
+
void Promise.resolve(callback(request))
|
|
1019
|
+
.then((response) => response && client.sendExtensionUIResponse(response))
|
|
1020
|
+
.catch(reportActionFailure("host UI response"));
|
|
707
1021
|
},
|
|
708
1022
|
refresh,
|
|
1023
|
+
aroundLocalReplacement: async (body) => {
|
|
1024
|
+
localReplacementDepth++;
|
|
1025
|
+
try {
|
|
1026
|
+
return await body();
|
|
1027
|
+
}
|
|
1028
|
+
finally {
|
|
1029
|
+
localReplacementDepth--;
|
|
1030
|
+
}
|
|
1031
|
+
},
|
|
709
1032
|
abortLocalBash: () => localBashAbortController?.abort(),
|
|
710
1033
|
createReplacedSessionContext: () => {
|
|
711
1034
|
const context = local.createReplacedSessionContext();
|
|
712
1035
|
Object.defineProperty(context, "cwd", { value: state.cwd });
|
|
713
1036
|
Object.defineProperty(context, "sessionManager", { value: remoteSessionManager });
|
|
714
|
-
context.sendMessage = (message, options) => client.sendCustomMessage(message, {
|
|
1037
|
+
context.sendMessage = (message, options) => transportCall("sendMessage", () => client.sendCustomMessage(message, {
|
|
715
1038
|
triggerTurn: options?.triggerTurn,
|
|
716
1039
|
deliverAs: options?.deliverAs,
|
|
717
|
-
});
|
|
1040
|
+
}));
|
|
718
1041
|
context.sendUserMessage = (content, options) => {
|
|
719
1042
|
if (typeof content === "string")
|
|
720
|
-
return client.prompt(content, {
|
|
1043
|
+
return transportCall("sendUserMessage", () => client.prompt(content, {
|
|
721
1044
|
streamingBehavior: options?.deliverAs,
|
|
722
1045
|
expandPromptTemplates: options?.expandPromptTemplates,
|
|
723
|
-
});
|
|
1046
|
+
}));
|
|
724
1047
|
const text = content
|
|
725
1048
|
.filter((part) => part.type === "text")
|
|
726
1049
|
.map((part) => part.text)
|
|
727
1050
|
.join("\n");
|
|
728
1051
|
const images = content.filter((part) => part.type === "image");
|
|
729
|
-
return client.prompt(text, {
|
|
1052
|
+
return transportCall("sendUserMessage", () => client.prompt(text, {
|
|
730
1053
|
images,
|
|
731
1054
|
streamingBehavior: options?.deliverAs,
|
|
732
1055
|
expandPromptTemplates: options?.expandPromptTemplates,
|
|
733
|
-
});
|
|
1056
|
+
}));
|
|
734
1057
|
};
|
|
735
1058
|
return context;
|
|
736
1059
|
},
|