botmux 2.110.0 → 2.111.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/dist/adapters/backend/tmux-backend.d.ts +25 -5
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +59 -93
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +5 -0
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/zellij-backend.d.ts +30 -0
- package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zellij-backend.js +80 -13
- package/dist/adapters/backend/zellij-backend.js.map +1 -1
- package/dist/adapters/backend/zellij-socket-probe.d.ts +18 -0
- package/dist/adapters/backend/zellij-socket-probe.d.ts.map +1 -0
- package/dist/adapters/backend/zellij-socket-probe.js +146 -0
- package/dist/adapters/backend/zellij-socket-probe.js.map +1 -0
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +5 -0
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/codex-app-turn.d.ts +43 -0
- package/dist/adapters/cli/codex-app-turn.d.ts.map +1 -0
- package/dist/adapters/cli/codex-app-turn.js +110 -0
- package/dist/adapters/cli/codex-app-turn.js.map +1 -0
- package/dist/adapters/cli/codex-app.d.ts.map +1 -1
- package/dist/adapters/cli/codex-app.js +6 -0
- package/dist/adapters/cli/codex-app.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +1 -0
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/runner-input.d.ts +3 -2
- package/dist/adapters/cli/runner-input.d.ts.map +1 -1
- package/dist/adapters/cli/runner-input.js +7 -4
- package/dist/adapters/cli/runner-input.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +16 -0
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +5 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +1 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/codex-app-runner.js +75 -12
- package/dist/codex-app-runner.js.map +1 -1
- package/dist/core/command-handler.d.ts +11 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +101 -7
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +69 -11
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/doc-comment-prompt.d.ts +57 -0
- package/dist/core/doc-comment-prompt.d.ts.map +1 -1
- package/dist/core/doc-comment-prompt.js +147 -2
- package/dist/core/doc-comment-prompt.js.map +1 -1
- package/dist/core/inflight-input-tracker.d.ts +2 -0
- package/dist/core/inflight-input-tracker.d.ts.map +1 -1
- package/dist/core/inflight-input-tracker.js.map +1 -1
- package/dist/core/passthrough-commands.d.ts.map +1 -1
- package/dist/core/passthrough-commands.js +1 -1
- package/dist/core/passthrough-commands.js.map +1 -1
- package/dist/core/persistent-backend.d.ts +4 -0
- package/dist/core/persistent-backend.d.ts.map +1 -1
- package/dist/core/persistent-backend.js +6 -0
- package/dist/core/persistent-backend.js.map +1 -1
- package/dist/core/plugins/cli-generation.d.ts +3 -0
- package/dist/core/plugins/cli-generation.d.ts.map +1 -1
- package/dist/core/plugins/cli-generation.js +1 -0
- package/dist/core/plugins/cli-generation.js.map +1 -1
- package/dist/core/session-board.d.ts +2 -1
- package/dist/core/session-board.d.ts.map +1 -1
- package/dist/core/session-board.js +3 -2
- package/dist/core/session-board.js.map +1 -1
- package/dist/core/session-create.d.ts +38 -0
- package/dist/core/session-create.d.ts.map +1 -1
- package/dist/core/session-create.js +43 -9
- package/dist/core/session-create.js.map +1 -1
- package/dist/core/session-manager.d.ts +65 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +285 -68
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-rename.d.ts +25 -0
- package/dist/core/session-rename.d.ts.map +1 -0
- package/dist/core/session-rename.js +43 -0
- package/dist/core/session-rename.js.map +1 -0
- package/dist/core/session-title.d.ts +11 -0
- package/dist/core/session-title.d.ts.map +1 -0
- package/dist/core/session-title.js +17 -0
- package/dist/core/session-title.js.map +1 -0
- package/dist/core/trigger-session.d.ts +11 -0
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +61 -20
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +16 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +6 -2
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +48 -15
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/core/zellij-adopt-discovery.d.ts +17 -0
- package/dist/core/zellij-adopt-discovery.d.ts.map +1 -1
- package/dist/core/zellij-adopt-discovery.js +24 -4
- package/dist/core/zellij-adopt-discovery.js.map +1 -1
- package/dist/core/zellij-session-discovery.d.ts +3 -0
- package/dist/core/zellij-session-discovery.d.ts.map +1 -1
- package/dist/core/zellij-session-discovery.js +1 -0
- package/dist/core/zellij-session-discovery.js.map +1 -1
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +181 -81
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +1 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +1 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/federated-group-core.d.ts +29 -8
- package/dist/dashboard/federated-group-core.d.ts.map +1 -1
- package/dist/dashboard/federated-group-core.js +68 -6
- package/dist/dashboard/federated-group-core.js.map +1 -1
- package/dist/dashboard/federation-api.d.ts +10 -9
- package/dist/dashboard/federation-api.d.ts.map +1 -1
- package/dist/dashboard/federation-api.js +82 -3
- package/dist/dashboard/federation-api.js.map +1 -1
- package/dist/dashboard/federation-spoke-api.d.ts +8 -9
- package/dist/dashboard/federation-spoke-api.d.ts.map +1 -1
- package/dist/dashboard/federation-spoke-api.js +7 -2
- package/dist/dashboard/federation-spoke-api.js.map +1 -1
- package/dist/dashboard/team-group.d.ts +21 -0
- package/dist/dashboard/team-group.d.ts.map +1 -1
- package/dist/dashboard/team-group.js +16 -0
- package/dist/dashboard/team-group.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +10 -0
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +33 -1
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +1 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +8 -0
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +5 -1
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/ui.d.ts.map +1 -1
- package/dist/dashboard/web/ui.js +2 -4
- package/dist/dashboard/web/ui.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-7T7DPOV6.js +4 -0
- package/dist/dashboard-web/chunks/{chunk-SHZ5PUYF.js → chunk-IGXYYRYJ.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-GF7DU2B5.js → chunk-ISOD7VDX.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-TORH53FI.js → chunk-OEJIT66L.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-7UY333A5.js → chunk-PNK3HQBI.js} +2 -2
- package/dist/dashboard-web/chunks/{chunk-IAES47WV.js → chunk-YQFXGRSD.js} +1 -1
- package/dist/dashboard-web/chunks/{connectors-page-N6GKR6JL.js → connectors-page-UQ4QBE45.js} +1 -1
- package/dist/dashboard-web/chunks/{groups-page-IK3GXSQU.js → groups-page-5AJPPJH2.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-T2PDFPGO.js → insights-page-R3DFECX4.js} +1 -1
- package/dist/dashboard-web/chunks/{legacy-workflow-page-YQPLKH74.js → legacy-workflow-page-QMC7OZXH.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-7EL3LVJS.js → monitor-room-NLMKJLDY.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-BCADF56L.js → monitoring-page-EHSOX4GV.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-KP7BDLAE.js → office-page-OXJKFJ4R.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-6GWDHDTI.js → overview-page-GJHIVXJF.js} +1 -1
- package/dist/dashboard-web/chunks/{roles-page-2OFR2IBG.js → roles-page-6ZODQISW.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-TAWMTSNL.js → schedules-page-DAGMHVCH.js} +1 -1
- package/dist/dashboard-web/chunks/{sessions-page-42QZHPPP.js → sessions-page-NNZIDHZM.js} +1 -1
- package/dist/dashboard-web/chunks/{settings-page-OVKXUXSV.js → settings-page-LJIXX5LQ.js} +1 -1
- package/dist/dashboard-web/chunks/{skills-page-Y2EC3NBI.js → skills-page-J7EVUHXP.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-QYMJUH5F.js → team-federation-page-YM5JBIII.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-FZJCW5QG.js → v3-page-YLYVHGKD.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-FBHBBCAL.js → whiteboards-page-75N2U6W4.js} +1 -1
- package/dist/dashboard.js +46 -7
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +8 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +8 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +96 -32
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +16 -5
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/services/bot-config-store.d.ts.map +1 -1
- package/dist/services/bot-config-store.js +1 -0
- package/dist/services/bot-config-store.js.map +1 -1
- package/dist/services/card-prefs-store.d.ts +4 -0
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +8 -0
- package/dist/services/card-prefs-store.js.map +1 -1
- package/dist/services/default-worktree.d.ts.map +1 -1
- package/dist/services/default-worktree.js +4 -1
- package/dist/services/default-worktree.js.map +1 -1
- package/dist/services/group-creator.d.ts +19 -0
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +62 -28
- package/dist/services/group-creator.js.map +1 -1
- package/dist/services/groups-store.d.ts +7 -5
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +10 -8
- package/dist/services/groups-store.js.map +1 -1
- package/dist/setup/ensure-tmux.d.ts +17 -0
- package/dist/setup/ensure-tmux.d.ts.map +1 -1
- package/dist/setup/ensure-tmux.js +72 -1
- package/dist/setup/ensure-tmux.js.map +1 -1
- package/dist/types.d.ts +56 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/child-env.d.ts +27 -0
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +59 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/utils/codex-app-context.d.ts +9 -0
- package/dist/utils/codex-app-context.d.ts.map +1 -0
- package/dist/utils/codex-app-context.js +47 -0
- package/dist/utils/codex-app-context.js.map +1 -0
- package/dist/utils/pending-input-queue.d.ts +2 -0
- package/dist/utils/pending-input-queue.d.ts.map +1 -1
- package/dist/utils/pending-input-queue.js +6 -0
- package/dist/utils/pending-input-queue.js.map +1 -1
- package/dist/worker.js +197 -38
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-QXZ2VFJJ.js +0 -4
package/dist/worker.js
CHANGED
|
@@ -98,6 +98,7 @@ import * as pty from 'node-pty';
|
|
|
98
98
|
import { createHash } from 'node:crypto';
|
|
99
99
|
import { installHook } from './adapters/hook-installer.js';
|
|
100
100
|
import { hookCommandFor } from './adapters/hook-command.js';
|
|
101
|
+
import { withCodexAppContext } from './utils/codex-app-context.js';
|
|
101
102
|
// ─── State ───────────────────────────────────────────────────────────────────
|
|
102
103
|
let cliAdapter = null;
|
|
103
104
|
let backend = null;
|
|
@@ -154,6 +155,9 @@ function refreshCliPluginGeneration(cfg, adapter) {
|
|
|
154
155
|
throw new Error(t('worker.skill_delivery_failed', { reason }));
|
|
155
156
|
}
|
|
156
157
|
cfg.prompt = generation.prompt;
|
|
158
|
+
if (cfg.promptCodexAppInput && generation.skillCatalog) {
|
|
159
|
+
cfg.promptCodexAppInput = withCodexAppContext(cfg.promptCodexAppInput, 'botmux_plugin_skills', generation.skillCatalog, 'application');
|
|
160
|
+
}
|
|
157
161
|
cfg.skillPluginDir = generation.skillPluginDir;
|
|
158
162
|
cfg.skillReadonlyRoots = generation.skillReadonlyRoots;
|
|
159
163
|
deferredPluginSkillCatalog = generation.deferredSkillCatalog ?? null;
|
|
@@ -554,6 +558,26 @@ async function sendRawCommandLine(be, content) {
|
|
|
554
558
|
be.write('\r');
|
|
555
559
|
}
|
|
556
560
|
}
|
|
561
|
+
/** Serialize only the literal command-line write window (text -> beat -> Enter).
|
|
562
|
+
* raw_input deliberately keeps its legacy "send while busy" behaviour; this
|
|
563
|
+
* narrow mutex merely prevents concurrent IPC handlers (or native /rename)
|
|
564
|
+
* from splicing keystrokes into one another. */
|
|
565
|
+
let commandLineWriteTail = Promise.resolve();
|
|
566
|
+
let commandLineWritesPending = 0;
|
|
567
|
+
async function sendRawCommandLineSerially(be, content) {
|
|
568
|
+
const previous = commandLineWriteTail;
|
|
569
|
+
let release;
|
|
570
|
+
commandLineWriteTail = new Promise(resolve => { release = resolve; });
|
|
571
|
+
commandLineWritesPending += 1;
|
|
572
|
+
await previous;
|
|
573
|
+
try {
|
|
574
|
+
await sendRawCommandLine(be, content);
|
|
575
|
+
}
|
|
576
|
+
finally {
|
|
577
|
+
commandLineWritesPending -= 1;
|
|
578
|
+
release();
|
|
579
|
+
}
|
|
580
|
+
}
|
|
557
581
|
/** Resolve once the PTY has been quiet for `quietMs`, or after `capMs` total.
|
|
558
582
|
* Spaces out startup commands so each is processed before the next is typed. */
|
|
559
583
|
function awaitPtyQuiescence(quietMs, capMs) {
|
|
@@ -597,7 +621,7 @@ async function runStartupCommands() {
|
|
|
597
621
|
if (!backend)
|
|
598
622
|
break;
|
|
599
623
|
try {
|
|
600
|
-
await
|
|
624
|
+
await sendRawCommandLineSerially(backend, cmd);
|
|
601
625
|
await awaitPtyQuiescence(STARTUP_CMD_QUIET_MS, STARTUP_CMD_CAP_MS);
|
|
602
626
|
log(`Startup command sent: ${cmd}`);
|
|
603
627
|
}
|
|
@@ -610,6 +634,85 @@ async function runStartupCommands() {
|
|
|
610
634
|
idleDetector?.reset();
|
|
611
635
|
}
|
|
612
636
|
const pendingMessages = [];
|
|
637
|
+
/** Literal commands that arrived while native /rename owned the TUI. Normal
|
|
638
|
+
* raw_input commands are still delivered immediately (including while busy). */
|
|
639
|
+
const pendingRawInputs = [];
|
|
640
|
+
/** Latest requested canonical session title. Unlike a normal prompt this is an
|
|
641
|
+
* administrative TUI command: never type-ahead while the agent is busy, never
|
|
642
|
+
* open a model turn, and latest-wins if several renames arrive before idle. */
|
|
643
|
+
let pendingSessionRename = null;
|
|
644
|
+
/** True after the rename command's Enter lands and until the TUI returns to its
|
|
645
|
+
* prompt. Blocks type-ahead user messages from racing into the command UI. */
|
|
646
|
+
let sessionRenameInFlight = false;
|
|
647
|
+
const SESSION_RENAME_IDLE_TIMEOUT_MS = 5_000;
|
|
648
|
+
let sessionRenameIdleTimer = null;
|
|
649
|
+
function clearSessionRenameInFlight() {
|
|
650
|
+
if (sessionRenameIdleTimer) {
|
|
651
|
+
clearTimeout(sessionRenameIdleTimer);
|
|
652
|
+
sessionRenameIdleTimer = null;
|
|
653
|
+
}
|
|
654
|
+
sessionRenameInFlight = false;
|
|
655
|
+
}
|
|
656
|
+
/** Fail open if a CLI executes /rename without emitting enough redraw output
|
|
657
|
+
* for IdleDetector to rediscover the prompt. The verified Codex/Claude paths
|
|
658
|
+
* normally settle immediately; this cap prevents one administrative command
|
|
659
|
+
* from wedging all later Lark messages forever. */
|
|
660
|
+
function armSessionRenameIdleTimeout() {
|
|
661
|
+
if (!sessionRenameInFlight)
|
|
662
|
+
return;
|
|
663
|
+
if (sessionRenameIdleTimer)
|
|
664
|
+
clearTimeout(sessionRenameIdleTimer);
|
|
665
|
+
sessionRenameIdleTimer = setTimeout(() => {
|
|
666
|
+
sessionRenameIdleTimer = null;
|
|
667
|
+
if (!sessionRenameInFlight)
|
|
668
|
+
return;
|
|
669
|
+
sessionRenameInFlight = false;
|
|
670
|
+
// The timeout specifically means prompt redraw detection failed. Fail open
|
|
671
|
+
// by restoring the gate explicitly; otherwise a deferred raw_input is the
|
|
672
|
+
// only queued work and flushPending would keep waiting for the very signal
|
|
673
|
+
// this timer exists to replace.
|
|
674
|
+
isPromptReady = true;
|
|
675
|
+
log(`Native session rename idle timeout after ${SESSION_RENAME_IDLE_TIMEOUT_MS}ms; releasing queued input`);
|
|
676
|
+
void flushPending();
|
|
677
|
+
}, SESSION_RENAME_IDLE_TIMEOUT_MS);
|
|
678
|
+
sessionRenameIdleTimer.unref?.();
|
|
679
|
+
}
|
|
680
|
+
/** Deliver passthrough exactly as before: it may be injected while the CLI is
|
|
681
|
+
* busy (notably Codex /btw). Only the short keystroke sequence is serialized.
|
|
682
|
+
* Commands received while /rename owns the TUI are deferred by the IPC handler
|
|
683
|
+
* and come through this same function after the prompt returns. */
|
|
684
|
+
async function deliverRawInput(msg) {
|
|
685
|
+
renderer?.markNewTurn();
|
|
686
|
+
usageLimitTracker.beginTurn(currentUsageLimitSnapshot());
|
|
687
|
+
if (tmuxScrolledHalfPages > 0)
|
|
688
|
+
exitTmuxScrollMode();
|
|
689
|
+
const targetBackend = backend;
|
|
690
|
+
if (!targetBackend)
|
|
691
|
+
return;
|
|
692
|
+
let sent = false;
|
|
693
|
+
try {
|
|
694
|
+
await sendRawCommandLineSerially(targetBackend, msg.content);
|
|
695
|
+
sent = true;
|
|
696
|
+
isPromptReady = false;
|
|
697
|
+
idleDetector?.reset();
|
|
698
|
+
log(`Passthrough slash command: ${msg.content}`);
|
|
699
|
+
}
|
|
700
|
+
catch (err) {
|
|
701
|
+
// Do not send another queued command against a backend whose write failed.
|
|
702
|
+
isPromptReady = false;
|
|
703
|
+
log(`Passthrough slash command failed (${msg.content}): ${err?.message ?? err}`);
|
|
704
|
+
}
|
|
705
|
+
// Follow-up rides on the same IPC and is enqueued only after this command's
|
|
706
|
+
// Enter lands. sendToPty also observes commandLineWritesPending, so another
|
|
707
|
+
// raw command's text -> Enter window cannot be interrupted by the follow-up.
|
|
708
|
+
if (sent && msg.followUpContent) {
|
|
709
|
+
sendToPty(msg.followUpContent, undefined, msg.followUpCodexAppInput);
|
|
710
|
+
log(`Enqueued follow-up after raw input (${msg.followUpContent.length} chars)`);
|
|
711
|
+
}
|
|
712
|
+
// A pending /rename may have been held by the command-write mutex. It still
|
|
713
|
+
// waits for a genuine prompt because isPromptReady was cleared above.
|
|
714
|
+
void flushPending();
|
|
715
|
+
}
|
|
613
716
|
/** Inputs written to the CLI whose turn hasn't completed — re-queued across a
|
|
614
717
|
* CLI crash so a submit-time death can't silently eat user messages. */
|
|
615
718
|
const inflightInputs = new InflightInputTracker();
|
|
@@ -3500,6 +3603,7 @@ function markPromptReady() {
|
|
|
3500
3603
|
return;
|
|
3501
3604
|
}
|
|
3502
3605
|
isPromptReady = true;
|
|
3606
|
+
clearSessionRenameInFlight();
|
|
3503
3607
|
// CLI 实际启动成功(回到 prompt):复位连续重启计数。
|
|
3504
3608
|
// 任何能到这一步的 spawn 都算"成功"——后续即便再崩溃(不是 resume 目标不存在
|
|
3505
3609
|
// 的问题),下一轮也该有新的 2 次重试预算,而不是被历史重启计数卡住。
|
|
@@ -3522,7 +3626,7 @@ function markPromptReady() {
|
|
|
3522
3626
|
// make the CLI busy, so the idle state is transient and shouldn't appear
|
|
3523
3627
|
// in the card. This avoids a false "就绪" flash on daemon restart
|
|
3524
3628
|
// (where the initial prompt is queued before the CLI becomes idle).
|
|
3525
|
-
if (renderer && pendingMessages.length === 0 && !isFlushing) {
|
|
3629
|
+
if (renderer && pendingMessages.length === 0 && pendingRawInputs.length === 0 && pendingSessionRename === null && !isFlushing) {
|
|
3526
3630
|
const { content } = renderer.snapshot();
|
|
3527
3631
|
send({ type: 'screen_update', content, ...usageLimitTracker.classify(content, 'idle'), turnId: currentBotmuxTurnId });
|
|
3528
3632
|
}
|
|
@@ -3766,8 +3870,12 @@ async function flushPending() {
|
|
|
3766
3870
|
return; // while loop in active flush will pick up new messages
|
|
3767
3871
|
if (!backend || !cliAdapter)
|
|
3768
3872
|
return;
|
|
3769
|
-
if (pendingMessages.length === 0)
|
|
3873
|
+
if (pendingMessages.length === 0 && pendingRawInputs.length === 0 && pendingSessionRename === null)
|
|
3770
3874
|
return; // nothing to flush — keep isPromptReady
|
|
3875
|
+
if (sessionRenameInFlight)
|
|
3876
|
+
return; // wait for /rename to finish before any user input
|
|
3877
|
+
if (commandLineWritesPending > 0)
|
|
3878
|
+
return; // do not splice into text -> Enter
|
|
3771
3879
|
if (bareShellLaunchBlocked)
|
|
3772
3880
|
return; // launch failed into a bare shell — don't type prompts into it
|
|
3773
3881
|
// Ready-gate: hold the FIRST prompt until the SessionStart hook fires a true-
|
|
@@ -3811,6 +3919,21 @@ async function flushPending() {
|
|
|
3811
3919
|
const claudeBridgeActive = !!bridgeJsonlPath && !lastInitConfig?.adoptMode;
|
|
3812
3920
|
const codexBridgeActive = codexBridgeFallbackActive();
|
|
3813
3921
|
const typeAheadAllowed = cliAdapter.supportsTypeAhead;
|
|
3922
|
+
// Native /rename is an administrative command, not a steer/queued model
|
|
3923
|
+
// message. It must wait for a real prompt even on type-ahead CLIs. Normal
|
|
3924
|
+
// pending messages can still drain while busy; the rename stays queued.
|
|
3925
|
+
const sessionRenameReady = isPromptReady && pendingSessionRename !== null;
|
|
3926
|
+
const rawInputReady = isPromptReady && pendingRawInputs.length > 0;
|
|
3927
|
+
let supportedSessionRenameReady = sessionRenameReady;
|
|
3928
|
+
if (sessionRenameReady && (!cliAdapter.buildSessionRenameCommand || effectiveBackendType === 'riff')) {
|
|
3929
|
+
pendingSessionRename = null;
|
|
3930
|
+
supportedSessionRenameReady = false;
|
|
3931
|
+
log(`Ignoring native session rename — unsupported by ${cliName()}${effectiveBackendType === 'riff' ? ' on riff backend' : ''}`);
|
|
3932
|
+
if (pendingMessages.length === 0 && pendingRawInputs.length === 0)
|
|
3933
|
+
return;
|
|
3934
|
+
}
|
|
3935
|
+
if (!isPromptReady && pendingMessages.length === 0)
|
|
3936
|
+
return;
|
|
3814
3937
|
if (!isPromptReady && !typeAheadAllowed)
|
|
3815
3938
|
return;
|
|
3816
3939
|
isFlushing = true;
|
|
@@ -3844,6 +3967,38 @@ async function flushPending() {
|
|
|
3844
3967
|
hasRunStartupCommands = true;
|
|
3845
3968
|
await runStartupCommands();
|
|
3846
3969
|
}
|
|
3970
|
+
// Commands deferred behind a previous rename run before the latest pending
|
|
3971
|
+
// rename. Some passthroughs (/clear, /new) can rotate the native session;
|
|
3972
|
+
// applying the canonical title last keeps the resume-picker label aligned.
|
|
3973
|
+
if (rawInputReady && pendingRawInputs.length > 0 && backend) {
|
|
3974
|
+
const raw = pendingRawInputs.shift();
|
|
3975
|
+
await deliverRawInput(raw);
|
|
3976
|
+
return;
|
|
3977
|
+
}
|
|
3978
|
+
if (supportedSessionRenameReady && pendingSessionRename !== null && backend && cliAdapter) {
|
|
3979
|
+
const title = pendingSessionRename;
|
|
3980
|
+
const buildRename = cliAdapter.buildSessionRenameCommand;
|
|
3981
|
+
pendingSessionRename = null;
|
|
3982
|
+
sessionRenameInFlight = true;
|
|
3983
|
+
try {
|
|
3984
|
+
await sendRawCommandLineSerially(backend, buildRename(title));
|
|
3985
|
+
armSessionRenameIdleTimeout();
|
|
3986
|
+
idleDetector?.reset();
|
|
3987
|
+
log(`Native session rename command sent (${cliName()}): ${title}`);
|
|
3988
|
+
}
|
|
3989
|
+
catch (err) {
|
|
3990
|
+
// Local title persistence is authoritative; native sync is best-effort.
|
|
3991
|
+
// Do not blindly replay a partially typed command after a backend error.
|
|
3992
|
+
// Keep the rename gate for the same bounded fail-open window: the write
|
|
3993
|
+
// may have stopped after typing only part of the command, so immediately
|
|
3994
|
+
// appending a deferred raw_input could corrupt both commands.
|
|
3995
|
+
armSessionRenameIdleTimeout();
|
|
3996
|
+
log(`Native session rename command failed (${cliName()}): ${err?.message ?? err}; waiting for prompt or fail-open timeout`);
|
|
3997
|
+
}
|
|
3998
|
+
// Wait for the command to finish and the TUI to become idle again before
|
|
3999
|
+
// sending queued user prompts; otherwise they can land in its picker.
|
|
4000
|
+
return;
|
|
4001
|
+
}
|
|
3847
4002
|
while (pendingMessages.length > 0 && backend && cliAdapter) {
|
|
3848
4003
|
const item = pendingMessages.shift();
|
|
3849
4004
|
// Track as in-flight until the CLI returns to idle (markPromptReady).
|
|
@@ -3884,7 +4039,9 @@ async function flushPending() {
|
|
|
3884
4039
|
// worker — exactly the failure mode this change is closing. Contain it.
|
|
3885
4040
|
let result;
|
|
3886
4041
|
try {
|
|
3887
|
-
result =
|
|
4042
|
+
result = item.codexAppInput && cliAdapter.writeStructuredInput
|
|
4043
|
+
? await cliAdapter.writeStructuredInput(backend, msg, item.codexAppInput)
|
|
4044
|
+
: await cliAdapter.writeInput(backend, msg);
|
|
3888
4045
|
scheduleBusyPatternIdleProbe(`${cliName()} post-submit`);
|
|
3889
4046
|
}
|
|
3890
4047
|
catch (err) {
|
|
@@ -3929,10 +4086,10 @@ async function flushPending() {
|
|
|
3929
4086
|
isFlushing = false;
|
|
3930
4087
|
}
|
|
3931
4088
|
}
|
|
3932
|
-
function sendToPty(content, turnId) {
|
|
4089
|
+
function sendToPty(content, turnId, codexAppInput) {
|
|
3933
4090
|
if (!backend || !cliAdapter)
|
|
3934
4091
|
return;
|
|
3935
|
-
const next = { content, turnId };
|
|
4092
|
+
const next = { content, turnId, codexAppInput };
|
|
3936
4093
|
const shouldMergeQueued = !isFlushing && !shouldWriteNow({
|
|
3937
4094
|
isPromptReady,
|
|
3938
4095
|
isFlushing,
|
|
@@ -3969,7 +4126,7 @@ function sendToPty(content, turnId) {
|
|
|
3969
4126
|
// type-ahead write is dropped (no input box yet) — markPromptReady()'s flush
|
|
3970
4127
|
// delivers queued messages instead. See input-gate.ts; this fixes dispatch's
|
|
3971
4128
|
// brief reaching Codex before its first idle and never landing.
|
|
3972
|
-
if (shouldWriteNow({ isPromptReady, isFlushing, supportsTypeAhead: cliAdapter.supportsTypeAhead === true, awaitingFirstPrompt })) {
|
|
4129
|
+
if (!sessionRenameInFlight && commandLineWritesPending === 0 && shouldWriteNow({ isPromptReady, isFlushing, supportsTypeAhead: cliAdapter.supportsTypeAhead === true, awaitingFirstPrompt })) {
|
|
3973
4130
|
if (!mergedQueued)
|
|
3974
4131
|
log(`Writing to PTY: "${content.substring(0, 80)}"`);
|
|
3975
4132
|
flushPending(); // fire-and-forget async; no-op if already flushing
|
|
@@ -4345,6 +4502,7 @@ function scheduleBusyPatternIdleProbe(source) {
|
|
|
4345
4502
|
busyPatternIdleProbeTimer.unref?.();
|
|
4346
4503
|
}
|
|
4347
4504
|
function spawnCli(cfg) {
|
|
4505
|
+
clearSessionRenameInFlight();
|
|
4348
4506
|
// (startupCommands one-shot is re-armed below, AFTER the reattach-vs-fresh
|
|
4349
4507
|
// prediction — only a genuinely fresh CLI process replays them; see
|
|
4350
4508
|
// willReattachPersistent.)
|
|
@@ -5850,7 +6008,11 @@ function killCli() {
|
|
|
5850
6008
|
sandboxCleanup = null;
|
|
5851
6009
|
}
|
|
5852
6010
|
isPromptReady = false;
|
|
6011
|
+
clearSessionRenameInFlight();
|
|
5853
6012
|
pendingMessages.length = 0;
|
|
6013
|
+
// pendingRawInputs contains only commands that were accepted but never typed
|
|
6014
|
+
// because /rename owned the TUI. Preserve them across restart; unlike an
|
|
6015
|
+
// in-flight raw command, replaying these cannot duplicate a side effect.
|
|
5854
6016
|
scrollback = '';
|
|
5855
6017
|
altBufferActive = false;
|
|
5856
6018
|
trustHandled = false;
|
|
@@ -6960,7 +7122,11 @@ process.on('message', async (raw) => {
|
|
|
6960
7122
|
codexBridgeMarkPendingTurn(msg.prompt, msg.turnId);
|
|
6961
7123
|
}
|
|
6962
7124
|
if (msg.prompt && (!cliAdapter?.passesInitialPromptViaArgs || deferInitialPrompt)) {
|
|
6963
|
-
pendingMessages.push({
|
|
7125
|
+
pendingMessages.push({
|
|
7126
|
+
content: msg.prompt,
|
|
7127
|
+
turnId: msg.turnId,
|
|
7128
|
+
codexAppInput: msg.promptCodexAppInput,
|
|
7129
|
+
});
|
|
6964
7130
|
}
|
|
6965
7131
|
// Riff (remote HTTP backends): spawnCli already marked the prompt ready
|
|
6966
7132
|
// (no local boot → isPromptReady=true immediately), but the initial prompt
|
|
@@ -6986,8 +7152,12 @@ process.on('message', async (raw) => {
|
|
|
6986
7152
|
if (tmuxScrolledHalfPages > 0)
|
|
6987
7153
|
exitTmuxScrollMode();
|
|
6988
7154
|
let content = msg.content;
|
|
7155
|
+
let codexAppInput = msg.codexAppInput;
|
|
6989
7156
|
if (deferredPluginSkillCatalog && !lastInitConfig?.adoptMode) {
|
|
6990
7157
|
content = `${content}\n\n${deferredPluginSkillCatalog}`;
|
|
7158
|
+
if (codexAppInput) {
|
|
7159
|
+
codexAppInput = withCodexAppContext(codexAppInput, 'botmux_plugin_skills', deferredPluginSkillCatalog, 'application');
|
|
7160
|
+
}
|
|
6991
7161
|
deferredPluginSkillCatalog = null;
|
|
6992
7162
|
log('Attached refreshed plugin Skill catalog to the first turn of this CLI generation');
|
|
6993
7163
|
}
|
|
@@ -7103,43 +7273,32 @@ process.on('message', async (raw) => {
|
|
|
7103
7273
|
// arrival. Marking now would race with a still-running previous
|
|
7104
7274
|
// turn whose `botmux send` could sneak its sentAtMs past this
|
|
7105
7275
|
// turn's markTimeMs and falsely suppress its fallback.
|
|
7106
|
-
sendToPty(content, msg.turnId);
|
|
7276
|
+
sendToPty(content, msg.turnId, codexAppInput);
|
|
7107
7277
|
}
|
|
7108
7278
|
break;
|
|
7109
7279
|
}
|
|
7110
7280
|
case 'raw_input': {
|
|
7111
|
-
//
|
|
7112
|
-
//
|
|
7113
|
-
//
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
if (backend) {
|
|
7121
|
-
// sendRawCommandLine: literal text → 200ms beat (so the CLI's slash-
|
|
7122
|
-
// command picker registers the match before submit; without it Codex /
|
|
7123
|
-
// other Ink TUIs fire Enter while the picker is still building, dismiss
|
|
7124
|
-
// the match, and submit the literal `/clear` as a regular user prompt —
|
|
7125
|
-
// visible to the user as "/clear + 换行" stuck in history; the 200ms
|
|
7126
|
-
// mirrors the codex adapter's own writeInput paste-detection delay) →
|
|
7127
|
-
// Enter. Shared with runStartupCommands so both stay in lockstep.
|
|
7128
|
-
await sendRawCommandLine(backend, msg.content);
|
|
7129
|
-
isPromptReady = false;
|
|
7130
|
-
idleDetector?.reset();
|
|
7131
|
-
log(`Passthrough slash command: ${msg.content}`);
|
|
7132
|
-
// Follow-up rides on the SAME IPC (see DaemonToWorker.raw_input) so it
|
|
7133
|
-
// cannot race the 200ms text→Enter window above. Enqueue only after the
|
|
7134
|
-
// Enter landed: sendToPty queues it as the next turn (type-ahead /
|
|
7135
|
-
// pendingMessages), exactly like a Lark message arriving while busy.
|
|
7136
|
-
if (msg.followUpContent) {
|
|
7137
|
-
sendToPty(msg.followUpContent);
|
|
7138
|
-
log(`Enqueued follow-up after raw input (${msg.followUpContent.length} chars)`);
|
|
7139
|
-
}
|
|
7281
|
+
// Preserve legacy busy delivery (/btw and other steering commands). A
|
|
7282
|
+
// native /rename already submitted to the TUI is the sole exception: its
|
|
7283
|
+
// command UI must return to the prompt before another slash command lands.
|
|
7284
|
+
if (sessionRenameInFlight) {
|
|
7285
|
+
pendingRawInputs.push(msg);
|
|
7286
|
+
log(`Deferred passthrough slash command until native rename settles: ${msg.content}`);
|
|
7287
|
+
}
|
|
7288
|
+
else {
|
|
7289
|
+
await deliverRawInput(msg);
|
|
7140
7290
|
}
|
|
7141
7291
|
break;
|
|
7142
7292
|
}
|
|
7293
|
+
case 'rename_session': {
|
|
7294
|
+
// IPC handlers are concurrent with async init, so queue first even when
|
|
7295
|
+
// the adapter/backend has not finished initializing. flushPending will
|
|
7296
|
+
// capability-check and deliver the latest title once a real prompt is idle.
|
|
7297
|
+
pendingSessionRename = msg.title;
|
|
7298
|
+
log(`Queued native session rename: ${msg.title}`);
|
|
7299
|
+
void flushPending();
|
|
7300
|
+
break;
|
|
7301
|
+
}
|
|
7143
7302
|
case 'park_diagnostic': {
|
|
7144
7303
|
// The daemon gave up auto-restarting (crash loop) and wants the last
|
|
7145
7304
|
// terminal output preserved. Park the diagnostic shell now — deferred from
|