aicodeman 1.45.0 → 1.47.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 +26 -6
- 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/cli-agent.d.ts +31 -2
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +279 -34
- 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/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/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/ralph-fix-plan-watcher.d.ts +13 -1
- package/dist/ralph-fix-plan-watcher.d.ts.map +1 -1
- package/dist/ralph-fix-plan-watcher.js +21 -3
- package/dist/ralph-fix-plan-watcher.js.map +1 -1
- package/dist/ralph-tracker.d.ts +6 -0
- package/dist/ralph-tracker.d.ts.map +1 -1
- package/dist/ralph-tracker.js +8 -0
- package/dist/ralph-tracker.js.map +1 -1
- package/dist/session.d.ts +113 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +341 -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 +23 -1
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +54 -6
- package/dist/tmux-manager.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/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.fe8a6b4c.js +43 -0
- package/dist/web/public/app.fe8a6b4c.js.br +0 -0
- package/dist/web/public/app.fe8a6b4c.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.0b3cdde9.js → constants.affcdde6.js} +611 -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.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.a618ef4e.js +2 -0
- package/dist/web/public/i18n.a618ef4e.js.br +0 -0
- package/dist/web/public/i18n.a618ef4e.js.gz +0 -0
- package/dist/web/public/{image-input.941386e7.js → image-input.a5d0f8f3.js} +18 -9
- package/dist/web/public/image-input.a5d0f8f3.js.br +0 -0
- package/dist/web/public/image-input.a5d0f8f3.js.gz +0 -0
- package/dist/web/public/index.html +110 -19
- 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 → keyboard-accessory.ff64fafc.js} +5 -1
- package/dist/web/public/keyboard-accessory.ff64fafc.js.br +0 -0
- package/dist/web/public/keyboard-accessory.ff64fafc.js.gz +0 -0
- package/dist/web/public/{mobile-handlers.1c0cbcd8.js → mobile-handlers.f7c3f692.js} +22 -0
- 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.78c3defb.js → panels-ui.4a56ee63.js} +1 -1
- 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.ea6b1994.js → session-ui.426fb671.js} +8 -8
- package/dist/web/public/session-ui.426fb671.js.br +0 -0
- package/dist/web/public/session-ui.426fb671.js.gz +0 -0
- package/dist/web/public/{settings-ui.085dd202.js → settings-ui.a191154e.js} +11 -11
- 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.4f0c67a0.css +1 -0
- package/dist/web/public/styles.4f0c67a0.css.br +0 -0
- package/dist/web/public/styles.4f0c67a0.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.98843069.js +1 -0
- package/dist/web/public/terminal-keycode229-recovery.98843069.js.br +0 -0
- package/dist/web/public/terminal-keycode229-recovery.98843069.js.gz +0 -0
- package/dist/web/public/terminal-split.js.gz +0 -0
- package/dist/web/public/terminal-tile.js +10 -6
- 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 +8 -9
- package/dist/web/request-body-parser.d.ts +23 -0
- package/dist/web/request-body-parser.d.ts.map +1 -0
- package/dist/web/request-body-parser.js +57 -0
- package/dist/web/request-body-parser.js.map +1 -0
- 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 +32 -26
- 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/file-routes.d.ts +5 -0
- package/dist/web/routes/file-routes.d.ts.map +1 -1
- package/dist/web/routes/file-routes.js +5 -1
- package/dist/web/routes/file-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/index.d.ts +1 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +1 -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 +52 -18
- 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 +101 -28
- 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 +87 -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 +30 -7
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +40 -1
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +5 -2
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +102 -18
- package/dist/web/server.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/package.json +1 -1
- package/skills/codeman/SKILL.md +25 -6
- 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 +18 -3
- package/dist/web/public/app.42d0abae.js +0 -43
- package/dist/web/public/app.42d0abae.js.br +0 -0
- package/dist/web/public/app.42d0abae.js.gz +0 -0
- package/dist/web/public/constants.0b3cdde9.js.br +0 -0
- package/dist/web/public/constants.0b3cdde9.js.gz +0 -0
- package/dist/web/public/i18n.1e68220d.js +0 -2
- package/dist/web/public/i18n.1e68220d.js.br +0 -0
- package/dist/web/public/i18n.1e68220d.js.gz +0 -0
- package/dist/web/public/image-input.941386e7.js.br +0 -0
- package/dist/web/public/image-input.941386e7.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.4e8b20ac.js.br +0 -0
- package/dist/web/public/keyboard-accessory.4e8b20ac.js.gz +0 -0
- package/dist/web/public/mobile-handlers.1c0cbcd8.js.br +0 -0
- package/dist/web/public/mobile-handlers.1c0cbcd8.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.78c3defb.js.br +0 -0
- package/dist/web/public/panels-ui.78c3defb.js.gz +0 -0
- package/dist/web/public/session-ui.ea6b1994.js.br +0 -0
- package/dist/web/public/session-ui.ea6b1994.js.gz +0 -0
- package/dist/web/public/settings-ui.085dd202.js.br +0 -0
- package/dist/web/public/settings-ui.085dd202.js.gz +0 -0
- package/dist/web/public/styles.3b59b41d.css +0 -1
- package/dist/web/public/styles.3b59b41d.css.br +0 -0
- package/dist/web/public/styles.3b59b41d.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-keycode229-recovery.af09f99a.js +0 -1
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.br +0 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
- package/dist/web/public/terminal-ui.fb2a27f5.js +0 -2
- package/dist/web/public/terminal-ui.fb2a27f5.js.br +0 -0
- package/dist/web/public/terminal-ui.fb2a27f5.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';
|
|
@@ -299,6 +302,16 @@ export function resolveMuxAttachCwd(workingDir, remote, docker) {
|
|
|
299
302
|
// wrapper pane never needs the workspace as its cwd, so launch it in /tmp.
|
|
300
303
|
return remote || docker ? '/tmp' : workingDir;
|
|
301
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
|
+
}
|
|
302
315
|
/**
|
|
303
316
|
* Whether a programmatic write leaves the keep-alive turn running: the nudge itself, or a bare
|
|
304
317
|
* Enter (which submits nothing new). Anything else carrying an Enter starts a turn of its own.
|
|
@@ -450,6 +463,12 @@ export class Session extends EventEmitter {
|
|
|
450
463
|
_keepWarmEnabled;
|
|
451
464
|
/** Last status the keep-warm service published for this session; ephemeral. */
|
|
452
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;
|
|
453
472
|
/** Until when the current turn counts as Codeman's own keep-alive (hooks open pre-acknowledged). */
|
|
454
473
|
_keepAliveTurnUntil = 0;
|
|
455
474
|
/** Last keystroke a person sent through a `fromUser` write. */
|
|
@@ -721,6 +740,10 @@ export class Session extends EventEmitter {
|
|
|
721
740
|
// from before the field has only `lastSubmitAt`, and every submit then was a real one.
|
|
722
741
|
this._lastRealPromptAt = config.lastRealPromptAt ?? config.lastSubmitAt ?? 0;
|
|
723
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;
|
|
724
747
|
// Restored chain: its tail is the conversation the CLI was actually on when
|
|
725
748
|
// the server stopped, which outranks the launch id seeded just above. The
|
|
726
749
|
// FIRST-HAND flag is deliberately NOT restored — a persisted claim is not a
|
|
@@ -980,6 +1003,8 @@ export class Session extends EventEmitter {
|
|
|
980
1003
|
}
|
|
981
1004
|
/** Set the owning username (used by recovery to restore ownership). */
|
|
982
1005
|
set owner(username) {
|
|
1006
|
+
if (this._owner !== username)
|
|
1007
|
+
this._guardedPaneRevision++;
|
|
983
1008
|
this._owner = username;
|
|
984
1009
|
}
|
|
985
1010
|
// Adopt a Claude conversation ID observed from an external source (e.g. hook
|
|
@@ -1081,10 +1106,13 @@ export class Session extends EventEmitter {
|
|
|
1081
1106
|
* tmux session whose record is about to be deleted (Ark0N/Codeman#446).
|
|
1082
1107
|
*/
|
|
1083
1108
|
markClosing(closing) {
|
|
1109
|
+
if (this._closing !== closing)
|
|
1110
|
+
this._guardedPaneRevision++;
|
|
1084
1111
|
this._closing = closing;
|
|
1085
1112
|
}
|
|
1086
1113
|
/** Run one pane start, attach or relaunch with {@link paneLifecycleInFlight} raised. */
|
|
1087
1114
|
async _withPaneLifecycle(op) {
|
|
1115
|
+
this._guardedPaneRevision++;
|
|
1088
1116
|
this._paneLifecycleOps++;
|
|
1089
1117
|
try {
|
|
1090
1118
|
return await op();
|
|
@@ -1111,6 +1139,7 @@ export class Session extends EventEmitter {
|
|
|
1111
1139
|
* Ark0N/Codeman#446 review; both now persist on their success path.
|
|
1112
1140
|
*/
|
|
1113
1141
|
clearPaneExitForNewPane() {
|
|
1142
|
+
this._guardedPaneRevision++;
|
|
1114
1143
|
this.setPaneExit(undefined);
|
|
1115
1144
|
if (this._muxSession)
|
|
1116
1145
|
this._mux?.clearPaneExit?.(this._muxSession.muxName);
|
|
@@ -1233,6 +1262,32 @@ export class Session extends EventEmitter {
|
|
|
1233
1262
|
this._keepWarm = status;
|
|
1234
1263
|
return changed;
|
|
1235
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
|
+
}
|
|
1236
1291
|
/**
|
|
1237
1292
|
* True while the running turn is Codeman's own keep-alive nudge: its stop hook and idle
|
|
1238
1293
|
* prompt open pre-acknowledged, and its submit moves `lastSubmitAt` (it IS a submit, and the
|
|
@@ -1699,6 +1754,9 @@ export class Session extends EventEmitter {
|
|
|
1699
1754
|
statusLineExporter: this.statusLineExporter,
|
|
1700
1755
|
keepWarmEnabled: this._keepWarmEnabled,
|
|
1701
1756
|
keepWarm: this._keepWarm ?? undefined,
|
|
1757
|
+
gitFetchEnabled: this._gitFetchEnabled,
|
|
1758
|
+
gitFetchIntervalSeconds: this._gitFetchIntervalSeconds,
|
|
1759
|
+
gitFetch: this._gitFetch ?? undefined,
|
|
1702
1760
|
imageWatcherEnabled: this._imageWatcherEnabled,
|
|
1703
1761
|
pinned: this._pinned || undefined,
|
|
1704
1762
|
pinnedAt: this._pinned ? (this._pinnedAt ?? undefined) : undefined,
|
|
@@ -3705,6 +3763,11 @@ export class Session extends EventEmitter {
|
|
|
3705
3763
|
* ```
|
|
3706
3764
|
*/
|
|
3707
3765
|
async runPrompt(prompt, options) {
|
|
3766
|
+
if (this.guardedInputHeld) {
|
|
3767
|
+
this._guardedInputRevision++;
|
|
3768
|
+
throw new Error('Guarded submission in progress');
|
|
3769
|
+
}
|
|
3770
|
+
this._guardedPaneRevision++;
|
|
3708
3771
|
return new Promise((resolve, reject) => {
|
|
3709
3772
|
if (this.ptyProcess) {
|
|
3710
3773
|
reject(new Error('Session already has a running process'));
|
|
@@ -4143,6 +4206,236 @@ export class Session extends EventEmitter {
|
|
|
4143
4206
|
}
|
|
4144
4207
|
}
|
|
4145
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
|
+
}
|
|
4146
4439
|
/**
|
|
4147
4440
|
* Sends input directly to the PTY process.
|
|
4148
4441
|
*
|
|
@@ -4162,6 +4455,10 @@ export class Session extends EventEmitter {
|
|
|
4162
4455
|
* input could disappear while the caller believed it had been delivered.
|
|
4163
4456
|
*/
|
|
4164
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;
|
|
4165
4462
|
const submittedPrompt = this._trackSubmit(data, options);
|
|
4166
4463
|
if (!this.ptyProcess)
|
|
4167
4464
|
return false;
|
|
@@ -4209,6 +4506,7 @@ export class Session extends EventEmitter {
|
|
|
4209
4506
|
* separator. Reports submissions like a write would (a line feed never is one).
|
|
4210
4507
|
*/
|
|
4211
4508
|
trackUserInput(data) {
|
|
4509
|
+
this._observeGuardedKeyboardInput(data);
|
|
4212
4510
|
if (!this._acceptsPrompts)
|
|
4213
4511
|
return;
|
|
4214
4512
|
this._emitSubmittedPrompt(this._submittedPromptTracker.feed(data));
|
|
@@ -4229,6 +4527,7 @@ export class Session extends EventEmitter {
|
|
|
4229
4527
|
* their prompt must count as real and alert normally.
|
|
4230
4528
|
*/
|
|
4231
4529
|
markPromptSubmitted(prompt) {
|
|
4530
|
+
this._guardedInputRevision++;
|
|
4232
4531
|
if (prompt !== undefined && prompt.trim() !== KEEP_WARM_NUDGE)
|
|
4233
4532
|
this._keepAliveTurnUntil = 0;
|
|
4234
4533
|
this._stampSubmit();
|
|
@@ -4323,22 +4622,36 @@ export class Session extends EventEmitter {
|
|
|
4323
4622
|
* ```
|
|
4324
4623
|
*/
|
|
4325
4624
|
async writeViaMux(data, options = {}) {
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
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);
|
|
4330
4647
|
this._emitSubmittedPrompt(submittedPrompt);
|
|
4331
|
-
|
|
4648
|
+
return true;
|
|
4332
4649
|
}
|
|
4333
|
-
return
|
|
4650
|
+
return false;
|
|
4334
4651
|
}
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
this.ptyProcess.write(data);
|
|
4338
|
-
this._emitSubmittedPrompt(submittedPrompt);
|
|
4339
|
-
return true;
|
|
4652
|
+
finally {
|
|
4653
|
+
this._guardedPendingWrites--;
|
|
4340
4654
|
}
|
|
4341
|
-
return false;
|
|
4342
4655
|
}
|
|
4343
4656
|
/**
|
|
4344
4657
|
* Arm the composer check for a prompt that went out some other way than
|
|
@@ -4365,7 +4678,16 @@ export class Session extends EventEmitter {
|
|
|
4365
4678
|
capture: () => this._isStopped || !this._mux || !this._muxSession
|
|
4366
4679
|
? null
|
|
4367
4680
|
: this._mux.capturePaneText?.(this._muxSession.muxName),
|
|
4368
|
-
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
|
+
},
|
|
4369
4691
|
// ⚠ NO fallback glyph here, unlike the screen-reading probe elsewhere in this file.
|
|
4370
4692
|
// Only claude, codex, pi, opencode, omp, gemini and antigravity declare a promptGlyph; the other modes would fall back
|
|
4371
4693
|
// to claude's `❯`, which is ALSO starship's default shell prompt (and pure's, and
|
|
@@ -4392,6 +4714,7 @@ export class Session extends EventEmitter {
|
|
|
4392
4714
|
* `resize()` skipped a real resize that happened to match them.
|
|
4393
4715
|
*/
|
|
4394
4716
|
_notePtySpawnGeometry(cols, rows) {
|
|
4717
|
+
this._guardedPaneRevision++;
|
|
4395
4718
|
this._ptyCols = cols;
|
|
4396
4719
|
this._ptyRows = rows;
|
|
4397
4720
|
}
|
|
@@ -4504,6 +4827,7 @@ export class Session extends EventEmitter {
|
|
|
4504
4827
|
}
|
|
4505
4828
|
const dimsChanged = cols !== this._ptyCols || rows !== this._ptyRows;
|
|
4506
4829
|
if (this.ptyProcess && (dimsChanged || options.force)) {
|
|
4830
|
+
this._guardedGeometryRevision++;
|
|
4507
4831
|
this._ptyCols = cols;
|
|
4508
4832
|
this._ptyRows = rows;
|
|
4509
4833
|
if (!IS_TEST_MODE && this._mux && this._muxSession) {
|
|
@@ -4518,6 +4842,10 @@ export class Session extends EventEmitter {
|
|
|
4518
4842
|
}
|
|
4519
4843
|
// Legacy method for sending input - wraps runPrompt
|
|
4520
4844
|
async sendInput(input) {
|
|
4845
|
+
if (this.guardedInputHeld) {
|
|
4846
|
+
this._guardedInputRevision++;
|
|
4847
|
+
throw new Error('Guarded submission in progress');
|
|
4848
|
+
}
|
|
4521
4849
|
this._status = 'busy';
|
|
4522
4850
|
this._markActivity(true);
|
|
4523
4851
|
this.runPrompt(input).catch((err) => {
|