aicodeman 1.44.0 → 1.46.0
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/README.md +32 -8
- package/dist/agent-codex-identity.d.ts +26 -0
- package/dist/agent-codex-identity.d.ts.map +1 -0
- package/dist/agent-codex-identity.js +253 -0
- package/dist/agent-codex-identity.js.map +1 -0
- package/dist/agent-inspection.d.ts +32 -0
- package/dist/agent-inspection.d.ts.map +1 -0
- package/dist/agent-inspection.js +196 -0
- package/dist/agent-inspection.js.map +1 -0
- package/dist/agent-staging.d.ts +130 -0
- package/dist/agent-staging.d.ts.map +1 -0
- package/dist/agent-staging.js +312 -0
- package/dist/agent-staging.js.map +1 -0
- package/dist/case-rename.d.ts +65 -0
- package/dist/case-rename.d.ts.map +1 -0
- package/dist/case-rename.js +198 -0
- package/dist/case-rename.js.map +1 -0
- package/dist/cli-agent.d.ts +91 -3
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +501 -44
- package/dist/cli-agent.js.map +1 -1
- package/dist/cli-history.d.ts +20 -0
- package/dist/cli-history.d.ts.map +1 -0
- package/dist/cli-history.js +61 -0
- package/dist/cli-history.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -1
- package/dist/cli.js.map +1 -1
- package/dist/codex-rollout-format.d.ts +66 -0
- package/dist/codex-rollout-format.d.ts.map +1 -0
- package/dist/codex-rollout-format.js +111 -0
- package/dist/codex-rollout-format.js.map +1 -0
- package/dist/codex-transcript.d.ts +3 -0
- package/dist/codex-transcript.d.ts.map +1 -1
- package/dist/codex-transcript.js +13 -41
- package/dist/codex-transcript.js.map +1 -1
- package/dist/config/auth-config.d.ts +16 -1
- package/dist/config/auth-config.d.ts.map +1 -1
- package/dist/config/auth-config.js +16 -1
- package/dist/config/auth-config.js.map +1 -1
- package/dist/config/cli-registry/schema.d.ts +12 -7
- package/dist/config/cli-registry/schema.d.ts.map +1 -1
- package/dist/config/cli-registry/schema.js +26 -0
- package/dist/config/cli-registry/schema.js.map +1 -1
- package/dist/config/cli-registry/stock.d.ts.map +1 -1
- package/dist/config/cli-registry/stock.js +40 -1
- package/dist/config/cli-registry/stock.js.map +1 -1
- package/dist/config/cli-registry/types.d.ts +57 -0
- package/dist/config/cli-registry/types.d.ts.map +1 -1
- package/dist/config/server-timing.d.ts +6 -0
- package/dist/config/server-timing.d.ts.map +1 -1
- package/dist/config/server-timing.js +9 -0
- package/dist/config/server-timing.js.map +1 -1
- package/dist/daemon-control.d.ts +1 -0
- package/dist/daemon-control.d.ts.map +1 -1
- package/dist/daemon-control.js +2 -0
- package/dist/daemon-control.js.map +1 -1
- package/dist/deepseek-transcript.d.ts +9 -7
- package/dist/deepseek-transcript.d.ts.map +1 -1
- package/dist/deepseek-transcript.js +9 -7
- package/dist/deepseek-transcript.js.map +1 -1
- package/dist/docker-hosts.d.ts +8 -0
- package/dist/docker-hosts.d.ts.map +1 -1
- package/dist/docker-hosts.js +29 -0
- package/dist/docker-hosts.js.map +1 -1
- package/dist/git-fetch-scheduler.d.ts +124 -0
- package/dist/git-fetch-scheduler.d.ts.map +1 -0
- package/dist/git-fetch-scheduler.js +249 -0
- package/dist/git-fetch-scheduler.js.map +1 -0
- package/dist/git-fetch.d.ts +241 -0
- package/dist/git-fetch.d.ts.map +1 -0
- package/dist/git-fetch.js +392 -0
- package/dist/git-fetch.js.map +1 -0
- package/dist/git-workspace-status.d.ts +36 -1
- package/dist/git-workspace-status.d.ts.map +1 -1
- package/dist/git-workspace-status.js +42 -5
- package/dist/git-workspace-status.js.map +1 -1
- package/dist/hooks-config.d.ts +14 -2
- package/dist/hooks-config.d.ts.map +1 -1
- package/dist/hooks-config.js +39 -3
- package/dist/hooks-config.js.map +1 -1
- package/dist/host-sensors.d.ts +115 -0
- package/dist/host-sensors.d.ts.map +1 -0
- package/dist/host-sensors.js +457 -0
- package/dist/host-sensors.js.map +1 -0
- package/dist/local-history.d.ts +71 -0
- package/dist/local-history.d.ts.map +1 -0
- package/dist/local-history.js +750 -0
- package/dist/local-history.js.map +1 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +2 -2
- package/dist/mcp-server.js.map +1 -1
- package/dist/mux-interface.d.ts +5 -2
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/prompt-cache-keepwarm.d.ts.map +1 -1
- package/dist/prompt-cache-keepwarm.js +5 -3
- package/dist/prompt-cache-keepwarm.js.map +1 -1
- package/dist/remote-hosts.d.ts +7 -0
- package/dist/remote-hosts.d.ts.map +1 -1
- package/dist/remote-hosts.js +17 -3
- package/dist/remote-hosts.js.map +1 -1
- package/dist/remote-pane-history.d.ts.map +1 -1
- package/dist/remote-pane-history.js +2 -2
- package/dist/remote-pane-history.js.map +1 -1
- package/dist/session.d.ts +113 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +354 -13
- package/dist/session.js.map +1 -1
- package/dist/terminal-input-classifier.d.ts +8 -0
- package/dist/terminal-input-classifier.d.ts.map +1 -0
- package/dist/terminal-input-classifier.js +60 -0
- package/dist/terminal-input-classifier.js.map +1 -0
- package/dist/tmux-manager.d.ts +30 -4
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +101 -21
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-prefix-table.d.ts +37 -5
- package/dist/tmux-prefix-table.d.ts.map +1 -1
- package/dist/tmux-prefix-table.js +40 -5
- package/dist/tmux-prefix-table.js.map +1 -1
- package/dist/tui/tui-app.d.ts +8 -1
- package/dist/tui/tui-app.d.ts.map +1 -1
- package/dist/tui/tui-app.js +49 -12
- package/dist/tui/tui-app.js.map +1 -1
- package/dist/tui/tui-client.d.ts +90 -3
- package/dist/tui/tui-client.d.ts.map +1 -1
- package/dist/tui/tui-client.js +195 -26
- package/dist/tui/tui-client.js.map +1 -1
- package/dist/types/session.d.ts +15 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/web/agent-inbox-persistence.d.ts +52 -0
- package/dist/web/agent-inbox-persistence.d.ts.map +1 -0
- package/dist/web/agent-inbox-persistence.js +139 -0
- package/dist/web/agent-inbox-persistence.js.map +1 -0
- package/dist/web/agent-inbox.d.ts +140 -0
- package/dist/web/agent-inbox.d.ts.map +1 -0
- package/dist/web/agent-inbox.js +258 -0
- package/dist/web/agent-inbox.js.map +1 -0
- package/dist/web/middleware/auth.d.ts +2 -0
- package/dist/web/middleware/auth.d.ts.map +1 -1
- package/dist/web/middleware/auth.js +145 -52
- package/dist/web/middleware/auth.js.map +1 -1
- package/dist/web/network-auth-policy.d.ts +13 -3
- package/dist/web/network-auth-policy.d.ts.map +1 -1
- package/dist/web/network-auth-policy.js +15 -3
- package/dist/web/network-auth-policy.js.map +1 -1
- package/dist/web/ports/infra-port.d.ts +2 -0
- package/dist/web/ports/infra-port.d.ts.map +1 -1
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.3093527f.js +43 -0
- package/dist/web/public/app.3093527f.js.br +0 -0
- package/dist/web/public/app.3093527f.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.4866ecc3.js → constants.affcdde6.js} +622 -12
- package/dist/web/public/constants.affcdde6.js.br +0 -0
- package/dist/web/public/constants.affcdde6.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/git-status-ui.js +88 -5
- package/dist/web/public/git-status-ui.js.br +0 -0
- package/dist/web/public/git-status-ui.js.gz +0 -0
- package/dist/web/public/header-divider.eb2d2463.js +1 -0
- package/dist/web/public/header-divider.eb2d2463.js.br +0 -0
- package/dist/web/public/header-divider.eb2d2463.js.gz +0 -0
- package/dist/web/public/home-sessions.js.gz +0 -0
- package/dist/web/public/host-wake-ui.js.gz +0 -0
- package/dist/web/public/i18n.70265257.js +2 -0
- package/dist/web/public/i18n.70265257.js.br +0 -0
- package/dist/web/public/i18n.70265257.js.gz +0 -0
- package/dist/web/public/image-input.941386e7.js.gz +0 -0
- package/dist/web/public/index.html +170 -16
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.4e8b20ac.js.gz +0 -0
- package/dist/web/public/{mobile-handlers.44adaa07.js → mobile-handlers.f7c3f692.js} +26 -2
- package/dist/web/public/mobile-handlers.f7c3f692.js.br +0 -0
- package/dist/web/public/mobile-handlers.f7c3f692.js.gz +0 -0
- package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.1bbd2d67.css +1 -0
- package/dist/web/public/mobile.1bbd2d67.css.br +0 -0
- package/dist/web/public/mobile.1bbd2d67.css.gz +0 -0
- package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.cbb24ba1.js → panels-ui.4a56ee63.js} +76 -70
- package/dist/web/public/panels-ui.4a56ee63.js.br +0 -0
- package/dist/web/public/panels-ui.4a56ee63.js.gz +0 -0
- package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
- package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
- package/dist/web/public/readmymind-ui.js.gz +0 -0
- package/dist/web/public/reboot-restore-ui.js.gz +0 -0
- package/dist/web/public/respawn-ui.d7adcf6a.js.gz +0 -0
- package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/session-ui.186f9419.js +106 -0
- package/dist/web/public/session-ui.186f9419.js.br +0 -0
- package/dist/web/public/session-ui.186f9419.js.gz +0 -0
- package/dist/web/public/settings-ui.a191154e.js +106 -0
- package/dist/web/public/settings-ui.a191154e.js.br +0 -0
- package/dist/web/public/settings-ui.a191154e.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview.75c751de.js.gz +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
- package/dist/web/public/styles.7238eae8.css +1 -0
- package/dist/web/public/styles.7238eae8.css.br +0 -0
- package/dist/web/public/styles.7238eae8.css.gz +0 -0
- package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
- package/dist/web/public/sw.js +2 -2
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/tab-layout-browser.2d6189f7.js.gz +0 -0
- package/dist/web/public/{tab-rail-resize.7157e0bd.js → tab-rail-resize.b5cdceaf.js} +1 -1
- package/dist/web/public/tab-rail-resize.b5cdceaf.js.br +0 -0
- package/dist/web/public/tab-rail-resize.b5cdceaf.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
- package/dist/web/public/terminal-split.js.gz +0 -0
- package/dist/web/public/terminal-tile.js +10 -1
- package/dist/web/public/terminal-tile.js.br +0 -0
- package/dist/web/public/terminal-tile.js.gz +0 -0
- package/dist/web/public/terminal-ui.7e787829.js +4 -0
- package/dist/web/public/terminal-ui.7e787829.js.br +0 -0
- package/dist/web/public/terminal-ui.7e787829.js.gz +0 -0
- package/dist/web/public/tile-grid.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
- package/dist/web/public/vendor/fflate.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/{xterm-predictive-echo.ae5e3d5d.js → xterm-predictive-echo.276b11d9.js} +1 -1
- package/dist/web/public/vendor/xterm-predictive-echo.276b11d9.js.br +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.276b11d9.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.1be42307.js +5 -0
- package/dist/web/public/vendor/xterm-zerolag-input.1be42307.js.br +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.1be42307.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/public/whats-new.json +9 -9
- package/dist/web/route-helpers.d.ts +57 -6
- package/dist/web/route-helpers.d.ts.map +1 -1
- package/dist/web/route-helpers.js +72 -4
- package/dist/web/route-helpers.js.map +1 -1
- package/dist/web/routes/agent-session-routes.d.ts +20 -0
- package/dist/web/routes/agent-session-routes.d.ts.map +1 -0
- package/dist/web/routes/agent-session-routes.js +160 -0
- package/dist/web/routes/agent-session-routes.js.map +1 -0
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +203 -16
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/config-backup-routes.js +2 -2
- package/dist/web/routes/config-backup-routes.js.map +1 -1
- package/dist/web/routes/git-status-routes.d.ts +21 -7
- package/dist/web/routes/git-status-routes.d.ts.map +1 -1
- package/dist/web/routes/git-status-routes.js +78 -12
- package/dist/web/routes/git-status-routes.js.map +1 -1
- package/dist/web/routes/inbox-routes.d.ts +43 -0
- package/dist/web/routes/inbox-routes.d.ts.map +1 -0
- package/dist/web/routes/inbox-routes.js +148 -0
- package/dist/web/routes/inbox-routes.js.map +1 -0
- package/dist/web/routes/index.d.ts +2 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +2 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/ralph-routes.d.ts.map +1 -1
- package/dist/web/routes/ralph-routes.js +7 -16
- package/dist/web/routes/ralph-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +120 -74
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +107 -59
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/routes/ws-routes.d.ts +5 -0
- package/dist/web/routes/ws-routes.d.ts.map +1 -1
- package/dist/web/routes/ws-routes.js +84 -47
- package/dist/web/routes/ws-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +56 -7
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +88 -1
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/sensitive-path.d.ts.map +1 -1
- package/dist/web/sensitive-path.js +2 -0
- package/dist/web/sensitive-path.js.map +1 -1
- package/dist/web/server.d.ts +7 -2
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +144 -16
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-wait-registry.d.ts +48 -4
- package/dist/web/session-wait-registry.d.ts.map +1 -1
- package/dist/web/session-wait-registry.js +144 -6
- package/dist/web/session-wait-registry.js.map +1 -1
- package/dist/web/settings-writer.d.ts +47 -0
- package/dist/web/settings-writer.d.ts.map +1 -0
- package/dist/web/settings-writer.js +110 -0
- package/dist/web/settings-writer.js.map +1 -0
- package/dist/web/sse-events.d.ts +4 -1
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +4 -1
- package/dist/web/sse-events.js.map +1 -1
- package/package.json +1 -1
- package/skills/codeman/SKILL.md +30 -8
- package/skills/codeman/preamble.sh +3 -3
- package/skills/codeman/reference/endpoints.md +24 -3
- package/skills/codeman/reference/history.md +84 -0
- package/skills/codeman/reference/inspection-and-staging.md +152 -0
- package/skills/codeman/reference/messaging.md +1 -1
- package/skills/codeman/reference/verbs.md +65 -3
- package/dist/web/public/app.9314ee60.js +0 -43
- package/dist/web/public/app.9314ee60.js.br +0 -0
- package/dist/web/public/app.9314ee60.js.gz +0 -0
- package/dist/web/public/constants.4866ecc3.js.br +0 -0
- package/dist/web/public/constants.4866ecc3.js.gz +0 -0
- package/dist/web/public/i18n.22ca902b.js +0 -2
- package/dist/web/public/i18n.22ca902b.js.br +0 -0
- package/dist/web/public/i18n.22ca902b.js.gz +0 -0
- package/dist/web/public/mobile-handlers.44adaa07.js.br +0 -0
- package/dist/web/public/mobile-handlers.44adaa07.js.gz +0 -0
- package/dist/web/public/mobile.12fb18de.css +0 -1
- package/dist/web/public/mobile.12fb18de.css.br +0 -0
- package/dist/web/public/mobile.12fb18de.css.gz +0 -0
- package/dist/web/public/panels-ui.cbb24ba1.js.br +0 -0
- package/dist/web/public/panels-ui.cbb24ba1.js.gz +0 -0
- package/dist/web/public/session-ui.9c1f009b.js +0 -104
- package/dist/web/public/session-ui.9c1f009b.js.br +0 -0
- package/dist/web/public/session-ui.9c1f009b.js.gz +0 -0
- package/dist/web/public/settings-ui.fea9ddae.js +0 -106
- package/dist/web/public/settings-ui.fea9ddae.js.br +0 -0
- package/dist/web/public/settings-ui.fea9ddae.js.gz +0 -0
- package/dist/web/public/styles.e096fcd6.css +0 -1
- package/dist/web/public/styles.e096fcd6.css.br +0 -0
- package/dist/web/public/styles.e096fcd6.css.gz +0 -0
- package/dist/web/public/tab-rail-resize.7157e0bd.js.br +0 -0
- package/dist/web/public/tab-rail-resize.7157e0bd.js.gz +0 -0
- package/dist/web/public/terminal-ui.68dbd352.js +0 -2
- package/dist/web/public/terminal-ui.68dbd352.js.br +0 -0
- package/dist/web/public/terminal-ui.68dbd352.js.gz +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.br +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js +0 -4
- package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.br +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
package/dist/session.js
CHANGED
|
@@ -49,6 +49,9 @@ import { DEFAULT_TMUX_HISTORY_LIMIT } from './config/terminal-history.js';
|
|
|
49
49
|
import { EXEC_TIMEOUT_MS } from './config/exec-timeout.js';
|
|
50
50
|
import { getCli } from './config/cli-registry/registry.js';
|
|
51
51
|
import { SubmitVerifier } from './session-submit-verifier.js';
|
|
52
|
+
import { guardedComposerChrome, promptSha256, readGuardedSnapshotDetails, } from './agent-staging.js';
|
|
53
|
+
import { isKeyboardTerminalInput } from './terminal-input-classifier.js';
|
|
54
|
+
import { stripAnsi } from './utils/regex-patterns.js';
|
|
52
55
|
import { KEEP_WARM_NUDGE } from './prompt-cache-keepwarm.js';
|
|
53
56
|
import { compileVersionRegex } from './config/cli-registry/patterns.js';
|
|
54
57
|
import { resolveSessionCliVersion } from './utils/cli-resolver.js';
|
|
@@ -61,6 +64,7 @@ import { isGeneratedSessionName, SubmittedPromptTracker } from './session-auto-n
|
|
|
61
64
|
import { parseTerminalAttachmentRequests } from './attachment-magic.js';
|
|
62
65
|
import { sanitizeAttachmentHistory, upsertAttachmentHistory as upsertAttachmentHistoryList, } from './session-attachment-history.js';
|
|
63
66
|
import { resolveConfigModel } from './model-config-resolvers.js';
|
|
67
|
+
import { caseSessionSettled, isCasePathLocked, noteCaseSessionStarting } from './case-rename.js';
|
|
64
68
|
import { legacyConfigForMode } from './session-cli-registry-bridge.js';
|
|
65
69
|
import { launchModelFor, readScreenModel, resolveDisplayModel, restoredReportedModel, sanitizeModelName, } from './session-display-model.js';
|
|
66
70
|
/** Line buffer flush interval (100ms) - forces processing of partial lines */
|
|
@@ -298,6 +302,16 @@ export function resolveMuxAttachCwd(workingDir, remote, docker) {
|
|
|
298
302
|
// wrapper pane never needs the workspace as its cwd, so launch it in /tmp.
|
|
299
303
|
return remote || docker ? '/tmp' : workingDir;
|
|
300
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* A write that a guarded submit's lease could not hold, because the per-lease hold
|
|
307
|
+
* queue (`Session.MAX_GUARDED_HELD_INPUTS`) was full. Nothing was written; retry it.
|
|
308
|
+
*/
|
|
309
|
+
export class GuardedInputBusyError extends Error {
|
|
310
|
+
constructor() {
|
|
311
|
+
super('A guarded submit is in progress on this session; nothing was written. Retry the input.');
|
|
312
|
+
this.name = 'GuardedInputBusyError';
|
|
313
|
+
}
|
|
314
|
+
}
|
|
301
315
|
/**
|
|
302
316
|
* Whether a programmatic write leaves the keep-alive turn running: the nudge itself, or a bare
|
|
303
317
|
* Enter (which submits nothing new). Anything else carrying an Enter starts a turn of its own.
|
|
@@ -449,6 +463,12 @@ export class Session extends EventEmitter {
|
|
|
449
463
|
_keepWarmEnabled;
|
|
450
464
|
/** Last status the keep-warm service published for this session; ephemeral. */
|
|
451
465
|
_keepWarm = null;
|
|
466
|
+
/** Git fetch on an interval for this session; undefined = follow the app-wide default. Persisted. */
|
|
467
|
+
_gitFetchEnabled;
|
|
468
|
+
/** Seconds between this session's fetch runs; undefined = follow the app-wide default. Persisted. */
|
|
469
|
+
_gitFetchIntervalSeconds;
|
|
470
|
+
/** Last status the fetch scheduler published for this session; ephemeral. */
|
|
471
|
+
_gitFetch = null;
|
|
452
472
|
/** Until when the current turn counts as Codeman's own keep-alive (hooks open pre-acknowledged). */
|
|
453
473
|
_keepAliveTurnUntil = 0;
|
|
454
474
|
/** Last keystroke a person sent through a `fromUser` write. */
|
|
@@ -655,6 +675,16 @@ export class Session extends EventEmitter {
|
|
|
655
675
|
static EXPENSIVE_PROCESS_INTERVAL_MS = 150; // Process at most every 150ms
|
|
656
676
|
constructor(config) {
|
|
657
677
|
super();
|
|
678
|
+
// A case folder being renamed (src/case-rename.ts) must not gain a session mid-move: its pane would
|
|
679
|
+
// start in a directory that is about to have a different path.
|
|
680
|
+
if (config.workingDir && isCasePathLocked(config.workingDir)) {
|
|
681
|
+
throw new Error(`The case folder ${config.workingDir} is being renamed. Try again in a moment.`);
|
|
682
|
+
}
|
|
683
|
+
// And a rename must not start until this session is in the server's map, where it can be listed and
|
|
684
|
+
// stopped: until registerSessionWithLayout() (or stop()) settles it, the rename route refuses the move.
|
|
685
|
+
// A remote session's workingDir is the remote path, which the route never counts as the folder's.
|
|
686
|
+
if (config.workingDir && !config.remote)
|
|
687
|
+
noteCaseSessionStarting(this, config.workingDir);
|
|
658
688
|
this.setMaxListeners(25);
|
|
659
689
|
// Default error handler prevents unhandled 'error' events from crashing the process.
|
|
660
690
|
// Server attaches its own handler after construction — this is a safety net for the gap.
|
|
@@ -710,6 +740,10 @@ export class Session extends EventEmitter {
|
|
|
710
740
|
// from before the field has only `lastSubmitAt`, and every submit then was a real one.
|
|
711
741
|
this._lastRealPromptAt = config.lastRealPromptAt ?? config.lastSubmitAt ?? 0;
|
|
712
742
|
this._keepWarmEnabled = config.keepWarmEnabled;
|
|
743
|
+
// Restored, like the keep-warm choice: the interval is the user's decision about their repositories
|
|
744
|
+
// and outlives the run that made it, so a restart keeps it rather than falling back to the app default.
|
|
745
|
+
this._gitFetchEnabled = config.gitFetchEnabled;
|
|
746
|
+
this._gitFetchIntervalSeconds = config.gitFetchIntervalSeconds;
|
|
713
747
|
// Restored chain: its tail is the conversation the CLI was actually on when
|
|
714
748
|
// the server stopped, which outranks the launch id seeded just above. The
|
|
715
749
|
// FIRST-HAND flag is deliberately NOT restored — a persisted claim is not a
|
|
@@ -969,6 +1003,8 @@ export class Session extends EventEmitter {
|
|
|
969
1003
|
}
|
|
970
1004
|
/** Set the owning username (used by recovery to restore ownership). */
|
|
971
1005
|
set owner(username) {
|
|
1006
|
+
if (this._owner !== username)
|
|
1007
|
+
this._guardedPaneRevision++;
|
|
972
1008
|
this._owner = username;
|
|
973
1009
|
}
|
|
974
1010
|
// Adopt a Claude conversation ID observed from an external source (e.g. hook
|
|
@@ -1070,10 +1106,13 @@ export class Session extends EventEmitter {
|
|
|
1070
1106
|
* tmux session whose record is about to be deleted (Ark0N/Codeman#446).
|
|
1071
1107
|
*/
|
|
1072
1108
|
markClosing(closing) {
|
|
1109
|
+
if (this._closing !== closing)
|
|
1110
|
+
this._guardedPaneRevision++;
|
|
1073
1111
|
this._closing = closing;
|
|
1074
1112
|
}
|
|
1075
1113
|
/** Run one pane start, attach or relaunch with {@link paneLifecycleInFlight} raised. */
|
|
1076
1114
|
async _withPaneLifecycle(op) {
|
|
1115
|
+
this._guardedPaneRevision++;
|
|
1077
1116
|
this._paneLifecycleOps++;
|
|
1078
1117
|
try {
|
|
1079
1118
|
return await op();
|
|
@@ -1100,6 +1139,7 @@ export class Session extends EventEmitter {
|
|
|
1100
1139
|
* Ark0N/Codeman#446 review; both now persist on their success path.
|
|
1101
1140
|
*/
|
|
1102
1141
|
clearPaneExitForNewPane() {
|
|
1142
|
+
this._guardedPaneRevision++;
|
|
1103
1143
|
this.setPaneExit(undefined);
|
|
1104
1144
|
if (this._muxSession)
|
|
1105
1145
|
this._mux?.clearPaneExit?.(this._muxSession.muxName);
|
|
@@ -1222,6 +1262,32 @@ export class Session extends EventEmitter {
|
|
|
1222
1262
|
this._keepWarm = status;
|
|
1223
1263
|
return changed;
|
|
1224
1264
|
}
|
|
1265
|
+
/** Git fetch override for this session; undefined means the app-wide setting applies. */
|
|
1266
|
+
get gitFetchEnabled() {
|
|
1267
|
+
return this._gitFetchEnabled;
|
|
1268
|
+
}
|
|
1269
|
+
/** Interval override for this session; undefined means the app-wide setting applies. */
|
|
1270
|
+
get gitFetchIntervalSeconds() {
|
|
1271
|
+
return this._gitFetchIntervalSeconds;
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Set the session's override. The Session Options rows save on their own, so a caller passes what it has
|
|
1275
|
+
* and leaves the other half as it was; `undefined` on either means the app-wide setting applies again.
|
|
1276
|
+
*/
|
|
1277
|
+
setGitFetchSettings(enabled, intervalSeconds) {
|
|
1278
|
+
this._gitFetchEnabled = enabled;
|
|
1279
|
+
this._gitFetchIntervalSeconds = intervalSeconds;
|
|
1280
|
+
}
|
|
1281
|
+
/** What the fetch scheduler last reported for this session, or null before its first run. */
|
|
1282
|
+
get gitFetch() {
|
|
1283
|
+
return this._gitFetch;
|
|
1284
|
+
}
|
|
1285
|
+
/** Replace the fetch status; true when it differs from the previous one. */
|
|
1286
|
+
setGitFetchStatus(status) {
|
|
1287
|
+
const changed = JSON.stringify(status) !== JSON.stringify(this._gitFetch);
|
|
1288
|
+
this._gitFetch = status;
|
|
1289
|
+
return changed;
|
|
1290
|
+
}
|
|
1225
1291
|
/**
|
|
1226
1292
|
* True while the running turn is Codeman's own keep-alive nudge: its stop hook and idle
|
|
1227
1293
|
* prompt open pre-acknowledged, and its submit moves `lastSubmitAt` (it IS a submit, and the
|
|
@@ -1688,6 +1754,9 @@ export class Session extends EventEmitter {
|
|
|
1688
1754
|
statusLineExporter: this.statusLineExporter,
|
|
1689
1755
|
keepWarmEnabled: this._keepWarmEnabled,
|
|
1690
1756
|
keepWarm: this._keepWarm ?? undefined,
|
|
1757
|
+
gitFetchEnabled: this._gitFetchEnabled,
|
|
1758
|
+
gitFetchIntervalSeconds: this._gitFetchIntervalSeconds,
|
|
1759
|
+
gitFetch: this._gitFetch ?? undefined,
|
|
1691
1760
|
imageWatcherEnabled: this._imageWatcherEnabled,
|
|
1692
1761
|
pinned: this._pinned || undefined,
|
|
1693
1762
|
pinnedAt: this._pinned ? (this._pinnedAt ?? undefined) : undefined,
|
|
@@ -3694,6 +3763,11 @@ export class Session extends EventEmitter {
|
|
|
3694
3763
|
* ```
|
|
3695
3764
|
*/
|
|
3696
3765
|
async runPrompt(prompt, options) {
|
|
3766
|
+
if (this.guardedInputHeld) {
|
|
3767
|
+
this._guardedInputRevision++;
|
|
3768
|
+
throw new Error('Guarded submission in progress');
|
|
3769
|
+
}
|
|
3770
|
+
this._guardedPaneRevision++;
|
|
3697
3771
|
return new Promise((resolve, reject) => {
|
|
3698
3772
|
if (this.ptyProcess) {
|
|
3699
3773
|
reject(new Error('Session already has a running process'));
|
|
@@ -4132,6 +4206,236 @@ export class Session extends EventEmitter {
|
|
|
4132
4206
|
}
|
|
4133
4207
|
}
|
|
4134
4208
|
// Note: checkAutoCompact/checkAutoClear moved to SessionAutoOps (this._autoOps)
|
|
4209
|
+
_inspectionInstance = uuidv4();
|
|
4210
|
+
_guardedPaneRevision = 0;
|
|
4211
|
+
_guardedGeometryRevision = 0;
|
|
4212
|
+
_guardedInputRevision = 0;
|
|
4213
|
+
_guardedPendingWrites = 0;
|
|
4214
|
+
_guardedSubmitInFlight = false;
|
|
4215
|
+
/**
|
|
4216
|
+
* Input that arrived while a guarded submit held the lease, in arrival order. Each
|
|
4217
|
+
* entry runs when the lease is released; an entry that returns a promise (a mux
|
|
4218
|
+
* write, a named key) is waited for before the next runs, so nothing overtakes it.
|
|
4219
|
+
*/
|
|
4220
|
+
_guardedHeld = [];
|
|
4221
|
+
_guardedHeldBytes = 0;
|
|
4222
|
+
/** True while the drain waits for a held asynchronous write to finish. */
|
|
4223
|
+
_guardedHeldDraining = false;
|
|
4224
|
+
/** Held entries per lease. A lease lasts milliseconds; past this the caller backs off. */
|
|
4225
|
+
static MAX_GUARDED_HELD_INPUTS = 256;
|
|
4226
|
+
/** Held bytes per lease (16 browser frames at the 64 KiB frame cap). */
|
|
4227
|
+
static MAX_GUARDED_HELD_BYTES = 16 * 64 * 1024;
|
|
4228
|
+
/** Ephemeral identity, never persisted or based on a potentially reused pid alone. */
|
|
4229
|
+
get inspectionGeneration() {
|
|
4230
|
+
return `${this._inspectionInstance}:${this._guardedPaneRevision}:${this.owner ?? ''}`;
|
|
4231
|
+
}
|
|
4232
|
+
/** Staging additionally binds geometry; transcript cursors survive a terminal resize. */
|
|
4233
|
+
get guardedInputGeneration() {
|
|
4234
|
+
return `${this.inspectionGeneration}:${this._guardedGeometryRevision}`;
|
|
4235
|
+
}
|
|
4236
|
+
get guardedInputPendingWrites() {
|
|
4237
|
+
return this._guardedPendingWrites;
|
|
4238
|
+
}
|
|
4239
|
+
/**
|
|
4240
|
+
* True while a guarded submit holds the input lease (a few milliseconds). Input that
|
|
4241
|
+
* arrives in that span is held and written after it (see {@link guardedInputHeld}).
|
|
4242
|
+
*/
|
|
4243
|
+
get guardedSubmitInFlight() {
|
|
4244
|
+
return this._guardedSubmitInFlight;
|
|
4245
|
+
}
|
|
4246
|
+
/**
|
|
4247
|
+
* True while input is being held behind a guarded submit: the lease itself, or held
|
|
4248
|
+
* input not yet written after it. A write that is not routed through
|
|
4249
|
+
* {@link holdForGuardedLease} must refuse in this window, or it overtakes held input.
|
|
4250
|
+
* `write()` is the known exception: it refuses during the lease only, since the drain
|
|
4251
|
+
* writes held input through it, so a direct `write()` caller (cron's `paste` mode in
|
|
4252
|
+
* `cron/cron-service.ts`) can still land ahead of input that is draining.
|
|
4253
|
+
*/
|
|
4254
|
+
get guardedInputHeld() {
|
|
4255
|
+
return this._guardedSubmitInFlight || this._guardedHeld.length > 0 || this._guardedHeldDraining;
|
|
4256
|
+
}
|
|
4257
|
+
/**
|
|
4258
|
+
* Hold input behind a guarded submit's lease instead of refusing it.
|
|
4259
|
+
*
|
|
4260
|
+
* `free`: nothing is held, so the caller writes inline as it always did. `held`: `run`
|
|
4261
|
+
* is queued and runs, in arrival order with every other held input, once the lease is
|
|
4262
|
+
* released (whatever the submit's outcome). `full`: the per-lease cap is reached and
|
|
4263
|
+
* nothing was queued; the caller must stop delivering from that source until it can
|
|
4264
|
+
* resend in order. Refusing instead lost input: the browser's WebSocket path does not
|
|
4265
|
+
* wait for ACKs, so a refused seq N was overtaken by N+1 and its redelivery was then
|
|
4266
|
+
* rejected as a duplicate. `data`, when given, is observed as input on arrival, so a
|
|
4267
|
+
* keystroke held during the lease still moves the input revision at once.
|
|
4268
|
+
*/
|
|
4269
|
+
holdForGuardedLease(run, bytes = 0, data) {
|
|
4270
|
+
if (!this.guardedInputHeld)
|
|
4271
|
+
return 'free';
|
|
4272
|
+
if (data !== undefined)
|
|
4273
|
+
this._observeGuardedKeyboardInput(data);
|
|
4274
|
+
if (this._guardedHeld.length >= Session.MAX_GUARDED_HELD_INPUTS ||
|
|
4275
|
+
this._guardedHeldBytes + bytes > Session.MAX_GUARDED_HELD_BYTES)
|
|
4276
|
+
return 'full';
|
|
4277
|
+
this._guardedHeld.push({ run, bytes });
|
|
4278
|
+
this._guardedHeldBytes += bytes;
|
|
4279
|
+
return 'held';
|
|
4280
|
+
}
|
|
4281
|
+
/**
|
|
4282
|
+
* Run an asynchronous write now, or in its turn behind a guarded submit's lease. The
|
|
4283
|
+
* turn lasts until the write settles, so later held input waits for it. Resolves
|
|
4284
|
+
* `whenFull` without running anything when the hold queue is full.
|
|
4285
|
+
*/
|
|
4286
|
+
_inGuardedTurn(write, whenFull) {
|
|
4287
|
+
if (!this.guardedInputHeld)
|
|
4288
|
+
return write();
|
|
4289
|
+
return new Promise((resolve, reject) => {
|
|
4290
|
+
const held = this.holdForGuardedLease(() => {
|
|
4291
|
+
const running = write();
|
|
4292
|
+
running.then(resolve, reject);
|
|
4293
|
+
return running;
|
|
4294
|
+
});
|
|
4295
|
+
if (held === 'full') {
|
|
4296
|
+
try {
|
|
4297
|
+
resolve(whenFull());
|
|
4298
|
+
}
|
|
4299
|
+
catch (err) {
|
|
4300
|
+
reject(err);
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
});
|
|
4304
|
+
}
|
|
4305
|
+
/** Run held input in arrival order until it is all written or a new lease is taken. */
|
|
4306
|
+
_drainGuardedHeld() {
|
|
4307
|
+
while (this._guardedHeld.length > 0 && !this._guardedSubmitInFlight && !this._guardedHeldDraining) {
|
|
4308
|
+
const entry = this._guardedHeld.shift();
|
|
4309
|
+
this._guardedHeldBytes -= entry.bytes;
|
|
4310
|
+
let running;
|
|
4311
|
+
try {
|
|
4312
|
+
running = entry.run();
|
|
4313
|
+
}
|
|
4314
|
+
catch (err) {
|
|
4315
|
+
console.error(`[Session ${this.id}] held input failed after a guarded submit:`, getErrorMessage(err));
|
|
4316
|
+
continue;
|
|
4317
|
+
}
|
|
4318
|
+
if (running && typeof running.then === 'function') {
|
|
4319
|
+
this._guardedHeldDraining = true;
|
|
4320
|
+
void running
|
|
4321
|
+
.catch(() => { })
|
|
4322
|
+
.then(() => {
|
|
4323
|
+
this._guardedHeldDraining = false;
|
|
4324
|
+
this._drainGuardedHeld();
|
|
4325
|
+
});
|
|
4326
|
+
}
|
|
4327
|
+
}
|
|
4328
|
+
}
|
|
4329
|
+
/** Expose only the path selection needed for attributed Codex evidence, never credentials. */
|
|
4330
|
+
getCodexHomeOverride() {
|
|
4331
|
+
return this._envOverrides?.CODEX_HOME || undefined;
|
|
4332
|
+
}
|
|
4333
|
+
_observeGuardedKeyboardInput(data) {
|
|
4334
|
+
if (data && isKeyboardTerminalInput(data))
|
|
4335
|
+
this._guardedInputRevision++;
|
|
4336
|
+
}
|
|
4337
|
+
guardedInputSnapshot() {
|
|
4338
|
+
return this._readGuardedInputSnapshot(false);
|
|
4339
|
+
}
|
|
4340
|
+
_readGuardedInputSnapshot(allowLease) {
|
|
4341
|
+
if (this._isStopped ||
|
|
4342
|
+
this._closing ||
|
|
4343
|
+
this.paneLifecycleInFlight ||
|
|
4344
|
+
this._guardedPendingWrites ||
|
|
4345
|
+
(!allowLease && this.guardedInputHeld) ||
|
|
4346
|
+
this.remote ||
|
|
4347
|
+
this.docker ||
|
|
4348
|
+
this.paneExit ||
|
|
4349
|
+
!this._muxSession)
|
|
4350
|
+
return null;
|
|
4351
|
+
// Only a CLI whose registry entry describes its composer chrome takes guarded input.
|
|
4352
|
+
const chrome = guardedComposerChrome(getCli(this.mode));
|
|
4353
|
+
if (!chrome)
|
|
4354
|
+
return null;
|
|
4355
|
+
const capture = {};
|
|
4356
|
+
const screen = this._mux?.capturePaneBuffer?.(this._muxSession.muxName, undefined, capture);
|
|
4357
|
+
if (!screen)
|
|
4358
|
+
return null;
|
|
4359
|
+
// tmux's replay has CUP addresses rather than line breaks. Read the SAME
|
|
4360
|
+
// fresh frame for dialog/work chrome and exact composer/caret proof.
|
|
4361
|
+
// eslint-disable-next-line no-control-regex
|
|
4362
|
+
const plain = stripAnsi(screen.replace(/\x1b\[\d+;\d+H/g, '\n'));
|
|
4363
|
+
const working = this._workingLinePattern();
|
|
4364
|
+
working.lastIndex = 0;
|
|
4365
|
+
if (this._paneShowsBlocked(plain) || working.test(plain) || this._paneAwaitsWorkers(plain))
|
|
4366
|
+
return null;
|
|
4367
|
+
const composer = readGuardedSnapshotDetails(screen, chrome);
|
|
4368
|
+
return composer === null
|
|
4369
|
+
? null
|
|
4370
|
+
: {
|
|
4371
|
+
generation: this.guardedInputGeneration,
|
|
4372
|
+
revision: this._guardedInputRevision,
|
|
4373
|
+
composerSha: promptSha256(composer.composer),
|
|
4374
|
+
availableColumns: Number.isFinite(capture.capturedGeometry?.cols)
|
|
4375
|
+
? capture.capturedGeometry.cols - composer.prefixWidth
|
|
4376
|
+
: 0,
|
|
4377
|
+
};
|
|
4378
|
+
}
|
|
4379
|
+
/** Stage through ordinary delivery, after cancelling any older Enter retry timer. */
|
|
4380
|
+
async stageGuardedInput(message) {
|
|
4381
|
+
if (/[\r\n]/.test(message))
|
|
4382
|
+
return false;
|
|
4383
|
+
if (this._guardedPendingWrites || this.guardedInputHeld) {
|
|
4384
|
+
this._guardedInputRevision++;
|
|
4385
|
+
return false;
|
|
4386
|
+
}
|
|
4387
|
+
this._submitVerifier?.cancel();
|
|
4388
|
+
return this.writeViaMux(message, { fromUser: true });
|
|
4389
|
+
}
|
|
4390
|
+
/** One conditional Enter. An ambiguous acknowledgement is never replayed. */
|
|
4391
|
+
async submitGuardedInput(expected) {
|
|
4392
|
+
const matches = (current) => !!current &&
|
|
4393
|
+
current.generation === expected.generation &&
|
|
4394
|
+
current.revision === expected.revision &&
|
|
4395
|
+
current.composerSha === expected.composerSha &&
|
|
4396
|
+
current.availableColumns === expected.availableColumns;
|
|
4397
|
+
if (!matches(this.guardedInputSnapshot()) || !this._mux || !this._muxSession)
|
|
4398
|
+
return false;
|
|
4399
|
+
this._guardedSubmitInFlight = true;
|
|
4400
|
+
let dispatched = false;
|
|
4401
|
+
try {
|
|
4402
|
+
const sent = await this._mux.sendInput(this.id, '\r', {
|
|
4403
|
+
beforeDispatch: () => {
|
|
4404
|
+
if (!matches(this._readGuardedInputSnapshot(true)))
|
|
4405
|
+
return false;
|
|
4406
|
+
this._guardedInputRevision++;
|
|
4407
|
+
dispatched = true;
|
|
4408
|
+
return true;
|
|
4409
|
+
},
|
|
4410
|
+
});
|
|
4411
|
+
if (sent && dispatched)
|
|
4412
|
+
this._emitSubmittedPrompt(this._trackSubmit('\r', { fromUser: true }));
|
|
4413
|
+
return sent && dispatched;
|
|
4414
|
+
}
|
|
4415
|
+
finally {
|
|
4416
|
+
this._guardedSubmitInFlight = false;
|
|
4417
|
+
this._drainGuardedHeld();
|
|
4418
|
+
}
|
|
4419
|
+
}
|
|
4420
|
+
/**
|
|
4421
|
+
* Lease/pending boundary for named keys whose bytes bypass the ordinary write methods.
|
|
4422
|
+
* During a guarded submit the operation waits for the lease like any other write;
|
|
4423
|
+
* only a full hold queue refuses it, with {@link GuardedInputBusyError}.
|
|
4424
|
+
*/
|
|
4425
|
+
async performAuxiliaryInput(operation) {
|
|
4426
|
+
this._guardedInputRevision++;
|
|
4427
|
+
return this._inGuardedTurn(async () => {
|
|
4428
|
+
this._guardedPendingWrites++;
|
|
4429
|
+
try {
|
|
4430
|
+
await operation();
|
|
4431
|
+
}
|
|
4432
|
+
finally {
|
|
4433
|
+
this._guardedPendingWrites--;
|
|
4434
|
+
}
|
|
4435
|
+
}, () => {
|
|
4436
|
+
throw new GuardedInputBusyError();
|
|
4437
|
+
});
|
|
4438
|
+
}
|
|
4135
4439
|
/**
|
|
4136
4440
|
* Sends input directly to the PTY process.
|
|
4137
4441
|
*
|
|
@@ -4151,6 +4455,10 @@ export class Session extends EventEmitter {
|
|
|
4151
4455
|
* input could disappear while the caller believed it had been delivered.
|
|
4152
4456
|
*/
|
|
4153
4457
|
write(data, options = {}) {
|
|
4458
|
+
this._observeGuardedKeyboardInput(data);
|
|
4459
|
+
// The lease only, not the drain, which writes held input through here (see guardedInputHeld).
|
|
4460
|
+
if (this._guardedSubmitInFlight)
|
|
4461
|
+
return false;
|
|
4154
4462
|
const submittedPrompt = this._trackSubmit(data, options);
|
|
4155
4463
|
if (!this.ptyProcess)
|
|
4156
4464
|
return false;
|
|
@@ -4198,6 +4506,7 @@ export class Session extends EventEmitter {
|
|
|
4198
4506
|
* separator. Reports submissions like a write would (a line feed never is one).
|
|
4199
4507
|
*/
|
|
4200
4508
|
trackUserInput(data) {
|
|
4509
|
+
this._observeGuardedKeyboardInput(data);
|
|
4201
4510
|
if (!this._acceptsPrompts)
|
|
4202
4511
|
return;
|
|
4203
4512
|
this._emitSubmittedPrompt(this._submittedPromptTracker.feed(data));
|
|
@@ -4218,6 +4527,7 @@ export class Session extends EventEmitter {
|
|
|
4218
4527
|
* their prompt must count as real and alert normally.
|
|
4219
4528
|
*/
|
|
4220
4529
|
markPromptSubmitted(prompt) {
|
|
4530
|
+
this._guardedInputRevision++;
|
|
4221
4531
|
if (prompt !== undefined && prompt.trim() !== KEEP_WARM_NUDGE)
|
|
4222
4532
|
this._keepAliveTurnUntil = 0;
|
|
4223
4533
|
this._stampSubmit();
|
|
@@ -4312,22 +4622,36 @@ export class Session extends EventEmitter {
|
|
|
4312
4622
|
* ```
|
|
4313
4623
|
*/
|
|
4314
4624
|
async writeViaMux(data, options = {}) {
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4625
|
+
this._observeGuardedKeyboardInput(data);
|
|
4626
|
+
// A guarded submit's lease lasts for its Enter only: wait for it, in arrival order,
|
|
4627
|
+
// rather than refusing, so an approval answer or a programmatic prompt is not
|
|
4628
|
+
// dropped. Without a lease the write starts synchronously, so the pending count
|
|
4629
|
+
// rises before any await and no lease can be taken while it is in flight.
|
|
4630
|
+
return this._inGuardedTurn(() => this._writeViaMuxNow(data, options), () => false);
|
|
4631
|
+
}
|
|
4632
|
+
async _writeViaMuxNow(data, options) {
|
|
4633
|
+
this._guardedPendingWrites++;
|
|
4634
|
+
try {
|
|
4635
|
+
const submittedPrompt = this._trackSubmit(data, options);
|
|
4636
|
+
if (this._mux && this._muxSession) {
|
|
4637
|
+
const sent = await this._mux.sendInput(this.id, data);
|
|
4638
|
+
if (sent) {
|
|
4639
|
+
this._emitSubmittedPrompt(submittedPrompt);
|
|
4640
|
+
this._verifySubmitted(data);
|
|
4641
|
+
}
|
|
4642
|
+
return sent;
|
|
4643
|
+
}
|
|
4644
|
+
// Fallback to PTY write
|
|
4645
|
+
if (this.ptyProcess) {
|
|
4646
|
+
this.ptyProcess.write(data);
|
|
4319
4647
|
this._emitSubmittedPrompt(submittedPrompt);
|
|
4320
|
-
|
|
4648
|
+
return true;
|
|
4321
4649
|
}
|
|
4322
|
-
return
|
|
4650
|
+
return false;
|
|
4323
4651
|
}
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
this.ptyProcess.write(data);
|
|
4327
|
-
this._emitSubmittedPrompt(submittedPrompt);
|
|
4328
|
-
return true;
|
|
4652
|
+
finally {
|
|
4653
|
+
this._guardedPendingWrites--;
|
|
4329
4654
|
}
|
|
4330
|
-
return false;
|
|
4331
4655
|
}
|
|
4332
4656
|
/**
|
|
4333
4657
|
* Arm the composer check for a prompt that went out some other way than
|
|
@@ -4354,7 +4678,16 @@ export class Session extends EventEmitter {
|
|
|
4354
4678
|
capture: () => this._isStopped || !this._mux || !this._muxSession
|
|
4355
4679
|
? null
|
|
4356
4680
|
: this._mux.capturePaneText?.(this._muxSession.muxName),
|
|
4357
|
-
sendEnter: () =>
|
|
4681
|
+
sendEnter: () => {
|
|
4682
|
+
// A retry Enter is never held behind a guarded submit: once that submit's
|
|
4683
|
+
// Enter went out, a re-press would submit whatever is on the composer next.
|
|
4684
|
+
if (this.guardedInputHeld) {
|
|
4685
|
+
this._guardedInputRevision++;
|
|
4686
|
+
this._submitVerifier?.cancel();
|
|
4687
|
+
return Promise.resolve(false);
|
|
4688
|
+
}
|
|
4689
|
+
return this.writeViaMux('\r');
|
|
4690
|
+
},
|
|
4358
4691
|
// ⚠ NO fallback glyph here, unlike the screen-reading probe elsewhere in this file.
|
|
4359
4692
|
// Only claude, codex, pi, opencode, omp, gemini and antigravity declare a promptGlyph; the other modes would fall back
|
|
4360
4693
|
// to claude's `❯`, which is ALSO starship's default shell prompt (and pure's, and
|
|
@@ -4381,6 +4714,7 @@ export class Session extends EventEmitter {
|
|
|
4381
4714
|
* `resize()` skipped a real resize that happened to match them.
|
|
4382
4715
|
*/
|
|
4383
4716
|
_notePtySpawnGeometry(cols, rows) {
|
|
4717
|
+
this._guardedPaneRevision++;
|
|
4384
4718
|
this._ptyCols = cols;
|
|
4385
4719
|
this._ptyRows = rows;
|
|
4386
4720
|
}
|
|
@@ -4493,6 +4827,7 @@ export class Session extends EventEmitter {
|
|
|
4493
4827
|
}
|
|
4494
4828
|
const dimsChanged = cols !== this._ptyCols || rows !== this._ptyRows;
|
|
4495
4829
|
if (this.ptyProcess && (dimsChanged || options.force)) {
|
|
4830
|
+
this._guardedGeometryRevision++;
|
|
4496
4831
|
this._ptyCols = cols;
|
|
4497
4832
|
this._ptyRows = rows;
|
|
4498
4833
|
if (!IS_TEST_MODE && this._mux && this._muxSession) {
|
|
@@ -4507,6 +4842,10 @@ export class Session extends EventEmitter {
|
|
|
4507
4842
|
}
|
|
4508
4843
|
// Legacy method for sending input - wraps runPrompt
|
|
4509
4844
|
async sendInput(input) {
|
|
4845
|
+
if (this.guardedInputHeld) {
|
|
4846
|
+
this._guardedInputRevision++;
|
|
4847
|
+
throw new Error('Guarded submission in progress');
|
|
4848
|
+
}
|
|
4510
4849
|
this._status = 'busy';
|
|
4511
4850
|
this._markActivity(true);
|
|
4512
4851
|
this.runPrompt(input).catch((err) => {
|
|
@@ -4580,6 +4919,8 @@ export class Session extends EventEmitter {
|
|
|
4580
4919
|
async stop(killMux = true) {
|
|
4581
4920
|
// Set stopped flag first to prevent new timers from being created
|
|
4582
4921
|
this._isStopped = true;
|
|
4922
|
+
// A session stopped before it was registered (a create that failed) no longer holds up a case rename.
|
|
4923
|
+
caseSessionSettled(this);
|
|
4583
4924
|
// A pane that is gone is watching nothing. Nothing probes a stopped session, so
|
|
4584
4925
|
// without this the last chip it drew would ride along on its row forever.
|
|
4585
4926
|
this._watching = null;
|