botmux 2.74.0 → 2.75.1

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 (112) hide show
  1. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  2. package/dist/adapters/cli/claude-code.js +23 -19
  3. package/dist/adapters/cli/claude-code.js.map +1 -1
  4. package/dist/adapters/cli/types.d.ts +6 -0
  5. package/dist/adapters/cli/types.d.ts.map +1 -1
  6. package/dist/adapters/hook-installer.d.ts +4 -0
  7. package/dist/adapters/hook-installer.d.ts.map +1 -1
  8. package/dist/adapters/hook-installer.js +32 -2
  9. package/dist/adapters/hook-installer.js.map +1 -1
  10. package/dist/bot-registry.d.ts +10 -0
  11. package/dist/bot-registry.d.ts.map +1 -1
  12. package/dist/bot-registry.js +22 -5
  13. package/dist/bot-registry.js.map +1 -1
  14. package/dist/cli/pm2-command.d.ts +6 -0
  15. package/dist/cli/pm2-command.d.ts.map +1 -0
  16. package/dist/cli/pm2-command.js +7 -0
  17. package/dist/cli/pm2-command.js.map +1 -0
  18. package/dist/cli/stdin-encoding.d.ts +3 -0
  19. package/dist/cli/stdin-encoding.d.ts.map +1 -0
  20. package/dist/cli/stdin-encoding.js +19 -0
  21. package/dist/cli/stdin-encoding.js.map +1 -0
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +48 -13
  24. package/dist/cli.js.map +1 -1
  25. package/dist/core/command-handler.d.ts.map +1 -1
  26. package/dist/core/command-handler.js +3 -1
  27. package/dist/core/command-handler.js.map +1 -1
  28. package/dist/core/maintenance.d.ts +18 -0
  29. package/dist/core/maintenance.d.ts.map +1 -1
  30. package/dist/core/maintenance.js +56 -4
  31. package/dist/core/maintenance.js.map +1 -1
  32. package/dist/core/worker-pool.d.ts.map +1 -1
  33. package/dist/core/worker-pool.js +1 -0
  34. package/dist/core/worker-pool.js.map +1 -1
  35. package/dist/dashboard/auth.d.ts.map +1 -1
  36. package/dist/dashboard/auth.js +2 -1
  37. package/dist/dashboard/auth.js.map +1 -1
  38. package/dist/dashboard/bot-onboarding.d.ts +2 -0
  39. package/dist/dashboard/bot-onboarding.d.ts.map +1 -1
  40. package/dist/dashboard/bot-onboarding.js +2 -0
  41. package/dist/dashboard/bot-onboarding.js.map +1 -1
  42. package/dist/dashboard/hd2d-assets.d.ts +20 -0
  43. package/dist/dashboard/hd2d-assets.d.ts.map +1 -0
  44. package/dist/dashboard/hd2d-assets.js +216 -0
  45. package/dist/dashboard/hd2d-assets.js.map +1 -0
  46. package/dist/dashboard/web/app.d.ts.map +1 -1
  47. package/dist/dashboard/web/app.js +3 -0
  48. package/dist/dashboard/web/app.js.map +1 -1
  49. package/dist/dashboard/web/office.d.ts +2 -0
  50. package/dist/dashboard/web/office.d.ts.map +1 -0
  51. package/dist/dashboard/web/office.js +140 -0
  52. package/dist/dashboard/web/office.js.map +1 -0
  53. package/dist/dashboard-web/app.js +327 -299
  54. package/dist/dashboard-web/game/index.apple-touch-icon.png +0 -0
  55. package/dist/dashboard-web/game/index.audio.position.worklet.js +66 -0
  56. package/dist/dashboard-web/game/index.audio.worklet.js +213 -0
  57. package/dist/dashboard-web/game/index.html +224 -0
  58. package/dist/dashboard-web/game/index.icon.png +0 -0
  59. package/dist/dashboard-web/game/index.js +909 -0
  60. package/dist/dashboard-web/game/index.png +0 -0
  61. package/dist/dashboard-web/index.html +1 -0
  62. package/dist/dashboard.js +87 -13
  63. package/dist/dashboard.js.map +1 -1
  64. package/dist/global-config.d.ts +5 -0
  65. package/dist/global-config.d.ts.map +1 -1
  66. package/dist/global-config.js +2 -0
  67. package/dist/global-config.js.map +1 -1
  68. package/dist/i18n/en.js +2 -2
  69. package/dist/i18n/en.js.map +1 -1
  70. package/dist/i18n/zh.js +2 -2
  71. package/dist/i18n/zh.js.map +1 -1
  72. package/dist/im/lark/card-handler.d.ts.map +1 -1
  73. package/dist/im/lark/card-handler.js +3 -1
  74. package/dist/im/lark/card-handler.js.map +1 -1
  75. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  76. package/dist/im/lark/event-dispatcher.js +66 -6
  77. package/dist/im/lark/event-dispatcher.js.map +1 -1
  78. package/dist/im/lark/reply-mode-command.d.ts.map +1 -1
  79. package/dist/im/lark/reply-mode-command.js +4 -1
  80. package/dist/im/lark/reply-mode-command.js.map +1 -1
  81. package/dist/services/bridge-rotation-policy.d.ts +40 -0
  82. package/dist/services/bridge-rotation-policy.d.ts.map +1 -1
  83. package/dist/services/bridge-rotation-policy.js +38 -0
  84. package/dist/services/bridge-rotation-policy.js.map +1 -1
  85. package/dist/services/chat-reply-mode-store.d.ts +1 -1
  86. package/dist/services/chat-reply-mode-store.d.ts.map +1 -1
  87. package/dist/services/chat-reply-mode-store.js +8 -9
  88. package/dist/services/chat-reply-mode-store.js.map +1 -1
  89. package/dist/setup/bot-config-editor.d.ts +9 -0
  90. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  91. package/dist/setup/bot-config-editor.js +11 -0
  92. package/dist/setup/bot-config-editor.js.map +1 -1
  93. package/dist/setup/cli-selection.d.ts +65 -0
  94. package/dist/setup/cli-selection.d.ts.map +1 -0
  95. package/dist/setup/cli-selection.js +112 -0
  96. package/dist/setup/cli-selection.js.map +1 -0
  97. package/dist/setup/interactive-select.d.ts +30 -0
  98. package/dist/setup/interactive-select.d.ts.map +1 -0
  99. package/dist/setup/interactive-select.js +177 -0
  100. package/dist/setup/interactive-select.js.map +1 -0
  101. package/dist/skills/definitions.d.ts.map +1 -1
  102. package/dist/skills/definitions.js +17 -3
  103. package/dist/skills/definitions.js.map +1 -1
  104. package/dist/types.d.ts +1 -0
  105. package/dist/types.d.ts.map +1 -1
  106. package/dist/utils/ready-gate.d.ts +6 -0
  107. package/dist/utils/ready-gate.d.ts.map +1 -1
  108. package/dist/utils/ready-gate.js +6 -0
  109. package/dist/utils/ready-gate.js.map +1 -1
  110. package/dist/worker.js +105 -7
  111. package/dist/worker.js.map +1 -1
  112. package/package.json +2 -2
package/dist/worker.js CHANGED
@@ -22,7 +22,7 @@ import { shouldSuppressBridgeEmit } from './services/bridge-fallback-gate.js';
22
22
  import { shouldWriteNow } from './utils/input-gate.js';
23
23
  import { ReadyGate, shouldArmReadyGate } from './utils/ready-gate.js';
24
24
  import { InflightInputTracker } from './core/inflight-input-tracker.js';
25
- import { shouldRunQuietRotation, evaluatePidResolverPullback, decideFingerprintSwitch, sessionIdFromJsonlPath, SESSION_ID_FILENAME_RE, } from './services/bridge-rotation-policy.js';
25
+ import { shouldRunQuietRotation, evaluatePidResolverPullback, decideFingerprintSwitch, shouldHealAbsentBaseline, sessionIdFromJsonlPath, SESSION_ID_FILENAME_RE, } from './services/bridge-rotation-policy.js';
26
26
  import { CodexBridgeQueue } from './services/codex-bridge-queue.js';
27
27
  import { drainCodexRollout, findCodexRolloutBySessionId, findCodexRolloutByPid, splitCodexEventsByCutoff, extractLastCodexTurn } from './services/codex-transcript.js';
28
28
  import { findTraexRolloutBySessionId, findTraexRolloutByPid } from './services/traex-transcript.js';
@@ -39,6 +39,7 @@ import { t, setDefaultLocale } from './i18n/index.js';
39
39
  import { TerminalRenderer } from './utils/terminal-renderer.js';
40
40
  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';
41
41
  import { createCliAdapterSync, locateOnPath } from './adapters/cli/registry.js';
42
+ import { buildWrappedLaunch } from './setup/cli-selection.js';
42
43
  import { claudeJsonlPathForSession, resolveJsonlFromPid, findOpenClaudeSessionIds, DEFAULT_CLAUDE_DATA_DIR } from './adapters/cli/claude-code.js';
43
44
  import { mtrSessionIdForBotmuxSession } from './adapters/cli/mtr.js';
44
45
  import { HerdrBackend } from './adapters/backend/herdr-backend.js';
@@ -147,6 +148,41 @@ let readySignalTimer = null;
147
148
  * back. The real signal lands within ~ms of the input box rendering, so this is
148
149
  * pure insurance against a missing/failed hook — generous but bounded. */
149
150
  const READY_SIGNAL_TIMEOUT_MS = 45_000;
151
+ /** Epoch ms of the most recent PTY output — used to settle for quiescence
152
+ * before the first flush (see settleThenFlush). */
153
+ let lastPtyOutputAtMs = 0;
154
+ /** After the SessionStart signal fires, the input box has appeared but Ink's
155
+ * startup render isn't fully drained yet — typing immediately trips Claude's
156
+ * paste-burst heuristic and the `\` soft-newline markers (claude-code
157
+ * writeInput) get kept literally. This is pronounced under wrapperCli launchers
158
+ * (e.g. `aiden x claude`) whose Claude renders more at startup. So we wait for
159
+ * the PTY to fall quiet for SETTLE_MS before the first flush — the signal still
160
+ * gates readiness (anti-selector), the settle just lets the render drain. */
161
+ const READY_FLUSH_SETTLE_MS = 1_000;
162
+ /** Upper bound on the settle so a chatty startup (spinners, periodic redraw)
163
+ * can't stall the first prompt indefinitely. */
164
+ const READY_FLUSH_SETTLE_CAP_MS = 6_000;
165
+ let readyFlushSettleTimer = null;
166
+ /** True while the post-signal quiescence settle is in progress — flushPending
167
+ * holds (just like the gate) so a message arriving mid-settle can't type-ahead
168
+ * past the settle and re-trigger paste-burst. */
169
+ let isSettlingFirstFlush = false;
170
+ /** Wait until the PTY has been quiet for READY_FLUSH_SETTLE_MS (Ink render
171
+ * drained), capped at READY_FLUSH_SETTLE_CAP_MS, then flush the held prompt. */
172
+ function settleThenFlush(startedAtMs) {
173
+ readyFlushSettleTimer = null;
174
+ const now = Date.now();
175
+ const quietForMs = now - lastPtyOutputAtMs;
176
+ if (quietForMs >= READY_FLUSH_SETTLE_MS || now - startedAtMs >= READY_FLUSH_SETTLE_CAP_MS) {
177
+ isSettlingFirstFlush = false;
178
+ log(`Ready-gate settle done (quiet ${quietForMs}ms); delivering held first prompt`);
179
+ void flushPending();
180
+ return;
181
+ }
182
+ const wait = Math.min(READY_FLUSH_SETTLE_MS - quietForMs, READY_FLUSH_SETTLE_CAP_MS - (now - startedAtMs));
183
+ readyFlushSettleTimer = setTimeout(() => settleThenFlush(startedAtMs), Math.max(50, wait));
184
+ readyFlushSettleTimer.unref?.();
185
+ }
150
186
  /** Release the ready-gate and flush anything it held. No-op when the gate was
151
187
  * never armed (other CLIs / adopt) or already released (idempotent). */
152
188
  function releaseReadyGate(reason) {
@@ -155,8 +191,13 @@ function releaseReadyGate(reason) {
155
191
  readySignalTimer = null;
156
192
  }
157
193
  if (readyGate.receive()) {
158
- log(`Ready gate released (${reason}); delivering held first prompt`);
159
- flushPending();
194
+ log(`Ready gate released (${reason}); settling for PTY quiescence before first flush`);
195
+ if (readyFlushSettleTimer) {
196
+ clearTimeout(readyFlushSettleTimer);
197
+ readyFlushSettleTimer = null;
198
+ }
199
+ isSettlingFirstFlush = true;
200
+ settleThenFlush(Date.now());
160
201
  }
161
202
  }
162
203
  const pendingMessages = [];
@@ -1375,8 +1416,28 @@ function bridgeMarkPendingTurn(messageText, preferredTurnId) {
1375
1416
  if (!bridgeJsonlPath)
1376
1417
  return undefined;
1377
1418
  if (!bridgeBaselineDone) {
1378
- log('Bridge baseline not ready this turn will not have transcript-driven final_output');
1379
- return undefined;
1419
+ // Self-heal a stuck baseline: the guessed transcript path never
1420
+ // materialised (Claude wrote under a different sessionId, a stale resume
1421
+ // id, or an /adopt sid persisted as the botmux sid). An absent file has
1422
+ // no history to absorb, so arm fresh-empty readiness so THIS turn gets
1423
+ // marked — the mark arms the per-tick exact-content fingerprint recovery,
1424
+ // which finds the jsonl Claude actually wrote this message to and switches
1425
+ // the bridge onto it (no dependence on Claude-internal pid files, so
1426
+ // version-robust). See shouldHealAbsentBaseline for the full rationale.
1427
+ if (shouldHealAbsentBaseline({
1428
+ baselineDone: bridgeBaselineDone,
1429
+ hasJsonlPath: !!bridgeJsonlPath,
1430
+ jsonlFileExists: existsSyncSafe(bridgeJsonlPath),
1431
+ })) {
1432
+ bridgeOffset = 0;
1433
+ bridgePendingTail = '';
1434
+ bridgeBaselineDone = true;
1435
+ log(`Bridge baseline self-healed: guessed transcript ${bridgeJsonlPath} absent; fresh-empty readiness armed for fingerprint recovery`);
1436
+ }
1437
+ else {
1438
+ log('Bridge baseline not ready — this turn will not have transcript-driven final_output');
1439
+ return undefined;
1440
+ }
1380
1441
  }
1381
1442
  const fingerprint = makeFingerprint(messageText);
1382
1443
  // Full normalised content powers the unknown-sid recovery path. When a
@@ -2785,7 +2846,9 @@ function onPtyData(data) {
2785
2846
  return;
2786
2847
  }
2787
2848
  }
2788
- // Delegate idle detection to IdleDetector
2849
+ // Track last PTY output time for the ready-gate quiescence settle (see
2850
+ // settleThenFlush) and delegate idle detection to IdleDetector.
2851
+ lastPtyOutputAtMs = Date.now();
2789
2852
  idleDetector?.feed(data);
2790
2853
  }
2791
2854
  function markPromptReady() {
@@ -2970,6 +3033,12 @@ async function flushPending() {
2970
3033
  log(`Holding ${pendingMessages.length} pending message(s) until SessionStart ready signal`);
2971
3034
  return;
2972
3035
  }
3036
+ // Post-signal quiescence settle in progress — hold so the first write lands
3037
+ // after Ink's startup render has drained (else paste-burst keeps `\` literal).
3038
+ if (isSettlingFirstFlush) {
3039
+ log(`Holding ${pendingMessages.length} pending message(s) until ready-gate settle completes`);
3040
+ return;
3041
+ }
2973
3042
  // Type-ahead adapters flush even while the CLI is busy; others wait for
2974
3043
  // idle. Claude bridge fallback used to also disable type-ahead because
2975
3044
  // BridgeTurnQueue.ingest didn't recognise the `attachment(queued_command)`
@@ -3838,6 +3907,23 @@ function spawnCli(cfg) {
3838
3907
  throw err;
3839
3908
  }
3840
3909
  }
3910
+ // 通用启动前缀(wrapperCli):把启动命令重写成 `<wrapperCli> <CLI 参数>`(首 token 当
3911
+ // bin 走 PATH 解析),无需 wrapper 脚本、跨系统。aiden x claude 形态会剥掉 aiden 拒收的
3912
+ // --settings(见 buildWrappedLaunch)。与文件沙盒互斥:沙盒已把命令重写成 bwrap,叠加
3913
+ // 前缀会破坏隔离,故 sandboxOn 时跳过并告警(网关 + oncall 沙盒本就不是合理组合)。
3914
+ if (cfg.wrapperCli && cfg.wrapperCli.trim()) {
3915
+ if (sandboxOn) {
3916
+ log(`wrapperCli="${cfg.wrapperCli}" ignored: file sandbox enabled and takes precedence (cannot combine launch prefix with bwrap)`);
3917
+ }
3918
+ else {
3919
+ const launch = buildWrappedLaunch(cfg.wrapperCli, spawnArgs, (b) => locateOnPath(b) ?? b);
3920
+ if (launch.bin) {
3921
+ spawnBin = launch.bin;
3922
+ spawnArgs = launch.args;
3923
+ log(`Launch prefix: spawning ${spawnBin} ${spawnArgs.slice(0, 2).join(' ')} … (cliId=${cfg.cliId})`);
3924
+ }
3925
+ }
3926
+ }
3841
3927
  backend.spawn(spawnBin, spawnArgs, {
3842
3928
  cwd: spawnCwd,
3843
3929
  cols: PTY_COLS,
@@ -4010,6 +4096,13 @@ function spawnCli(cfg) {
4010
4096
  clearTimeout(readySignalTimer);
4011
4097
  readySignalTimer = null;
4012
4098
  }
4099
+ if (readyFlushSettleTimer) {
4100
+ clearTimeout(readyFlushSettleTimer);
4101
+ readyFlushSettleTimer = null;
4102
+ }
4103
+ isSettlingFirstFlush = false;
4104
+ // Reset quiescence baseline so the settle measures silence from THIS spawn.
4105
+ lastPtyOutputAtMs = Date.now();
4013
4106
  if (shouldArmReadyGate({
4014
4107
  injectsReadyHook: cliAdapter.injectsReadyHook === true,
4015
4108
  adoptMode: cfg.adoptMode === true,
@@ -4092,11 +4185,16 @@ function spawnCli(cfg) {
4092
4185
  function killCli() {
4093
4186
  idleDetector?.dispose();
4094
4187
  idleDetector = null;
4095
- // Cancel any pending ready-gate fallback timer; spawnCli re-arms on respawn.
4188
+ // Cancel any pending ready-gate fallback / settle timers; spawnCli re-arms on respawn.
4096
4189
  if (readySignalTimer) {
4097
4190
  clearTimeout(readySignalTimer);
4098
4191
  readySignalTimer = null;
4099
4192
  }
4193
+ if (readyFlushSettleTimer) {
4194
+ clearTimeout(readyFlushSettleTimer);
4195
+ readyFlushSettleTimer = null;
4196
+ }
4197
+ isSettlingFirstFlush = false;
4100
4198
  stopScreenAnalyzer();
4101
4199
  stopScreenUpdates();
4102
4200
  backend?.kill();