aicodeman 1.34.0 → 1.40.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 +11 -0
- package/README.zh-CN.md +8 -0
- package/dist/attachment-registry.d.ts +6 -0
- package/dist/attachment-registry.d.ts.map +1 -1
- package/dist/attachment-registry.js +9 -3
- package/dist/attachment-registry.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/config/cli-registry/patterns.d.ts +6 -0
- package/dist/config/cli-registry/patterns.d.ts.map +1 -1
- package/dist/config/cli-registry/patterns.js +13 -0
- package/dist/config/cli-registry/patterns.js.map +1 -1
- package/dist/config/cli-registry/schema.d.ts +13 -0
- package/dist/config/cli-registry/schema.d.ts.map +1 -1
- package/dist/config/cli-registry/schema.js +60 -2
- 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 +175 -7
- package/dist/config/cli-registry/stock.js.map +1 -1
- package/dist/config/cli-registry/types.d.ts +93 -6
- package/dist/config/cli-registry/types.d.ts.map +1 -1
- package/dist/config/dependency-registry.d.ts +7 -0
- package/dist/config/dependency-registry.d.ts.map +1 -1
- package/dist/config/dependency-registry.js +15 -0
- package/dist/config/dependency-registry.js.map +1 -1
- package/dist/config/path-probe.d.ts +37 -0
- package/dist/config/path-probe.d.ts.map +1 -0
- package/dist/config/path-probe.js +43 -0
- package/dist/config/path-probe.js.map +1 -0
- package/dist/deepseek-route-config.d.ts +110 -0
- package/dist/deepseek-route-config.d.ts.map +1 -0
- package/dist/deepseek-route-config.js +477 -0
- package/dist/deepseek-route-config.js.map +1 -0
- package/dist/git-workspace-status.d.ts +246 -0
- package/dist/git-workspace-status.d.ts.map +1 -0
- package/dist/git-workspace-status.js +626 -0
- package/dist/git-workspace-status.js.map +1 -0
- package/dist/hooks-config.d.ts.map +1 -1
- package/dist/hooks-config.js +83 -13
- package/dist/hooks-config.js.map +1 -1
- package/dist/model-config-resolvers.d.ts +29 -0
- package/dist/model-config-resolvers.d.ts.map +1 -0
- package/dist/model-config-resolvers.js +34 -0
- package/dist/model-config-resolvers.js.map +1 -0
- package/dist/session-display-model.d.ts +110 -0
- package/dist/session-display-model.d.ts.map +1 -0
- package/dist/session-display-model.js +154 -0
- package/dist/session-display-model.js.map +1 -0
- package/dist/session.d.ts +145 -8
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +280 -35
- package/dist/session.js.map +1 -1
- package/dist/types/api.d.ts +7 -0
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/api.js.map +1 -1
- package/dist/types/session.d.ts +28 -1
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js.map +1 -1
- package/dist/types/tools.d.ts +1 -1
- package/dist/types/tools.d.ts.map +1 -1
- package/dist/utils/bounded-path-probe.d.ts +99 -0
- package/dist/utils/bounded-path-probe.d.ts.map +1 -0
- package/dist/utils/bounded-path-probe.js +228 -0
- package/dist/utils/bounded-path-probe.js.map +1 -0
- package/dist/utils/cli-executable-resolver.d.ts +2 -0
- package/dist/utils/cli-executable-resolver.d.ts.map +1 -1
- package/dist/utils/cli-executable-resolver.js +2 -1
- package/dist/utils/cli-executable-resolver.js.map +1 -1
- package/dist/utils/deepseek-cli-resolver.d.ts +8 -0
- package/dist/utils/deepseek-cli-resolver.d.ts.map +1 -1
- package/dist/utils/deepseek-cli-resolver.js +18 -2
- package/dist/utils/deepseek-cli-resolver.js.map +1 -1
- package/dist/utils/dependency-checker.d.ts +2 -0
- package/dist/utils/dependency-checker.d.ts.map +1 -1
- package/dist/utils/dependency-checker.js +37 -9
- package/dist/utils/dependency-checker.js.map +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/web/case-path.d.ts +58 -0
- package/dist/web/case-path.d.ts.map +1 -0
- package/dist/web/case-path.js +182 -0
- package/dist/web/case-path.js.map +1 -0
- package/dist/web/launch-defaults.d.ts +22 -0
- package/dist/web/launch-defaults.d.ts.map +1 -0
- package/dist/web/launch-defaults.js +46 -0
- package/dist/web/launch-defaults.js.map +1 -0
- 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.abadc970.js +43 -0
- package/dist/web/public/app.abadc970.js.br +0 -0
- package/dist/web/public/app.abadc970.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.d95fade9.js → constants.c0f8f9de.js} +1009 -127
- package/dist/web/public/constants.c0f8f9de.js.br +0 -0
- package/dist/web/public/constants.c0f8f9de.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js +1 -0
- package/dist/web/public/entrance-animations.js.br +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/git-status-ui.js +719 -0
- 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/home-sessions.js +12 -9
- package/dist/web/public/home-sessions.js.br +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.a31747a9.js +2 -0
- package/dist/web/public/i18n.a31747a9.js.br +0 -0
- package/dist/web/public/i18n.a31747a9.js.gz +0 -0
- package/dist/web/public/{image-input.a216cf9e.js → image-input.898a538b.js} +18 -8
- package/dist/web/public/image-input.898a538b.js.br +0 -0
- package/dist/web/public/image-input.898a538b.js.gz +0 -0
- package/dist/web/public/index.html +205 -42
- 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.c1e7c7b7.js.gz +0 -0
- package/dist/web/public/{mobile-handlers.23a85db1.js → mobile-handlers.0ef9c311.js} +20 -0
- package/dist/web/public/mobile-handlers.0ef9c311.js.br +0 -0
- package/dist/web/public/mobile-handlers.0ef9c311.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.111b187d.css +1 -0
- package/dist/web/public/mobile.111b187d.css.br +0 -0
- package/dist/web/public/mobile.111b187d.css.gz +0 -0
- package/dist/web/public/notification-manager.50bb87d8.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.6a5898d9.js → panels-ui.4735dfc2.js} +4 -4
- package/dist/web/public/panels-ui.4735dfc2.js.br +0 -0
- package/dist/web/public/{panels-ui.6a5898d9.js.gz → panels-ui.4735dfc2.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.ff0dae4c.js.gz +0 -0
- package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
- package/dist/web/public/session-lineage.js +229 -86
- package/dist/web/public/session-lineage.js.br +0 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/session-ui.f731602c.js +100 -0
- package/dist/web/public/session-ui.f731602c.js.br +0 -0
- package/dist/web/public/session-ui.f731602c.js.gz +0 -0
- package/dist/web/public/settings-ui.a97ab16f.js +97 -0
- package/dist/web/public/settings-ui.a97ab16f.js.br +0 -0
- package/dist/web/public/settings-ui.a97ab16f.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js +290 -0
- package/dist/web/public/spreadsheet-preview-worker.js.br +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview.af82fa2f.js +555 -0
- package/dist/web/public/spreadsheet-preview.af82fa2f.js.br +0 -0
- package/dist/web/public/spreadsheet-preview.af82fa2f.js.gz +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js +1006 -0
- package/dist/web/public/spreadsheet-xlsx-core.js.br +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
- package/dist/web/public/styles.8ee12666.css +1 -0
- package/dist/web/public/styles.8ee12666.css.br +0 -0
- package/dist/web/public/styles.8ee12666.css.gz +0 -0
- package/dist/web/public/subagent-windows.e6ca799f.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.065fcba4.js +1 -0
- package/dist/web/public/tab-layout-browser.065fcba4.js.br +0 -0
- package/dist/web/public/tab-layout-browser.065fcba4.js.gz +0 -0
- package/dist/web/public/{tab-rail-resize.dcdc5772.js → tab-rail-resize.a5867793.js} +1 -1
- package/dist/web/public/tab-rail-resize.a5867793.js.br +0 -0
- package/dist/web/public/tab-rail-resize.a5867793.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js +1 -0
- 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-split.js +146 -649
- package/dist/web/public/terminal-split.js.br +0 -0
- package/dist/web/public/terminal-split.js.gz +0 -0
- package/dist/web/public/terminal-tile.js +1611 -0
- 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.5059b30e.js +2 -0
- package/dist/web/public/terminal-ui.5059b30e.js.br +0 -0
- package/dist/web/public/terminal-ui.5059b30e.js.gz +0 -0
- package/dist/web/public/tile-grid.js +2684 -0
- package/dist/web/public/tile-grid.js.br +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 +44 -0
- package/dist/web/public/vendor/exceljs.min.js.br +0 -0
- package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
- package/dist/web/public/vendor/fflate.min.js +1 -0
- package/dist/web/public/vendor/fflate.min.js.br +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.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.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.c4b51eb6.js → voice-input.2342c37b.js} +64 -10
- package/dist/web/public/voice-input.2342c37b.js.br +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 +9 -1
- package/dist/web/public/webview-tabs.js.br +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +136 -18
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/cli-registry-routes.d.ts.map +1 -1
- package/dist/web/routes/cli-registry-routes.js +3 -1
- package/dist/web/routes/cli-registry-routes.js.map +1 -1
- package/dist/web/routes/doctor-routes.d.ts +23 -0
- package/dist/web/routes/doctor-routes.d.ts.map +1 -0
- package/dist/web/routes/doctor-routes.js +83 -0
- package/dist/web/routes/doctor-routes.js.map +1 -0
- package/dist/web/routes/file-routes.d.ts +7 -0
- package/dist/web/routes/file-routes.d.ts.map +1 -1
- package/dist/web/routes/file-routes.js +34 -4
- package/dist/web/routes/file-routes.js.map +1 -1
- package/dist/web/routes/git-status-routes.d.ts +16 -0
- package/dist/web/routes/git-status-routes.d.ts.map +1 -0
- package/dist/web/routes/git-status-routes.js +98 -0
- package/dist/web/routes/git-status-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/session-routes.d.ts +18 -0
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +106 -42
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/status-telemetry-routes.d.ts +3 -0
- package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
- package/dist/web/routes/status-telemetry-routes.js +12 -0
- package/dist/web/routes/status-telemetry-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +13 -0
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +28 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +41 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +5 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +35 -12
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-listener-wiring.d.ts +1 -0
- package/dist/web/session-listener-wiring.d.ts.map +1 -1
- package/dist/web/session-listener-wiring.js +11 -0
- package/dist/web/session-listener-wiring.js.map +1 -1
- package/package.json +3 -1
- package/scripts/postinstall.js +16 -0
- package/skills/codeman/reference/verbs.md +3 -0
- package/dist/web/public/app.cf0db8ce.js +0 -43
- package/dist/web/public/app.cf0db8ce.js.br +0 -0
- package/dist/web/public/app.cf0db8ce.js.gz +0 -0
- package/dist/web/public/constants.d95fade9.js.br +0 -0
- package/dist/web/public/constants.d95fade9.js.gz +0 -0
- package/dist/web/public/i18n.398b8331.js +0 -1
- package/dist/web/public/i18n.398b8331.js.br +0 -0
- package/dist/web/public/i18n.398b8331.js.gz +0 -0
- package/dist/web/public/image-input.a216cf9e.js.br +0 -0
- package/dist/web/public/image-input.a216cf9e.js.gz +0 -0
- package/dist/web/public/mobile-handlers.23a85db1.js.br +0 -0
- package/dist/web/public/mobile-handlers.23a85db1.js.gz +0 -0
- package/dist/web/public/mobile.1aa4952d.css +0 -1
- package/dist/web/public/mobile.1aa4952d.css.br +0 -0
- package/dist/web/public/mobile.1aa4952d.css.gz +0 -0
- package/dist/web/public/panels-ui.6a5898d9.js.br +0 -0
- package/dist/web/public/session-ui.bf66c13b.js +0 -89
- package/dist/web/public/session-ui.bf66c13b.js.br +0 -0
- package/dist/web/public/session-ui.bf66c13b.js.gz +0 -0
- package/dist/web/public/settings-ui.331d3b1d.js +0 -97
- package/dist/web/public/settings-ui.331d3b1d.js.br +0 -0
- package/dist/web/public/settings-ui.331d3b1d.js.gz +0 -0
- package/dist/web/public/styles.8c485d26.css +0 -1
- package/dist/web/public/styles.8c485d26.css.br +0 -0
- package/dist/web/public/styles.8c485d26.css.gz +0 -0
- package/dist/web/public/tab-layout-browser.4e0ed50c.js +0 -1
- package/dist/web/public/tab-layout-browser.4e0ed50c.js.br +0 -0
- package/dist/web/public/tab-layout-browser.4e0ed50c.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.dcdc5772.js.br +0 -0
- package/dist/web/public/tab-rail-resize.dcdc5772.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js +0 -1
- package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.br +0 -0
- package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.gz +0 -0
- package/dist/web/public/terminal-ui.a4535311.js +0 -2
- package/dist/web/public/terminal-ui.a4535311.js.br +0 -0
- package/dist/web/public/terminal-ui.a4535311.js.gz +0 -0
- package/dist/web/public/upload.html +0 -155
- package/dist/web/public/upload.html.br +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/voice-input.c4b51eb6.js.br +0 -0
- package/dist/web/public/voice-input.c4b51eb6.js.gz +0 -0
package/dist/session.js
CHANGED
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* @module session
|
|
29
29
|
*/
|
|
30
30
|
import { EventEmitter } from 'node:events';
|
|
31
|
+
import { basename } from 'node:path';
|
|
31
32
|
import { execSync, execFileSync } from 'node:child_process';
|
|
32
33
|
import { v4 as uuidv4 } from 'uuid';
|
|
33
34
|
import * as pty from 'node-pty';
|
|
@@ -58,6 +59,9 @@ import { InteractivePtyExitBreaker } from './session-pty-exit-breaker.js';
|
|
|
58
59
|
import { isGeneratedSessionName, SubmittedPromptTracker } from './session-auto-name.js';
|
|
59
60
|
import { parseTerminalAttachmentRequests } from './attachment-magic.js';
|
|
60
61
|
import { sanitizeAttachmentHistory, upsertAttachmentHistory as upsertAttachmentHistoryList, } from './session-attachment-history.js';
|
|
62
|
+
import { resolveConfigModel } from './model-config-resolvers.js';
|
|
63
|
+
import { legacyConfigForMode } from './session-cli-registry-bridge.js';
|
|
64
|
+
import { launchModelFor, readScreenModel, resolveDisplayModel, restoredReportedModel, sanitizeModelName, } from './session-display-model.js';
|
|
61
65
|
/** Line buffer flush interval (100ms) - forces processing of partial lines */
|
|
62
66
|
const LINE_BUFFER_FLUSH_INTERVAL = 100;
|
|
63
67
|
// ============================================================================
|
|
@@ -163,10 +167,11 @@ function cliExportsTruecolor(mode) {
|
|
|
163
167
|
* Codex, Claude Code, and Gemini are known, controlled (Ink/React) TUIs that
|
|
164
168
|
* repaint via cursor positioning, so dropping the alt-screen switch is safe —
|
|
165
169
|
* content stays in the normal buffer. Excluded: `shell` (arbitrary programs like
|
|
166
|
-
* vim/less/htop legitimately need the alt screen), `opencode` (
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
+
* vim/less/htop legitimately need the alt screen), `opencode` (its own MIDDLE strip,
|
|
171
|
+
* isMuxMouseStripMode), `pi` (below) and `grok` (a fullscreen alt-screen TUI with
|
|
172
|
+
* mouse support). Keep parity with the replay-side strip (`stripReplayBuffer` in
|
|
173
|
+
* session-routes.ts); the table in `CliCapabilities.altScreen` is pinned for every
|
|
174
|
+
* stock CLI in test/claude-scrollback-strip.test.ts.
|
|
170
175
|
*
|
|
171
176
|
* ⚠️ Being excluded here does NOT preserve the alt screen. Every excluded mode
|
|
172
177
|
* falls through to isMuxAltScreenOnlyStripMode(), which strips the alt-screen
|
|
@@ -185,8 +190,10 @@ export function isAltScreenStripMode(mode) {
|
|
|
185
190
|
}
|
|
186
191
|
/**
|
|
187
192
|
* Modes that need the NARROW strip: alt-screen toggles only, leaving `\x1b[3J`
|
|
188
|
-
* and the mouse-tracking DECSETs alone. Applies to every mode
|
|
189
|
-
*
|
|
193
|
+
* and the mouse-tracking DECSETs alone. Applies to every mode that is neither
|
|
194
|
+
* `strip-full` (isAltScreenStripMode) nor `strip-mux-and-mouse` (isMuxMouseStripMode),
|
|
195
|
+
* so `strip-mux-only` and `preserve` alike, but ONLY when the session is tmux-backed
|
|
196
|
+
* (`useMux`).
|
|
190
197
|
*
|
|
191
198
|
* The bug (issue #205): the tmux CLIENT emits `smcup` (`\x1b[?1049h`) as its first
|
|
192
199
|
* bytes on attach, before any program has run. Unstripped, xterm.js parks in the
|
|
@@ -211,7 +218,31 @@ export function isAltScreenStripMode(mode) {
|
|
|
211
218
|
* `\x1b[3J` from a user's own `clear` is a deliberate "wipe my scrollback".
|
|
212
219
|
*/
|
|
213
220
|
export function isMuxAltScreenOnlyStripMode(mode, useMux) {
|
|
214
|
-
|
|
221
|
+
if (!useMux)
|
|
222
|
+
return false;
|
|
223
|
+
const altScreen = getCli(mode)?.capabilities.altScreen;
|
|
224
|
+
return altScreen !== 'strip-full' && altScreen !== 'strip-mux-and-mouse';
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Modes whose mouse-tracking DECSETs must be stripped, leaving `3J` alone:
|
|
228
|
+
* `altScreen: 'strip-mux-and-mouse'`, i.e. a mouse-capable full-screen TUI.
|
|
229
|
+
*
|
|
230
|
+
* Why this exists (opencode, measured 2026-09-16): the TUI enables tracking
|
|
231
|
+
* DECSETs, tmux runs with `mouse off` and therefore passes the PANE's DECSETs
|
|
232
|
+
* straight through to the tmux client, and the browser's xterm obeyed them —
|
|
233
|
+
* `mouseTrackingMode` flipped to `'any'` and xterm then reported DRAGS to the TUI
|
|
234
|
+
* instead of selecting locally. "Mark text, copy on select" silently did nothing
|
|
235
|
+
* (measured 62 `none` / 18 `any` over 16s, and 5/5 dead drags while `any`), and
|
|
236
|
+
* the obvious fallback — Ctrl+C — is opencode's `app_exit`, so the failure also
|
|
237
|
+
* ended sessions. Stripping at the source keeps xterm in selection mode; clicks
|
|
238
|
+
* still reach the CLI through the browser's hand-encoded tap, which this strip
|
|
239
|
+
* publishes as `cliMouseTracking` (`_recordStrippedMouseMode`).
|
|
240
|
+
*
|
|
241
|
+
* Gated on `useMux` for the same reason as the narrow strip: on the direct-PTY
|
|
242
|
+
* fallback the program's own DECSETs really do reach xterm and must be honoured.
|
|
243
|
+
*/
|
|
244
|
+
export function isMuxMouseStripMode(mode, useMux) {
|
|
245
|
+
return useMux && getCli(mode)?.capabilities.altScreen === 'strip-mux-and-mouse';
|
|
215
246
|
}
|
|
216
247
|
// Note: Claude CLI PATH resolution moved to session-cli-builder.ts (buildClaudeEnv)
|
|
217
248
|
/** PTY fallback geometry when tmux can't be queried (matches pre-#80 hardcoded values). */
|
|
@@ -405,6 +436,27 @@ export class Session extends EventEmitter {
|
|
|
405
436
|
_watchingWindow = WATCHING_TAIL_LINES;
|
|
406
437
|
/** Lazily compiled `capabilities.workDetect.awaitingLine`. See _awaitingLinePattern(). */
|
|
407
438
|
_awaitingLineRe = undefined;
|
|
439
|
+
/**
|
|
440
|
+
* The newest model the running CLI reported for itself (its statusline, or its own
|
|
441
|
+
* footer read off the probe's capture), or null when none has. Feeds `displayModel`
|
|
442
|
+
* (src/session-display-model.ts). Persisted through `toState()` and restored after a
|
|
443
|
+
* restart, so an idle session keeps naming its model until the next report.
|
|
444
|
+
*/
|
|
445
|
+
_reportedModel = null;
|
|
446
|
+
/**
|
|
447
|
+
* The model the CLI's own config pins for this session (`modelDetect.configResolver`),
|
|
448
|
+
* read at each pane start, attach or relaunch; null when it pins none. Below any
|
|
449
|
+
* report from the running CLI in `displayModel`. Not persisted: the next start reads it.
|
|
450
|
+
*/
|
|
451
|
+
_configModel = null;
|
|
452
|
+
/** Bumped per config read, so a read that lands after a newer one is dropped. */
|
|
453
|
+
_configModelGen = 0;
|
|
454
|
+
/** Lazily compiled `capabilities.modelDetect.screenLine`. See _modelLinePattern(). */
|
|
455
|
+
_modelLineRe = undefined;
|
|
456
|
+
/** Resolved with the pattern above: how many rows at the foot of the screen it sees. */
|
|
457
|
+
_modelLineRows = 1;
|
|
458
|
+
/** Resolved with the pattern above: the fields it shows that are never the model. */
|
|
459
|
+
_modelRejectWords = [];
|
|
408
460
|
_trustDialogAccepted = false; // Stops the trust-dialog scan (answered, or given up)
|
|
409
461
|
_trustDialogAttempts = 0; // Keystrokes sent at the trust dialog
|
|
410
462
|
_lastTrustDialogScanAt = 0; // Throttle for the trust-dialog screen read
|
|
@@ -697,6 +749,7 @@ export class Session extends EventEmitter {
|
|
|
697
749
|
// replaces it with a first-hand reading. NOT the stats collector, which a
|
|
698
750
|
// browser panel arms and disarms — see `startPaneExitWatcher`.
|
|
699
751
|
this.setPaneExit(config.paneExit);
|
|
752
|
+
this._reportedModel = restoredReportedModel(config.displayModel) ?? null;
|
|
700
753
|
// Never self-parent: a session pointing at itself would draw a zero-length
|
|
701
754
|
// lineage arc under its own tab. Only reachable via the recovery path, where
|
|
702
755
|
// both the id and the saved parent come from disk.
|
|
@@ -964,6 +1017,9 @@ export class Session extends EventEmitter {
|
|
|
964
1017
|
finally {
|
|
965
1018
|
this._paneLifecycleOps--;
|
|
966
1019
|
this._paneStartedAt = Date.now();
|
|
1020
|
+
// A start, attach or relaunch is when the CLI read its config, so it is when
|
|
1021
|
+
// the model that config pins is read here too.
|
|
1022
|
+
this._refreshConfigModel();
|
|
967
1023
|
}
|
|
968
1024
|
}
|
|
969
1025
|
/**
|
|
@@ -1010,7 +1066,7 @@ export class Session extends EventEmitter {
|
|
|
1010
1066
|
/**
|
|
1011
1067
|
* True when this session's PTY is a tmux client rather than the program itself.
|
|
1012
1068
|
* Read by the replay-side alt-screen strip, which must apply the same
|
|
1013
|
-
* `useMux` gate as the live strip (isMuxAltScreenOnlyStripMode).
|
|
1069
|
+
* `useMux` gate as the live strip (isMuxAltScreenOnlyStripMode, isMuxMouseStripMode).
|
|
1014
1070
|
*/
|
|
1015
1071
|
get usesMux() {
|
|
1016
1072
|
return this._useMux;
|
|
@@ -1482,6 +1538,7 @@ export class Session extends EventEmitter {
|
|
|
1482
1538
|
model: cliTakesSessionModel(this.mode) ? this._model : undefined,
|
|
1483
1539
|
advisorModel: this._advisorModel,
|
|
1484
1540
|
customModel: this.customModel,
|
|
1541
|
+
displayModel: this.displayModel,
|
|
1485
1542
|
// COD-118: runtime-only — surfaced so the frontend can require explicit user
|
|
1486
1543
|
// intent before restarting a crash-looped session. Deliberately NOT restored
|
|
1487
1544
|
// by the constructor: a Codeman restart starts with a fresh breaker so boot
|
|
@@ -2094,14 +2151,21 @@ export class Session extends EventEmitter {
|
|
|
2094
2151
|
// redraws overwrite only the cells they target, so non-erased rows keep
|
|
2095
2152
|
// their content. Gated to Codex/Claude/Gemini (isAltScreenStripMode).
|
|
2096
2153
|
//
|
|
2097
|
-
// Every OTHER mode (shell/
|
|
2098
|
-
// is tmux-backed: alt-screen toggles only, because the sequence that breaks
|
|
2154
|
+
// Every OTHER mode (shell/antigravity/pi/grok/deepseek/omp) gets the NARROW strip
|
|
2155
|
+
// when it is tmux-backed: alt-screen toggles only, because the sequence that breaks
|
|
2099
2156
|
// scrollback there is tmux's own client-side smcup at attach, not anything the
|
|
2100
2157
|
// program in the pane emitted (issue #205, see isMuxAltScreenOnlyStripMode).
|
|
2101
2158
|
// 3J and the mouse DECSETs stay, so `clear` and mouse-aware TUIs keep working.
|
|
2159
|
+
//
|
|
2160
|
+
// The MIDDLE case (isMuxMouseStripMode) is a mouse-capable full-screen TUI:
|
|
2161
|
+
// it needs smcup AND the mouse DECSETs gone — otherwise the pane's tracking
|
|
2162
|
+
// reaches xterm and every drag becomes a mouse report instead of a text
|
|
2163
|
+
// selection, which is what killed mark-and-copy in opencode — while 3J stays,
|
|
2164
|
+
// because a TUI is not a `clear` consumer.
|
|
2102
2165
|
const fullStrip = isAltScreenStripMode(this.mode);
|
|
2103
|
-
const
|
|
2104
|
-
|
|
2166
|
+
const mouseStrip = isMuxMouseStripMode(this.mode, this._useMux);
|
|
2167
|
+
const altOnlyStrip = !fullStrip && !mouseStrip && isMuxAltScreenOnlyStripMode(this.mode, this._useMux);
|
|
2168
|
+
if (fullStrip || mouseStrip || altOnlyStrip) {
|
|
2105
2169
|
// Reassemble sequences split across PTY chunk boundaries first: a chunk
|
|
2106
2170
|
// ending mid-sequence ('\x1b[?104' now, '9h' next) would slip past the
|
|
2107
2171
|
// strip below and leave xterm stuck in the scrollback-less alt buffer
|
|
@@ -2121,11 +2185,13 @@ export class Session extends EventEmitter {
|
|
|
2121
2185
|
// eslint-disable-next-line no-control-regex
|
|
2122
2186
|
data = data.replace(/\x1b\[\?(?:47|1047|1049)[hl]/g, '');
|
|
2123
2187
|
if (fullStrip) {
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2188
|
+
// eslint-disable-next-line no-control-regex
|
|
2189
|
+
data = data.replace(/\x1b\[3J/g, '');
|
|
2190
|
+
}
|
|
2191
|
+
if (fullStrip || mouseStrip) {
|
|
2192
|
+
data = data.replace(
|
|
2193
|
+
// eslint-disable-next-line no-control-regex
|
|
2194
|
+
/\x1b\[\?(?:1000|1001|1002|1003|1005|1006|1007)[hl]/g, (seq) => {
|
|
2129
2195
|
this._recordStrippedMouseMode(seq);
|
|
2130
2196
|
return '';
|
|
2131
2197
|
});
|
|
@@ -2337,20 +2403,14 @@ export class Session extends EventEmitter {
|
|
|
2337
2403
|
this._codexConfig?.resumeSessionId ||
|
|
2338
2404
|
this.id;
|
|
2339
2405
|
// For NEW mux sessions: wait for readiness then clean buffer
|
|
2340
|
-
// For RESTORED mux sessions:
|
|
2406
|
+
// For RESTORED mux sessions: leave the buffer alone - client will fetch it on tab switch
|
|
2341
2407
|
if (!isRestored) {
|
|
2342
2408
|
if (isExternalCliMode(this.mode)) {
|
|
2343
2409
|
// External CLIs use custom TUIs — no ❯ prompt to detect.
|
|
2344
2410
|
// Wait for TUI to stabilize (output stops changing), then mark ready.
|
|
2345
2411
|
// Don't clear the buffer — the TUI's initial render IS the useful content.
|
|
2346
2412
|
// Emit needsRefresh so the client fetches the full buffer once the TUI has rendered.
|
|
2347
|
-
this.
|
|
2348
|
-
this._promptCheckTimeout = null;
|
|
2349
|
-
if (this._isStopped)
|
|
2350
|
-
return;
|
|
2351
|
-
this._status = 'idle';
|
|
2352
|
-
this.emit('needsRefresh');
|
|
2353
|
-
}, 3000);
|
|
2413
|
+
this._armPaneSettle(false);
|
|
2354
2414
|
}
|
|
2355
2415
|
else {
|
|
2356
2416
|
// Claude mode: wait for ❯ prompt
|
|
@@ -2384,6 +2444,9 @@ export class Session extends EventEmitter {
|
|
|
2384
2444
|
}, 5000);
|
|
2385
2445
|
}
|
|
2386
2446
|
}
|
|
2447
|
+
else {
|
|
2448
|
+
this._armPaneSettle(true);
|
|
2449
|
+
}
|
|
2387
2450
|
}
|
|
2388
2451
|
catch (err) {
|
|
2389
2452
|
console.error('[Session] Failed to create mux session, falling back to direct PTY:', err);
|
|
@@ -2707,8 +2770,128 @@ export class Session extends EventEmitter {
|
|
|
2707
2770
|
this._lastPaneProbeWorking =
|
|
2708
2771
|
text === null ? null : this._workingLinePattern().test(text) || this._paneAwaitsWorkers(text);
|
|
2709
2772
|
this._readWatching(text);
|
|
2773
|
+
this._readScreenModel(text);
|
|
2710
2774
|
return this._lastPaneProbeWorking;
|
|
2711
2775
|
}
|
|
2776
|
+
/**
|
|
2777
|
+
* Read the model the CLI's own footer names off the same capture, for a CLI whose
|
|
2778
|
+
* registry entry declares `capabilities.modelDetect`.
|
|
2779
|
+
*
|
|
2780
|
+
* Unlike `_readWatching`, a capture that could not be read, or a footer the pattern
|
|
2781
|
+
* does not find (a popup covering it, a footer turned off), KEEPS the last model. The
|
|
2782
|
+
* two are not symmetric: background work ends and its badge must go, while a model does
|
|
2783
|
+
* not stop running because something was drawn over the row that names it.
|
|
2784
|
+
*/
|
|
2785
|
+
_readScreenModel(paneText) {
|
|
2786
|
+
if (paneText === null)
|
|
2787
|
+
return;
|
|
2788
|
+
const pattern = this._modelLinePattern();
|
|
2789
|
+
if (!pattern)
|
|
2790
|
+
return;
|
|
2791
|
+
const model = readScreenModel(paneText, pattern, this._modelLineRows, {
|
|
2792
|
+
rejectWords: this._modelRejectWords,
|
|
2793
|
+
// A footer field equal to the folder this session runs in is the folder, never the
|
|
2794
|
+
// model: the generic half of the rule, for every CLI.
|
|
2795
|
+
cwdBasename: basename(this.workingDir),
|
|
2796
|
+
});
|
|
2797
|
+
if (model)
|
|
2798
|
+
this.noteReportedModel('screen', model);
|
|
2799
|
+
}
|
|
2800
|
+
/**
|
|
2801
|
+
* The regex reading this CLI's model off its footer, or null for a CLI that declares
|
|
2802
|
+
* none. Compiled once per session through `compileVersionRegex()` (null, never a throw,
|
|
2803
|
+
* for a pattern it refuses), like the working- and watching-line patterns.
|
|
2804
|
+
*/
|
|
2805
|
+
_modelLinePattern() {
|
|
2806
|
+
if (this._modelLineRe === undefined) {
|
|
2807
|
+
const detect = getCli(this.mode)?.capabilities.modelDetect;
|
|
2808
|
+
this._modelLineRe = detect?.screenLine ? compileVersionRegex(detect.screenLine) : null;
|
|
2809
|
+
this._modelLineRows = detect?.screenLines ?? 1;
|
|
2810
|
+
this._modelRejectWords = detect?.rejectWords ?? [];
|
|
2811
|
+
}
|
|
2812
|
+
return this._modelLineRe;
|
|
2813
|
+
}
|
|
2814
|
+
/**
|
|
2815
|
+
* Record a model the running CLI reported for itself: its statusline (claude's
|
|
2816
|
+
* exporter, via `POST /api/status-telemetry`) or its own footer. The newest report
|
|
2817
|
+
* wins whatever its source. An empty or unprintable report changes nothing.
|
|
2818
|
+
*
|
|
2819
|
+
* @returns true when the reported model changed (and `displayModelChanged` was emitted)
|
|
2820
|
+
*/
|
|
2821
|
+
noteReportedModel(source, raw) {
|
|
2822
|
+
const model = sanitizeModelName(raw);
|
|
2823
|
+
if (!model)
|
|
2824
|
+
return false;
|
|
2825
|
+
if (this._reportedModel?.model === model && this._reportedModel.source === source)
|
|
2826
|
+
return false;
|
|
2827
|
+
this._reportedModel = { model, source };
|
|
2828
|
+
// The status does not change with it, so it needs a broadcast (and a persist) of its own.
|
|
2829
|
+
this.emit('displayModelChanged');
|
|
2830
|
+
return true;
|
|
2831
|
+
}
|
|
2832
|
+
/**
|
|
2833
|
+
* The model this session runs as far as the server knows, and where that came from:
|
|
2834
|
+
* the custom endpoint's model, else the newest report from the CLI, else the launch
|
|
2835
|
+
* model (src/session-display-model.ts). Undefined when none is known.
|
|
2836
|
+
*/
|
|
2837
|
+
get displayModel() {
|
|
2838
|
+
return resolveDisplayModel({
|
|
2839
|
+
customModelId: this._customModel?.modelId,
|
|
2840
|
+
reported: this._reportedModel,
|
|
2841
|
+
configModel: this._configModel,
|
|
2842
|
+
launchModel: launchModelFor(this.mode, this._launchOptionBag()),
|
|
2843
|
+
});
|
|
2844
|
+
}
|
|
2845
|
+
/**
|
|
2846
|
+
* The same option bag the spawn reads its launch params from: `model` at the top for
|
|
2847
|
+
* claude (the `--model` or app-wide default it was created with; inert for every other
|
|
2848
|
+
* CLI, which is why it is not handed over for them), each other CLI's own
|
|
2849
|
+
* `<Mode>Config`. Where a param lives is registry data (`legacyConfigForMode`).
|
|
2850
|
+
*/
|
|
2851
|
+
_launchOptionBag() {
|
|
2852
|
+
return {
|
|
2853
|
+
model: cliTakesSessionModel(this.mode) ? this._model : undefined,
|
|
2854
|
+
openCodeConfig: this._openCodeConfig,
|
|
2855
|
+
codexConfig: this._codexConfig,
|
|
2856
|
+
geminiConfig: this._geminiConfig,
|
|
2857
|
+
antigravityConfig: this._antigravityConfig,
|
|
2858
|
+
piConfig: this._piConfig,
|
|
2859
|
+
grokConfig: this._grokConfig,
|
|
2860
|
+
deepSeekConfig: this._deepSeekConfig,
|
|
2861
|
+
ompConfig: this._ompConfig,
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
/**
|
|
2865
|
+
* Read the model this session's CLI config pins, with the reader its registry entry
|
|
2866
|
+
* names (`capabilities.modelDetect.configResolver`), and announce a change. Async and
|
|
2867
|
+
* bounded (the reader probes before it reads); a read that lands after a newer one,
|
|
2868
|
+
* or after the session stopped, is dropped. A remote or docker session's CLI reads its
|
|
2869
|
+
* config on another machine or in its container, so nothing local is read for it.
|
|
2870
|
+
*/
|
|
2871
|
+
_refreshConfigModel() {
|
|
2872
|
+
const name = getCli(this.mode)?.capabilities.modelDetect?.configResolver;
|
|
2873
|
+
if (!name || this._remote || this._docker)
|
|
2874
|
+
return;
|
|
2875
|
+
const gen = ++this._configModelGen;
|
|
2876
|
+
const overrides = this._envOverrides;
|
|
2877
|
+
resolveConfigModel(name, {
|
|
2878
|
+
config: legacyConfigForMode(this.mode, this._launchOptionBag()),
|
|
2879
|
+
// The session's own env first (already clamped for a non-granted owner), then the
|
|
2880
|
+
// server's: what the pane's CLI inherits.
|
|
2881
|
+
env: (key) => overrides?.[key] ?? process.env[key],
|
|
2882
|
+
}).then((model) => {
|
|
2883
|
+
if (gen !== this._configModelGen || this._isStopped)
|
|
2884
|
+
return;
|
|
2885
|
+
// Sanitized where it is published (resolveDisplayModel), like every source.
|
|
2886
|
+
const next = model || null;
|
|
2887
|
+
if (next === this._configModel)
|
|
2888
|
+
return;
|
|
2889
|
+
this._configModel = next;
|
|
2890
|
+
this.emit('displayModelChanged');
|
|
2891
|
+
}, () => {
|
|
2892
|
+
/* A reader answers null on doubt and never throws; a throw changes nothing. */
|
|
2893
|
+
});
|
|
2894
|
+
}
|
|
2712
2895
|
/**
|
|
2713
2896
|
* Read the background-work chip off the same capture the working probe just took.
|
|
2714
2897
|
*
|
|
@@ -2843,15 +3026,77 @@ export class Session extends EventEmitter {
|
|
|
2843
3026
|
// 1. Claude was working and is now at prompt (normal case)
|
|
2844
3027
|
// 2. Session just started and is ready (status is 'busy' but _isWorking is false)
|
|
2845
3028
|
const wasWorking = this._isWorking;
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
3029
|
+
if (wasWorking || this._status === 'busy')
|
|
3030
|
+
this._concludeIdle(wasWorking);
|
|
3031
|
+
}
|
|
3032
|
+
/**
|
|
3033
|
+
* The one place a pane is concluded idle: status, working flag and prompt stamp
|
|
3034
|
+
* change together, and the change is ANNOUNCED. The `idle` event is what the web
|
|
3035
|
+
* server turns into `session:idle` plus a state broadcast, so a path that flips
|
|
3036
|
+
* `_status` without it leaves every browser on the `busy` it was last sent. A fresh
|
|
3037
|
+
* codex pane used to stay "working" in the UI for its whole life that way.
|
|
3038
|
+
*
|
|
3039
|
+
* @param turnEnded a real turn just finished (not a pane becoming ready at launch)
|
|
3040
|
+
*/
|
|
3041
|
+
_concludeIdle(turnEnded) {
|
|
3042
|
+
this._isWorking = false;
|
|
3043
|
+
this._status = 'idle';
|
|
3044
|
+
this._lastPromptTime = Date.now();
|
|
3045
|
+
// Only a finished turn proves omp has written its session file; a pane that is
|
|
3046
|
+
// merely ready has nothing to resolve yet and could claim a neighbour's file.
|
|
3047
|
+
if (turnEnded)
|
|
3048
|
+
this._maybeCaptureOmpSessionId();
|
|
3049
|
+
this.emit('idle');
|
|
3050
|
+
}
|
|
3051
|
+
/**
|
|
3052
|
+
* Arm the launch settle (`_settlePaneStartup`) for a pane `startInteractive()` just
|
|
3053
|
+
* started or re-attached, when one applies:
|
|
3054
|
+
* - a NEW pane of an external CLI, whose TUI has no ❯ for the Claude wait to find;
|
|
3055
|
+
* - a RESTORED pane (Codeman restart, auto-reattach, tile Attach) of a CLI that
|
|
3056
|
+
* declares no `capabilities.workDetect`. It is `busy` from `_resetBuffers()` like a
|
|
3057
|
+
* new pane, and with no composer glyph to arm `_confirmIdle()` nothing else would
|
|
3058
|
+
* ever settle it. A restored claude or codex pane is left to its glyph, which
|
|
3059
|
+
* reads the screen first, so a restart in mid-turn is never called idle.
|
|
3060
|
+
*/
|
|
3061
|
+
_armPaneSettle(isRestored) {
|
|
3062
|
+
const applies = isRestored ? !getCli(this.mode)?.capabilities.workDetect : isExternalCliMode(this.mode);
|
|
3063
|
+
if (!applies)
|
|
3064
|
+
return;
|
|
3065
|
+
const armedAt = Date.now();
|
|
3066
|
+
this._promptCheckTimeout = setTimeout(() => this._settlePaneStartup(!isRestored, armedAt), 3000);
|
|
3067
|
+
}
|
|
3068
|
+
/**
|
|
3069
|
+
* The launch settle: 3 s after a NEW external-CLI pane spawned, or after ANY pane of a
|
|
3070
|
+
* CLI without work detection was re-attached, its TUI is taken to have rendered. A pane
|
|
3071
|
+
* still in its spawn-time `busy` is concluded idle (announced, see `_concludeIdle`),
|
|
3072
|
+
* then, for a new pane, the browser is told to refetch the rendered screen.
|
|
3073
|
+
*
|
|
3074
|
+
* ⚠️ This used to set `_status = 'idle'` without an event. When the launch paint
|
|
3075
|
+
* never tripped `_markWorking()`, the later `_confirmIdle()` found the status
|
|
3076
|
+
* already idle and emitted nothing, so no browser ever learned the pane was ready.
|
|
3077
|
+
*
|
|
3078
|
+
* A pane is left to `_confirmIdle()` only when its CLI declares
|
|
3079
|
+
* `capabilities.workDetect` (its composer glyph arms that confirmation, which reads
|
|
3080
|
+
* the screen first) AND it is already working, or a prompt was submitted since the
|
|
3081
|
+
* timer was armed: a turn started 2.9 s in is not marked working before the deferred
|
|
3082
|
+
* parsers run, and an idle edge here would end a send-and-wait registered for it.
|
|
3083
|
+
* For every other CLI this timer is the only thing that ever settles a fresh pane,
|
|
3084
|
+
* so it settles it even if a stray spinner glyph in the launch paint latched
|
|
3085
|
+
* `_isWorking`.
|
|
3086
|
+
*
|
|
3087
|
+
* @param refreshScreen emit `needsRefresh` (a new pane; an attach refetches by itself)
|
|
3088
|
+
* @param armedAt when the timer was armed (a submit at or after it means a prompt)
|
|
3089
|
+
*/
|
|
3090
|
+
_settlePaneStartup(refreshScreen, armedAt) {
|
|
3091
|
+
this._promptCheckTimeout = null;
|
|
3092
|
+
if (this._isStopped)
|
|
3093
|
+
return;
|
|
3094
|
+
const busyWithTurn = this._isWorking || this.lastSubmitAt >= armedAt;
|
|
3095
|
+
const leaveToConfirm = busyWithTurn && !!getCli(this.mode)?.capabilities.workDetect;
|
|
3096
|
+
if (this._status === 'busy' && !leaveToConfirm)
|
|
3097
|
+
this._concludeIdle(false);
|
|
3098
|
+
if (refreshScreen)
|
|
3099
|
+
this.emit('needsRefresh');
|
|
2855
3100
|
}
|
|
2856
3101
|
/**
|
|
2857
3102
|
* A brand-new omp session (never yet respawned, so
|
|
@@ -3736,7 +3981,7 @@ export class Session extends EventEmitter {
|
|
|
3736
3981
|
: this._mux.capturePaneText?.(this._muxSession.muxName),
|
|
3737
3982
|
sendEnter: () => this._mux?.sendInput(this.id, '\r'),
|
|
3738
3983
|
// ⚠ NO fallback glyph here, unlike the screen-reading probe elsewhere in this file.
|
|
3739
|
-
// Only claude and
|
|
3984
|
+
// Only claude, codex, pi, opencode, omp and gemini declare a promptGlyph; the other modes would fall back
|
|
3740
3985
|
// to claude's `❯`, which is ALSO starship's default shell prompt (and pure's, and
|
|
3741
3986
|
// spaceship's, and p10k lean's). On a shell session the line `❯ npm run build` sits
|
|
3742
3987
|
// on screen for as long as the command runs, promptStillInComposer() reads that as
|