botmux 3.4.2 → 3.5.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 +10 -7
- package/README.md +11 -6
- package/dist/adapters/backend/herdr-backend.d.ts +43 -6
- package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
- package/dist/adapters/backend/herdr-backend.js +362 -28
- package/dist/adapters/backend/herdr-backend.js.map +1 -1
- package/dist/adapters/backend/reproduce-command.d.ts +39 -0
- package/dist/adapters/backend/reproduce-command.d.ts.map +1 -0
- package/dist/adapters/backend/reproduce-command.js +63 -0
- package/dist/adapters/backend/reproduce-command.js.map +1 -0
- package/dist/adapters/backend/session-backend-selector.d.ts +10 -1
- package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -1
- package/dist/adapters/backend/session-backend-selector.js +73 -4
- package/dist/adapters/backend/session-backend-selector.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +16 -0
- package/dist/adapters/backend/types.d.ts.map +1 -1
- package/dist/adapters/backend/types.js.map +1 -1
- package/dist/adapters/cli/pi-initial-prompt-extension.d.ts +27 -0
- package/dist/adapters/cli/pi-initial-prompt-extension.d.ts.map +1 -0
- package/dist/adapters/cli/pi-initial-prompt-extension.js +38 -0
- package/dist/adapters/cli/pi-initial-prompt-extension.js.map +1 -0
- package/dist/adapters/cli/pi-initial-prompt.d.ts +21 -0
- package/dist/adapters/cli/pi-initial-prompt.d.ts.map +1 -0
- package/dist/adapters/cli/pi-initial-prompt.js +59 -0
- package/dist/adapters/cli/pi-initial-prompt.js.map +1 -0
- package/dist/adapters/cli/pi.d.ts.map +1 -1
- package/dist/adapters/cli/pi.js +15 -4
- package/dist/adapters/cli/pi.js.map +1 -1
- package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
- package/dist/adapters/cli/read-isolation.js +5 -0
- package/dist/adapters/cli/read-isolation.js.map +1 -1
- package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
- package/dist/adapters/cli/shared-hints.js +8 -0
- package/dist/adapters/cli/shared-hints.js.map +1 -1
- package/dist/adapters/cli/traex.d.ts.map +1 -1
- package/dist/adapters/cli/traex.js +5 -0
- package/dist/adapters/cli/traex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +23 -3
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/adapters/hook-installer.d.ts +7 -0
- package/dist/adapters/hook-installer.d.ts.map +1 -1
- package/dist/adapters/hook-installer.js +56 -0
- package/dist/adapters/hook-installer.js.map +1 -1
- package/dist/bot-registry.d.ts +13 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +118 -2
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/capabilities.d.ts +24 -0
- package/dist/cli/capabilities.d.ts.map +1 -0
- package/dist/cli/capabilities.js +25 -0
- package/dist/cli/capabilities.js.map +1 -0
- package/dist/cli/create-group-resolver.d.ts +24 -0
- package/dist/cli/create-group-resolver.d.ts.map +1 -1
- package/dist/cli/create-group-resolver.js +23 -0
- package/dist/cli/create-group-resolver.js.map +1 -1
- package/dist/cli/daemon-lifecycle-env.d.ts +5 -7
- package/dist/cli/daemon-lifecycle-env.d.ts.map +1 -1
- package/dist/cli/daemon-lifecycle-env.js +13 -7
- package/dist/cli/daemon-lifecycle-env.js.map +1 -1
- package/dist/cli/exact-chat-grant-client.d.ts +48 -0
- package/dist/cli/exact-chat-grant-client.d.ts.map +1 -0
- package/dist/cli/exact-chat-grant-client.js +108 -0
- package/dist/cli/exact-chat-grant-client.js.map +1 -0
- package/dist/cli/exact-chat-grant.d.ts +18 -0
- package/dist/cli/exact-chat-grant.d.ts.map +1 -0
- package/dist/cli/exact-chat-grant.js +97 -0
- package/dist/cli/exact-chat-grant.js.map +1 -0
- package/dist/cli.d.ts +10 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +770 -126
- package/dist/cli.js.map +1 -1
- package/dist/codex-rpc-engine.d.ts +26 -0
- package/dist/codex-rpc-engine.d.ts.map +1 -1
- package/dist/codex-rpc-engine.js +113 -2
- package/dist/codex-rpc-engine.js.map +1 -1
- package/dist/config.d.ts +16 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +16 -0
- package/dist/config.js.map +1 -1
- package/dist/core/bot-live-control.d.ts +41 -0
- package/dist/core/bot-live-control.d.ts.map +1 -0
- package/dist/core/bot-live-control.js +135 -0
- package/dist/core/bot-live-control.js.map +1 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +28 -8
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/daemon-config-fence.d.ts +8 -0
- package/dist/core/daemon-config-fence.d.ts.map +1 -0
- package/dist/core/daemon-config-fence.js +13 -0
- package/dist/core/daemon-config-fence.js.map +1 -0
- package/dist/core/dashboard-events.d.ts +5 -0
- package/dist/core/dashboard-events.d.ts.map +1 -1
- package/dist/core/dashboard-events.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +21 -2
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +232 -5
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dispatch-registry.d.ts +12 -0
- package/dist/core/dispatch-registry.d.ts.map +1 -0
- package/dist/core/dispatch-registry.js +33 -0
- package/dist/core/dispatch-registry.js.map +1 -0
- package/dist/core/dispatch.d.ts +99 -0
- package/dist/core/dispatch.d.ts.map +1 -1
- package/dist/core/dispatch.js +136 -0
- package/dist/core/dispatch.js.map +1 -1
- package/dist/core/inflight-input-tracker.d.ts +1 -0
- package/dist/core/inflight-input-tracker.d.ts.map +1 -1
- package/dist/core/inflight-input-tracker.js.map +1 -1
- package/dist/core/maintenance.d.ts.map +1 -1
- package/dist/core/maintenance.js +10 -3
- package/dist/core/maintenance.js.map +1 -1
- package/dist/core/persistent-backend.d.ts +16 -2
- package/dist/core/persistent-backend.d.ts.map +1 -1
- package/dist/core/persistent-backend.js +40 -0
- package/dist/core/persistent-backend.js.map +1 -1
- package/dist/core/session-discovery.d.ts +8 -1
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +140 -8
- 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 +12 -8
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-title.d.ts +8 -0
- package/dist/core/session-title.d.ts.map +1 -1
- package/dist/core/session-title.js +54 -0
- package/dist/core/session-title.js.map +1 -1
- package/dist/core/terminal-write-auth.d.ts +15 -0
- package/dist/core/terminal-write-auth.d.ts.map +1 -1
- package/dist/core/terminal-write-auth.js +20 -0
- package/dist/core/terminal-write-auth.js.map +1 -1
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +3 -2
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +33 -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 +27 -8
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +389 -19
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts +4 -2
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +397 -26
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/bot-onboarding.d.ts +127 -1
- package/dist/dashboard/bot-onboarding.d.ts.map +1 -1
- package/dist/dashboard/bot-onboarding.js +1104 -25
- package/dist/dashboard/bot-onboarding.js.map +1 -1
- package/dist/dashboard/debug-terminal.d.ts +18 -0
- package/dist/dashboard/debug-terminal.d.ts.map +1 -0
- package/dist/dashboard/debug-terminal.js +338 -0
- package/dist/dashboard/debug-terminal.js.map +1 -0
- package/dist/dashboard/public-redact.d.ts +2 -0
- package/dist/dashboard/public-redact.d.ts.map +1 -1
- package/dist/dashboard/public-redact.js +12 -3
- package/dist/dashboard/public-redact.js.map +1 -1
- package/dist/dashboard/registry.d.ts +8 -0
- package/dist/dashboard/registry.d.ts.map +1 -1
- package/dist/dashboard/registry.js +13 -0
- package/dist/dashboard/registry.js.map +1 -1
- package/dist/dashboard/settings-write-applier.d.ts +47 -1
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +102 -1
- package/dist/dashboard/settings-write-applier.js.map +1 -1
- package/dist/dashboard/web/app.d.ts.map +1 -1
- package/dist/dashboard/web/app.js +2 -0
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +1 -0
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +24 -5
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +15 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +19 -0
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +76 -6
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/icon-tooltip.d.ts +3 -0
- package/dist/dashboard/web/icon-tooltip.d.ts.map +1 -0
- package/dist/dashboard/web/icon-tooltip.js +108 -0
- package/dist/dashboard/web/icon-tooltip.js.map +1 -0
- package/dist/dashboard/web/sessions-kanban.js +2 -2
- package/dist/dashboard/web/sessions-kanban.js.map +1 -1
- package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-page.js +48 -9
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts +2 -0
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +46 -2
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/settings-page.d.ts +78 -0
- package/dist/dashboard/web/settings-page.d.ts.map +1 -1
- package/dist/dashboard/web/settings-page.js +88 -1
- package/dist/dashboard/web/settings-page.js.map +1 -1
- package/dist/dashboard/web/store.d.ts +2 -0
- package/dist/dashboard/web/store.d.ts.map +1 -1
- package/dist/dashboard/web/store.js +13 -1
- package/dist/dashboard/web/store.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-O376WSER.js +5 -0
- package/dist/dashboard-web/chunks/{chunk-JNRSPKLS.js → chunk-EJDYUINZ.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-CUV65BIQ.js → chunk-H7P5UV4W.js} +6 -6
- package/dist/dashboard-web/chunks/{chunk-P6GO5KT4.js → chunk-QCPPKCX3.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-UBOLHQCK.js → chunk-WXPMNEYV.js} +1 -1
- package/dist/dashboard-web/chunks/{connectors-page-KTC2QWNW.js → connectors-page-IR54AOAI.js} +1 -1
- package/dist/dashboard-web/chunks/{groups-page-B3EKGOLY.js → groups-page-3P4TLUMF.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-X6RXNCWX.js → insights-page-TDCZV56A.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-LLRA5LQW.js → monitor-room-QWCWL3SX.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-7MT5FEOS.js → monitoring-page-IOWFSNVC.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-KD46D5TD.js → office-page-5AUPEHZP.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-JUWNH32O.js → overview-page-QJMI6NLW.js} +1 -1
- package/dist/dashboard-web/chunks/{roles-page-P6F7NLOI.js → roles-page-WJVB52ET.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-XIHWZST4.js → schedules-page-RVRWHP4V.js} +1 -1
- package/dist/dashboard-web/chunks/sessions-page-KWLGBX6G.js +3 -0
- package/dist/dashboard-web/chunks/settings-page-TNZEPC7V.js +2 -0
- package/dist/dashboard-web/chunks/{skills-page-A46KVWBL.js → skills-page-7YT5TIX5.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-SDUC56DF.js → team-federation-page-NJZKTU7A.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-J5JOTUSQ.js → v3-page-NQ73UA2Q.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-U7LSOAWI.js → whiteboards-page-DSM3MDMN.js} +1 -1
- package/dist/dashboard-web/style.css +198 -7
- package/dist/dashboard.js +294 -7
- package/dist/dashboard.js.map +1 -1
- package/dist/features/codex-notifier/adoption.d.ts +11 -0
- package/dist/features/codex-notifier/adoption.d.ts.map +1 -0
- package/dist/features/codex-notifier/adoption.js +11 -0
- package/dist/features/codex-notifier/adoption.js.map +1 -0
- package/dist/features/codex-notifier/app-opener.d.ts +21 -0
- package/dist/features/codex-notifier/app-opener.d.ts.map +1 -0
- package/dist/features/codex-notifier/app-opener.js +33 -0
- package/dist/features/codex-notifier/app-opener.js.map +1 -0
- package/dist/features/codex-notifier/card-action.d.ts +27 -0
- package/dist/features/codex-notifier/card-action.d.ts.map +1 -0
- package/dist/features/codex-notifier/card-action.js +84 -0
- package/dist/features/codex-notifier/card-action.js.map +1 -0
- package/dist/features/codex-notifier/card.d.ts +12 -0
- package/dist/features/codex-notifier/card.d.ts.map +1 -0
- package/dist/features/codex-notifier/card.js +150 -0
- package/dist/features/codex-notifier/card.js.map +1 -0
- package/dist/features/codex-notifier/codex-context.d.ts +12 -0
- package/dist/features/codex-notifier/codex-context.d.ts.map +1 -0
- package/dist/features/codex-notifier/codex-context.js +161 -0
- package/dist/features/codex-notifier/codex-context.js.map +1 -0
- package/dist/features/codex-notifier/config.d.ts +9 -0
- package/dist/features/codex-notifier/config.d.ts.map +1 -0
- package/dist/features/codex-notifier/config.js +11 -0
- package/dist/features/codex-notifier/config.js.map +1 -0
- package/dist/features/codex-notifier/confirmed-turn.d.ts +20 -0
- package/dist/features/codex-notifier/confirmed-turn.d.ts.map +1 -0
- package/dist/features/codex-notifier/confirmed-turn.js +130 -0
- package/dist/features/codex-notifier/confirmed-turn.js.map +1 -0
- package/dist/features/codex-notifier/emitter.d.ts +13 -0
- package/dist/features/codex-notifier/emitter.d.ts.map +1 -0
- package/dist/features/codex-notifier/emitter.js +33 -0
- package/dist/features/codex-notifier/emitter.js.map +1 -0
- package/dist/features/codex-notifier/event-store.d.ts +29 -0
- package/dist/features/codex-notifier/event-store.d.ts.map +1 -0
- package/dist/features/codex-notifier/event-store.js +217 -0
- package/dist/features/codex-notifier/event-store.js.map +1 -0
- package/dist/features/codex-notifier/event.d.ts +46 -0
- package/dist/features/codex-notifier/event.d.ts.map +1 -0
- package/dist/features/codex-notifier/event.js +188 -0
- package/dist/features/codex-notifier/event.js.map +1 -0
- package/dist/features/codex-notifier/hook-cli.d.ts +23 -0
- package/dist/features/codex-notifier/hook-cli.d.ts.map +1 -0
- package/dist/features/codex-notifier/hook-cli.js +130 -0
- package/dist/features/codex-notifier/hook-cli.js.map +1 -0
- package/dist/features/codex-notifier/hook-installer.d.ts +19 -0
- package/dist/features/codex-notifier/hook-installer.d.ts.map +1 -0
- package/dist/features/codex-notifier/hook-installer.js +158 -0
- package/dist/features/codex-notifier/hook-installer.js.map +1 -0
- package/dist/features/codex-notifier/index.d.ts +21 -0
- package/dist/features/codex-notifier/index.d.ts.map +1 -0
- package/dist/features/codex-notifier/index.js +19 -0
- package/dist/features/codex-notifier/index.js.map +1 -0
- package/dist/features/codex-notifier/internal-turn.d.ts +5 -0
- package/dist/features/codex-notifier/internal-turn.d.ts.map +1 -0
- package/dist/features/codex-notifier/internal-turn.js +43 -0
- package/dist/features/codex-notifier/internal-turn.js.map +1 -0
- package/dist/features/codex-notifier/outbox-worker.d.ts +71 -0
- package/dist/features/codex-notifier/outbox-worker.d.ts.map +1 -0
- package/dist/features/codex-notifier/outbox-worker.js +290 -0
- package/dist/features/codex-notifier/outbox-worker.js.map +1 -0
- package/dist/features/codex-notifier/outbox.d.ts +25 -0
- package/dist/features/codex-notifier/outbox.d.ts.map +1 -0
- package/dist/features/codex-notifier/outbox.js +225 -0
- package/dist/features/codex-notifier/outbox.js.map +1 -0
- package/dist/features/codex-notifier/paths.d.ts +7 -0
- package/dist/features/codex-notifier/paths.d.ts.map +1 -0
- package/dist/features/codex-notifier/paths.js +20 -0
- package/dist/features/codex-notifier/paths.js.map +1 -0
- package/dist/features/codex-notifier/screen-lock.d.ts +12 -0
- package/dist/features/codex-notifier/screen-lock.d.ts.map +1 -0
- package/dist/features/codex-notifier/screen-lock.js +47 -0
- package/dist/features/codex-notifier/screen-lock.js.map +1 -0
- package/dist/features/codex-notifier/side-conversation-monitor.d.ts +102 -0
- package/dist/features/codex-notifier/side-conversation-monitor.d.ts.map +1 -0
- package/dist/features/codex-notifier/side-conversation-monitor.js +774 -0
- package/dist/features/codex-notifier/side-conversation-monitor.js.map +1 -0
- package/dist/features/codex-notifier/types.d.ts +42 -0
- package/dist/features/codex-notifier/types.d.ts.map +1 -0
- package/dist/features/codex-notifier/types.js +2 -0
- package/dist/features/codex-notifier/types.js.map +1 -0
- package/dist/features/codex-notifier/worker-lock.d.ts +8 -0
- package/dist/features/codex-notifier/worker-lock.d.ts.map +1 -0
- package/dist/features/codex-notifier/worker-lock.js +108 -0
- package/dist/features/codex-notifier/worker-lock.js.map +1 -0
- package/dist/global-config.d.ts +13 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +30 -0
- package/dist/global-config.js.map +1 -1
- package/dist/im/lark/card-builder.js +2 -2
- package/dist/im/lark/card-builder.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 +165 -34
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +62 -4
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +237 -29
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +20 -30
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/platform/secure-host-file.d.ts.map +1 -1
- package/dist/platform/secure-host-file.js +173 -54
- package/dist/platform/secure-host-file.js.map +1 -1
- package/dist/services/codex-app-threads.d.ts +47 -0
- package/dist/services/codex-app-threads.d.ts.map +1 -1
- package/dist/services/codex-app-threads.js +502 -10
- package/dist/services/codex-app-threads.js.map +1 -1
- package/dist/services/exact-chat-grant.d.ts +83 -0
- package/dist/services/exact-chat-grant.d.ts.map +1 -0
- package/dist/services/exact-chat-grant.js +249 -0
- package/dist/services/exact-chat-grant.js.map +1 -0
- package/dist/services/group-creator.d.ts +11 -0
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +18 -9
- package/dist/services/group-creator.js.map +1 -1
- package/dist/services/local-cli-opener.d.ts.map +1 -1
- package/dist/services/local-cli-opener.js +10 -5
- package/dist/services/local-cli-opener.js.map +1 -1
- package/dist/services/traex-user-input.d.ts +19 -0
- package/dist/services/traex-user-input.d.ts.map +1 -0
- package/dist/services/traex-user-input.js +57 -0
- package/dist/services/traex-user-input.js.map +1 -0
- package/dist/setup/open-platform-automation.d.ts +24 -3
- package/dist/setup/open-platform-automation.d.ts.map +1 -1
- package/dist/setup/open-platform-automation.js +45 -8
- package/dist/setup/open-platform-automation.js.map +1 -1
- package/dist/setup/verify-permissions.d.ts +18 -0
- package/dist/setup/verify-permissions.d.ts.map +1 -1
- package/dist/setup/verify-permissions.js +60 -0
- package/dist/setup/verify-permissions.js.map +1 -1
- package/dist/types.d.ts +70 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/child-env.d.ts +1 -1
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +2 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/utils/pending-input-queue.d.ts +19 -0
- package/dist/utils/pending-input-queue.d.ts.map +1 -1
- package/dist/utils/pending-input-queue.js +21 -1
- package/dist/utils/pending-input-queue.js.map +1 -1
- package/dist/utils/stuck-detector.d.ts +45 -0
- package/dist/utils/stuck-detector.d.ts.map +1 -0
- package/dist/utils/stuck-detector.js +188 -0
- package/dist/utils/stuck-detector.js.map +1 -0
- package/dist/utils/stuck-key-guard.d.ts +75 -0
- package/dist/utils/stuck-key-guard.d.ts.map +1 -0
- package/dist/utils/stuck-key-guard.js +77 -0
- package/dist/utils/stuck-key-guard.js.map +1 -0
- package/dist/worker.js +807 -117
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-B5AY4B2J.js +0 -5
- package/dist/dashboard-web/chunks/sessions-page-2VECQ27T.js +0 -3
- package/dist/dashboard-web/chunks/settings-page-HMIUE2JA.js +0 -2
package/dist/core/worker-pool.js
CHANGED
|
@@ -11,7 +11,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
11
11
|
import { ensureSkills, ensureAskSkill, ensurePluginSkills, ensureWhiteboardSkill, removeGlobalBotmuxSkills } from '../skills/installer.js';
|
|
12
12
|
import { shouldInstallGlobalSkills } from '../skills/injection-mode.js';
|
|
13
13
|
import { whiteboardEnabled } from '../services/whiteboard-store.js';
|
|
14
|
-
import { installHook } from '../adapters/hook-installer.js';
|
|
14
|
+
import { cleanupTraexAskHooks, installHook } from '../adapters/hook-installer.js';
|
|
15
15
|
import { hookCommandFor } from '../adapters/hook-command.js';
|
|
16
16
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
17
17
|
import { config } from '../config.js';
|
|
@@ -24,6 +24,7 @@ import { loadFrozenCards, saveFrozenCards } from '../services/frozen-card-store.
|
|
|
24
24
|
import { hashUrlForLog, cancelRiffTaskById } from '../adapters/backend/riff-backend.js';
|
|
25
25
|
import { logger } from '../utils/logger.js';
|
|
26
26
|
import { createCliAdapterSync } from '../adapters/cli/registry.js';
|
|
27
|
+
import { traeHome } from '../services/traex-paths.js';
|
|
27
28
|
import { botLocale, localeForBot, t as tr } from '../i18n/index.js';
|
|
28
29
|
import { claudeJsonlPathForSession } from '../adapters/cli/claude-code.js';
|
|
29
30
|
import { findUniqueClaudeSessionByCwd } from './session-discovery.js';
|
|
@@ -34,7 +35,7 @@ import { listDocSubscriptionsForSession, removeDocSubscription } from '../servic
|
|
|
34
35
|
import { TmuxBackend } from '../adapters/backend/tmux-backend.js';
|
|
35
36
|
import { HerdrBackend } from '../adapters/backend/herdr-backend.js';
|
|
36
37
|
import { sandboxEnabled } from '../adapters/backend/sandbox.js';
|
|
37
|
-
import { isSuspendableBackendType, getSessionPersistentBackendType,
|
|
38
|
+
import { isSuspendableBackendType, getSessionPersistentBackendType, persistentBackendTargetForSession, killPersistentBackendTarget, managedTargetsForCliChange, resolvePairedSpawnBackendType, resolvePersistentBackendTarget } from './persistent-backend.js';
|
|
38
39
|
import { getBot, getAllBots, resolveBrandLabel } from '../bot-registry.js';
|
|
39
40
|
/** A random id minted once per daemon process (this lifetime). Stamped onto
|
|
40
41
|
* isolated persistent panes so a suspend→resume reattach (same id) is
|
|
@@ -79,7 +80,9 @@ import { isLocalCliOpenEnabled, isLocalCliOpenReady } from '../services/local-cl
|
|
|
79
80
|
import { isSilentScheduledTurn } from './silent-schedule-turns.js';
|
|
80
81
|
import { writeDeferredTopicBinding } from './deferred-topic-binding.js';
|
|
81
82
|
import { deferWorkerSpawnDuringDeviceIsolation } from './device-isolation-activation.js';
|
|
83
|
+
import { buildBotmuxLarkNativeSessionTitle, extractBotmuxLarkNativeSessionTitlePrompt, } from './session-title.js';
|
|
82
84
|
import { acknowledgeSessionReady } from './session-ready-handshake.js';
|
|
85
|
+
import { recordDispatchInputCommit } from './dispatch.js';
|
|
83
86
|
const __filename = fileURLToPath(import.meta.url);
|
|
84
87
|
const __dirname = dirname(__filename);
|
|
85
88
|
const WORKER_SIGTERM_BACKSTOP_MS = 2_000;
|
|
@@ -652,13 +655,13 @@ export async function postPrivateSnapshotCard(ds, audience) {
|
|
|
652
655
|
* Deliver the write-enabled session card (the "🔑 获取操作链接" card, which carries
|
|
653
656
|
* a write-token terminal URL + manage buttons) privately to a single operator.
|
|
654
657
|
*
|
|
655
|
-
* Prefers an in-chat "visible-to-you" ephemeral card
|
|
656
|
-
* to leave the conversation.
|
|
657
|
-
*
|
|
658
|
-
*
|
|
659
|
-
*
|
|
660
|
-
*
|
|
661
|
-
*
|
|
658
|
+
* Prefers an in-chat "visible-to-you" ephemeral card in a flat group so the
|
|
659
|
+
* operator never has to leave the conversation. Thread-scope sessions and
|
|
660
|
+
* chat-scope sessions currently folded into a thread go straight to DM:
|
|
661
|
+
* Feishu may accept their ephemeral message without rendering it in the topic
|
|
662
|
+
* panel. p2p chats also DM directly (the DM lands in that same 1:1 chat).
|
|
663
|
+
*
|
|
664
|
+
* Other group chats attempt ephemeral first and fall back to DM on ANY failure.
|
|
662
665
|
*
|
|
663
666
|
* Both channels are private, so the DM fallback never leaks the write token —
|
|
664
667
|
* unlike the private /card snapshot (which fails closed), here we fail OVER.
|
|
@@ -667,7 +670,10 @@ export async function postPrivateSnapshotCard(ds, audience) {
|
|
|
667
670
|
*/
|
|
668
671
|
export async function deliverWriteLinkCard(ds, operatorOpenId, cardJson) {
|
|
669
672
|
const who = operatorOpenId.substring(0, 8);
|
|
670
|
-
|
|
673
|
+
const replyTarget = ds.currentReplyTarget ?? ds.session.currentReplyTarget;
|
|
674
|
+
const isThreaded = ds.scope === 'thread'
|
|
675
|
+
|| (!!replyTarget?.rootMessageId && replyTarget.quoteOnly !== true);
|
|
676
|
+
if (ds.chatType !== 'p2p' && !isThreaded) {
|
|
671
677
|
try {
|
|
672
678
|
await sendEphemeralCard(ds.larkAppId, ds.chatId, operatorOpenId, cardJson);
|
|
673
679
|
logger.info(`[${tag(ds)}] write link delivered via ephemeral card to ${who}…`);
|
|
@@ -909,6 +915,12 @@ const skillsInstalledCliIds = new Set();
|
|
|
909
915
|
*/
|
|
910
916
|
export function ensureCliSkills(cliId, cliPathOverride) {
|
|
911
917
|
const adapter = createCliAdapterSync(cliId, cliPathOverride);
|
|
918
|
+
if (cliId === 'traex') {
|
|
919
|
+
cleanupTraexAskHooks([
|
|
920
|
+
join(traeHome(), 'hooks.json'),
|
|
921
|
+
join(traeHome(), 'cli', 'hooks.json'),
|
|
922
|
+
]);
|
|
923
|
+
}
|
|
912
924
|
// Claude-family CLIs deliver skills per-session via `--plugin-dir` (no global
|
|
913
925
|
// leak), so they always materialise their plugin dir — the builtin-injection
|
|
914
926
|
// mode does not apply to them. Everything below the branch is the global-dir
|
|
@@ -1084,6 +1096,10 @@ export function killWorker(ds) {
|
|
|
1084
1096
|
// daemon-side copy synchronously; the worker may never get a chance to send
|
|
1085
1097
|
// its ordered revoke IPC on close/crash paths.
|
|
1086
1098
|
ds.managedTurnOrigin = undefined;
|
|
1099
|
+
// The worker/CLI generation is ending — any outstanding stuck-warning card
|
|
1100
|
+
// must be invalidated so a late click cannot inject keys into a replacement
|
|
1101
|
+
// worker (or into nothing, if no replacement comes).
|
|
1102
|
+
invalidateStuckWarning(ds, 'killWorker');
|
|
1087
1103
|
if (!ds.worker || ds.worker.killed) {
|
|
1088
1104
|
// No live worker to receive {type:'close'}, so its destroySession() — which
|
|
1089
1105
|
// tears down the persistent backing session (tmux/herdr/zellij) — never
|
|
@@ -1149,7 +1165,7 @@ function destroyOrphanedBackingSession(ds) {
|
|
|
1149
1165
|
if (!backendType)
|
|
1150
1166
|
return;
|
|
1151
1167
|
try {
|
|
1152
|
-
|
|
1168
|
+
killPersistentBackendTarget(persistentBackendTargetForSession(ds));
|
|
1153
1169
|
logger.info(`[${tag(ds)}] killWorker: no live worker — destroyed orphaned ${backendType} backing session`);
|
|
1154
1170
|
}
|
|
1155
1171
|
catch (err) {
|
|
@@ -1201,6 +1217,10 @@ export function suspendWorker(ds, reason = 'suspended_idle') {
|
|
|
1201
1217
|
ds.workerToken = null;
|
|
1202
1218
|
ds.workerViewToken = null;
|
|
1203
1219
|
ds.managedTurnOrigin = undefined;
|
|
1220
|
+
// The worker is being suspended — the CLI will be destroyed and cold-resumed
|
|
1221
|
+
// later. Invalidate any stuck-warning card so a late click cannot inject keys
|
|
1222
|
+
// after the CLI is gone (or into a different CLI on resume).
|
|
1223
|
+
invalidateStuckWarning(ds, 'suspendWorker');
|
|
1204
1224
|
// Screen state describes the process we just stopped. Keeping it would make
|
|
1205
1225
|
// the dashboard hydrate this process-less logical session as idle/working.
|
|
1206
1226
|
ds.lastScreenStatus = undefined;
|
|
@@ -1617,11 +1637,32 @@ export function sendWorkerInput(ds, payload, turnId, opts = {}) {
|
|
|
1617
1637
|
return false;
|
|
1618
1638
|
const normalized = typeof payload === 'string' ? { content: payload } : payload;
|
|
1619
1639
|
const codexAppInput = codexAppInputForSession(ds, normalized.codexAppInput, turnId);
|
|
1640
|
+
let nativeSessionTitlePrompt;
|
|
1641
|
+
let nativeSessionTitle;
|
|
1642
|
+
if (ds.session.nativeSessionTitleAwaitingContent && !ds.session.nativeSessionTitleUserDefined && !ds.adoptedFrom) {
|
|
1643
|
+
const bot = getBot(ds.larkAppId);
|
|
1644
|
+
const effectiveCliId = ds.session.cliId ?? bot.config.cliId;
|
|
1645
|
+
if (effectiveCliId === 'codex') {
|
|
1646
|
+
nativeSessionTitlePrompt = extractBotmuxLarkNativeSessionTitlePrompt(normalized.codexAppInput?.text ?? normalized.content, bot.botName ? [{ name: bot.botName }] : undefined);
|
|
1647
|
+
if (nativeSessionTitlePrompt) {
|
|
1648
|
+
nativeSessionTitle = buildBotmuxLarkNativeSessionTitle(nativeSessionTitlePrompt);
|
|
1649
|
+
ds.session.nativeSessionTitle = nativeSessionTitle;
|
|
1650
|
+
ds.session.nativeSessionTitleAwaitingContent = undefined;
|
|
1651
|
+
if (ds.initConfig) {
|
|
1652
|
+
ds.initConfig.nativeSessionTitle = nativeSessionTitle;
|
|
1653
|
+
ds.initConfig.nativeSessionTitlePrompt = nativeSessionTitlePrompt;
|
|
1654
|
+
}
|
|
1655
|
+
sessionStore.updateSession(ds.session);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1620
1659
|
const vcMeetingImTurnOrigin = resolveVcMeetingImTurnOrigin(ds.session, turnId);
|
|
1621
1660
|
ds.worker.send({
|
|
1622
1661
|
type: 'message',
|
|
1623
1662
|
content: normalized.content,
|
|
1624
1663
|
...(codexAppInput ? { codexAppInput } : {}),
|
|
1664
|
+
...(nativeSessionTitle ? { nativeSessionTitle } : {}),
|
|
1665
|
+
...(nativeSessionTitlePrompt ? { nativeSessionTitlePrompt } : {}),
|
|
1625
1666
|
...(turnId ? { turnId } : {}),
|
|
1626
1667
|
...(opts.dispatchAttempt !== undefined ? { dispatchAttempt: opts.dispatchAttempt } : {}),
|
|
1627
1668
|
...(vcMeetingImTurnOrigin
|
|
@@ -1737,6 +1778,10 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1737
1778
|
}
|
|
1738
1779
|
sessionStore.updateSession(ds.session);
|
|
1739
1780
|
}
|
|
1781
|
+
// Reserve and durably publish the replacement lifetime before killing an
|
|
1782
|
+
// existing worker. A failed reservation leaves the old worker untouched;
|
|
1783
|
+
// a successful reservation immediately invalidates any late old-worker ACK.
|
|
1784
|
+
const workerGeneration = reserveWorkerGeneration(ds);
|
|
1740
1785
|
// Guard against double-fork: if a worker is already running, kill it first
|
|
1741
1786
|
if (ds.worker && !ds.worker.killed) {
|
|
1742
1787
|
logger.warn(`[${t}] Worker already running (pid: ${ds.worker.pid}), killing before re-fork`);
|
|
@@ -1769,6 +1814,38 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1769
1814
|
reclaimParkedCrashDiagnostic(ds);
|
|
1770
1815
|
const agentCfg = sessionAgentConfig(ds, botCfg);
|
|
1771
1816
|
ensureCliEnv(agentCfg.cliId, agentCfg.cliPathOverride);
|
|
1817
|
+
let nativeSessionTitle;
|
|
1818
|
+
let nativeSessionTitlePrompt;
|
|
1819
|
+
if (agentCfg.cliId === 'codex' && !ds.adoptedFrom) {
|
|
1820
|
+
const isFreshNativeSession = !resume && !ds.session.cliSessionId;
|
|
1821
|
+
const titlePrompt = extractBotmuxLarkNativeSessionTitlePrompt(promptPayload.codexAppInput?.text ?? prompt, bot.botName ? [{ name: bot.botName }] : undefined);
|
|
1822
|
+
if (isFreshNativeSession && !ds.session.nativeSessionTitleUserDefined) {
|
|
1823
|
+
ds.session.nativeSessionTitle = buildBotmuxLarkNativeSessionTitle(titlePrompt ? ds.session.title : undefined, bot.botName ? [{ name: bot.botName }] : undefined, ds.chatType === 'group' ? ds.session.chatDisplayName : undefined);
|
|
1824
|
+
ds.session.nativeSessionTitleAwaitingContent = titlePrompt ? undefined : true;
|
|
1825
|
+
nativeSessionTitlePrompt = titlePrompt;
|
|
1826
|
+
sessionStore.updateSession(ds.session);
|
|
1827
|
+
}
|
|
1828
|
+
else if (ds.session.nativeSessionTitleAwaitingContent
|
|
1829
|
+
&& !ds.session.nativeSessionTitleUserDefined
|
|
1830
|
+
&& titlePrompt) {
|
|
1831
|
+
ds.session.nativeSessionTitle = buildBotmuxLarkNativeSessionTitle(titlePrompt);
|
|
1832
|
+
ds.session.nativeSessionTitleAwaitingContent = undefined;
|
|
1833
|
+
nativeSessionTitlePrompt = titlePrompt;
|
|
1834
|
+
sessionStore.updateSession(ds.session);
|
|
1835
|
+
}
|
|
1836
|
+
else if (isFreshNativeSession && !ds.session.nativeSessionTitle) {
|
|
1837
|
+
ds.session.nativeSessionTitle = ds.session.title;
|
|
1838
|
+
sessionStore.updateSession(ds.session);
|
|
1839
|
+
}
|
|
1840
|
+
if (isFreshNativeSession
|
|
1841
|
+
|| (resume && !!ds.session.cliSessionId && !!ds.session.nativeSessionTitle)
|
|
1842
|
+
|| (resume
|
|
1843
|
+
&& !!ds.session.nativeSessionTitleAwaitingContent
|
|
1844
|
+
&& !!ds.session.nativeSessionTitle)
|
|
1845
|
+
|| (!!nativeSessionTitlePrompt && !!ds.session.nativeSessionTitle)) {
|
|
1846
|
+
nativeSessionTitle = ds.session.nativeSessionTitle;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1772
1849
|
// Claude Code blocks on the interactive folder-trust dialog the first time
|
|
1773
1850
|
// it runs in an untrusted workingDir; pre-accept it so the spawn doesn't hang.
|
|
1774
1851
|
// Seed CLI (Claude Code fork) has the same dialog — drive both off the
|
|
@@ -1893,10 +1970,15 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1893
1970
|
// getSessionPersistentBackendType). A brand-new session (no stamp) resolves
|
|
1894
1971
|
// from live config, so a dashboard backend switch only affects NEW sessions.
|
|
1895
1972
|
backendType: resolvePairedSpawnBackendType(agentCfg.cliId, ds.session.backendType, botCfg.backendType, config.daemon.backendType),
|
|
1973
|
+
// Shared Herdr is not derivable from sessionId: preserve the exact host +
|
|
1974
|
+
// managed-agent affinity across daemon/worker replacement.
|
|
1975
|
+
persistentBackendTarget: ds.session.persistentBackendTarget,
|
|
1896
1976
|
backendConfig: botCfg.riff,
|
|
1897
1977
|
riffParentTaskId: ds.session.riffParentTaskId,
|
|
1898
1978
|
riffRepoDirs: ds.session.riffRepoDirs,
|
|
1899
1979
|
deferredScheduleRun: ds.session.deferredScheduleRun,
|
|
1980
|
+
...(nativeSessionTitle ? { nativeSessionTitle } : {}),
|
|
1981
|
+
...(nativeSessionTitlePrompt ? { nativeSessionTitlePrompt } : {}),
|
|
1900
1982
|
prompt,
|
|
1901
1983
|
...(promptCodexAppInput ? { promptCodexAppInput } : {}),
|
|
1902
1984
|
resume,
|
|
@@ -1937,7 +2019,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1937
2019
|
sessionStore.updateSession(ds.session);
|
|
1938
2020
|
}
|
|
1939
2021
|
// Use shared handler for IPC messages and exit
|
|
1940
|
-
setupWorkerHandlers(ds, worker, startupState);
|
|
2022
|
+
setupWorkerHandlers(ds, worker, startupState, workerGeneration);
|
|
1941
2023
|
ds.worker = worker;
|
|
1942
2024
|
ds.spawnedAt = Date.now();
|
|
1943
2025
|
ds.cliVersion = currentCliVersion;
|
|
@@ -1967,11 +2049,57 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1967
2049
|
recordOwnershipForDaemonSession(ds);
|
|
1968
2050
|
}
|
|
1969
2051
|
// ─── Shared worker IPC handler ──────────────────────────────────────────────
|
|
1970
|
-
|
|
2052
|
+
/**
|
|
2053
|
+
* Clear the stuck-warning authority markers WITHOUT patching the card. Used by
|
|
2054
|
+
* ACK paths (tui_keys_delivered / stuck_warning_expired) where the caller has
|
|
2055
|
+
* already resolved the card to its final state — we only need to drop the
|
|
2056
|
+
* nonce/cardId/turnId so a late duplicate click cannot re-inject keys.
|
|
2057
|
+
*/
|
|
2058
|
+
export function clearStuckWarningAuthority(ds) {
|
|
2059
|
+
ds.stuckWarningCardId = undefined;
|
|
2060
|
+
ds.stuckWarningTurnId = undefined;
|
|
2061
|
+
ds.stuckWarningNonce = undefined;
|
|
2062
|
+
ds.stuckWarningPageType = undefined;
|
|
2063
|
+
ds.stuckWarningProcessing = false;
|
|
2064
|
+
ds.stuckWarningCliLifetime = undefined;
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Resolve any active stuck-warning card (PATCH it to "done") AND clear its
|
|
2068
|
+
* markers. Called from every path where the CLI can recover or be replaced:
|
|
2069
|
+
* prompt_ready, claude_exit, worker exit/kill/suspend/refork, and the worker's
|
|
2070
|
+
* own stale-card notification. Centralising here avoids missing an exit path
|
|
2071
|
+
* and leaving a clickable card that can inject keys into a different CLI.
|
|
2072
|
+
*
|
|
2073
|
+
* ACK paths (tui_keys_delivered / stuck_warning_expired) do NOT use this —
|
|
2074
|
+
* they patch the card themselves with a context-specific message and then call
|
|
2075
|
+
* clearStuckWarningAuthority() to drop the markers.
|
|
2076
|
+
*/
|
|
2077
|
+
export function invalidateStuckWarning(ds, reason) {
|
|
2078
|
+
if (!ds.stuckWarningCardId && !ds.stuckWarningTurnId && ds.stuckWarningNonce === undefined)
|
|
2079
|
+
return;
|
|
2080
|
+
const t = tag(ds);
|
|
2081
|
+
if (ds.stuckWarningCardId) {
|
|
2082
|
+
const locDs = localeForBot(ds.larkAppId);
|
|
2083
|
+
const resolvedCard = buildTuiPromptResolvedCard(tr('card.action.tui_done', undefined, locDs), locDs);
|
|
2084
|
+
updateMessage(ds.larkAppId, ds.stuckWarningCardId, resolvedCard).catch(err => logger.debug(`[${t}] Failed to resolve stuck-warning card (${reason}): ${err}`));
|
|
2085
|
+
}
|
|
2086
|
+
logger.debug(`[${t}] invalidateStuckWarning (${reason}): turn=${ds.stuckWarningTurnId ?? 'none'} nonce=${ds.stuckWarningNonce ?? 'none'}`);
|
|
2087
|
+
clearStuckWarningAuthority(ds);
|
|
2088
|
+
}
|
|
2089
|
+
function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureNotified: false }, reservedWorkerGeneration) {
|
|
1971
2090
|
const cb = requireCallbacks();
|
|
1972
2091
|
const t = tag(ds);
|
|
1973
|
-
const workerGeneration =
|
|
1974
|
-
|
|
2092
|
+
const workerGeneration = reservedWorkerGeneration
|
|
2093
|
+
?? reserveWorkerGeneration(ds);
|
|
2094
|
+
if (ds.workerGeneration !== workerGeneration
|
|
2095
|
+
|| ds.session.workerGeneration !== workerGeneration) {
|
|
2096
|
+
throw new Error('worker generation reservation changed before IPC setup');
|
|
2097
|
+
}
|
|
2098
|
+
// A new worker generation is starting. As a backstop, invalidate any
|
|
2099
|
+
// stuck-warning card posted by the previous generation — explicit kill/suspend/
|
|
2100
|
+
// exit paths should already have done this, but fork/refork/takeover paths
|
|
2101
|
+
// can leave a stale card that would otherwise inject keys into the new CLI.
|
|
2102
|
+
invalidateStuckWarning(ds, 'new_worker_generation');
|
|
1975
2103
|
// Managed turn authority is issued by one concrete worker lifetime. A
|
|
1976
2104
|
// replacement must advertise a fresh capability before daemon-mediated
|
|
1977
2105
|
// exits may use it; carrying the old value across a restore/refork would
|
|
@@ -2063,6 +2191,29 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2063
2191
|
worker.on('message', async (msg) => {
|
|
2064
2192
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
2065
2193
|
switch (msg.type) {
|
|
2194
|
+
case 'persistent_backend_target': {
|
|
2195
|
+
ds.session.persistentBackendTarget = msg.target;
|
|
2196
|
+
sessionStore.updateSession(ds.session);
|
|
2197
|
+
break;
|
|
2198
|
+
}
|
|
2199
|
+
case 'turn_input_committed': {
|
|
2200
|
+
// Bind the receipt to the exact live worker generation. A late ACK
|
|
2201
|
+
// from a replaced worker cannot make the replacement appear to have
|
|
2202
|
+
// accepted this dispatch turn.
|
|
2203
|
+
if (ds.worker !== worker
|
|
2204
|
+
|| ds.workerGeneration !== workerGeneration
|
|
2205
|
+
|| ds.session.workerGeneration !== workerGeneration) {
|
|
2206
|
+
logger.warn(`[${t}] Ignored turn_input_committed from stale worker generation`);
|
|
2207
|
+
break;
|
|
2208
|
+
}
|
|
2209
|
+
if (recordDispatchInputCommit(ds.session, msg.turnId, workerGeneration)) {
|
|
2210
|
+
sessionStore.updateSession(ds.session);
|
|
2211
|
+
}
|
|
2212
|
+
else {
|
|
2213
|
+
logger.warn(`[${t}] Ignored unbound input commit turn=${msg.turnId.slice(0, 16)}`);
|
|
2214
|
+
}
|
|
2215
|
+
break;
|
|
2216
|
+
}
|
|
2066
2217
|
case 'session_ready_ack': {
|
|
2067
2218
|
if (ds.worker !== worker) {
|
|
2068
2219
|
logger.warn(`[${t}] Ignored session_ready_ack from stale worker generation`);
|
|
@@ -2096,6 +2247,10 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2096
2247
|
ds.workerViewToken = msg.viewToken ?? null;
|
|
2097
2248
|
// Persist port so it can be reused after daemon restart
|
|
2098
2249
|
ds.session.webPort = msg.port;
|
|
2250
|
+
// Dashboard「复现命令」:worker 上报本次冷启的近似复现命令。只存内存字段、
|
|
2251
|
+
// 绝不落盘(含凭证);warm reattach 时 worker 不重算,故为空——这是有意的
|
|
2252
|
+
// (reattach 不代表本次新算命令真的执行了)。worker 每次 ready 会重报。
|
|
2253
|
+
ds.spawnCommand = msg.spawnCommand;
|
|
2099
2254
|
sessionStore.updateSession(ds.session);
|
|
2100
2255
|
const readOnlyUrl = buildTerminalUrl(ds);
|
|
2101
2256
|
const writeUrl = buildTerminalUrl(ds, { write: true });
|
|
@@ -2123,6 +2278,18 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2123
2278
|
logger.info(`[${t}] Managed/silent turn — suppressing ready/streaming card output`);
|
|
2124
2279
|
break;
|
|
2125
2280
|
}
|
|
2281
|
+
// Pi can finish a very fast startup turn through `botmux send` before
|
|
2282
|
+
// Herdr reports idle and the worker emits ready. Posting the initial
|
|
2283
|
+
// card now would put a stale "Starting" card *after* the final reply,
|
|
2284
|
+
// with no later transcript output to withdraw it (the explicit send
|
|
2285
|
+
// intentionally suppresses bridge fallback). Keep the terminal/runtime
|
|
2286
|
+
// state above, but suppress this already-completed turn's card.
|
|
2287
|
+
if (msg.replyAlreadySent) {
|
|
2288
|
+
ds.streamCardPending = false;
|
|
2289
|
+
persistStreamCardState(ds);
|
|
2290
|
+
logger.info(`[${t}] Explicit reply landed before worker ready — skipping stale starting card`);
|
|
2291
|
+
break;
|
|
2292
|
+
}
|
|
2126
2293
|
// Bot opted out of the streaming card: the terminal is up and the
|
|
2127
2294
|
// final answer will still arrive via `botmux send`; just don't post the
|
|
2128
2295
|
// live status card. (workerPort/token above are still set so the web
|
|
@@ -2277,6 +2444,8 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2277
2444
|
break;
|
|
2278
2445
|
}
|
|
2279
2446
|
case 'prompt_ready': {
|
|
2447
|
+
if (ds.worker !== worker)
|
|
2448
|
+
break;
|
|
2280
2449
|
logger.info(`[${t}] ${getCliDisplayName(effectiveCliId)} is ready for input`);
|
|
2281
2450
|
// A live prompt means a (re)spawn reached a working CLI — clear the lazy
|
|
2282
2451
|
// cold-resume marker set when we parked a crash diagnostic shell. The
|
|
@@ -2319,6 +2488,8 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2319
2488
|
...(followUpCodexAppInput ? { codexAppInput: followUpCodexAppInput } : {}),
|
|
2320
2489
|
}, { codexAppInputAccepted: !!followUpCodexAppInput });
|
|
2321
2490
|
}
|
|
2491
|
+
// CLI reached its prompt — any previously posted stuck warning is stale.
|
|
2492
|
+
invalidateStuckWarning(ds, 'prompt_ready');
|
|
2322
2493
|
break;
|
|
2323
2494
|
}
|
|
2324
2495
|
case 'cli_session_id': {
|
|
@@ -2340,6 +2511,21 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2340
2511
|
}
|
|
2341
2512
|
break;
|
|
2342
2513
|
}
|
|
2514
|
+
case 'native_session_title_generated': {
|
|
2515
|
+
if (ds.worker !== worker || ds.session.nativeSessionTitleUserDefined)
|
|
2516
|
+
break;
|
|
2517
|
+
const title = msg.title.trim();
|
|
2518
|
+
if (!title)
|
|
2519
|
+
break;
|
|
2520
|
+
ds.session.nativeSessionTitle = title;
|
|
2521
|
+
ds.session.nativeSessionTitleAwaitingContent = undefined;
|
|
2522
|
+
if (ds.initConfig) {
|
|
2523
|
+
ds.initConfig.nativeSessionTitle = title;
|
|
2524
|
+
ds.initConfig.nativeSessionTitlePrompt = undefined;
|
|
2525
|
+
}
|
|
2526
|
+
sessionStore.updateSession(ds.session);
|
|
2527
|
+
break;
|
|
2528
|
+
}
|
|
2343
2529
|
case 'screen_update': {
|
|
2344
2530
|
// Wait for `ready` (workerPort) before any card work — the read link
|
|
2345
2531
|
// is the LOCAL log terminal for every backend including riff
|
|
@@ -2551,6 +2737,137 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2551
2737
|
}
|
|
2552
2738
|
break;
|
|
2553
2739
|
}
|
|
2740
|
+
case 'stuck_warning': {
|
|
2741
|
+
// Ignore stuck_warning from a stale worker generation — the CLI may have
|
|
2742
|
+
// been replaced and a new worker owns the session now.
|
|
2743
|
+
if (ds.worker !== worker)
|
|
2744
|
+
break;
|
|
2745
|
+
// AI-free StuckDetector fired: a written input hasn't produced a
|
|
2746
|
+
// completed turn within the timeout AND the PTY has been quiet, AND the
|
|
2747
|
+
// snapshot matches a known Codex hook-review screen. The detector only
|
|
2748
|
+
// emits when it matches level 1 or level 2 — there is no "unknown
|
|
2749
|
+
// stall" path, so we always build an interactive card with the exact
|
|
2750
|
+
// keys the screen documents.
|
|
2751
|
+
// Dedup: skip if we already posted a warning for THIS turn, OR if a
|
|
2752
|
+
// stuck-warning card is already active (covers the no-turnId case where
|
|
2753
|
+
// a second stall fires before the first card is resolved).
|
|
2754
|
+
const isDuplicateTurn = msg.turnId !== undefined && ds.stuckWarningTurnId === msg.turnId;
|
|
2755
|
+
const hasActiveCard = !!ds.stuckWarningCardId;
|
|
2756
|
+
if (isDuplicateTurn || hasActiveCard) {
|
|
2757
|
+
logger.debug(`[${t}] Stuck warning dedup skipped (turn=${msg.turnId ?? 'none'}, activeCard=${hasActiveCard})`);
|
|
2758
|
+
break;
|
|
2759
|
+
}
|
|
2760
|
+
// Allocate a daemon-side nonce for this warning (NOT the worker's
|
|
2761
|
+
// generation — the daemon is the authority on which warning is active).
|
|
2762
|
+
// If the CLI recovers (prompt_ready) or the worker is replaced while the
|
|
2763
|
+
// POST is in flight, invalidateStuckWarning clears the nonce; when the
|
|
2764
|
+
// POST returns we check it is still current and, if not, resolve the card
|
|
2765
|
+
// immediately instead of registering it as active.
|
|
2766
|
+
// Allocate a daemon-side nonce for this warning from a monotonic
|
|
2767
|
+
// counter that is NEVER cleared (even when the active authority is
|
|
2768
|
+
// dropped). This prevents the nonce-reuse race: warning nonce=1 POST
|
|
2769
|
+
// awaits → prompt_ready clears active nonce → warning nonce=1 again
|
|
2770
|
+
// would let the old POST register against the new authority. With a
|
|
2771
|
+
// monotonic counter the second warning gets nonce=2, and the old
|
|
2772
|
+
// POST's nonce=1 check fails.
|
|
2773
|
+
const nonce = (ds.stuckWarningNonceCounter ?? 0) + 1;
|
|
2774
|
+
ds.stuckWarningNonceCounter = nonce;
|
|
2775
|
+
ds.stuckWarningNonce = nonce;
|
|
2776
|
+
ds.stuckWarningTurnId = msg.turnId;
|
|
2777
|
+
ds.stuckWarningCliLifetime = msg.cliLifetime;
|
|
2778
|
+
const pageType = msg.matchedPattern;
|
|
2779
|
+
ds.stuckWarningPageType = pageType;
|
|
2780
|
+
const secs = Math.round(msg.elapsedMs / 1000);
|
|
2781
|
+
logger.info(`[${t}] Stuck warning: turn unresolved for ${secs}s (${pageType}) nonce=${nonce}`);
|
|
2782
|
+
emitSessionLifecycleHook(ds, 'session.requires_attention', {
|
|
2783
|
+
reason: 'stuck_warning',
|
|
2784
|
+
elapsedMs: msg.elapsedMs,
|
|
2785
|
+
matchedPattern: pageType,
|
|
2786
|
+
});
|
|
2787
|
+
if (managedAuxUiSuppressed(msg.turnId, msg.dispatchAttempt))
|
|
2788
|
+
break;
|
|
2789
|
+
let description;
|
|
2790
|
+
let options;
|
|
2791
|
+
if (pageType === 'hook review level 2') {
|
|
2792
|
+
// Level 2 — per-hook review: t=trust, Esc=go back. No Enter here.
|
|
2793
|
+
description = `⚠️ Codex 卡在单项 hook 审核界面——消息已写入但 ${secs}s 未完成处理。\n\n选择操作以继续,或打开终端手动处理:`;
|
|
2794
|
+
options = [
|
|
2795
|
+
{ label: 't', text: '信任 (trust)', selected: false, type: 'confirm', keys: ['t'] },
|
|
2796
|
+
{ label: 'Esc', text: '返回 (go back)', selected: false, type: 'select', keys: ['Escape'] },
|
|
2797
|
+
];
|
|
2798
|
+
}
|
|
2799
|
+
else {
|
|
2800
|
+
// Level 1 — hooks browser: t=trust all, Enter=review hooks, Esc=close.
|
|
2801
|
+
description = `⚠️ Codex 卡在 hook 审核界面——消息已写入但 ${secs}s 未完成处理。\n\n选择操作以继续,或打开终端手动处理:`;
|
|
2802
|
+
options = [
|
|
2803
|
+
{ label: 't', text: '信任全部 (trust all)', selected: false, type: 'confirm', keys: ['t'] },
|
|
2804
|
+
{ label: 'Enter', text: '逐项审核 (review hooks)', selected: false, type: 'select', keys: ['Enter'] },
|
|
2805
|
+
{ label: 'Esc', text: '关闭 (close)', selected: false, type: 'select', keys: ['Escape'] },
|
|
2806
|
+
];
|
|
2807
|
+
}
|
|
2808
|
+
try {
|
|
2809
|
+
const cardJson = buildTuiPromptCard(sessionAnchorId(ds), ds.session.sessionId, description, options, false, undefined, loc);
|
|
2810
|
+
const cardMsgId = await scopedReply(cardJson, 'interactive', msg.turnId);
|
|
2811
|
+
// Authority check: if the warning was invalidated (prompt_ready,
|
|
2812
|
+
// CLI exit, worker replace) OR the worker was replaced while the POST
|
|
2813
|
+
// was in flight, resolve the card immediately and do NOT register it
|
|
2814
|
+
// as active — a late click would otherwise inject keys into a
|
|
2815
|
+
// recovered/replaced CLI. Verify the full tuple: same worker process,
|
|
2816
|
+
// same worker generation, same nonce.
|
|
2817
|
+
if (ds.worker !== worker || ds.workerGeneration !== workerGeneration || ds.stuckWarningNonce !== nonce) {
|
|
2818
|
+
logger.debug(`[${t}] Stuck warning card POSTed but authority stale (nonce=${nonce}, current=${ds.stuckWarningNonce ?? 'none'}, workerGen=${workerGeneration}) — resolving`);
|
|
2819
|
+
const locDs = localeForBot(ds.larkAppId);
|
|
2820
|
+
const resolvedCard = buildTuiPromptResolvedCard(tr('card.action.tui_done', undefined, locDs), locDs);
|
|
2821
|
+
updateMessage(ds.larkAppId, cardMsgId, resolvedCard).catch(err => logger.debug(`[${t}] Failed to resolve stale stuck-warning card: ${err}`));
|
|
2822
|
+
break;
|
|
2823
|
+
}
|
|
2824
|
+
ds.stuckWarningCardId = cardMsgId;
|
|
2825
|
+
publishAttentionPatch(ds);
|
|
2826
|
+
}
|
|
2827
|
+
catch (err) {
|
|
2828
|
+
logger.warn(`[${t}] Failed to post stuck warning card: ${err}`);
|
|
2829
|
+
// Card send failed — clear markers ONLY if this nonce is still
|
|
2830
|
+
// current AND we still own the worker. A newer warning (nonce+1) may
|
|
2831
|
+
// have started while this POST was in flight; we must not wipe the
|
|
2832
|
+
// newer state.
|
|
2833
|
+
if (ds.worker === worker && ds.workerGeneration === workerGeneration && ds.stuckWarningNonce === nonce) {
|
|
2834
|
+
clearStuckWarningAuthority(ds);
|
|
2835
|
+
}
|
|
2836
|
+
}
|
|
2837
|
+
break;
|
|
2838
|
+
}
|
|
2839
|
+
case 'stuck_warning_expired': {
|
|
2840
|
+
// Ignore from a stale worker generation.
|
|
2841
|
+
if (ds.worker !== worker || ds.workerGeneration !== workerGeneration)
|
|
2842
|
+
break;
|
|
2843
|
+
// Worker refused to inject keys from a stuck-warning card because the
|
|
2844
|
+
// current screen no longer matches the page type the card was built for.
|
|
2845
|
+
// Resolve the card with a "page changed" message so the user knows the
|
|
2846
|
+
// action was NOT performed, then clear the authority.
|
|
2847
|
+
if (ds.stuckWarningNonce === msg.nonce && ds.stuckWarningCardId) {
|
|
2848
|
+
const locDs = localeForBot(ds.larkAppId);
|
|
2849
|
+
const resolvedCard = buildTuiPromptResolvedCard('页面已变化,未发送按键', locDs);
|
|
2850
|
+
updateMessage(ds.larkAppId, ds.stuckWarningCardId, resolvedCard).catch(err => logger.debug(`[${t}] Failed to update stuck-warning card on expired: ${err}`));
|
|
2851
|
+
clearStuckWarningAuthority(ds);
|
|
2852
|
+
}
|
|
2853
|
+
break;
|
|
2854
|
+
}
|
|
2855
|
+
case 'tui_keys_delivered': {
|
|
2856
|
+
// Ignore from a stale worker generation.
|
|
2857
|
+
if (ds.worker !== worker || ds.workerGeneration !== workerGeneration)
|
|
2858
|
+
break;
|
|
2859
|
+
// Worker confirmed the keys were written to the PTY. Clear the card
|
|
2860
|
+
// authority and render success. We only do this AFTER the worker ACK
|
|
2861
|
+
// (not on click), so a rejected click (stuck_warning_expired) does not
|
|
2862
|
+
// falsely report success.
|
|
2863
|
+
if (ds.stuckWarningNonce === msg.nonce && ds.stuckWarningCardId) {
|
|
2864
|
+
const locDs = localeForBot(ds.larkAppId);
|
|
2865
|
+
const resolvedCard = buildTuiPromptResolvedCard(tr('card.action.tui_done', undefined, locDs), locDs);
|
|
2866
|
+
updateMessage(ds.larkAppId, ds.stuckWarningCardId, resolvedCard).catch(err => logger.debug(`[${t}] Failed to resolve stuck-warning card on delivered: ${err}`));
|
|
2867
|
+
clearStuckWarningAuthority(ds);
|
|
2868
|
+
}
|
|
2869
|
+
break;
|
|
2870
|
+
}
|
|
2554
2871
|
case 'claude_exit': {
|
|
2555
2872
|
// CLI-generation authority must not outlive the concrete worker/CLI
|
|
2556
2873
|
// pair that issued it. A delayed message from a replaced Node worker
|
|
@@ -2560,6 +2877,10 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2560
2877
|
break;
|
|
2561
2878
|
}
|
|
2562
2879
|
ds.managedTurnOrigin = undefined;
|
|
2880
|
+
// The worker/CLI generation ended. Disable an outstanding stuck-warning
|
|
2881
|
+
// card before any replacement worker can be attached; otherwise a late
|
|
2882
|
+
// click could inject its keys into the replacement CLI.
|
|
2883
|
+
invalidateStuckWarning(ds, 'claude_exit');
|
|
2563
2884
|
logger.info(`[${t}] ${getCliDisplayName(effectiveCliId)} exited (code: ${msg.code}, signal: ${msg.signal})`);
|
|
2564
2885
|
ds.hasHistory = true;
|
|
2565
2886
|
try {
|
|
@@ -2979,6 +3300,19 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2979
3300
|
ds.worker = null;
|
|
2980
3301
|
ds.workerPort = null;
|
|
2981
3302
|
ds.managedTurnOrigin = undefined;
|
|
3303
|
+
// This worker generation is gone. Invalidate any stuck-warning card it
|
|
3304
|
+
// posted so a late click cannot inject keys into a replacement worker.
|
|
3305
|
+
invalidateStuckWarning(ds, 'worker_exit');
|
|
3306
|
+
// Fence this lifetime before a polling dispatcher can observe its last
|
|
3307
|
+
// ACK. Keeping the old receipt is useful audit evidence, but the
|
|
3308
|
+
// persisted current generation advances immediately so it cannot count
|
|
3309
|
+
// as acceptance after the worker has died. A stale takeover worker never
|
|
3310
|
+
// enters this branch and therefore cannot fence the replacement.
|
|
3311
|
+
const fencedGeneration = Math.max(workerGeneration, ds.workerGeneration ?? 0, ds.session.workerGeneration ?? 0) + 1;
|
|
3312
|
+
ds.workerGeneration = fencedGeneration;
|
|
3313
|
+
ds.session.workerGeneration = fencedGeneration;
|
|
3314
|
+
ds.session.pid = undefined;
|
|
3315
|
+
sessionStore.updateSession(ds.session);
|
|
2982
3316
|
}
|
|
2983
3317
|
try {
|
|
2984
3318
|
const notified = cb.onWorkerExit?.(ds, {
|
|
@@ -3345,6 +3679,28 @@ export const __testOnly_setupWorkerHandlers = setupWorkerHandlers;
|
|
|
3345
3679
|
export const __testOnly_finishTurnReactions = finishTurnReactions;
|
|
3346
3680
|
export const __testOnly_finalOutputDedupeKey = finalOutputDedupeKey;
|
|
3347
3681
|
// ─── Fork adopt worker ──────────────────────────────────────────────────────
|
|
3682
|
+
function reserveWorkerGeneration(ds) {
|
|
3683
|
+
const previousDaemonGeneration = ds.workerGeneration;
|
|
3684
|
+
const previousSessionGeneration = ds.session.workerGeneration;
|
|
3685
|
+
const workerGeneration = Math.max(previousDaemonGeneration ?? 0, previousSessionGeneration ?? 0) + 1;
|
|
3686
|
+
ds.workerGeneration = workerGeneration;
|
|
3687
|
+
ds.session.workerGeneration = workerGeneration;
|
|
3688
|
+
try {
|
|
3689
|
+
sessionStore.updateSession(ds.session);
|
|
3690
|
+
}
|
|
3691
|
+
catch (error) {
|
|
3692
|
+
if (previousDaemonGeneration === undefined)
|
|
3693
|
+
delete ds.workerGeneration;
|
|
3694
|
+
else
|
|
3695
|
+
ds.workerGeneration = previousDaemonGeneration;
|
|
3696
|
+
if (previousSessionGeneration === undefined)
|
|
3697
|
+
delete ds.session.workerGeneration;
|
|
3698
|
+
else
|
|
3699
|
+
ds.session.workerGeneration = previousSessionGeneration;
|
|
3700
|
+
throw error;
|
|
3701
|
+
}
|
|
3702
|
+
return workerGeneration;
|
|
3703
|
+
}
|
|
3348
3704
|
export function forkAdoptWorker(ds, opts) {
|
|
3349
3705
|
const cb = requireCallbacks();
|
|
3350
3706
|
const workerPath = join(__dirname, '..', 'worker.js');
|
|
@@ -3362,6 +3718,9 @@ export function forkAdoptWorker(ds, opts) {
|
|
|
3362
3718
|
logger.warn(`[${t}] read-isolation bot: refusing to adopt existing CLI (would run unisolated); will cold-start isolated on next message`);
|
|
3363
3719
|
return;
|
|
3364
3720
|
}
|
|
3721
|
+
// Reserve before replacing an existing bridge worker for the same reason as
|
|
3722
|
+
// forkWorker: persistence failure must leave the old lifetime untouched.
|
|
3723
|
+
const workerGeneration = reserveWorkerGeneration(ds);
|
|
3365
3724
|
// Guard against double-fork
|
|
3366
3725
|
if (ds.worker && !ds.worker.killed) {
|
|
3367
3726
|
logger.warn(`[${t}] Worker already running, killing before adopt-fork`);
|
|
@@ -3541,7 +3900,7 @@ export function forkAdoptWorker(ds, opts) {
|
|
|
3541
3900
|
sessionStore.updateSession(ds.session);
|
|
3542
3901
|
}
|
|
3543
3902
|
// Use shared handler
|
|
3544
|
-
setupWorkerHandlers(ds, worker, startupState);
|
|
3903
|
+
setupWorkerHandlers(ds, worker, startupState, workerGeneration);
|
|
3545
3904
|
ds.worker = worker;
|
|
3546
3905
|
ds.spawnedAt = Date.now();
|
|
3547
3906
|
ds.cliVersion = '';
|
|
@@ -3705,9 +4064,17 @@ function cleanupPersistentBackendSessions(backendType, activeSessions_) {
|
|
|
3705
4064
|
const currentCliId = config.daemon.cliId;
|
|
3706
4065
|
if (!multiBot && lastCliId && lastCliId !== currentCliId) {
|
|
3707
4066
|
logger.info(`CLI_ID changed (${lastCliId} → ${currentCliId}), killing all ${backendType} sessions`);
|
|
4067
|
+
// Legacy per-topic hosts are still enumerable by bmx-* name.
|
|
3708
4068
|
for (const name of backend.listBotmuxSessions()) {
|
|
3709
4069
|
backend.killSession(name);
|
|
3710
4070
|
}
|
|
4071
|
+
// Machine-wide Herdr agents are not separate bmx-* sessions. Tear down
|
|
4072
|
+
// each persisted managed target precisely so a CLI switch cannot reattach
|
|
4073
|
+
// the old executable, while never stopping the shared `botmux` host or an
|
|
4074
|
+
// explicitly adopted user pane.
|
|
4075
|
+
for (const target of managedTargetsForCliChange(backendType, activeSessions_)) {
|
|
4076
|
+
killPersistentBackendTarget(target);
|
|
4077
|
+
}
|
|
3711
4078
|
}
|
|
3712
4079
|
else {
|
|
3713
4080
|
const activeNames = new Set(activeSessions_.map(s => backend.sessionName(s.sessionId)));
|
|
@@ -3730,9 +4097,12 @@ function cleanupPersistentBackendSessions(backendType, activeSessions_) {
|
|
|
3730
4097
|
continue;
|
|
3731
4098
|
}
|
|
3732
4099
|
if (botCliId && sessionCliId !== botCliId) {
|
|
3733
|
-
const
|
|
3734
|
-
|
|
3735
|
-
|
|
4100
|
+
const target = resolvePersistentBackendTarget(backendType, session.sessionId, session.persistentBackendTarget);
|
|
4101
|
+
const label = target.backendType === 'herdr' && target.agentName
|
|
4102
|
+
? `${target.sessionName}/${target.agentName}`
|
|
4103
|
+
: target.sessionName;
|
|
4104
|
+
logger.info(`CLI mismatch for ${session.sessionId.substring(0, 8)} (session=${sessionCliId}, bot=${botCliId}), killing ${backendType} ${label}`);
|
|
4105
|
+
killPersistentBackendTarget(target);
|
|
3736
4106
|
}
|
|
3737
4107
|
}
|
|
3738
4108
|
}
|