botmux 3.1.0 → 3.2.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/sandbox.d.ts +23 -0
- package/dist/adapters/backend/sandbox.d.ts.map +1 -1
- package/dist/adapters/backend/sandbox.js +69 -4
- package/dist/adapters/backend/sandbox.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +11 -1
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/cli/read-isolation.d.ts +1 -1
- package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
- package/dist/adapters/cli/read-isolation.js +15 -7
- package/dist/adapters/cli/read-isolation.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +117 -25
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +154 -90
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +1 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +132 -0
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/maintenance.d.ts +23 -1
- package/dist/core/maintenance.d.ts.map +1 -1
- package/dist/core/maintenance.js +63 -25
- package/dist/core/maintenance.js.map +1 -1
- package/dist/core/plugins/cli-generation.d.ts.map +1 -1
- package/dist/core/plugins/cli-generation.js +7 -0
- package/dist/core/plugins/cli-generation.js.map +1 -1
- package/dist/core/plugins/convention-scanner.d.ts +3 -3
- package/dist/core/plugins/convention-scanner.d.ts.map +1 -1
- package/dist/core/plugins/convention-scanner.js.map +1 -1
- package/dist/core/plugins/install.d.ts.map +1 -1
- package/dist/core/plugins/install.js +84 -26
- package/dist/core/plugins/install.js.map +1 -1
- package/dist/core/plugins/mcp/environment.d.ts +12 -0
- package/dist/core/plugins/mcp/environment.d.ts.map +1 -0
- package/dist/core/plugins/mcp/environment.js +17 -0
- package/dist/core/plugins/mcp/environment.js.map +1 -0
- package/dist/core/plugins/mcp/gateway-installer.d.ts.map +1 -1
- package/dist/core/plugins/mcp/gateway-installer.js +9 -3
- package/dist/core/plugins/mcp/gateway-installer.js.map +1 -1
- package/dist/core/plugins/mcp/gateway.d.ts +13 -1
- package/dist/core/plugins/mcp/gateway.d.ts.map +1 -1
- package/dist/core/plugins/mcp/gateway.js +143 -23
- package/dist/core/plugins/mcp/gateway.js.map +1 -1
- package/dist/core/plugins/mcp/host.d.ts +18 -0
- package/dist/core/plugins/mcp/host.d.ts.map +1 -0
- package/dist/core/plugins/mcp/host.js +126 -0
- package/dist/core/plugins/mcp/host.js.map +1 -0
- package/dist/core/plugins/mcp/private-store.d.ts +15 -0
- package/dist/core/plugins/mcp/private-store.d.ts.map +1 -0
- package/dist/core/plugins/mcp/private-store.js +162 -0
- package/dist/core/plugins/mcp/private-store.js.map +1 -0
- package/dist/core/plugins/mcp/session-runtime.d.ts +25 -0
- package/dist/core/plugins/mcp/session-runtime.d.ts.map +1 -0
- package/dist/core/plugins/mcp/session-runtime.js +76 -0
- package/dist/core/plugins/mcp/session-runtime.js.map +1 -0
- package/dist/core/plugins/mcp/socket-auth.d.ts +9 -0
- package/dist/core/plugins/mcp/socket-auth.d.ts.map +1 -0
- package/dist/core/plugins/mcp/socket-auth.js +94 -0
- package/dist/core/plugins/mcp/socket-auth.js.map +1 -0
- package/dist/core/plugins/paths.d.ts +3 -0
- package/dist/core/plugins/paths.d.ts.map +1 -1
- package/dist/core/plugins/paths.js +9 -0
- package/dist/core/plugins/paths.js.map +1 -1
- package/dist/core/plugins/pm2.d.ts.map +1 -1
- package/dist/core/plugins/pm2.js +3 -1
- package/dist/core/plugins/pm2.js.map +1 -1
- package/dist/core/plugins/runtime.d.ts +2 -0
- package/dist/core/plugins/runtime.d.ts.map +1 -1
- package/dist/core/plugins/runtime.js.map +1 -1
- package/dist/core/plugins/service-manager.d.ts +22 -0
- package/dist/core/plugins/service-manager.d.ts.map +1 -1
- package/dist/core/plugins/service-manager.js +189 -52
- package/dist/core/plugins/service-manager.js.map +1 -1
- package/dist/core/plugins/types.d.ts +15 -1
- package/dist/core/plugins/types.d.ts.map +1 -1
- package/dist/core/restart-report.d.ts +1 -1
- package/dist/core/restart-report.d.ts.map +1 -1
- package/dist/core/restart-report.js +5 -3
- package/dist/core/restart-report.js.map +1 -1
- package/dist/core/scheduler.d.ts +47 -0
- package/dist/core/scheduler.d.ts.map +1 -1
- package/dist/core/scheduler.js +117 -0
- package/dist/core/scheduler.js.map +1 -1
- package/dist/core/session-manager.d.ts +7 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +130 -33
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-marker.d.ts.map +1 -1
- package/dist/core/session-marker.js +5 -2
- package/dist/core/session-marker.js.map +1 -1
- package/dist/core/silent-schedule-turns.d.ts +5 -0
- package/dist/core/silent-schedule-turns.d.ts.map +1 -0
- package/dist/core/silent-schedule-turns.js +47 -0
- package/dist/core/silent-schedule-turns.js.map +1 -0
- package/dist/core/types.d.ts +54 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +55 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/update-check.d.ts +16 -0
- package/dist/core/update-check.d.ts.map +1 -1
- package/dist/core/update-check.js +52 -0
- package/dist/core/update-check.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +40 -31
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +90 -9
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/schedule-card-model.d.ts +3 -0
- package/dist/dashboard/schedule-card-model.d.ts.map +1 -1
- package/dist/dashboard/schedule-card-model.js +5 -0
- package/dist/dashboard/schedule-card-model.js.map +1 -1
- package/dist/dashboard/web/app.d.ts.map +1 -1
- package/dist/dashboard/web/app.js +295 -6
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +92 -0
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/plugin-page.d.ts.map +1 -1
- package/dist/dashboard/web/plugin-page.js +1 -3
- package/dist/dashboard/web/plugin-page.js.map +1 -1
- package/dist/dashboard/web/schedules-page.d.ts.map +1 -1
- package/dist/dashboard/web/schedules-page.js +111 -4
- package/dist/dashboard/web/schedules-page.js.map +1 -1
- package/dist/dashboard/web/settings-page.d.ts.map +1 -1
- package/dist/dashboard/web/settings-page.js +5 -1
- package/dist/dashboard/web/settings-page.js.map +1 -1
- package/dist/dashboard/web/update-action.d.ts +16 -0
- package/dist/dashboard/web/update-action.d.ts.map +1 -0
- package/dist/dashboard/web/update-action.js +64 -0
- package/dist/dashboard/web/update-action.js.map +1 -0
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/{bot-defaults-page-KLDCZNLP.js → bot-defaults-page-4PM4T6QQ.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-FMJNOATE.js → chunk-3PMN55Z6.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-MLNI5BKE.js → chunk-AIDN2WZB.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-EZAW2EPM.js → chunk-IKCZ677K.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-KEAOTUL3.js → chunk-RXVA6XW3.js} +7 -7
- package/dist/dashboard-web/chunks/{connectors-page-O32LFAL5.js → connectors-page-2MKF4YTN.js} +1 -1
- package/dist/dashboard-web/chunks/{groups-page-PSAFIZ6M.js → groups-page-TAU2UIVS.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-V23LLATX.js → insights-page-PJ2XVM6T.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-7X5AKYOF.js → monitor-room-D6445HJO.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-CBA3CQYL.js → monitoring-page-GUHMCKSO.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-RL6QSEM7.js → office-page-6K6U2MS5.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-XQIPO6KH.js → overview-page-USDYU7U5.js} +1 -1
- package/dist/dashboard-web/chunks/plugin-page-JXC5F5YV.js +1 -0
- package/dist/dashboard-web/chunks/{roles-page-ACIWV7QS.js → roles-page-O4OUA7ZH.js} +1 -1
- package/dist/dashboard-web/chunks/schedules-page-LNIBXJSZ.js +1 -0
- package/dist/dashboard-web/chunks/{sessions-page-2WDWHSP4.js → sessions-page-B6U6TCFF.js} +1 -1
- package/dist/dashboard-web/chunks/{settings-page-6VX4OUGG.js → settings-page-PXLRFF7I.js} +2 -2
- package/dist/dashboard-web/chunks/{skills-page-WXE3UYLT.js → skills-page-M5KYNXBA.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-PYTF4FLM.js → team-federation-page-GT7UFQMN.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-G6CD6O3I.js → v3-page-UYZIRJ2C.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-6SG33GP4.js → whiteboards-page-WAR77DQE.js} +1 -1
- package/dist/dashboard-web/style.css +697 -0
- package/dist/dashboard.js +404 -29
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +5 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +5 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +2 -0
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +219 -139
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/schedules-card.d.ts.map +1 -1
- package/dist/im/lark/schedules-card.js +2 -0
- package/dist/im/lark/schedules-card.js.map +1 -1
- package/dist/services/plugin-registry-store.d.ts.map +1 -1
- package/dist/services/plugin-registry-store.js +100 -18
- package/dist/services/plugin-registry-store.js.map +1 -1
- package/dist/services/restart-intent-store.d.ts +17 -2
- package/dist/services/restart-intent-store.d.ts.map +1 -1
- package/dist/services/restart-intent-store.js +110 -0
- package/dist/services/restart-intent-store.js.map +1 -1
- package/dist/services/schedule-store.d.ts +3 -1
- package/dist/services/schedule-store.d.ts.map +1 -1
- package/dist/services/schedule-store.js +5 -0
- package/dist/services/schedule-store.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +3 -2
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/child-env.d.ts +9 -1
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +24 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/utils/global-install.d.ts +2 -0
- package/dist/utils/global-install.d.ts.map +1 -1
- package/dist/utils/global-install.js +14 -0
- package/dist/utils/global-install.js.map +1 -1
- package/dist/worker.js +135 -18
- package/dist/worker.js.map +1 -1
- package/dist/workflows/hostExecutors/botmux-schedule.d.ts +3 -0
- package/dist/workflows/hostExecutors/botmux-schedule.d.ts.map +1 -1
- package/dist/workflows/hostExecutors/botmux-schedule.js +9 -0
- package/dist/workflows/hostExecutors/botmux-schedule.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/plugin-page-7FW2LPJH.js +0 -1
- package/dist/dashboard-web/chunks/schedules-page-KJDUKOJV.js +0 -1
package/dist/worker.js
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
import { randomBytes } from 'node:crypto';
|
|
16
16
|
import { mkdirSync, writeFileSync, unlinkSync, existsSync, statSync, readdirSync, readlinkSync, readFileSync, realpathSync, copyFileSync, watch as fsWatch, createWriteStream, openSync, closeSync, fstatSync, constants as fsConstants } from 'node:fs';
|
|
17
17
|
import { atomicWriteFileSync } from './utils/atomic-write.js';
|
|
18
|
-
import { join, basename } from 'node:path';
|
|
19
|
-
import { homedir } from 'node:os';
|
|
18
|
+
import { join, basename, dirname } from 'node:path';
|
|
19
|
+
import { homedir, tmpdir } from 'node:os';
|
|
20
20
|
import { spawnSync } from 'node:child_process';
|
|
21
21
|
import { evaluateReadIsolationGate, buildSeatbeltProfile, isolatedPaneReattachSafe, sendCredFilePath, botHomePath, buildV2DenyPaths, buildV2DenyRegexes, buildV2CarveOuts, buildReadIsolationProtectedWriteRules, buildCliExecutableReadCarveOuts, buildWriteSandboxRules, buildLinuxReadIsolationMasks, isolationPaneMarkerContent, } from './adapters/cli/read-isolation.js';
|
|
22
22
|
import { killPersistentSession } from './core/persistent-backend.js';
|
|
@@ -35,7 +35,10 @@ import { shouldRunStartupCommandsOnSpawn, shouldDeferInitialPromptForStartup } f
|
|
|
35
35
|
import { sanitizePerBotEnv } from './core/per-bot-env.js';
|
|
36
36
|
import { evaluateVcMeetingManagedSend, } from './services/vc-meeting-send-policy.js';
|
|
37
37
|
import { TurnTerminalDeduper } from './services/turn-terminal-deduper.js';
|
|
38
|
-
import { ensureGatewayEntry } from './core/plugins/mcp/gateway-installer.js';
|
|
38
|
+
import { defaultGatewayEntry, ensureGatewayEntry } from './core/plugins/mcp/gateway-installer.js';
|
|
39
|
+
import { sessionMcpGatewayPathRegex, startSessionMcpGatewayHost, } from './core/plugins/mcp/host.js';
|
|
40
|
+
import { MCP_GATEWAY_REQUIRED_ENV, MCP_GATEWAY_SOCKET_ENV, } from './core/plugins/mcp/environment.js';
|
|
41
|
+
import { readSessionMcpRuntimeManifest, sessionMcpRuntimeHostOnlyPaths, } from './core/plugins/mcp/session-runtime.js';
|
|
39
42
|
import { prepareCliPluginGeneration } from './core/plugins/cli-generation.js';
|
|
40
43
|
import { loadBotConfigs } from './bot-registry.js';
|
|
41
44
|
import { readGlobalConfig } from './global-config.js';
|
|
@@ -59,7 +62,6 @@ import { drainCursorTranscript, findCursorChatIdByPid, findCursorTranscriptByCha
|
|
|
59
62
|
import { shouldObserveCursorChatId, shouldPersistObservedCursorChatId } from './services/cursor-resume-policy.js';
|
|
60
63
|
import { extractKiroSessionIdFromOutput } from './services/kiro-session.js';
|
|
61
64
|
import { baselineJsonlCursor } from './services/jsonl-cursor.js';
|
|
62
|
-
import { dirname } from 'node:path';
|
|
63
65
|
import { createServer as createHttpServer } from 'node:http';
|
|
64
66
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
65
67
|
import { listenWebTerminalWithFallback } from './utils/web-terminal-listen.js';
|
|
@@ -121,6 +123,7 @@ let sandboxRelayOutbox = null;
|
|
|
121
123
|
let sandboxRelayCapability = null;
|
|
122
124
|
let readIsolationOriginCapabilityFile = null;
|
|
123
125
|
let sandboxTeardownDone = false; // guards the exit-time best-effort teardown from double-running / running on suspend-for-resume
|
|
126
|
+
let sessionMcpGatewayHost = null;
|
|
124
127
|
/** Counts consecutive in-worker restart cycles (see case 'restart'). Used by
|
|
125
128
|
* the SECONDARY guard so an adapter whose checkResumeTargetExists misses
|
|
126
129
|
* (returns undefined) or whose resume target vanishes between the check and
|
|
@@ -136,6 +139,15 @@ let tmuxRestartTimer = null;
|
|
|
136
139
|
let resumeFallbackNotified = false;
|
|
137
140
|
/** Skill catalog to attach to the first user turn after a prompt-less CLI restart. */
|
|
138
141
|
let deferredPluginSkillCatalog = null;
|
|
142
|
+
function stopSessionMcpGatewayHost() {
|
|
143
|
+
const host = sessionMcpGatewayHost;
|
|
144
|
+
sessionMcpGatewayHost = null;
|
|
145
|
+
if (!host)
|
|
146
|
+
return;
|
|
147
|
+
void host.close().catch((error) => {
|
|
148
|
+
log(`[mcp-gateway] host shutdown failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
139
151
|
function refreshCliPluginGeneration(cfg, adapter) {
|
|
140
152
|
let bot = {
|
|
141
153
|
larkAppId: cfg.larkAppId,
|
|
@@ -749,6 +761,15 @@ async function deliverRawInput(msg) {
|
|
|
749
761
|
const targetBackend = backend;
|
|
750
762
|
if (!targetBackend)
|
|
751
763
|
return;
|
|
764
|
+
// A passthrough is still an accepted input boundary. Rotate (or revoke) the
|
|
765
|
+
// marker immediately before the literal command reaches the CLI so it can
|
|
766
|
+
// never inherit a previous human turn. System-generated raw commands omit
|
|
767
|
+
// turnId and therefore publish an explicitly unattributed marker.
|
|
768
|
+
currentBotmuxTurnId = msg.turnId;
|
|
769
|
+
currentBotmuxDispatchAttempt = undefined;
|
|
770
|
+
currentVcMeetingImTurnOrigin = undefined;
|
|
771
|
+
writeCliPidMarker();
|
|
772
|
+
publishSandboxRelayCapability();
|
|
752
773
|
let sent = false;
|
|
753
774
|
try {
|
|
754
775
|
await sendRawCommandLineSerially(targetBackend, msg.content);
|
|
@@ -766,7 +787,7 @@ async function deliverRawInput(msg) {
|
|
|
766
787
|
// Enter lands. sendToPty also observes commandLineWritesPending, so another
|
|
767
788
|
// raw command's text -> Enter window cannot be interrupted by the follow-up.
|
|
768
789
|
if (sent && msg.followUpContent) {
|
|
769
|
-
sendToPty(msg.followUpContent,
|
|
790
|
+
sendToPty(msg.followUpContent, msg.followUpTurnId, {
|
|
770
791
|
codexAppInput: msg.followUpCodexAppInput,
|
|
771
792
|
});
|
|
772
793
|
log(`Enqueued follow-up after raw input (${msg.followUpContent.length} chars)`);
|
|
@@ -3421,7 +3442,12 @@ function startScreenAnalyzer() {
|
|
|
3421
3442
|
},
|
|
3422
3443
|
onTuiPromptResolved: (selectedText) => {
|
|
3423
3444
|
tuiPromptBlocking = false;
|
|
3424
|
-
send({
|
|
3445
|
+
send({
|
|
3446
|
+
type: 'tui_prompt_resolved',
|
|
3447
|
+
selectedText,
|
|
3448
|
+
turnId: currentBotmuxTurnId,
|
|
3449
|
+
dispatchAttempt: currentBotmuxDispatchAttempt,
|
|
3450
|
+
});
|
|
3425
3451
|
// Flush any messages that were queued during the prompt
|
|
3426
3452
|
flushPending();
|
|
3427
3453
|
},
|
|
@@ -3570,7 +3596,13 @@ async function captureAndUpload() {
|
|
|
3570
3596
|
let status = isPromptReady ? 'idle' : 'working';
|
|
3571
3597
|
if (screenAnalyzer?.isAnalyzing)
|
|
3572
3598
|
status = 'analyzing';
|
|
3573
|
-
send({
|
|
3599
|
+
send({
|
|
3600
|
+
type: 'screenshot_uploaded',
|
|
3601
|
+
imageKey,
|
|
3602
|
+
...usageLimitTracker.classify(usageLimitContent, status),
|
|
3603
|
+
turnId: currentBotmuxTurnId,
|
|
3604
|
+
dispatchAttempt: currentBotmuxDispatchAttempt,
|
|
3605
|
+
});
|
|
3574
3606
|
}
|
|
3575
3607
|
function applyDisplayMode(mode) {
|
|
3576
3608
|
displayMode = mode;
|
|
@@ -4183,7 +4215,12 @@ function persistCliSessionId(cliSessionId) {
|
|
|
4183
4215
|
return;
|
|
4184
4216
|
if (lastInitConfig)
|
|
4185
4217
|
lastInitConfig.cliSessionId = cliSessionId;
|
|
4186
|
-
send({
|
|
4218
|
+
send({
|
|
4219
|
+
type: 'cli_session_id',
|
|
4220
|
+
cliSessionId,
|
|
4221
|
+
turnId: currentBotmuxTurnId,
|
|
4222
|
+
dispatchAttempt: currentBotmuxDispatchAttempt,
|
|
4223
|
+
});
|
|
4187
4224
|
try {
|
|
4188
4225
|
const session = sessionStore.getSession(sessionId);
|
|
4189
4226
|
if (!session || session.cliSessionId === cliSessionId)
|
|
@@ -4753,7 +4790,12 @@ function sendToPty(content, turnId, opts = {}) {
|
|
|
4753
4790
|
tuiPromptBlocking = false;
|
|
4754
4791
|
screenAnalyzer?.notifySelection('lark-input');
|
|
4755
4792
|
// Tear down the prompt card so the user doesn't see stale options.
|
|
4756
|
-
send({
|
|
4793
|
+
send({
|
|
4794
|
+
type: 'tui_prompt_resolved',
|
|
4795
|
+
selectedText: 'user-override',
|
|
4796
|
+
turnId: currentBotmuxTurnId,
|
|
4797
|
+
dispatchAttempt: currentBotmuxDispatchAttempt,
|
|
4798
|
+
});
|
|
4757
4799
|
}
|
|
4758
4800
|
// See flushPending: type-ahead adapters flush even while the CLI is busy.
|
|
4759
4801
|
// Claude attributes `attachment(queued_command)` identically to `role:user`;
|
|
@@ -5146,7 +5188,7 @@ function scheduleBusyPatternIdleProbe(source) {
|
|
|
5146
5188
|
busyPatternIdleProbeTimer = setTimeout(tick, IDLE_PROBE_INTERVAL_MS);
|
|
5147
5189
|
busyPatternIdleProbeTimer.unref?.();
|
|
5148
5190
|
}
|
|
5149
|
-
function spawnCli(cfg) {
|
|
5191
|
+
async function spawnCli(cfg) {
|
|
5150
5192
|
clearSessionRenameInFlight();
|
|
5151
5193
|
// (startupCommands one-shot is re-armed below, AFTER the reattach-vs-fresh
|
|
5152
5194
|
// prediction — only a genuinely fresh CLI process replays them; see
|
|
@@ -5201,7 +5243,12 @@ function spawnCli(cfg) {
|
|
|
5201
5243
|
setupAdoptIdleDetection(cfg, 'herdr');
|
|
5202
5244
|
backend.onData(onPtyData);
|
|
5203
5245
|
backend.onAccessUrl?.((url) => {
|
|
5204
|
-
send({
|
|
5246
|
+
send({
|
|
5247
|
+
type: 'riff_access_url',
|
|
5248
|
+
accessUrl: url,
|
|
5249
|
+
turnId: currentBotmuxTurnId,
|
|
5250
|
+
dispatchAttempt: currentBotmuxDispatchAttempt,
|
|
5251
|
+
});
|
|
5205
5252
|
});
|
|
5206
5253
|
backend.onExit((code, signal) => {
|
|
5207
5254
|
log(`Adopted herdr stream ended (code: ${code}, signal: ${signal})`);
|
|
@@ -5254,7 +5301,12 @@ function spawnCli(cfg) {
|
|
|
5254
5301
|
setupAdoptInputAdapter(cfg);
|
|
5255
5302
|
backend.onData(onPtyData);
|
|
5256
5303
|
backend.onAccessUrl?.((url) => {
|
|
5257
|
-
send({
|
|
5304
|
+
send({
|
|
5305
|
+
type: 'riff_access_url',
|
|
5306
|
+
accessUrl: url,
|
|
5307
|
+
turnId: currentBotmuxTurnId,
|
|
5308
|
+
dispatchAttempt: currentBotmuxDispatchAttempt,
|
|
5309
|
+
});
|
|
5258
5310
|
});
|
|
5259
5311
|
backend.onExit((code, signal) => {
|
|
5260
5312
|
log(`Adopted pipe-pane stream ended (code: ${code}, signal: ${signal})`);
|
|
@@ -5597,6 +5649,21 @@ function spawnCli(cfg) {
|
|
|
5597
5649
|
}
|
|
5598
5650
|
}
|
|
5599
5651
|
}
|
|
5652
|
+
let mcpRuntimeManifest = readSessionMcpRuntimeManifest(cfg.sessionId, config.session.dataDir);
|
|
5653
|
+
if (cliAdapter.mcpGateway && mcpRuntimeManifest?.entries.length && persistentSessionName && effectiveBackendType !== 'pty') {
|
|
5654
|
+
const paneLive = effectiveBackendType === 'tmux'
|
|
5655
|
+
? TmuxBackend.hasSession(persistentSessionName)
|
|
5656
|
+
: effectiveBackendType === 'zellij'
|
|
5657
|
+
? ZellijBackend.hasSession(persistentSessionName)
|
|
5658
|
+
: HerdrBackend.hasSession(persistentSessionName);
|
|
5659
|
+
if (paneLive) {
|
|
5660
|
+
// The trusted Gateway host belongs to the worker and cannot survive a
|
|
5661
|
+
// worker/daemon replacement. Cold-resume the CLI so its MCP client gets a
|
|
5662
|
+
// fresh relay socket instead of reattaching to a dead connection.
|
|
5663
|
+
log(`[mcp-gateway] persistent pane ${cfg.sessionId} has plugin MCP state — cold-resuming with a fresh host`);
|
|
5664
|
+
killPersistentSession(effectiveBackendType, persistentSessionName);
|
|
5665
|
+
}
|
|
5666
|
+
}
|
|
5600
5667
|
const willReattachPersistent = persistentSessionName
|
|
5601
5668
|
? effectiveBackendType === 'tmux'
|
|
5602
5669
|
? TmuxBackend.hasSession(persistentSessionName)
|
|
@@ -5607,8 +5674,19 @@ function spawnCli(cfg) {
|
|
|
5607
5674
|
// The plugin set is stable only for the lifetime of one real CLI process.
|
|
5608
5675
|
// A warm worker reattach keeps the existing Gateway and catalog untouched;
|
|
5609
5676
|
// every fresh/resumed CLI spawn atomically refreshes both from current Bot config.
|
|
5610
|
-
if (!willReattachPersistent)
|
|
5677
|
+
if (!willReattachPersistent) {
|
|
5611
5678
|
refreshCliPluginGeneration(cfg, cliAdapter);
|
|
5679
|
+
mcpRuntimeManifest = readSessionMcpRuntimeManifest(cfg.sessionId, config.session.dataDir);
|
|
5680
|
+
stopSessionMcpGatewayHost();
|
|
5681
|
+
if (cliAdapter.mcpGateway && mcpRuntimeManifest?.entries.length) {
|
|
5682
|
+
sessionMcpGatewayHost = await startSessionMcpGatewayHost({
|
|
5683
|
+
sessionId: cfg.sessionId,
|
|
5684
|
+
dataDir: config.session.dataDir,
|
|
5685
|
+
onError: error => log(`[mcp-gateway] host error: ${error.message}`),
|
|
5686
|
+
});
|
|
5687
|
+
log(`[mcp-gateway] trusted host listening for ${mcpRuntimeManifest.entries.length} plugin server(s)`);
|
|
5688
|
+
}
|
|
5689
|
+
}
|
|
5612
5690
|
// Re-arm the startup-commands one-shot ONLY for a genuinely fresh CLI process.
|
|
5613
5691
|
// A reattach to a LIVE persistent pane (daemon-restart recovery) is the SAME
|
|
5614
5692
|
// CLI with /effort etc. already applied — replaying would re-type them (and
|
|
@@ -5890,6 +5968,14 @@ function spawnCli(cfg) {
|
|
|
5890
5968
|
// namespaced BOTMUX_LARK_APP_ID injected below; the worker keeps its own
|
|
5891
5969
|
// bare creds (forkWorker) for lark-upload. See utils/child-env.ts.
|
|
5892
5970
|
const childEnv = redactChildEnv(process.env);
|
|
5971
|
+
if (sessionMcpGatewayHost) {
|
|
5972
|
+
childEnv[MCP_GATEWAY_SOCKET_ENV] = sessionMcpGatewayHost.socketPath;
|
|
5973
|
+
childEnv[MCP_GATEWAY_REQUIRED_ENV] = '1';
|
|
5974
|
+
}
|
|
5975
|
+
else {
|
|
5976
|
+
delete childEnv[MCP_GATEWAY_SOCKET_ENV];
|
|
5977
|
+
delete childEnv[MCP_GATEWAY_REQUIRED_ENV];
|
|
5978
|
+
}
|
|
5893
5979
|
// Put the daemon-written wrapper dir (~/.botmux/bin/botmux = THIS build) ahead of any
|
|
5894
5980
|
// stale npm-global botmux in PATH, so the agent's `botmux` is always this build. Matters
|
|
5895
5981
|
// most under read isolation: only this build has the send-cred reader — a shadowing stale
|
|
@@ -6032,6 +6118,24 @@ function spawnCli(cfg) {
|
|
|
6032
6118
|
sessionDataDirs: [readIsolationCtx.sessionDataDir],
|
|
6033
6119
|
});
|
|
6034
6120
|
}
|
|
6121
|
+
if (mcpRuntimeManifest) {
|
|
6122
|
+
finalDenyPaths.push(...sessionMcpRuntimeHostOnlyPaths(mcpRuntimeManifest, config.session.dataDir).map(canonical));
|
|
6123
|
+
}
|
|
6124
|
+
// Every isolated macOS CLI is denied access to all same-UID Gateway socket
|
|
6125
|
+
// directories. A session with its own Gateway gets one narrow, later READ
|
|
6126
|
+
// carve-out for its token; writes stay denied so the CLI cannot unlink or
|
|
6127
|
+
// replace even its own worker-managed socket. Sessions without plugin MCP
|
|
6128
|
+
// state get no read carve-out at all.
|
|
6129
|
+
const gatewaySocketRoot = canonical(sessionMcpGatewayHost ? dirname(sessionMcpGatewayHost.socketDir) : tmpdir());
|
|
6130
|
+
const gatewaySocketRegex = sessionMcpGatewayPathRegex(gatewaySocketRoot);
|
|
6131
|
+
denyRegexes.push(gatewaySocketRegex);
|
|
6132
|
+
if (sessionMcpGatewayHost)
|
|
6133
|
+
allowPaths.push(canonical(sessionMcpGatewayHost.socketDir));
|
|
6134
|
+
protectedWrites = {
|
|
6135
|
+
denyWritePaths: protectedWrites?.denyWritePaths ?? [],
|
|
6136
|
+
denyWriteRegexes: [...(protectedWrites?.denyWriteRegexes ?? []), gatewaySocketRegex],
|
|
6137
|
+
denyWriteLiterals: protectedWrites?.denyWriteLiterals ?? [],
|
|
6138
|
+
};
|
|
6035
6139
|
// WRITE rules — the macOS file-sandbox (Linux-bwrap twin). Realpath the writable
|
|
6036
6140
|
// zones + crown-jewel re-denies for the same symlink-safety reason as reads.
|
|
6037
6141
|
const writeRules = writeSandboxRules
|
|
@@ -6218,7 +6322,12 @@ function spawnCli(cfg) {
|
|
|
6218
6322
|
? [sendCredFilePath(dataDir, cfg.larkAppId)]
|
|
6219
6323
|
: [],
|
|
6220
6324
|
extraExecPaths: cliAdapter.sandboxExtraExecPaths?.(),
|
|
6221
|
-
readonlyRoots: [
|
|
6325
|
+
readonlyRoots: [
|
|
6326
|
+
...(cfg.skillReadonlyRoots ?? []),
|
|
6327
|
+
...(readIsoLinuxMasks?.ownReadOnlyPaths ?? []),
|
|
6328
|
+
],
|
|
6329
|
+
mcpGatewaySocketPath: sessionMcpGatewayHost?.socketPath,
|
|
6330
|
+
trustedBotmuxCommandPaths: [defaultGatewayEntry().command],
|
|
6222
6331
|
userReadonlyPaths: cfg.sandboxReadonlyPaths ?? [],
|
|
6223
6332
|
net: cfg.sandboxNetwork !== false,
|
|
6224
6333
|
});
|
|
@@ -6633,7 +6742,12 @@ function spawnCli(cfg) {
|
|
|
6633
6742
|
backend.onData(onPtyData);
|
|
6634
6743
|
const observedBackend = backend;
|
|
6635
6744
|
backend.onAccessUrl?.((url) => {
|
|
6636
|
-
send({
|
|
6745
|
+
send({
|
|
6746
|
+
type: 'riff_access_url',
|
|
6747
|
+
accessUrl: url,
|
|
6748
|
+
turnId: currentBotmuxTurnId,
|
|
6749
|
+
dispatchAttempt: currentBotmuxDispatchAttempt,
|
|
6750
|
+
});
|
|
6637
6751
|
});
|
|
6638
6752
|
// Remote-task turn boundary (riff): flushPending() marks the session busy on
|
|
6639
6753
|
// every write and riff has no PTY output, so the idle detector never re-arms
|
|
@@ -6663,6 +6777,7 @@ function spawnCli(cfg) {
|
|
|
6663
6777
|
log(`Ignored stale backend exit (code: ${code}, signal: ${signal})`);
|
|
6664
6778
|
return;
|
|
6665
6779
|
}
|
|
6780
|
+
stopSessionMcpGatewayHost();
|
|
6666
6781
|
const exitedTurnId = currentBotmuxTurnId;
|
|
6667
6782
|
const exitedDispatchAttempt = currentBotmuxDispatchAttempt;
|
|
6668
6783
|
// Fail closed as soon as this CLI generation ends. The Node worker may
|
|
@@ -6786,6 +6901,7 @@ function spawnCli(cfg) {
|
|
|
6786
6901
|
}
|
|
6787
6902
|
}
|
|
6788
6903
|
function killCli(opts = {}) {
|
|
6904
|
+
stopSessionMcpGatewayHost();
|
|
6789
6905
|
destroyCrashDiagnosticTerminal('killCli');
|
|
6790
6906
|
idleDetector?.dispose();
|
|
6791
6907
|
idleDetector = null;
|
|
@@ -6916,7 +7032,7 @@ async function restartCliProcess(reason, opts = {}) {
|
|
|
6916
7032
|
startScreenUpdates();
|
|
6917
7033
|
startScreenAnalyzer();
|
|
6918
7034
|
try {
|
|
6919
|
-
spawnCli({ ...lastInitConfig, resume: true, prompt: '' });
|
|
7035
|
+
await spawnCli({ ...lastInitConfig, resume: true, prompt: '' });
|
|
6920
7036
|
}
|
|
6921
7037
|
catch (err) {
|
|
6922
7038
|
cliRestartInProgress = false;
|
|
@@ -8181,7 +8297,7 @@ process.on('message', async (raw) => {
|
|
|
8181
8297
|
port = await startWebServer(config.web.workerHost, msg.webPort);
|
|
8182
8298
|
log('Workflow worker mode: web terminal enabled; skipping screen updates and screen analyzer');
|
|
8183
8299
|
}
|
|
8184
|
-
spawnCli(msg);
|
|
8300
|
+
await spawnCli(msg);
|
|
8185
8301
|
// Queue the initial prompt — flushed when CLI shows idle.
|
|
8186
8302
|
// Adapters with passesInitialPromptViaArgs (e.g. Gemini -i) bake the
|
|
8187
8303
|
// prompt into CLI args, so we normally skip queuing to avoid double-send.
|
|
@@ -8270,7 +8386,7 @@ process.on('message', async (raw) => {
|
|
|
8270
8386
|
startScreenUpdates();
|
|
8271
8387
|
startScreenAnalyzer();
|
|
8272
8388
|
try {
|
|
8273
|
-
spawnCli({ ...lastInitConfig, resume: true, prompt: '' });
|
|
8389
|
+
await spawnCli({ ...lastInitConfig, resume: true, prompt: '' });
|
|
8274
8390
|
}
|
|
8275
8391
|
catch (err) {
|
|
8276
8392
|
// Pass the message's own attempt (not the stale currentBotmux* from a
|
|
@@ -8657,6 +8773,7 @@ process.on('message', async (raw) => {
|
|
|
8657
8773
|
});
|
|
8658
8774
|
// ─── Cleanup ─────────────────────────────────────────────────────────────────
|
|
8659
8775
|
function cleanup() {
|
|
8776
|
+
stopSessionMcpGatewayHost();
|
|
8660
8777
|
if (tmuxRestartTimer) {
|
|
8661
8778
|
clearTimeout(tmuxRestartTimer);
|
|
8662
8779
|
tmuxRestartTimer = null;
|