botmux 3.0.0 → 3.1.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.
Files changed (207) hide show
  1. package/dist/adapters/backend/herdr-backend.d.ts +9 -7
  2. package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
  3. package/dist/adapters/backend/herdr-backend.js +63 -53
  4. package/dist/adapters/backend/herdr-backend.js.map +1 -1
  5. package/dist/adapters/backend/riff-backend.d.ts +6 -1
  6. package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
  7. package/dist/adapters/backend/riff-backend.js +7 -2
  8. package/dist/adapters/backend/riff-backend.js.map +1 -1
  9. package/dist/adapters/backend/sandbox.d.ts +20 -16
  10. package/dist/adapters/backend/sandbox.d.ts.map +1 -1
  11. package/dist/adapters/backend/sandbox.js +192 -65
  12. package/dist/adapters/backend/sandbox.js.map +1 -1
  13. package/dist/adapters/backend/tmux-backend.d.ts +39 -2
  14. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  15. package/dist/adapters/backend/tmux-backend.js +55 -5
  16. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  17. package/dist/adapters/backend/tmux-pipe-backend.js +2 -2
  18. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
  19. package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
  20. package/dist/adapters/backend/zellij-backend.js +9 -3
  21. package/dist/adapters/backend/zellij-backend.js.map +1 -1
  22. package/dist/adapters/cli/claude-code.d.ts +25 -0
  23. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  24. package/dist/adapters/cli/claude-code.js +79 -9
  25. package/dist/adapters/cli/claude-code.js.map +1 -1
  26. package/dist/adapters/cli/read-isolation.d.ts +2 -1
  27. package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
  28. package/dist/adapters/cli/read-isolation.js.map +1 -1
  29. package/dist/adapters/cli/types.d.ts +2 -2
  30. package/dist/adapters/hook-installer.d.ts +11 -0
  31. package/dist/adapters/hook-installer.d.ts.map +1 -1
  32. package/dist/adapters/hook-installer.js +72 -11
  33. package/dist/adapters/hook-installer.js.map +1 -1
  34. package/dist/cli.d.ts.map +1 -1
  35. package/dist/cli.js +15 -4
  36. package/dist/cli.js.map +1 -1
  37. package/dist/config.d.ts +6 -0
  38. package/dist/config.d.ts.map +1 -1
  39. package/dist/config.js +18 -0
  40. package/dist/config.js.map +1 -1
  41. package/dist/core/command-handler.d.ts.map +1 -1
  42. package/dist/core/command-handler.js +211 -57
  43. package/dist/core/command-handler.js.map +1 -1
  44. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  45. package/dist/core/dashboard-ipc-server.js +12 -5
  46. package/dist/core/dashboard-ipc-server.js.map +1 -1
  47. package/dist/core/dashboard-rows.d.ts +5 -0
  48. package/dist/core/dashboard-rows.d.ts.map +1 -1
  49. package/dist/core/dashboard-rows.js +2 -0
  50. package/dist/core/dashboard-rows.js.map +1 -1
  51. package/dist/core/doc-comment-prompt.d.ts +2 -0
  52. package/dist/core/doc-comment-prompt.d.ts.map +1 -1
  53. package/dist/core/doc-comment-prompt.js +30 -0
  54. package/dist/core/doc-comment-prompt.js.map +1 -1
  55. package/dist/core/github-auth.d.ts +8 -0
  56. package/dist/core/github-auth.d.ts.map +1 -0
  57. package/dist/core/github-auth.js +44 -0
  58. package/dist/core/github-auth.js.map +1 -0
  59. package/dist/core/managed-origin-capability.d.ts +7 -0
  60. package/dist/core/managed-origin-capability.d.ts.map +1 -1
  61. package/dist/core/managed-origin-capability.js +12 -0
  62. package/dist/core/managed-origin-capability.js.map +1 -1
  63. package/dist/core/passthrough-commands.d.ts.map +1 -1
  64. package/dist/core/passthrough-commands.js +1 -1
  65. package/dist/core/passthrough-commands.js.map +1 -1
  66. package/dist/core/restart-report.d.ts +7 -1
  67. package/dist/core/restart-report.d.ts.map +1 -1
  68. package/dist/core/restart-report.js +19 -5
  69. package/dist/core/restart-report.js.map +1 -1
  70. package/dist/core/session-activity.d.ts +8 -0
  71. package/dist/core/session-activity.d.ts.map +1 -1
  72. package/dist/core/session-activity.js +16 -0
  73. package/dist/core/session-activity.js.map +1 -1
  74. package/dist/core/session-discovery.d.ts.map +1 -1
  75. package/dist/core/session-discovery.js +4 -2
  76. package/dist/core/session-discovery.js.map +1 -1
  77. package/dist/core/update-check.d.ts +11 -0
  78. package/dist/core/update-check.d.ts.map +1 -1
  79. package/dist/core/update-check.js +6 -1
  80. package/dist/core/update-check.js.map +1 -1
  81. package/dist/core/vc-meeting-prepare-command.d.ts +22 -0
  82. package/dist/core/vc-meeting-prepare-command.d.ts.map +1 -0
  83. package/dist/core/vc-meeting-prepare-command.js +50 -0
  84. package/dist/core/vc-meeting-prepare-command.js.map +1 -0
  85. package/dist/core/worker-pool.d.ts.map +1 -1
  86. package/dist/core/worker-pool.js +11 -2
  87. package/dist/core/worker-pool.js.map +1 -1
  88. package/dist/daemon.d.ts +15 -5
  89. package/dist/daemon.d.ts.map +1 -1
  90. package/dist/daemon.js +555 -41
  91. package/dist/daemon.js.map +1 -1
  92. package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
  93. package/dist/dashboard/web/bot-defaults-page.js +6 -1
  94. package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
  95. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  96. package/dist/dashboard/web/i18n.js +34 -0
  97. package/dist/dashboard/web/i18n.js.map +1 -1
  98. package/dist/dashboard/web/preferences.d.ts +1 -1
  99. package/dist/dashboard/web/preferences.d.ts.map +1 -1
  100. package/dist/dashboard/web/preferences.js +1 -1
  101. package/dist/dashboard/web/preferences.js.map +1 -1
  102. package/dist/dashboard/web/sessions-page.d.ts +19 -0
  103. package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
  104. package/dist/dashboard/web/sessions-page.js +66 -7
  105. package/dist/dashboard/web/sessions-page.js.map +1 -1
  106. package/dist/dashboard/web/sessions.d.ts +31 -0
  107. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  108. package/dist/dashboard/web/sessions.js +82 -0
  109. package/dist/dashboard/web/sessions.js.map +1 -1
  110. package/dist/dashboard-web/app.js +1 -1
  111. package/dist/dashboard-web/chunks/{bot-defaults-page-MBORWWP7.js → bot-defaults-page-KLDCZNLP.js} +5 -5
  112. package/dist/dashboard-web/chunks/{chunk-G43RUPUN.js → chunk-EZAW2EPM.js} +1 -1
  113. package/dist/dashboard-web/chunks/{chunk-EIO5AWDA.js → chunk-FMJNOATE.js} +1 -1
  114. package/dist/dashboard-web/chunks/{chunk-4QTBR7UF.js → chunk-KEAOTUL3.js} +7 -7
  115. package/dist/dashboard-web/chunks/{chunk-7STGZ5BX.js → chunk-MLNI5BKE.js} +1 -1
  116. package/dist/dashboard-web/chunks/{connectors-page-LKDT4WBG.js → connectors-page-O32LFAL5.js} +1 -1
  117. package/dist/dashboard-web/chunks/{groups-page-XKIWJ2TP.js → groups-page-PSAFIZ6M.js} +1 -1
  118. package/dist/dashboard-web/chunks/{insights-page-CBEZCYKV.js → insights-page-V23LLATX.js} +1 -1
  119. package/dist/dashboard-web/chunks/{monitor-room-AQTHAWXA.js → monitor-room-7X5AKYOF.js} +1 -1
  120. package/dist/dashboard-web/chunks/{monitoring-page-2R4NCNBQ.js → monitoring-page-CBA3CQYL.js} +1 -1
  121. package/dist/dashboard-web/chunks/{office-page-5M6MQIIL.js → office-page-RL6QSEM7.js} +1 -1
  122. package/dist/dashboard-web/chunks/{overview-page-N22HDZ2V.js → overview-page-XQIPO6KH.js} +1 -1
  123. package/dist/dashboard-web/chunks/{roles-page-J6K2HWFX.js → roles-page-ACIWV7QS.js} +1 -1
  124. package/dist/dashboard-web/chunks/{schedules-page-CGAQCB4J.js → schedules-page-KJDUKOJV.js} +1 -1
  125. package/dist/dashboard-web/chunks/sessions-page-2WDWHSP4.js +3 -0
  126. package/dist/dashboard-web/chunks/{settings-page-GLRWV5KU.js → settings-page-6VX4OUGG.js} +1 -1
  127. package/dist/dashboard-web/chunks/{skills-page-KQVW42QU.js → skills-page-WXE3UYLT.js} +1 -1
  128. package/dist/dashboard-web/chunks/{team-federation-page-5PYUOV5M.js → team-federation-page-PYTF4FLM.js} +1 -1
  129. package/dist/dashboard-web/chunks/{v3-page-DXPFILRZ.js → v3-page-G6CD6O3I.js} +1 -1
  130. package/dist/dashboard-web/chunks/{whiteboards-page-NUH6VZ4I.js → whiteboards-page-6SG33GP4.js} +1 -1
  131. package/dist/dashboard-web/style.css +167 -2
  132. package/dist/i18n/en.d.ts.map +1 -1
  133. package/dist/i18n/en.js +18 -3
  134. package/dist/i18n/en.js.map +1 -1
  135. package/dist/i18n/zh.d.ts.map +1 -1
  136. package/dist/i18n/zh.js +18 -3
  137. package/dist/i18n/zh.js.map +1 -1
  138. package/dist/im/lark/card-builder.d.ts.map +1 -1
  139. package/dist/im/lark/card-builder.js +11 -2
  140. package/dist/im/lark/card-builder.js.map +1 -1
  141. package/dist/im/lark/event-dispatcher.d.ts +4 -0
  142. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  143. package/dist/im/lark/event-dispatcher.js +249 -5
  144. package/dist/im/lark/event-dispatcher.js.map +1 -1
  145. package/dist/im/lark/forward-followup-buffer.d.ts +26 -0
  146. package/dist/im/lark/forward-followup-buffer.d.ts.map +1 -0
  147. package/dist/im/lark/forward-followup-buffer.js +48 -0
  148. package/dist/im/lark/forward-followup-buffer.js.map +1 -0
  149. package/dist/im/lark/forward-followup-content.d.ts +6 -0
  150. package/dist/im/lark/forward-followup-content.d.ts.map +1 -0
  151. package/dist/im/lark/forward-followup-content.js +46 -0
  152. package/dist/im/lark/forward-followup-content.js.map +1 -0
  153. package/dist/im/lark/forward-followup-store.d.ts +9 -0
  154. package/dist/im/lark/forward-followup-store.d.ts.map +1 -0
  155. package/dist/im/lark/forward-followup-store.js +48 -0
  156. package/dist/im/lark/forward-followup-store.js.map +1 -0
  157. package/dist/im/lark/message-parser.d.ts +1 -1
  158. package/dist/im/lark/message-parser.d.ts.map +1 -1
  159. package/dist/im/lark/message-parser.js +1 -2
  160. package/dist/im/lark/message-parser.js.map +1 -1
  161. package/dist/platform/tunnel-client.js +3 -3
  162. package/dist/platform/tunnel-client.js.map +1 -1
  163. package/dist/services/local-cli-opener.d.ts +7 -0
  164. package/dist/services/local-cli-opener.d.ts.map +1 -1
  165. package/dist/services/local-cli-opener.js +111 -3
  166. package/dist/services/local-cli-opener.js.map +1 -1
  167. package/dist/services/skill-registry-store.d.ts.map +1 -1
  168. package/dist/services/skill-registry-store.js +15 -2
  169. package/dist/services/skill-registry-store.js.map +1 -1
  170. package/dist/services/vc-meeting-consumer-isolation.d.ts +7 -5
  171. package/dist/services/vc-meeting-consumer-isolation.d.ts.map +1 -1
  172. package/dist/services/vc-meeting-consumer-isolation.js +7 -5
  173. package/dist/services/vc-meeting-consumer-isolation.js.map +1 -1
  174. package/dist/services/vc-meeting-preparations-store.d.ts +22 -0
  175. package/dist/services/vc-meeting-preparations-store.d.ts.map +1 -0
  176. package/dist/services/vc-meeting-preparations-store.js +131 -0
  177. package/dist/services/vc-meeting-preparations-store.js.map +1 -0
  178. package/dist/services/vc-meeting-runtime-store.d.ts +9 -0
  179. package/dist/services/vc-meeting-runtime-store.d.ts.map +1 -1
  180. package/dist/services/vc-meeting-runtime-store.js +16 -0
  181. package/dist/services/vc-meeting-runtime-store.js.map +1 -1
  182. package/dist/setup/ensure-tmux.d.ts.map +1 -1
  183. package/dist/setup/ensure-tmux.js +9 -11
  184. package/dist/setup/ensure-tmux.js.map +1 -1
  185. package/dist/utils/child-env.d.ts +16 -8
  186. package/dist/utils/child-env.d.ts.map +1 -1
  187. package/dist/utils/child-env.js +30 -8
  188. package/dist/utils/child-env.js.map +1 -1
  189. package/dist/utils/daemon-discovery.d.ts +8 -0
  190. package/dist/utils/daemon-discovery.d.ts.map +1 -1
  191. package/dist/utils/daemon-discovery.js +19 -0
  192. package/dist/utils/daemon-discovery.js.map +1 -1
  193. package/dist/utils/ready-gate.d.ts +9 -3
  194. package/dist/utils/ready-gate.d.ts.map +1 -1
  195. package/dist/utils/ready-gate.js +12 -4
  196. package/dist/utils/ready-gate.js.map +1 -1
  197. package/dist/worker.js +131 -28
  198. package/dist/worker.js.map +1 -1
  199. package/dist/workflows/ops-projection.d.ts.map +1 -1
  200. package/dist/workflows/ops-projection.js +27 -5
  201. package/dist/workflows/ops-projection.js.map +1 -1
  202. package/dist/workflows/v3/distillation-runner.d.ts +4 -0
  203. package/dist/workflows/v3/distillation-runner.d.ts.map +1 -1
  204. package/dist/workflows/v3/distillation-runner.js +4 -2
  205. package/dist/workflows/v3/distillation-runner.js.map +1 -1
  206. package/package.json +1 -1
  207. package/dist/dashboard-web/chunks/sessions-page-WJ2QYTIE.js +0 -3
package/dist/worker.js CHANGED
@@ -72,7 +72,7 @@ import { createCliAdapterSync, locateOnPath } from './adapters/cli/registry.js';
72
72
  import { buildWrappedLaunch, parseWrapperCli, isTtadkWrapper } from './setup/cli-selection.js';
73
73
  import { cliUnavailableMessage } from './setup/cli-availability.js';
74
74
  import { findLaunchedCliPid, scheduleWrapperRealCliPid, readComm, isBareShellComm, bareShellLaunchKind } from './core/session-discovery.js';
75
- import { claudeJsonlPathForSession, resolveJsonlFromPid, findOpenClaudeSessionIds, DEFAULT_CLAUDE_DATA_DIR } from './adapters/cli/claude-code.js';
75
+ import { claudeJsonlPathForSession, resolveJsonlFromPid, findOpenClaudeSessionIds, syncClaudeResumeTargetToCwd, DEFAULT_CLAUDE_DATA_DIR } from './adapters/cli/claude-code.js';
76
76
  import { sessionReadyHookCommand } from './adapters/hook-command.js';
77
77
  import { mtrSessionIdForBotmuxSession } from './adapters/cli/mtr.js';
78
78
  import { HerdrBackend } from './adapters/backend/herdr-backend.js';
@@ -83,7 +83,7 @@ import { ZellijObserveBackend } from './adapters/backend/zellij-observe-backend.
83
83
  import { zellijEnv } from './setup/ensure-zellij.js';
84
84
  import { isObserveBackend } from './adapters/backend/types.js';
85
85
  import { selectSessionBackend, decideBackendGate, backendGateUserMessage } from './adapters/backend/session-backend-selector.js';
86
- import { deriveRiffReposFromDirs, deriveRiffRepoFromWorkingDir, isValidRiffBaseUrl } from './adapters/backend/riff-backend.js';
86
+ import { deriveRiffReposFromDirs, deriveRiffRepoFromWorkingDir, isValidRiffBaseUrl, isValidRiffSandboxCluster, } from './adapters/backend/riff-backend.js';
87
87
  import { prepareSandbox, attachSandboxOutbox, startOutboxWatcher, sandboxEnabled, sandboxedClaudeDataDir, localSandboxApplies } from './adapters/backend/sandbox.js';
88
88
  import { tmuxEnv, probeTmuxFunctionalWithRetry } from './setup/ensure-tmux.js';
89
89
  import { tmuxRestartJitterMs } from './core/tmux-recovery.js';
@@ -102,12 +102,13 @@ import { config, resolveChatBotDiscoveryConfig } from './config.js';
102
102
  import * as sessionStore from './services/session-store.js';
103
103
  import * as pty from 'node-pty';
104
104
  import { createHash } from 'node:crypto';
105
- import { installHook } from './adapters/hook-installer.js';
105
+ import { hasInstalledSessionReadyHook, installHook, } from './adapters/hook-installer.js';
106
106
  import { hookCommandFor } from './adapters/hook-command.js';
107
+ import { parseDaemonIpcPort } from './utils/daemon-discovery.js';
107
108
  import { withCodexAppContext } from './utils/codex-app-context.js';
108
109
  import { resolveCodexAppFinalTurnIdentity } from './adapters/cli/codex-app-turn.js';
109
110
  import { RunnerControlDecoder } from './adapters/cli/runner-control-channel.js';
110
- import { managedOriginCapabilityPath, RELAY_ORIGIN_CAPABILITY_BASENAME, replaceManagedOriginCapabilityFile, } from './core/managed-origin-capability.js';
111
+ import { hasMatchingManagedOriginCapability, managedOriginCapabilityPath, RELAY_ORIGIN_CAPABILITY_BASENAME, replaceManagedOriginCapabilityFile, } from './core/managed-origin-capability.js';
111
112
  // ─── State ───────────────────────────────────────────────────────────────────
112
113
  let cliAdapter = null;
113
114
  let backend = null;
@@ -186,6 +187,24 @@ function provisionIsolatedBotHome(botHome, workingDir, isClaude, cliId, hookInst
186
187
  if (isClaude) {
187
188
  const cdir = join(botHome, 'claude');
188
189
  mkdirSync(cdir, { recursive: true });
190
+ // Settings: read-isolated Claude uses a fresh CLAUDE_CONFIG_DIR, so it
191
+ // otherwise loses provider auth/model/proxy values held in the shared
192
+ // ~/.claude/settings.json `env` map. Merge that map on EVERY cold spawn,
193
+ // then install botmux hooks into the same per-bot file. Global hooks and
194
+ // unrelated top-level settings are not inherited.
195
+ const isolatedSettingsPath = join(cdir, 'settings.json');
196
+ if (hookInstall) {
197
+ try {
198
+ installHook(cliId, {
199
+ ...hookInstall,
200
+ configPath: isolatedSettingsPath,
201
+ inheritClaudeEnvFrom: join(homedir(), '.claude', 'settings.json'),
202
+ }, hookCommandFor(cliId));
203
+ }
204
+ catch (e) {
205
+ log(`[read-isolation] WARN per-bot settings/hook install failed: ${e.message}`);
206
+ }
207
+ }
189
208
  // Auth: a fresh CLAUDE_CONFIG_DIR does NOT inherit the shared account's OAuth
190
209
  // token → keep <cdir>/.credentials.json synced to the FRESHEST valid credential
191
210
  // on EVERY spawn (verified: Claude logs in from that file). Refreshing here (not
@@ -194,24 +213,14 @@ function provisionIsolatedBotHome(botHome, workingDir, isClaude, cliId, hookInst
194
213
  const fresh = freshestClaudeCred();
195
214
  if (fresh)
196
215
  writeCredIfChanged(join(cdir, '.credentials.json'), fresh);
197
- else if (!existsSync(join(cdir, '.credentials.json'))) {
198
- log(`[read-isolation] WARN no Claude credential found (keychain or ~/.claude/.credentials.json) — bot may hit login screen`);
216
+ else if (!existsSync(join(cdir, '.credentials.json'))
217
+ && !claudeSettingsHasProviderAuth(isolatedSettingsPath)) {
218
+ log(`[read-isolation] WARN no Claude provider auth found (global settings env, keychain, or ~/.claude/.credentials.json) — bot may hit login screen`);
199
219
  }
200
220
  // State: seed <cdir>/.claude.json from the GLOBAL one MINUS `projects` (keeps the
201
221
  // onboarding/promo "seen" flags + account so no dialogs appear, without leaking
202
222
  // other projects' data), then trust this bot's cwd. Merge-safe on resume.
203
223
  seedAndTrustClaudeState(join(cdir, '.claude.json'), workingDir, log);
204
- // Hooks: install the SessionStart-ready + askUserQuestion hooks into the PER-BOT
205
- // settings.json (global ~/.claude/settings.json is Seatbelt-denied), else the
206
- // worker's ready gate falls back to a slow timeout and AskUserQuestion won't relay.
207
- if (hookInstall) {
208
- try {
209
- installHook(cliId, { ...hookInstall, configPath: join(cdir, 'settings.json') }, hookCommandFor(cliId));
210
- }
211
- catch (e) {
212
- log(`[read-isolation] WARN per-bot hook install failed: ${e.message}`);
213
- }
214
- }
215
224
  }
216
225
  else {
217
226
  const cdir = join(botHome, 'codex');
@@ -232,6 +241,22 @@ function provisionIsolatedBotHome(botHome, workingDir, isClaude, cliId, hookInst
232
241
  log(`[read-isolation] WARN provisioning bot home failed: ${e.message}`);
233
242
  }
234
243
  }
244
+ function claudeSettingsHasProviderAuth(settingsPath) {
245
+ try {
246
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
247
+ const env = settings.env;
248
+ if (!env || typeof env !== 'object' || Array.isArray(env))
249
+ return false;
250
+ const record = env;
251
+ return ((typeof record.ANTHROPIC_AUTH_TOKEN === 'string' && record.ANTHROPIC_AUTH_TOKEN.length > 0)
252
+ || (typeof record.ANTHROPIC_API_KEY === 'string' && record.ANTHROPIC_API_KEY.length > 0)
253
+ || record.CLAUDE_CODE_USE_BEDROCK === '1'
254
+ || record.CLAUDE_CODE_USE_VERTEX === '1');
255
+ }
256
+ catch {
257
+ return false;
258
+ }
259
+ }
235
260
  /** Pick the FRESHEST valid Claude OAuth credential: macOS keychain vs the global
236
261
  * `~/.claude/.credentials.json`, by `claudeAiOauth.expiresAt` (longest runway
237
262
  * wins — a re-login updates one of the two, and this picks whichever is newer).
@@ -789,18 +814,28 @@ function publishSandboxRelayCapability(opts = {}) {
789
814
  }]
790
815
  : []),
791
816
  ];
817
+ let publishError;
792
818
  for (const file of files) {
793
819
  try {
794
820
  replaceManagedOriginCapabilityFile(file.path, file.body);
795
821
  }
796
822
  catch (err) {
797
823
  log(`Failed to publish managed origin capability: ${err?.message ?? err}`);
798
- if (opts.failClosed) {
799
- unlinkManagedOriginCapabilityFiles();
800
- throw err;
801
- }
824
+ publishError = err;
825
+ break;
802
826
  }
803
827
  }
828
+ if (publishError) {
829
+ // The disk/daemon/worker views must rotate as one authority generation. If
830
+ // the child-visible transport cannot publish the new token, revoke the old
831
+ // generation and leave no in-memory authority for ready/send preflights to
832
+ // mistake as usable. Any files written before a later failure are removed
833
+ // by the revocation helper as well.
834
+ completeManagedTurnOriginRevocation(sandboxRelayCapability, currentBotmuxTurnId, currentBotmuxDispatchAttempt);
835
+ if (opts.failClosed)
836
+ throw publishError;
837
+ return false;
838
+ }
804
839
  sandboxRelayCapability = capability;
805
840
  if (sessionId) {
806
841
  send({
@@ -813,6 +848,7 @@ function publishSandboxRelayCapability(opts = {}) {
813
848
  : {}),
814
849
  });
815
850
  }
851
+ return true;
816
852
  }
817
853
  function unlinkManagedOriginCapabilityFiles() {
818
854
  const files = [
@@ -4381,8 +4417,14 @@ function detectBareShellLaunch() {
4381
4417
  else {
4382
4418
  message =
4383
4419
  `⚠️ 会话没能启动:pane 里还停在 \`${comm}\`,${cli} 没真正跑起来——我没把消息打进去(否则会被当 shell 命令执行)。\n\n` +
4384
- `可能原因:rc 文件启动过慢/报错,或 \`${cli}\` 的可执行文件不在 PATH 上(CLI 没找到)。\n` +
4385
- `建议:在 web 终端里手动敲一下启动命令看报什么错;确认 CLI 二进制能在 PATH 上找到;或精简 rc 启动逻辑后重启 daemon 再试。`;
4420
+ `最常见原因:rc 文件里有交互式提示卡住了 shell 启动,例如:\n` +
4421
+ `① Oh My Zsh 升级提示("Would you like to update Oh My Zsh? [Y/n]")——${comm} source ~/.zshrc 时弹出,等你按 Y/n,CLI 的启动命令没机会跑\n` +
4422
+ `② git 凭据弹窗(GIT_TERMINAL_PROMPT)或其它需要交互输入的启动脚本\n` +
4423
+ `③ ${cli} 的可执行文件不在 PATH 上(CLI 没找到)\n\n` +
4424
+ `修法(任选其一,改完重启 daemon 再发一条消息):\n` +
4425
+ `• 最省事:升级 botmux 到含自动注入 DISABLE_AUTO_UPDATE=true 的版本(仅 botmux 托管 shell 启动时跳过 oh-my-zsh 升级检查,不影响你自己的终端)\n` +
4426
+ `• 手动修:在 ~/.zshrc 的 source $ZSH/oh-my-zsh.sh 之前加一行 DISABLE_UPDATE_PROMPT="true"(自动升级不弹提示);或加 DISABLE_AUTO_UPDATE="true"(完全跳过升级检查)\n` +
4427
+ `• 在 web 终端里手动敲一下启动命令看报什么错;确认 CLI 二进制能在 PATH 上找到;或精简 rc 启动逻辑后重启 daemon 再试`;
4386
4428
  }
4387
4429
  send({ type: 'user_notify', turnId: currentBotmuxTurnId, dispatchAttempt: currentBotmuxDispatchAttempt, message });
4388
4430
  return true;
@@ -5300,6 +5342,9 @@ function spawnCli(cfg) {
5300
5342
  if (!isValidRiffBaseUrl(cfg.backendConfig.baseUrl)) {
5301
5343
  throw new Error(`riff baseUrl 未配置或非法(需 http(s) URL,当前: ${JSON.stringify(cfg.backendConfig.baseUrl ?? null)})——请在 dashboard 的 Riff 配置中填写`);
5302
5344
  }
5345
+ if (cfg.backendConfig.sandboxCluster !== undefined && !isValidRiffSandboxCluster(cfg.backendConfig.sandboxCluster)) {
5346
+ throw new Error(`riff sandboxCluster 非法(仅支持 boe/cn,当前: ${JSON.stringify(cfg.backendConfig.sandboxCluster)})——请在 dashboard 的 Riff 配置中重新选择`);
5347
+ }
5303
5348
  const sessionEnv = {
5304
5349
  BOTMUX_SESSION_ID: cfg.sessionId,
5305
5350
  BOTMUX_CHAT_ID: cfg.chatId,
@@ -5372,6 +5417,7 @@ function spawnCli(cfg) {
5372
5417
  // JSONL/pid/bridge gate below keys off it instead of `cliId === 'claude-code'`,
5373
5418
  // so a fork inherits the whole submit-confirm + bridge-fallback machinery.
5374
5419
  let claudeDataDir = cliAdapter.claudeDataDir;
5420
+ let effectiveReadyHookInstall = cliAdapter.hookInstall;
5375
5421
  // When this session will be file-sandboxed, the CLI's session jsonl is written
5376
5422
  // into the overlay's EPHEMERAL home upper (CLAUDE_CONFIG_DIR lives under $HOME),
5377
5423
  // invisible at the real path the bridge normally watches → "Bridge mark expired"
@@ -5382,7 +5428,10 @@ function spawnCli(cfg) {
5382
5428
  (effectiveBackendType === 'pty' || effectiveBackendType === 'tmux') &&
5383
5429
  !!process.env.SESSION_DATA_DIR;
5384
5430
  if (claudeDataDir && willFileSandbox) {
5385
- const redirected = sandboxedClaudeDataDir(cfg.sessionId, claudeDataDir);
5431
+ const redirected = sandboxedClaudeDataDir(cfg.sessionId, claudeDataDir, {
5432
+ sourceWorkingDir: cfg.workingDir,
5433
+ dataDir: process.env.SESSION_DATA_DIR,
5434
+ });
5386
5435
  log(`[sandbox] redirecting Claude bridge dataDir → overlay upper: ${redirected}`);
5387
5436
  claudeDataDir = redirected;
5388
5437
  }
@@ -5465,6 +5514,12 @@ function spawnCli(cfg) {
5465
5514
  // Provision the per-bot config dir (auth + onboarding/trust seed + hooks for claude;
5466
5515
  // auth/config copy for codex) so the CLI starts fully set up under the Seatbelt wrapper.
5467
5516
  provisionIsolatedBotHome(isolationBotHome, cfg.workingDir, isClaudeFam, cfg.cliId, cliAdapter.hookInstall, log);
5517
+ if (isClaudeFam && effectiveReadyHookInstall) {
5518
+ effectiveReadyHookInstall = {
5519
+ ...effectiveReadyHookInstall,
5520
+ configPath: join(claudeDataDir, 'settings.json'),
5521
+ };
5522
+ }
5468
5523
  if (cliAdapter.mcpGateway) {
5469
5524
  const isolatedConfigPath = isClaudeFam
5470
5525
  ? join(claudeDataDir, '.claude.json')
@@ -5586,6 +5641,26 @@ function spawnCli(cfg) {
5586
5641
  let effectiveResume = cfg.resume ?? false;
5587
5642
  let effectiveCliSessionId = cfg.cliSessionId;
5588
5643
  let effectiveAdapterSessionId = adapterSessionId;
5644
+ // Claude-family transcripts are scoped by cwd. `/cd` keeps the same Botmux /
5645
+ // CLI session id, so mirror the newest native transcript into the new cwd's
5646
+ // project directory before the adapter probes or launches `--resume`.
5647
+ // `claudeDataDir` is already the effective root here (global, per-bot read
5648
+ // isolation root, or a preserved sandbox upper), so this never crosses bot
5649
+ // isolation boundaries.
5650
+ if (effectiveResume && !willReattachPersistent && claudeDataDir) {
5651
+ const resumeSessionId = effectiveCliSessionId ?? effectiveAdapterSessionId;
5652
+ try {
5653
+ const synced = syncClaudeResumeTargetToCwd(resumeSessionId, cfg.workingDir, claudeDataDir);
5654
+ if (synced.copied && synced.sourcePath) {
5655
+ log(`Claude resume transcript synced for cwd change: ${synced.sourcePath} → ${synced.targetPath}`);
5656
+ }
5657
+ }
5658
+ catch (err) {
5659
+ // Preserve the existing fail-safe: the adapter probe / two-tier fallback
5660
+ // below still decides whether resume is possible.
5661
+ log(`WARN Claude resume transcript sync failed: ${err.message}`);
5662
+ }
5663
+ }
5589
5664
  const tier2ForceFresh = effectiveResume && consecutiveInWorkerRestarts >= 2;
5590
5665
  let tier1ProbeFalse = false;
5591
5666
  if (effectiveResume && !tier2ForceFresh && !willReattachPersistent) {
@@ -6461,15 +6536,22 @@ function spawnCli(cfg) {
6461
6536
  codexBridgeStartTimer();
6462
6537
  }
6463
6538
  }
6464
- // Arm the ready-gate for FRESH Claude-family spawns (which inject the
6465
- // SessionStart hook via --settings; see claude-code.ts buildArgs). Until
6539
+ // Arm the ready-gate for FRESH ready-integrated spawns. Until
6466
6540
  // `botmux session-ready` fires (daemon → 'session_ready' IPC → releaseReadyGate)
6467
6541
  // we hold the first prompt so a cjadk-style startup selector's ❯ can't eat it.
6468
- // shouldArmReadyGate() excludes adopt (pre-existing pane, no --settings) AND
6542
+ // shouldArmReadyGate() excludes adopt (pre-existing pane, no fresh hook) AND
6469
6543
  // persistent-backend reattach (daemon restart re-attaches an already-running
6470
6544
  // tmux/zellij/herdr Claude WITHOUT re-running its bin/args → no new
6471
6545
  // SessionStart hook → arming would hold the first post-recovery message until
6472
- // the timeout). Fallback: release after READY_SIGNAL_TIMEOUT_MS → readyPattern.
6546
+ // the timeout).
6547
+ //
6548
+ // Installation is best-effort, so verify the hook in the EFFECTIVE config
6549
+ // (global for ordinary sessions, per-bot CLAUDE_CONFIG_DIR under read
6550
+ // isolation) before arming. Isolated children also need the injected loopback
6551
+ // port plus a published rotating capability; without either the hook could
6552
+ // run but never reach the daemon. A failed preflight leaves the gate open and
6553
+ // immediately falls back to the adapter's normal readyPattern/quiescence path
6554
+ // instead of blindly waiting READY_SIGNAL_TIMEOUT_MS.
6473
6555
  readyGate = new ReadyGate();
6474
6556
  if (readySignalTimer) {
6475
6557
  clearTimeout(readySignalTimer);
@@ -6483,8 +6565,29 @@ function spawnCli(cfg) {
6483
6565
  promptReadyDetectedDuringSettle = false;
6484
6566
  // Reset quiescence baseline so the settle measures silence from THIS spawn.
6485
6567
  lastPtyOutputAtMs = Date.now();
6568
+ const readyHookAvailable = effectiveReadyHookInstall
6569
+ ? hasInstalledSessionReadyHook(effectiveReadyHookInstall)
6570
+ : true; // Hermes emits BOTMUX_READY_COMMAND directly instead of a config hook.
6571
+ const isolatedReadyTransportRequired = sandboxOn || willReadIsolate;
6572
+ const readyPortAvailable = !isolatedReadyTransportRequired
6573
+ || parseDaemonIpcPort(childEnv.BOTMUX_DAEMON_IPC_PORT) !== undefined;
6574
+ const readyCapabilityAvailable = !isolatedReadyTransportRequired
6575
+ || hasMatchingManagedOriginCapability(process.env.SESSION_DATA_DIR ?? '', cfg.sessionId, sandboxRelayCapability?.token, sandboxRelayOutbox ?? undefined);
6576
+ const readySignalAvailable = readyHookAvailable && readyPortAvailable && readyCapabilityAvailable;
6577
+ const freshReadyGateCandidate = cliAdapter.injectsReadyHook === true
6578
+ && cfg.adoptMode !== true
6579
+ && !willReattachPersistent;
6580
+ if (freshReadyGateCandidate && !readySignalAvailable) {
6581
+ const reasons = [
6582
+ ...(!readyHookAvailable ? ['SessionStart hook missing from effective config'] : []),
6583
+ ...(!readyPortAvailable ? ['BOTMUX_DAEMON_IPC_PORT missing/invalid'] : []),
6584
+ ...(!readyCapabilityAvailable ? ['ready capability transport missing, unreadable, or stale'] : []),
6585
+ ];
6586
+ log(`Ready gate skipped — preflight failed: ${reasons.join('; ')}`);
6587
+ }
6486
6588
  if (shouldArmReadyGate({
6487
6589
  injectsReadyHook: cliAdapter.injectsReadyHook === true,
6590
+ readySignalAvailable,
6488
6591
  adoptMode: cfg.adoptMode === true,
6489
6592
  willReattachPersistent,
6490
6593
  })) {