botmux 2.94.0 → 2.96.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.en.md +4 -4
- package/README.md +3 -3
- package/dist/adapters/backend/tmux-backend.d.ts +18 -2
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +44 -11
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +8 -0
- package/dist/adapters/backend/types.d.ts.map +1 -1
- package/dist/adapters/backend/types.js.map +1 -1
- package/dist/adapters/backend/zellij-backend.js +1 -1
- package/dist/adapters/backend/zellij-backend.js.map +1 -1
- package/dist/adapters/cli/kimi.d.ts +4 -0
- package/dist/adapters/cli/kimi.d.ts.map +1 -0
- package/dist/adapters/cli/kimi.js +54 -0
- package/dist/adapters/cli/kimi.js.map +1 -0
- package/dist/adapters/cli/registry.d.ts +2 -1
- package/dist/adapters/cli/registry.d.ts.map +1 -1
- package/dist/adapters/cli/registry.js +6 -20
- package/dist/adapters/cli/registry.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +1 -1
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/adapters/hook-installer.d.ts.map +1 -1
- package/dist/adapters/hook-installer.js +138 -28
- package/dist/adapters/hook-installer.js.map +1 -1
- package/dist/bot-registry.d.ts +101 -4
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +192 -3
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/stdin-encoding.d.ts +21 -0
- package/dist/cli/stdin-encoding.d.ts.map +1 -1
- package/dist/cli/stdin-encoding.js +37 -0
- package/dist/cli/stdin-encoding.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +27 -4
- package/dist/cli.js.map +1 -1
- package/dist/core/ask-hook/opencode.d.ts +15 -17
- package/dist/core/ask-hook/opencode.d.ts.map +1 -1
- package/dist/core/ask-hook/opencode.js +17 -19
- package/dist/core/ask-hook/opencode.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +29 -3
- 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 +282 -5
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dashboard-rows.d.ts +2 -0
- package/dist/core/dashboard-rows.d.ts.map +1 -1
- package/dist/core/dashboard-rows.js +2 -0
- package/dist/core/dashboard-rows.js.map +1 -1
- package/dist/core/session-discovery.d.ts +9 -0
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +24 -0
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +7 -6
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/terminal-url.d.ts.map +1 -1
- package/dist/core/terminal-url.js +15 -0
- package/dist/core/terminal-url.js.map +1 -1
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +101 -9
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +16 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +69 -11
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +14 -11
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/aggregator.d.ts +5 -0
- package/dist/dashboard/aggregator.d.ts.map +1 -1
- package/dist/dashboard/aggregator.js +7 -0
- package/dist/dashboard/aggregator.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +8 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +8 -1
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts.map +1 -1
- package/dist/dashboard/connector-api.js +14 -3
- package/dist/dashboard/connector-api.js.map +1 -1
- package/dist/dashboard/session-cleanup.d.ts +1 -0
- package/dist/dashboard/session-cleanup.d.ts.map +1 -1
- package/dist/dashboard/session-cleanup.js +2 -0
- package/dist/dashboard/session-cleanup.js.map +1 -1
- package/dist/dashboard/settings-write-applier.d.ts +2 -1
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +7 -0
- package/dist/dashboard/settings-write-applier.js.map +1 -1
- package/dist/dashboard/trigger-api.d.ts +4 -0
- package/dist/dashboard/trigger-api.d.ts.map +1 -1
- package/dist/dashboard/trigger-api.js +20 -0
- package/dist/dashboard/trigger-api.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 +352 -37
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/connectors.d.ts +11 -0
- package/dist/dashboard/web/connectors.d.ts.map +1 -1
- package/dist/dashboard/web/connectors.js +45 -1
- package/dist/dashboard/web/connectors.js.map +1 -1
- package/dist/dashboard/web/groups.d.ts +7 -0
- package/dist/dashboard/web/groups.d.ts.map +1 -1
- package/dist/dashboard/web/groups.js +62 -15
- package/dist/dashboard/web/groups.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +82 -4
- 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 +149 -5
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/settings.d.ts.map +1 -1
- package/dist/dashboard/web/settings.js +11 -0
- package/dist/dashboard/web/settings.js.map +1 -1
- package/dist/dashboard/web/whiteboards.d.ts.map +1 -1
- package/dist/dashboard/web/whiteboards.js +14 -5
- package/dist/dashboard/web/whiteboards.js.map +1 -1
- package/dist/dashboard/webhook-routes.d.ts.map +1 -1
- package/dist/dashboard/webhook-routes.js +126 -6
- package/dist/dashboard/webhook-routes.js.map +1 -1
- package/dist/dashboard-web/app.js +814 -725
- package/dist/dashboard-web/apple-touch-icon.png +0 -0
- package/dist/dashboard-web/favicon.png +0 -0
- package/dist/dashboard-web/index.html +2 -0
- package/dist/dashboard-web/style.css +47 -0
- package/dist/dashboard.js +247 -16
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +10 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +8 -0
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +21 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +21 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +1 -0
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/client.d.ts +12 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +48 -21
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +15 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +199 -16
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-command.js +4 -4
- package/dist/im/lark/grant-command.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts +40 -1
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +37 -2
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/summary-command.d.ts +19 -0
- package/dist/im/lark/summary-command.d.ts.map +1 -0
- package/dist/im/lark/summary-command.js +276 -0
- package/dist/im/lark/summary-command.js.map +1 -0
- package/dist/mir-local-runtime.d.ts +40 -0
- package/dist/mir-local-runtime.d.ts.map +1 -1
- package/dist/mir-local-runtime.js +246 -52
- package/dist/mir-local-runtime.js.map +1 -1
- package/dist/mir-runner.js +26 -1
- package/dist/mir-runner.js.map +1 -1
- package/dist/platform/binding.d.ts +6 -0
- package/dist/platform/binding.d.ts.map +1 -1
- package/dist/platform/binding.js +17 -0
- package/dist/platform/binding.js.map +1 -1
- package/dist/platform/tunnel-client.d.ts +2 -0
- package/dist/platform/tunnel-client.d.ts.map +1 -1
- package/dist/platform/tunnel-client.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 +2 -2
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +5 -5
- package/dist/services/card-prefs-store.js.map +1 -1
- package/dist/services/chat-reply-mode-store.d.ts +6 -4
- package/dist/services/chat-reply-mode-store.d.ts.map +1 -1
- package/dist/services/chat-reply-mode-store.js +6 -4
- package/dist/services/chat-reply-mode-store.js.map +1 -1
- package/dist/services/oncall-store.d.ts +25 -0
- package/dist/services/oncall-store.d.ts.map +1 -1
- package/dist/services/oncall-store.js +56 -0
- package/dist/services/oncall-store.js.map +1 -1
- package/dist/services/pi-transcript.d.ts +19 -0
- package/dist/services/pi-transcript.d.ts.map +1 -0
- package/dist/services/pi-transcript.js +222 -0
- package/dist/services/pi-transcript.js.map +1 -0
- package/dist/services/summary-range-store.d.ts +21 -0
- package/dist/services/summary-range-store.d.ts.map +1 -0
- package/dist/services/summary-range-store.js +89 -0
- package/dist/services/summary-range-store.js.map +1 -0
- package/dist/services/transcript-resolver.d.ts.map +1 -1
- package/dist/services/transcript-resolver.js +21 -4
- package/dist/services/transcript-resolver.js.map +1 -1
- package/dist/services/trigger-types.d.ts +14 -2
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js +15 -1
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/setup/bot-config-editor.d.ts +7 -0
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +35 -0
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/types.d.ts +21 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/executable.d.ts +3 -0
- package/dist/utils/executable.d.ts.map +1 -0
- package/dist/utils/executable.js +24 -0
- package/dist/utils/executable.js.map +1 -0
- package/dist/utils/terminal-favicon.d.ts +2 -0
- package/dist/utils/terminal-favicon.d.ts.map +1 -0
- package/dist/utils/terminal-favicon.js +5 -0
- package/dist/utils/terminal-favicon.js.map +1 -0
- package/dist/worker.js +211 -12
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +8 -8
- package/dist/workflows/events/payloads.d.ts +10 -10
- package/dist/workflows/events/schema.d.ts +28 -28
- package/dist/workflows/v3/bot-resolve.d.ts +1 -1
- package/dist/workflows/v3/bot-resolve.d.ts.map +1 -1
- package/dist/workflows/v3/bot-resolve.js +2 -1
- package/dist/workflows/v3/bot-resolve.js.map +1 -1
- package/dist/workflows/v3/cli-run.js +2 -2
- package/dist/workflows/v3/cli-run.js.map +1 -1
- package/package.json +2 -2
package/dist/worker.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { randomBytes } from 'node:crypto';
|
|
16
16
|
import { mkdirSync, writeFileSync, unlinkSync, existsSync, statSync, readdirSync, readlinkSync, readFileSync, watch as fsWatch, createWriteStream } from 'node:fs';
|
|
17
17
|
import { atomicWriteFileSync } from './utils/atomic-write.js';
|
|
18
|
-
import { isAbsolute, join } from 'node:path';
|
|
18
|
+
import { isAbsolute, join, basename } from 'node:path';
|
|
19
19
|
import { drainTranscript, joinAssistantText, trailingAssistantText, findJsonlContainingFingerprint, findJsonlsContainingExactContent, findLatestJsonl, extractLastAssistantTurn, extractTurnStartText, splitTranscriptEventsByCutoff } from './services/claude-transcript.js';
|
|
20
20
|
import { BridgeTurnQueue, makeFingerprint, normaliseForFingerprint } from './services/bridge-turn-queue.js';
|
|
21
21
|
import { shouldSuppressBridgeEmit } from './services/bridge-fallback-gate.js';
|
|
@@ -34,6 +34,7 @@ import { findTraexRolloutBySessionId, findTraexRolloutByPid } from './services/t
|
|
|
34
34
|
import { cocoEventsPathForSession, drainCocoEvents, findCocoSessionByPid } from './services/coco-transcript.js';
|
|
35
35
|
import { currentHermesStateOffset, drainHermesStateDb } from './services/hermes-transcript.js';
|
|
36
36
|
import { currentMtrSessionOffset, drainMtrSession, findLatestMtrSessionByDirectory, findMtrSessionById } from './services/mtr-transcript.js';
|
|
37
|
+
import { drainPiTranscript, findPiTranscriptByPid, findPiTranscriptBySessionId } from './services/pi-transcript.js';
|
|
37
38
|
import { drainCursorTranscript, findCursorChatIdByPid, findCursorTranscriptByChatId, findCursorTranscriptByPid } from './services/cursor-transcript.js';
|
|
38
39
|
import { shouldObserveCursorChatId, shouldPersistObservedCursorChatId } from './services/cursor-resume-policy.js';
|
|
39
40
|
import { baselineJsonlCursor } from './services/jsonl-cursor.js';
|
|
@@ -41,12 +42,13 @@ import { dirname } from 'node:path';
|
|
|
41
42
|
import { createServer as createHttpServer } from 'node:http';
|
|
42
43
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
43
44
|
import { listenWebTerminalWithFallback } from './utils/web-terminal-listen.js';
|
|
45
|
+
import { TERMINAL_FAVICON_DATA_URI } from './utils/terminal-favicon.js';
|
|
44
46
|
import { t, setDefaultLocale } from './i18n/index.js';
|
|
45
47
|
import { TerminalRenderer } from './utils/terminal-renderer.js';
|
|
46
48
|
import { DEFAULT_RENDER_COLS, DEFAULT_RENDER_ROWS, MAX_RENDER_COLS, MAX_RENDER_ROWS, MIN_RENDER_COLS, MIN_RENDER_ROWS, clamp, resolveRenderDimensions, } from './utils/render-dimensions.js';
|
|
47
49
|
import { createCliAdapterSync, locateOnPath } from './adapters/cli/registry.js';
|
|
48
50
|
import { buildWrappedLaunch, parseWrapperCli, isTtadkWrapper } from './setup/cli-selection.js';
|
|
49
|
-
import { findLaunchedCliPid, scheduleWrapperRealCliPid } from './core/session-discovery.js';
|
|
51
|
+
import { findLaunchedCliPid, scheduleWrapperRealCliPid, readComm, isBareShellComm, bareShellLaunchKind } from './core/session-discovery.js';
|
|
50
52
|
import { claudeJsonlPathForSession, resolveJsonlFromPid, findOpenClaudeSessionIds, DEFAULT_CLAUDE_DATA_DIR } from './adapters/cli/claude-code.js';
|
|
51
53
|
import { mtrSessionIdForBotmuxSession } from './adapters/cli/mtr.js';
|
|
52
54
|
import { HerdrBackend } from './adapters/backend/herdr-backend.js';
|
|
@@ -134,6 +136,24 @@ const authedClients = new WeakSet();
|
|
|
134
136
|
/** Per-WS-client tmux/zellij attach PTYs. */
|
|
135
137
|
const clientPtys = new Map();
|
|
136
138
|
const writeToken = randomBytes(16).toString('hex');
|
|
139
|
+
/**
|
|
140
|
+
* Resolve terminal write permission for one request, honoring a platform-injected
|
|
141
|
+
* `X-Botmux-Role` header. The central platform fronts `/s/*` and sets the role
|
|
142
|
+
* (owner | teammate | guest) after authenticating the viewer, stripping any
|
|
143
|
+
* client-supplied header; it reaches the worker via dashboard /s bridge →
|
|
144
|
+
* terminal-proxy → here. When the header is present we trust it (platform-fronted
|
|
145
|
+
* access): only `owner` may drive the terminal; everything else (teammate / guest
|
|
146
|
+
* / anything else) is read-only. When the header is absent (local direct access,
|
|
147
|
+
* no platform in front), fall back to the legacy write-token query param.
|
|
148
|
+
*/
|
|
149
|
+
function resolveTerminalWrite(req, tokenMatches) {
|
|
150
|
+
const role = req.headers['x-botmux-role'];
|
|
151
|
+
if (typeof role === 'string' && role) {
|
|
152
|
+
const hasWrite = role === 'owner';
|
|
153
|
+
return { hasWrite, platformReadonly: !hasWrite };
|
|
154
|
+
}
|
|
155
|
+
return { hasWrite: tokenMatches, platformReadonly: false };
|
|
156
|
+
}
|
|
137
157
|
/** Lazily-written locked-mode zellij config for per-WS web-terminal attach
|
|
138
158
|
* clients: cleared keybinds + locked mode so every keystroke passes straight
|
|
139
159
|
* to the focused (codex) pane, never intercepted as a zellij shortcut. */
|
|
@@ -153,7 +173,7 @@ function ensureZellijAttachConfig() {
|
|
|
153
173
|
}
|
|
154
174
|
let sessionId = '';
|
|
155
175
|
let lastInitConfig = null;
|
|
156
|
-
const CLI_DISPLAY_NAMES = { 'claude-code': 'Claude', seed: 'Seed', relay: 'Relay', aiden: 'Aiden', coco: 'CoCo', codex: 'Codex', 'codex-app': 'Codex App', cursor: 'Cursor', gemini: 'Gemini', opencode: 'OpenCode', antigravity: 'Antigravity', mtr: 'MTR', hermes: 'Hermes', mira: 'Mira', mir: 'Mir CLI', traex: 'TRAE', pi: 'Pi', copilot: 'Copilot', 'oh-my-pi': 'Oh My Pi' };
|
|
176
|
+
const CLI_DISPLAY_NAMES = { 'claude-code': 'Claude', seed: 'Seed', relay: 'Relay', aiden: 'Aiden', coco: 'CoCo', codex: 'Codex', 'codex-app': 'Codex App', cursor: 'Cursor', gemini: 'Gemini', opencode: 'OpenCode', antigravity: 'Antigravity', mtr: 'MTR', hermes: 'Hermes', mira: 'Mira', mir: 'Mir CLI', traex: 'TRAE', pi: 'Pi', copilot: 'Copilot', 'oh-my-pi': 'Oh My Pi', kimi: 'Kimi' };
|
|
157
177
|
function cliName() { return CLI_DISPLAY_NAMES[lastInitConfig?.cliId ?? ''] ?? 'CLI'; }
|
|
158
178
|
let isPromptReady = false;
|
|
159
179
|
/** Mutex for async flushPending — prevents concurrent flush loops. */
|
|
@@ -164,6 +184,19 @@ let isFlushing = false;
|
|
|
164
184
|
* when the CLI restarts. Consumed inside flushPending right before the first
|
|
165
185
|
* user prompt is drained, so the commands always precede it (see runStartupCommands). */
|
|
166
186
|
let hasRunStartupCommands = false;
|
|
187
|
+
/** Per-spawn latch: set once the launch-failure detector has decided the pane
|
|
188
|
+
* leaf is a bare shell (the CLI never launched — e.g. a user rcfile that
|
|
189
|
+
* `exec`-trampolines into another shell pre-empted the wrapper's `exec <cli>`).
|
|
190
|
+
* Once set, flushPending refuses to type prompts into the bare shell (which
|
|
191
|
+
* would just produce `zsh: parse error`) and the user gets one diagnostic
|
|
192
|
+
* instead. Reset per spawn in spawnCli. */
|
|
193
|
+
let bareShellLaunchBlocked = false;
|
|
194
|
+
/** Per-spawn one-shot: has the bare-shell launch check already run for this
|
|
195
|
+
* spawn? Gates detectBareShellLaunch() to the FIRST flush only (the
|
|
196
|
+
* "about to type the first prompt" moment), independent of the startup-commands
|
|
197
|
+
* one-shot so it also covers a reattach onto a pane that degraded to a bare
|
|
198
|
+
* shell. Reset per spawn in spawnCli. */
|
|
199
|
+
let bareShellChecked = false;
|
|
167
200
|
/** Ready-gate (Claude-family): holds the first prompt until the SessionStart
|
|
168
201
|
* hook fires a true-ready signal, so a cjadk-style startup selector's ❯ (which
|
|
169
202
|
* falsely matches readyPattern) can't eat the first message. Recreated + armed
|
|
@@ -1693,7 +1726,7 @@ function codexBridgeFallbackActive() {
|
|
|
1693
1726
|
// True for transcript-backed CLIs whose final output can be harvested
|
|
1694
1727
|
// when the model forgets to call `botmux send`.
|
|
1695
1728
|
const id = lastInitConfig?.cliId;
|
|
1696
|
-
if (id === 'codex' || id === 'traex' || id === 'coco' || id === 'hermes' || id === 'mtr')
|
|
1729
|
+
if (id === 'codex' || id === 'traex' || id === 'coco' || id === 'hermes' || id === 'mtr' || id === 'pi')
|
|
1697
1730
|
return true;
|
|
1698
1731
|
// Cursor only harvests its transcript in adopt mode: a botmux-spawned
|
|
1699
1732
|
// cursor session carries the botmux skill and replies via `botmux send`,
|
|
@@ -1714,6 +1747,9 @@ function structuredBridgeIsHermes() {
|
|
|
1714
1747
|
function structuredBridgeIsMtr() {
|
|
1715
1748
|
return lastInitConfig?.cliId === 'mtr';
|
|
1716
1749
|
}
|
|
1750
|
+
function structuredBridgeIsPi() {
|
|
1751
|
+
return lastInitConfig?.cliId === 'pi';
|
|
1752
|
+
}
|
|
1717
1753
|
function codexBridgeIsCursor() {
|
|
1718
1754
|
return lastInitConfig?.cliId === 'cursor';
|
|
1719
1755
|
}
|
|
@@ -1722,6 +1758,8 @@ function structuredBridgeIngestPath(path, offset) {
|
|
|
1722
1758
|
return drainCodexRollout(path, offset);
|
|
1723
1759
|
if (codexBridgeIsCursor())
|
|
1724
1760
|
return drainCursorTranscript(path, offset);
|
|
1761
|
+
if (structuredBridgeIsPi())
|
|
1762
|
+
return drainPiTranscript(path, offset);
|
|
1725
1763
|
if (structuredBridgeIsHermes()) {
|
|
1726
1764
|
const result = drainHermesStateDb(offset);
|
|
1727
1765
|
return { events: result.events, newOffset: result.newOffset, pendingTail: '' };
|
|
@@ -1809,6 +1847,7 @@ function codexBridgeStartTimer() {
|
|
|
1809
1847
|
// sid, so the lookup is just a path computation + existence check.
|
|
1810
1848
|
const isCoco = lastInitConfig?.cliId === 'coco';
|
|
1811
1849
|
const isTraex = lastInitConfig?.cliId === 'traex';
|
|
1850
|
+
const isPi = lastInitConfig?.cliId === 'pi';
|
|
1812
1851
|
let path;
|
|
1813
1852
|
if (codexBridgePendingSessionId) {
|
|
1814
1853
|
if (isCoco) {
|
|
@@ -1816,6 +1855,9 @@ function codexBridgeStartTimer() {
|
|
|
1816
1855
|
if (path && !existsSync(path))
|
|
1817
1856
|
path = undefined;
|
|
1818
1857
|
}
|
|
1858
|
+
else if (isPi) {
|
|
1859
|
+
path = findPiTranscriptBySessionId(codexBridgePendingSessionId, lastInitConfig?.workingDir);
|
|
1860
|
+
}
|
|
1819
1861
|
else if (isTraex) {
|
|
1820
1862
|
path = findTraexRolloutBySessionId(codexBridgePendingSessionId);
|
|
1821
1863
|
}
|
|
@@ -1829,6 +1871,11 @@ function codexBridgeStartTimer() {
|
|
|
1829
1871
|
if (probed && existsSync(probed.eventsPath))
|
|
1830
1872
|
path = probed.eventsPath;
|
|
1831
1873
|
}
|
|
1874
|
+
else if (isPi) {
|
|
1875
|
+
const probed = findPiTranscriptByPid(codexAdoptPendingPid);
|
|
1876
|
+
if (probed)
|
|
1877
|
+
path = probed.path;
|
|
1878
|
+
}
|
|
1832
1879
|
else if (isTraex) {
|
|
1833
1880
|
const probed = findTraexRolloutByPid(codexAdoptPendingPid);
|
|
1834
1881
|
if (probed)
|
|
@@ -2075,7 +2122,9 @@ function codexBridgeNotifyCliSessionId(cliSessionId) {
|
|
|
2075
2122
|
}
|
|
2076
2123
|
const path = lastInitConfig?.cliId === 'traex'
|
|
2077
2124
|
? findTraexRolloutBySessionId(cliSessionId)
|
|
2078
|
-
:
|
|
2125
|
+
: lastInitConfig?.cliId === 'pi'
|
|
2126
|
+
? findPiTranscriptBySessionId(cliSessionId, lastInitConfig?.workingDir)
|
|
2127
|
+
: findCodexRolloutBySessionId(cliSessionId);
|
|
2079
2128
|
if (path) {
|
|
2080
2129
|
codexBridgePendingSessionId = undefined;
|
|
2081
2130
|
codexBridgeAttach(path, 'fresh-empty');
|
|
@@ -3264,6 +3313,69 @@ function scheduleSubmitFailureNotify(msg, recheck, transcriptLabel, bridgeTurnId
|
|
|
3264
3313
|
});
|
|
3265
3314
|
}, SUBMIT_DEFERRED_RECHECK_MS);
|
|
3266
3315
|
}
|
|
3316
|
+
/**
|
|
3317
|
+
* Launch-failure guard. Right before the FIRST prompt is typed, confirm the
|
|
3318
|
+
* pane's leaf process is the agent CLI — not a bare interactive shell. The
|
|
3319
|
+
* failure this catches: a user's login `$SHELL` (e.g. bash) whose rcfile
|
|
3320
|
+
* `exec`-trampolines into another shell (`[ -t 1 ] && exec zsh`). botmux's
|
|
3321
|
+
* tmux wrapper launches `<shell> -i -c '… exec /usr/bin/env <cli>'`; the `-i`
|
|
3322
|
+
* sources the rcfile, the `exec zsh` replaces the shell BEFORE the `-c` body
|
|
3323
|
+
* runs, and the pane is left at a bare shell. Typing the multi-line prompt into
|
|
3324
|
+
* it just yields `zsh: parse error near '\n'` and the user is stuck (the exact
|
|
3325
|
+
* bug this guards). Instead of typing into the shell we surface ONE actionable
|
|
3326
|
+
* diagnostic and latch the session so no further prompt is mis-typed.
|
|
3327
|
+
*
|
|
3328
|
+
* Why this is the right moment / low false-positive: the first prompt is held
|
|
3329
|
+
* until the CLI signals ready OR the 15s/45s first-prompt timeout fires, so by
|
|
3330
|
+
* the time we get here a healthy CLI has long since `exec`'d (leaf comm =
|
|
3331
|
+
* codex/node/…) — only a trampolined/failed launch is still a bare shell. We
|
|
3332
|
+
* skip wrapperCli/adopt (their leaf is legitimately a launcher/observed pane)
|
|
3333
|
+
* and the pty/herdr backends (which `exec` the CLI directly — getChildPid is the
|
|
3334
|
+
* CLI itself, never a shell).
|
|
3335
|
+
*
|
|
3336
|
+
* Returns true when a bare-shell launch was detected (caller must NOT flush).
|
|
3337
|
+
*/
|
|
3338
|
+
function detectBareShellLaunch() {
|
|
3339
|
+
if (bareShellLaunchBlocked)
|
|
3340
|
+
return true;
|
|
3341
|
+
if (lastInitConfig?.adoptMode)
|
|
3342
|
+
return false; // observing an existing pane, not launching
|
|
3343
|
+
if (lastInitConfig?.wrapperCli)
|
|
3344
|
+
return false; // launcher legitimately wraps the CLI (transient shell shim)
|
|
3345
|
+
const pid = backend?.getChildPid?.();
|
|
3346
|
+
if (!pid)
|
|
3347
|
+
return false;
|
|
3348
|
+
const comm = readComm(pid);
|
|
3349
|
+
if (!isBareShellComm(comm))
|
|
3350
|
+
return false; // CLI (rust/go/node) is running — healthy launch
|
|
3351
|
+
// Bare shell is the pane leaf → the CLI never launched. Tier the message on
|
|
3352
|
+
// whether the leaf shell differs from the one botmux launched with: a
|
|
3353
|
+
// mismatch is the unmistakable signature of an rcfile `exec`-trampoline.
|
|
3354
|
+
const launchShell = (lastInitConfig?.launchShell || process.env.SHELL || '').trim();
|
|
3355
|
+
const expectedShell = launchShell ? basename(launchShell) : '';
|
|
3356
|
+
const trampolined = bareShellLaunchKind(comm, expectedShell) === 'trampoline';
|
|
3357
|
+
bareShellLaunchBlocked = true;
|
|
3358
|
+
log(`Bare-shell launch detected: pane leaf comm=${comm}, expected launch shell=${expectedShell || '?'}, ` +
|
|
3359
|
+
`cli=${lastInitConfig?.cliId}; suppressing first-prompt write (${trampolined ? 'rc trampoline' : 'CLI did not start'})`);
|
|
3360
|
+
const cli = cliName();
|
|
3361
|
+
let message;
|
|
3362
|
+
if (trampolined) {
|
|
3363
|
+
message =
|
|
3364
|
+
`⚠️ 会话没能启动:pane 里现在是裸 \`${comm}\`,${cli} 没真正跑起来——所以我没把你的消息打进去(否则会被当 shell 命令执行,报 \`parse error\`)。\n\n` +
|
|
3365
|
+
`最可能原因:botmux 用 \`${expectedShell}\` 启动 CLI,但 pane 落到了 \`${comm}\`。通常是 rc 文件(如 \`~/.${expectedShell}rc\`)里有 \`exec ${comm}\` 这类跳转——\`${expectedShell} -i\` 会 source rc,于是 shell 被顶替,CLI 的启动命令没机会跑。\n\n` +
|
|
3366
|
+
`两种修法(任选其一,改完重启 daemon 再发一条消息):\n` +
|
|
3367
|
+
`① 给那行加守卫,只在手动开终端时切:\`[ -z "$BASH_EXECUTION_STRING" ] && [ -t 1 ] && exec ${comm}\`(注意 PATH/nvm 等导出放在它之前)\n` +
|
|
3368
|
+
`② 给这个 bot 配 \`launchShell: ${comm}\`(dashboard 机器人配置,或 \`/config launchShell ${comm}\`),直接用 \`${comm}\` 启动绕开 \`${expectedShell}\` 的 rc——但要确保 PATH/nvm 在 \`${comm}\` 的 rc 里。`;
|
|
3369
|
+
}
|
|
3370
|
+
else {
|
|
3371
|
+
message =
|
|
3372
|
+
`⚠️ 会话没能启动:pane 里还停在 \`${comm}\`,${cli} 没真正跑起来——我没把消息打进去(否则会被当 shell 命令执行)。\n\n` +
|
|
3373
|
+
`可能原因:rc 文件启动过慢/报错,或 \`${cli}\` 的可执行文件不在 PATH 上(CLI 没找到)。\n` +
|
|
3374
|
+
`建议:在 web 终端里手动敲一下启动命令看报什么错;确认 CLI 二进制能在 PATH 上找到;或精简 rc 启动逻辑后重启 daemon 再试。`;
|
|
3375
|
+
}
|
|
3376
|
+
send({ type: 'user_notify', turnId: currentBotmuxTurnId, message });
|
|
3377
|
+
return true;
|
|
3378
|
+
}
|
|
3267
3379
|
/**
|
|
3268
3380
|
* Drain the pending message queue sequentially.
|
|
3269
3381
|
* Async with isFlushing mutex: awaits each writeInput, then immediately
|
|
@@ -3277,6 +3389,8 @@ async function flushPending() {
|
|
|
3277
3389
|
return;
|
|
3278
3390
|
if (pendingMessages.length === 0)
|
|
3279
3391
|
return; // nothing to flush — keep isPromptReady
|
|
3392
|
+
if (bareShellLaunchBlocked)
|
|
3393
|
+
return; // launch failed into a bare shell — don't type prompts into it
|
|
3280
3394
|
// Ready-gate: hold the FIRST prompt until the SessionStart hook fires a true-
|
|
3281
3395
|
// ready signal. A cjadk-style startup selector's ❯ falsely matches readyPattern
|
|
3282
3396
|
// and would otherwise eat this message. releaseReadyGate() re-invokes us once
|
|
@@ -3326,6 +3440,21 @@ async function flushPending() {
|
|
|
3326
3440
|
idleDetector?.reset();
|
|
3327
3441
|
}
|
|
3328
3442
|
try {
|
|
3443
|
+
// Launch-failure guard, run ONCE per spawn on the first flush, BEFORE startup
|
|
3444
|
+
// commands or any user prompt: if the pane leaf is a bare shell (the CLI never
|
|
3445
|
+
// launched — e.g. a user rcfile that `exec`-trampolines into another shell, or
|
|
3446
|
+
// a reattached persistent pane that has dropped back to a shell), don't type
|
|
3447
|
+
// anything into it (it would just be `zsh: parse error`); surface one
|
|
3448
|
+
// diagnostic and bail. Gated by its own one-shot (NOT hasRunStartupCommands)
|
|
3449
|
+
// so it also covers reattach, where startup commands are intentionally
|
|
3450
|
+
// skipped. Must precede runStartupCommands so a bot with startupCommands
|
|
3451
|
+
// doesn't get them typed into the bare shell first.
|
|
3452
|
+
if (!bareShellChecked) {
|
|
3453
|
+
bareShellChecked = true;
|
|
3454
|
+
if (detectBareShellLaunch()) {
|
|
3455
|
+
return; // finally{} releases the mutex; pendingMessages stay queued, untouched
|
|
3456
|
+
}
|
|
3457
|
+
}
|
|
3329
3458
|
// One-shot per spawn: type the bot's startup commands (e.g. `/effort
|
|
3330
3459
|
// ultracode`) into the CLI before the first user prompt drains. Both ready
|
|
3331
3460
|
// paths funnel through flushPending — the ready-gate settle for Claude-family
|
|
@@ -3693,16 +3822,38 @@ function setupAdoptTranscriptBridges(cfg) {
|
|
|
3693
3822
|
codexBridgeStartTimer();
|
|
3694
3823
|
}
|
|
3695
3824
|
}
|
|
3825
|
+
else if (cfg.cliId === 'pi') {
|
|
3826
|
+
const adoptStartMs = Date.now();
|
|
3827
|
+
codexAdoptStartMs = adoptStartMs;
|
|
3828
|
+
codexBridgeQueue.setLocalTurns(true, adoptStartMs);
|
|
3829
|
+
let path;
|
|
3830
|
+
if (cfg.cliSessionId)
|
|
3831
|
+
path = findPiTranscriptBySessionId(cfg.cliSessionId, cfg.adoptCwd ?? cfg.workingDir);
|
|
3832
|
+
if (!path && cfg.adoptCliPid) {
|
|
3833
|
+
const probed = findPiTranscriptByPid(cfg.adoptCliPid);
|
|
3834
|
+
if (probed)
|
|
3835
|
+
path = probed.path;
|
|
3836
|
+
}
|
|
3837
|
+
if (path) {
|
|
3838
|
+
codexBridgeAttach(path, 'split-live');
|
|
3839
|
+
}
|
|
3840
|
+
else {
|
|
3841
|
+
if (cfg.cliSessionId)
|
|
3842
|
+
codexBridgePendingSessionId = cfg.cliSessionId;
|
|
3843
|
+
codexAdoptPendingPid = cfg.adoptCliPid;
|
|
3844
|
+
codexBridgeStartTimer();
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3696
3847
|
}
|
|
3697
3848
|
function adoptIdleAdapter(cfg) {
|
|
3698
3849
|
return cfg.bridgeJsonlPath
|
|
3699
3850
|
? createCliAdapterSync('claude-code', undefined)
|
|
3700
|
-
: cfg.cliId === 'codex' || cfg.cliId === 'traex' || cfg.cliId === 'coco' || cfg.cliId === 'mtr'
|
|
3851
|
+
: cfg.cliId === 'codex' || cfg.cliId === 'traex' || cfg.cliId === 'coco' || cfg.cliId === 'mtr' || cfg.cliId === 'pi'
|
|
3701
3852
|
? createCliAdapterSync(cfg.cliId, undefined)
|
|
3702
3853
|
: { completionPattern: undefined, readyPattern: undefined };
|
|
3703
3854
|
}
|
|
3704
3855
|
function setupAdoptInputAdapter(cfg) {
|
|
3705
|
-
if (cfg.cliId === 'codex' || cfg.cliId === 'traex') {
|
|
3856
|
+
if (cfg.cliId === 'codex' || cfg.cliId === 'traex' || cfg.cliId === 'pi') {
|
|
3706
3857
|
cliAdapter = createCliAdapterSync(cfg.cliId, cfg.cliPathOverride);
|
|
3707
3858
|
}
|
|
3708
3859
|
else if (cfg.cliId === 'mtr') {
|
|
@@ -3998,6 +4149,13 @@ function spawnCli(cfg) {
|
|
|
3998
4149
|
// arm it. spawnCli is synchronous up to backend spawn, so this lands before
|
|
3999
4150
|
// any flushPending consumes the flag.
|
|
4000
4151
|
hasRunStartupCommands = !shouldRunStartupCommandsOnSpawn({ willReattachPersistent });
|
|
4152
|
+
// Re-arm the bare-shell launch detector for this spawn (fresh OR reattach). It
|
|
4153
|
+
// runs once on the first flush and only fires when the pane leaf is actually a
|
|
4154
|
+
// bare shell, so a healthy reattach (leaf = the live CLI) self-excludes while a
|
|
4155
|
+
// reattach onto a pane that has degraded to a bare shell still gets the
|
|
4156
|
+
// diagnostic instead of having the prompt typed into it.
|
|
4157
|
+
bareShellLaunchBlocked = false;
|
|
4158
|
+
bareShellChecked = false;
|
|
4001
4159
|
// ── Resume pre-flight check + two-tier fallback ──────────────────────────
|
|
4002
4160
|
// Tier 1 (adapter probe): adapter.checkResumeTargetExists returns false
|
|
4003
4161
|
// → skip --resume, spawn FRESH.
|
|
@@ -4365,6 +4523,7 @@ function spawnCli(cfg) {
|
|
|
4365
4523
|
rows: PTY_ROWS,
|
|
4366
4524
|
env: childEnv,
|
|
4367
4525
|
injectEnv: perBotInjectKeys.length ? perBotInjectEnv : undefined,
|
|
4526
|
+
launchShell: lastInitConfig?.launchShell,
|
|
4368
4527
|
});
|
|
4369
4528
|
// Write CLI PID marker so agent-facing subcommands (`botmux send`, etc.)
|
|
4370
4529
|
// can verify they were spawned inside a botmux session by walking the
|
|
@@ -4565,6 +4724,22 @@ function spawnCli(cfg) {
|
|
|
4565
4724
|
codexBridgeStartTimer();
|
|
4566
4725
|
}
|
|
4567
4726
|
}
|
|
4727
|
+
else if (cfg.cliId === 'pi') {
|
|
4728
|
+
const piSessionId = effectiveCliSessionId ?? effectiveAdapterSessionId;
|
|
4729
|
+
if (piSessionId) {
|
|
4730
|
+
const transcriptPath = findPiTranscriptBySessionId(piSessionId, cfg.workingDir);
|
|
4731
|
+
if (transcriptPath) {
|
|
4732
|
+
codexBridgeAttach(transcriptPath, effectiveResume ? 'baseline-existing' : 'fresh-empty');
|
|
4733
|
+
}
|
|
4734
|
+
else {
|
|
4735
|
+
codexBridgePendingSessionId = piSessionId;
|
|
4736
|
+
codexBridgeStartTimer();
|
|
4737
|
+
}
|
|
4738
|
+
}
|
|
4739
|
+
else {
|
|
4740
|
+
codexBridgeStartTimer();
|
|
4741
|
+
}
|
|
4742
|
+
}
|
|
4568
4743
|
// Arm the ready-gate for FRESH Claude-family spawns (which inject the
|
|
4569
4744
|
// SessionStart hook via --settings; see claude-code.ts buildArgs). Until
|
|
4570
4745
|
// `botmux session-ready` fires (daemon → 'session_ready' IPC → releaseReadyGate)
|
|
@@ -4765,9 +4940,12 @@ function startWebServer(host, preferredPort) {
|
|
|
4765
4940
|
res.end('Bad Request');
|
|
4766
4941
|
return;
|
|
4767
4942
|
}
|
|
4768
|
-
const
|
|
4943
|
+
const tokenMatches = url.searchParams.get('token') === writeToken;
|
|
4944
|
+
const { hasWrite, platformReadonly } = resolveTerminalWrite(req, tokenMatches);
|
|
4945
|
+
const loginHdr = req.headers['x-botmux-login-url'];
|
|
4946
|
+
const loginUrl = typeof loginHdr === 'string' && /^https?:\/\/[^"'<>\s]+$/.test(loginHdr) ? loginHdr : '';
|
|
4769
4947
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
4770
|
-
res.end(getTerminalHtml(hasWrite));
|
|
4948
|
+
res.end(getTerminalHtml(hasWrite, platformReadonly, loginUrl));
|
|
4771
4949
|
});
|
|
4772
4950
|
wss = new WebSocketServer({ server: httpServer });
|
|
4773
4951
|
wss.on('connection', (ws, req) => {
|
|
@@ -4780,7 +4958,8 @@ function startWebServer(host, preferredPort) {
|
|
|
4780
4958
|
ws.close(1008, 'Bad Request');
|
|
4781
4959
|
return;
|
|
4782
4960
|
}
|
|
4783
|
-
const
|
|
4961
|
+
const tokenMatches = url.searchParams.get('token') === writeToken;
|
|
4962
|
+
const { hasWrite } = resolveTerminalWrite(req, tokenMatches);
|
|
4784
4963
|
if (hasWrite)
|
|
4785
4964
|
authedClients.add(ws);
|
|
4786
4965
|
log(`WS client connected (total: ${wsClients.size}, write: ${hasWrite})`);
|
|
@@ -5026,7 +5205,7 @@ function startWebServer(host, preferredPort) {
|
|
|
5026
5205
|
.then(resolve, reject);
|
|
5027
5206
|
});
|
|
5028
5207
|
}
|
|
5029
|
-
function getTerminalHtml(hasWrite) {
|
|
5208
|
+
function getTerminalHtml(hasWrite, platformReadonly = false, loginUrl = '') {
|
|
5030
5209
|
const label = sessionId.substring(0, 8);
|
|
5031
5210
|
return `<!DOCTYPE html>
|
|
5032
5211
|
<html>
|
|
@@ -5034,6 +5213,7 @@ function getTerminalHtml(hasWrite) {
|
|
|
5034
5213
|
<meta charset="utf-8">
|
|
5035
5214
|
<meta id="vp" name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
|
|
5036
5215
|
<title>${cliName()} - ${label}</title>
|
|
5216
|
+
<link rel="icon" type="image/png" href="${TERMINAL_FAVICON_DATA_URI}">
|
|
5037
5217
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@5/css/xterm.min.css">
|
|
5038
5218
|
<style>
|
|
5039
5219
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
@@ -5073,11 +5253,17 @@ body.touch #terminal .xterm-screen *{
|
|
|
5073
5253
|
background:rgba(247,118,142,0.12);border:1px solid rgba(247,118,142,0.35);border-radius:4px;
|
|
5074
5254
|
backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
|
|
5075
5255
|
#readonly-banner.show{display:inline-block}
|
|
5256
|
+
#login-banner{display:none;position:fixed;top:8px;left:50%;transform:translateX(-50%);z-index:50;
|
|
5257
|
+
padding:4px 10px;font:12px monospace;color:#e0af68;white-space:nowrap;text-decoration:none;cursor:pointer;
|
|
5258
|
+
background:rgba(224,175,104,0.12);border:1px solid rgba(224,175,104,0.35);border-radius:4px;
|
|
5259
|
+
backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px)}
|
|
5260
|
+
#login-banner.show{display:inline-block}
|
|
5076
5261
|
</style>
|
|
5077
5262
|
</head>
|
|
5078
5263
|
<body>
|
|
5079
5264
|
<div id="terminal"></div>
|
|
5080
5265
|
<div id="readonly-banner">只读模式 · 无写入权限</div>
|
|
5266
|
+
${loginUrl ? `<a id="login-banner" href="${loginUrl}" target="_top" rel="noopener">owner 登录后可操作 →</a>` : '<div id="login-banner">owner 登录后可操作</div>'}
|
|
5081
5267
|
<div id="toolbar">
|
|
5082
5268
|
<button data-k="esc">Esc</button>
|
|
5083
5269
|
<button data-k="ctrlc">^C</button>
|
|
@@ -5099,7 +5285,11 @@ body.touch #terminal .xterm-screen *{
|
|
|
5099
5285
|
var isTouch='ontouchstart'in window||navigator.maxTouchPoints>0;
|
|
5100
5286
|
if(isTouch){document.getElementById('vp').content='width=1100,viewport-fit=cover';document.body.classList.add('touch');}
|
|
5101
5287
|
var hasToken=${hasWrite};
|
|
5102
|
-
|
|
5288
|
+
var platformReadonly=${platformReadonly};
|
|
5289
|
+
if(!hasToken){
|
|
5290
|
+
if(platformReadonly){var _lb=document.getElementById('login-banner');_lb.classList.add('show');}
|
|
5291
|
+
else{var _rb=document.getElementById('readonly-banner');_rb.classList.add('show');_rb.addEventListener('click',function(){_rb.classList.remove('show')});}
|
|
5292
|
+
}
|
|
5103
5293
|
|
|
5104
5294
|
var term=new Terminal({
|
|
5105
5295
|
theme:{background:'#1a1b26',foreground:'#a9b1d6',cursor:'#c0caf5',
|
|
@@ -5395,6 +5585,15 @@ process.on('message', async (raw) => {
|
|
|
5395
5585
|
adoptMode: msg.adoptMode === true,
|
|
5396
5586
|
passesInitialPromptViaArgs: cliAdapter?.passesInitialPromptViaArgs === true,
|
|
5397
5587
|
});
|
|
5588
|
+
if (msg.prompt && cliAdapter?.passesInitialPromptViaArgs && !deferInitialPrompt && codexBridgeFallbackActive()) {
|
|
5589
|
+
// Args-baked first prompts (notably Pi) never pass through the normal
|
|
5590
|
+
// 'message' IPC path, so the structured bridge would otherwise see the
|
|
5591
|
+
// transcript final answer with no pending turn to attribute it to.
|
|
5592
|
+
// Mark it here before the CLI starts processing; late-attach is fine
|
|
5593
|
+
// because CodexBridgeQueue is path-agnostic until ingest discovers the
|
|
5594
|
+
// transcript file.
|
|
5595
|
+
codexBridgeMarkPendingTurn(msg.prompt, msg.turnId);
|
|
5596
|
+
}
|
|
5398
5597
|
if (msg.prompt && (!cliAdapter?.passesInitialPromptViaArgs || deferInitialPrompt)) {
|
|
5399
5598
|
pendingMessages.push({ content: msg.prompt, turnId: msg.turnId });
|
|
5400
5599
|
}
|