botmux 3.7.1 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.runtime-build-id +1 -1
- package/dist/adapters/backend/capabilities.d.ts +17 -0
- package/dist/adapters/backend/capabilities.d.ts.map +1 -0
- package/dist/adapters/backend/capabilities.js +22 -0
- package/dist/adapters/backend/capabilities.js.map +1 -0
- package/dist/adapters/backend/critical-control-key.d.ts +19 -0
- package/dist/adapters/backend/critical-control-key.d.ts.map +1 -0
- package/dist/adapters/backend/critical-control-key.js +34 -0
- package/dist/adapters/backend/critical-control-key.js.map +1 -0
- package/dist/adapters/backend/herdr-backend.d.ts +9 -0
- package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
- package/dist/adapters/backend/herdr-backend.js +23 -4
- package/dist/adapters/backend/herdr-backend.js.map +1 -1
- package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
- package/dist/adapters/backend/riff-backend.js +3 -2
- package/dist/adapters/backend/riff-backend.js.map +1 -1
- package/dist/adapters/backend/session-backend-selector.d.ts +61 -6
- package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -1
- package/dist/adapters/backend/session-backend-selector.js +173 -9
- package/dist/adapters/backend/session-backend-selector.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +10 -3
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +25 -8
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +3 -2
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +45 -4
- package/dist/adapters/backend/types.d.ts.map +1 -1
- package/dist/adapters/backend/types.js +11 -0
- package/dist/adapters/backend/types.js.map +1 -1
- package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zellij-backend.js +3 -2
- package/dist/adapters/backend/zellij-backend.js.map +1 -1
- package/dist/adapters/backend/zmx-backend.d.ts +286 -0
- package/dist/adapters/backend/zmx-backend.d.ts.map +1 -0
- package/dist/adapters/backend/zmx-backend.js +2396 -0
- package/dist/adapters/backend/zmx-backend.js.map +1 -0
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +3 -0
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/codex-app.d.ts.map +1 -1
- package/dist/adapters/cli/codex-app.js +5 -1
- package/dist/adapters/cli/codex-app.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +26 -1
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/fs-policy.d.ts +79 -1
- package/dist/adapters/cli/fs-policy.d.ts.map +1 -1
- package/dist/adapters/cli/fs-policy.js +290 -13
- package/dist/adapters/cli/fs-policy.js.map +1 -1
- package/dist/adapters/cli/grok.d.ts.map +1 -1
- package/dist/adapters/cli/grok.js +4 -1
- package/dist/adapters/cli/grok.js.map +1 -1
- package/dist/adapters/cli/oh-my-pi.d.ts.map +1 -1
- package/dist/adapters/cli/oh-my-pi.js +5 -3
- package/dist/adapters/cli/oh-my-pi.js.map +1 -1
- package/dist/adapters/cli/shared-hints.d.ts +7 -4
- package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
- package/dist/adapters/cli/shared-hints.js +52 -35
- package/dist/adapters/cli/shared-hints.js.map +1 -1
- package/dist/adapters/cli/strict-input-handle.d.ts +13 -0
- package/dist/adapters/cli/strict-input-handle.d.ts.map +1 -0
- package/dist/adapters/cli/strict-input-handle.js +39 -0
- package/dist/adapters/cli/strict-input-handle.js.map +1 -0
- package/dist/adapters/cli/traex.d.ts.map +1 -1
- package/dist/adapters/cli/traex.js +6 -4
- package/dist/adapters/cli/traex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +16 -0
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +137 -8
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +298 -12
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/session-list-liveness.d.ts +1 -1
- package/dist/cli/session-list-liveness.js +1 -1
- package/dist/cli/zmx-managed-attach.d.ts +41 -0
- package/dist/cli/zmx-managed-attach.d.ts.map +1 -0
- package/dist/cli/zmx-managed-attach.js +72 -0
- package/dist/cli/zmx-managed-attach.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +780 -131
- package/dist/cli.js.map +1 -1
- package/dist/codex-app-runner.js +90 -2
- package/dist/codex-app-runner.js.map +1 -1
- package/dist/codex-rpc-engine.d.ts.map +1 -1
- package/dist/codex-rpc-engine.js +22 -6
- package/dist/codex-rpc-engine.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +29 -1
- package/dist/config.js.map +1 -1
- package/dist/core/ask-broker.d.ts +22 -1
- package/dist/core/ask-broker.d.ts.map +1 -1
- package/dist/core/ask-broker.js +12 -5
- package/dist/core/ask-broker.js.map +1 -1
- package/dist/core/botmux-wrapper.d.ts +18 -0
- package/dist/core/botmux-wrapper.d.ts.map +1 -1
- package/dist/core/botmux-wrapper.js +25 -1
- package/dist/core/botmux-wrapper.js.map +1 -1
- package/dist/core/chat-rename.d.ts +27 -0
- package/dist/core/chat-rename.d.ts.map +1 -0
- package/dist/core/chat-rename.js +53 -0
- package/dist/core/chat-rename.js.map +1 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +160 -12
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/cost-calculator.d.ts +25 -1
- package/dist/core/cost-calculator.d.ts.map +1 -1
- package/dist/core/cost-calculator.js +102 -4
- package/dist/core/cost-calculator.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +16 -2
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +611 -62
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dashboard-rows.d.ts +22 -1
- package/dist/core/dashboard-rows.d.ts.map +1 -1
- package/dist/core/dashboard-rows.js +61 -0
- package/dist/core/dashboard-rows.js.map +1 -1
- package/dist/core/dashboard-url.d.ts +6 -0
- package/dist/core/dashboard-url.d.ts.map +1 -1
- package/dist/core/dashboard-url.js +11 -1
- package/dist/core/dashboard-url.js.map +1 -1
- package/dist/core/device-isolation-daemon.d.ts +28 -6
- package/dist/core/device-isolation-daemon.d.ts.map +1 -1
- package/dist/core/device-isolation-daemon.js +96 -13
- package/dist/core/device-isolation-daemon.js.map +1 -1
- package/dist/core/host-overload-alert.d.ts +75 -0
- package/dist/core/host-overload-alert.d.ts.map +1 -1
- package/dist/core/host-overload-alert.js +103 -0
- package/dist/core/host-overload-alert.js.map +1 -1
- package/dist/core/idle-worker-sweeper.js +1 -1
- package/dist/core/idle-worker-sweeper.js.map +1 -1
- package/dist/core/inflight-input-tracker.d.ts +3 -0
- package/dist/core/inflight-input-tracker.d.ts.map +1 -1
- package/dist/core/inflight-input-tracker.js +9 -0
- package/dist/core/inflight-input-tracker.js.map +1 -1
- package/dist/core/passthrough-commands.d.ts.map +1 -1
- package/dist/core/passthrough-commands.js +10 -0
- package/dist/core/passthrough-commands.js.map +1 -1
- package/dist/core/persistent-backend.d.ts +32 -4
- package/dist/core/persistent-backend.d.ts.map +1 -1
- package/dist/core/persistent-backend.js +75 -6
- package/dist/core/persistent-backend.js.map +1 -1
- package/dist/core/plugins/mcp/gateway.js +2 -2
- package/dist/core/plugins/mcp/gateway.js.map +1 -1
- package/dist/core/plugins/runtime.d.ts +3 -0
- package/dist/core/plugins/runtime.d.ts.map +1 -1
- package/dist/core/plugins/runtime.js.map +1 -1
- package/dist/core/plugins/service-manager.d.ts +4 -1
- package/dist/core/plugins/service-manager.d.ts.map +1 -1
- package/dist/core/plugins/service-manager.js +11 -6
- package/dist/core/plugins/service-manager.js.map +1 -1
- package/dist/core/role-library.d.ts +48 -1
- package/dist/core/role-library.d.ts.map +1 -1
- package/dist/core/role-library.js +111 -3
- package/dist/core/role-library.js.map +1 -1
- package/dist/core/role-resolver.d.ts +14 -2
- package/dist/core/role-resolver.d.ts.map +1 -1
- package/dist/core/role-resolver.js +47 -4
- package/dist/core/role-resolver.js.map +1 -1
- package/dist/core/session-activity.d.ts +14 -0
- package/dist/core/session-activity.d.ts.map +1 -1
- package/dist/core/session-activity.js +46 -1
- package/dist/core/session-activity.js.map +1 -1
- package/dist/core/session-board.d.ts +5 -2
- package/dist/core/session-board.d.ts.map +1 -1
- package/dist/core/session-board.js +5 -2
- package/dist/core/session-board.js.map +1 -1
- package/dist/core/session-liveness.d.ts +6 -5
- package/dist/core/session-liveness.d.ts.map +1 -1
- package/dist/core/session-liveness.js +34 -13
- package/dist/core/session-liveness.js.map +1 -1
- package/dist/core/session-manager.d.ts +3 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +397 -103
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-marker.d.ts +11 -2
- package/dist/core/session-marker.d.ts.map +1 -1
- package/dist/core/session-marker.js +50 -11
- package/dist/core/session-marker.js.map +1 -1
- package/dist/core/session-message-preview.d.ts +20 -0
- package/dist/core/session-message-preview.d.ts.map +1 -0
- package/dist/core/session-message-preview.js +188 -0
- package/dist/core/session-message-preview.js.map +1 -0
- package/dist/core/session-title.d.ts +5 -1
- package/dist/core/session-title.d.ts.map +1 -1
- package/dist/core/session-title.js +20 -3
- package/dist/core/session-title.js.map +1 -1
- package/dist/core/startup-commands.d.ts +1 -1
- package/dist/core/startup-commands.js +1 -1
- package/dist/core/terminal-url.d.ts.map +1 -1
- package/dist/core/terminal-url.js +3 -2
- package/dist/core/terminal-url.js.map +1 -1
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +75 -12
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +35 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +23 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-ipc.d.ts +11 -0
- package/dist/core/worker-ipc.d.ts.map +1 -0
- package/dist/core/worker-ipc.js +26 -0
- package/dist/core/worker-ipc.js.map +1 -0
- package/dist/core/worker-pool.d.ts +179 -29
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +2118 -331
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts +132 -3
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +1327 -449
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/auth.d.ts +2 -0
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +9 -0
- package/dist/dashboard/auth.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +4 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +5 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/federation-api.d.ts.map +1 -1
- package/dist/dashboard/federation-api.js +4 -0
- package/dist/dashboard/federation-api.js.map +1 -1
- package/dist/dashboard/federation-spoke-api.d.ts.map +1 -1
- package/dist/dashboard/federation-spoke-api.js +32 -5
- package/dist/dashboard/federation-spoke-api.js.map +1 -1
- package/dist/dashboard/public-redact.d.ts +8 -2
- package/dist/dashboard/public-redact.d.ts.map +1 -1
- package/dist/dashboard/public-redact.js +34 -7
- package/dist/dashboard/public-redact.js.map +1 -1
- package/dist/dashboard/session-card-model.d.ts.map +1 -1
- package/dist/dashboard/session-card-model.js +10 -2
- package/dist/dashboard/session-card-model.js.map +1 -1
- package/dist/dashboard/settings-write-applier.d.ts +35 -1
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +88 -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 +0 -1
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +15 -0
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +212 -58
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +3 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/bot-onboarding.d.ts +2 -1
- package/dist/dashboard/web/bot-onboarding.d.ts.map +1 -1
- package/dist/dashboard/web/bot-onboarding.js.map +1 -1
- package/dist/dashboard/web/clipboard.d.ts +2 -0
- package/dist/dashboard/web/clipboard.d.ts.map +1 -0
- package/dist/dashboard/web/clipboard.js +42 -0
- package/dist/dashboard/web/clipboard.js.map +1 -0
- package/dist/dashboard/web/connectors-page.d.ts.map +1 -1
- package/dist/dashboard/web/connectors-page.js +12 -8
- package/dist/dashboard/web/connectors-page.js.map +1 -1
- package/dist/dashboard/web/dashboard-components.d.ts +17 -16
- package/dist/dashboard/web/dashboard-components.d.ts.map +1 -1
- package/dist/dashboard/web/dashboard-components.js.map +1 -1
- package/dist/dashboard/web/groups-page.d.ts.map +1 -1
- package/dist/dashboard/web/groups-page.js +7 -3
- package/dist/dashboard/web/groups-page.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +205 -12
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/icon-tooltip.d.ts.map +1 -1
- package/dist/dashboard/web/icon-tooltip.js +16 -1
- package/dist/dashboard/web/icon-tooltip.js.map +1 -1
- package/dist/dashboard/web/listener-filters.d.ts +23 -0
- package/dist/dashboard/web/listener-filters.d.ts.map +1 -0
- package/dist/dashboard/web/listener-filters.js +36 -0
- package/dist/dashboard/web/listener-filters.js.map +1 -0
- package/dist/dashboard/web/overview-page.d.ts.map +1 -1
- package/dist/dashboard/web/overview-page.js.map +1 -1
- package/dist/dashboard/web/plugin-page.d.ts.map +1 -1
- package/dist/dashboard/web/plugin-page.js.map +1 -1
- package/dist/dashboard/web/preview-markdown.d.ts +5 -0
- package/dist/dashboard/web/preview-markdown.d.ts.map +1 -0
- package/dist/dashboard/web/preview-markdown.js +52 -0
- package/dist/dashboard/web/preview-markdown.js.map +1 -0
- package/dist/dashboard/web/roles-page.d.ts.map +1 -1
- package/dist/dashboard/web/roles-page.js +535 -10
- package/dist/dashboard/web/roles-page.js.map +1 -1
- package/dist/dashboard/web/roles.d.ts +77 -0
- package/dist/dashboard/web/roles.d.ts.map +1 -1
- package/dist/dashboard/web/roles.js +98 -0
- package/dist/dashboard/web/roles.js.map +1 -1
- package/dist/dashboard/web/session-terminal.d.ts.map +1 -1
- package/dist/dashboard/web/session-terminal.js +6 -4
- package/dist/dashboard/web/session-terminal.js.map +1 -1
- package/dist/dashboard/web/sessions-kanban.d.ts +4 -1
- package/dist/dashboard/web/sessions-kanban.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-kanban.js +1 -1
- package/dist/dashboard/web/sessions-kanban.js.map +1 -1
- package/dist/dashboard/web/sessions-page.d.ts +3 -2
- package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-page.js +163 -28
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts +25 -0
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +58 -7
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/settings-page.d.ts +28 -3
- package/dist/dashboard/web/settings-page.d.ts.map +1 -1
- package/dist/dashboard/web/settings-page.js +61 -1
- package/dist/dashboard/web/settings-page.js.map +1 -1
- package/dist/dashboard/web/skills-page.d.ts +6 -5
- package/dist/dashboard/web/skills-page.d.ts.map +1 -1
- package/dist/dashboard/web/skills-page.js +2 -0
- package/dist/dashboard/web/skills-page.js.map +1 -1
- package/dist/dashboard/web/v3-components.d.ts +3 -2
- package/dist/dashboard/web/v3-components.d.ts.map +1 -1
- package/dist/dashboard/web/v3-components.js.map +1 -1
- package/dist/dashboard/web/vc-consumer-profiles-section.d.ts +12 -2
- package/dist/dashboard/web/vc-consumer-profiles-section.d.ts.map +1 -1
- package/dist/dashboard/web/vc-consumer-profiles-section.js +0 -9
- package/dist/dashboard/web/vc-consumer-profiles-section.js.map +1 -1
- package/dist/dashboard/web/webhook-logs-page.d.ts +2 -1
- package/dist/dashboard/web/webhook-logs-page.d.ts.map +1 -1
- package/dist/dashboard/web/webhook-logs-page.js +4 -1
- package/dist/dashboard/web/webhook-logs-page.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-FAPIMQV3.js +10 -0
- package/dist/dashboard-web/chunks/{chunk-QFMH6ITB.js → chunk-5CJMHTT4.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-KQB42ML5.js → chunk-DLZK52JU.js} +14 -12
- package/dist/dashboard-web/chunks/chunk-FDZ7WOBW.js +16 -0
- package/dist/dashboard-web/chunks/{chunk-3QAUJXAS.js → chunk-GTAJV5YC.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-FNPGOZMX.js → chunk-IHNG6KE6.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-FLGXZJTD.js → chunk-VA6QWSGQ.js} +1 -1
- package/dist/dashboard-web/chunks/chunk-XJ7BGPJH.js +1 -0
- package/dist/dashboard-web/chunks/chunk-ZJVTGU5X.js +1 -0
- package/dist/dashboard-web/chunks/connectors-page-LKWRT3GS.js +1 -0
- package/dist/dashboard-web/chunks/groups-page-V6654ICQ.js +8 -0
- package/dist/dashboard-web/chunks/insights-page-237RMFQ5.js +7 -0
- package/dist/dashboard-web/chunks/{monitor-room-7IVIS75W.js → monitor-room-OO5Y4FRQ.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-5SSJRQZF.js → monitoring-page-OUEITM5P.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-EOND3L5D.js → office-page-4KNAHQ4Q.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-NISBVBBJ.js → overview-page-QDP4YK2S.js} +1 -1
- package/dist/dashboard-web/chunks/{plugin-page-JXC5F5YV.js → plugin-page-VEGTCEAS.js} +1 -1
- package/dist/dashboard-web/chunks/roles-page-PGXNZZNP.js +1 -0
- package/dist/dashboard-web/chunks/{schedules-page-AVOOKLUH.js → schedules-page-BJNC5TID.js} +1 -1
- package/dist/dashboard-web/chunks/sessions-page-M2ZYYCMO.js +6 -0
- package/dist/dashboard-web/chunks/settings-page-IJFZKUUW.js +2 -0
- package/dist/dashboard-web/chunks/{skills-page-5MW54PCC.js → skills-page-YRAIRZOQ.js} +3 -3
- package/dist/dashboard-web/chunks/{team-federation-page-2SNIMVXI.js → team-federation-page-DEU27GRU.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-W6SBXXQC.js → v3-page-6KHEW2YL.js} +5 -5
- package/dist/dashboard-web/chunks/{whiteboards-page-STC54KTP.js → whiteboards-page-VDECAALH.js} +1 -1
- package/dist/dashboard-web/style.css +12545 -11432
- package/dist/dashboard.js +215 -36
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +41 -2
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +47 -0
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +71 -19
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +71 -19
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/callback-button-marker.d.ts +34 -0
- package/dist/im/lark/callback-button-marker.d.ts.map +1 -0
- package/dist/im/lark/callback-button-marker.js +99 -0
- package/dist/im/lark/callback-button-marker.js.map +1 -0
- package/dist/im/lark/card-builder.d.ts +46 -10
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +299 -78
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +3 -4
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +380 -148
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +24 -1
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +101 -8
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/doc-comment.d.ts.map +1 -1
- package/dist/im/lark/doc-comment.js +6 -1
- package/dist/im/lark/doc-comment.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +115 -3
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +534 -147
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-command.d.ts +16 -12
- package/dist/im/lark/grant-command.d.ts.map +1 -1
- package/dist/im/lark/grant-command.js +48 -151
- package/dist/im/lark/grant-command.js.map +1 -1
- package/dist/im/lark/grant-pending.d.ts +12 -4
- package/dist/im/lark/grant-pending.d.ts.map +1 -1
- package/dist/im/lark/grant-pending.js +31 -8
- package/dist/im/lark/grant-pending.js.map +1 -1
- package/dist/im/lark/invite-command.d.ts +20 -0
- package/dist/im/lark/invite-command.d.ts.map +1 -0
- package/dist/im/lark/invite-command.js +239 -0
- package/dist/im/lark/invite-command.js.map +1 -0
- package/dist/im/lark/md-card.d.ts +66 -4
- package/dist/im/lark/md-card.d.ts.map +1 -1
- package/dist/im/lark/md-card.js +201 -31
- package/dist/im/lark/md-card.js.map +1 -1
- package/dist/im/lark/mention-targets.d.ts +43 -0
- package/dist/im/lark/mention-targets.d.ts.map +1 -0
- package/dist/im/lark/mention-targets.js +190 -0
- package/dist/im/lark/mention-targets.js.map +1 -0
- package/dist/im/lark/message-parser.d.ts +31 -0
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +305 -32
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/reply-card-footer-signature.d.ts +7 -0
- package/dist/im/lark/reply-card-footer-signature.d.ts.map +1 -0
- package/dist/im/lark/reply-card-footer-signature.js +7 -0
- package/dist/im/lark/reply-card-footer-signature.js.map +1 -0
- package/dist/im/lark/sessions-card.d.ts.map +1 -1
- package/dist/im/lark/sessions-card.js +4 -1
- package/dist/im/lark/sessions-card.js.map +1 -1
- package/dist/im/lark/substitute-command.d.ts.map +1 -1
- package/dist/im/lark/substitute-command.js +8 -0
- package/dist/im/lark/substitute-command.js.map +1 -1
- package/dist/im/lark/topic-root-context.d.ts +35 -0
- package/dist/im/lark/topic-root-context.d.ts.map +1 -0
- package/dist/im/lark/topic-root-context.js +37 -0
- package/dist/im/lark/topic-root-context.js.map +1 -0
- package/dist/im/lark/v3-blocked-card.d.ts.map +1 -1
- package/dist/im/lark/v3-blocked-card.js +2 -1
- package/dist/im/lark/v3-blocked-card.js.map +1 -1
- package/dist/im/lark/v3-gate-card.d.ts.map +1 -1
- package/dist/im/lark/v3-gate-card.js +2 -1
- package/dist/im/lark/v3-gate-card.js.map +1 -1
- package/dist/im/lark/v3-loop-grant-card.d.ts.map +1 -1
- package/dist/im/lark/v3-loop-grant-card.js +2 -1
- package/dist/im/lark/v3-loop-grant-card.js.map +1 -1
- package/dist/im/lark/v3-progress-card.d.ts.map +1 -1
- package/dist/im/lark/v3-progress-card.js +2 -1
- package/dist/im/lark/v3-progress-card.js.map +1 -1
- package/dist/im/lark/v3-revisit-grant-card.d.ts.map +1 -1
- package/dist/im/lark/v3-revisit-grant-card.js +2 -1
- package/dist/im/lark/v3-revisit-grant-card.js.map +1 -1
- package/dist/index-core-only.d.ts +3 -0
- package/dist/index-core-only.d.ts.map +1 -0
- package/dist/index-core-only.js +107 -0
- package/dist/index-core-only.js.map +1 -0
- package/dist/services/async-trigger-state.d.ts +8 -0
- package/dist/services/async-trigger-state.d.ts.map +1 -1
- package/dist/services/async-trigger-state.js +3 -2
- package/dist/services/async-trigger-state.js.map +1 -1
- package/dist/services/async-trigger-store.d.ts +10 -2
- package/dist/services/async-trigger-store.d.ts.map +1 -1
- package/dist/services/async-trigger-store.js +3 -2
- package/dist/services/async-trigger-store.js.map +1 -1
- package/dist/services/backend-availability.d.ts +3 -1
- package/dist/services/backend-availability.d.ts.map +1 -1
- package/dist/services/backend-availability.js +14 -1
- package/dist/services/backend-availability.js.map +1 -1
- package/dist/services/backend-type-store.d.ts.map +1 -1
- package/dist/services/backend-type-store.js +1 -1
- package/dist/services/backend-type-store.js.map +1 -1
- package/dist/services/bot-config-store.d.ts.map +1 -1
- package/dist/services/bot-config-store.js +2 -1
- package/dist/services/bot-config-store.js.map +1 -1
- package/dist/services/bridge-fallback-gate.d.ts +14 -1
- package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
- package/dist/services/bridge-fallback-gate.js +65 -1
- package/dist/services/bridge-fallback-gate.js.map +1 -1
- package/dist/services/card-prefs-store.d.ts +7 -2
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +30 -4
- package/dist/services/card-prefs-store.js.map +1 -1
- package/dist/services/codex-app-runner-protocol.d.ts +9 -0
- package/dist/services/codex-app-runner-protocol.d.ts.map +1 -1
- package/dist/services/codex-app-runner-protocol.js +17 -0
- package/dist/services/codex-app-runner-protocol.js.map +1 -1
- package/dist/services/codex-app-token-usage.d.ts +93 -0
- package/dist/services/codex-app-token-usage.d.ts.map +1 -0
- package/dist/services/codex-app-token-usage.js +190 -0
- package/dist/services/codex-app-token-usage.js.map +1 -0
- package/dist/services/codex-transcript.d.ts +6 -1
- package/dist/services/codex-transcript.d.ts.map +1 -1
- package/dist/services/codex-transcript.js +103 -32
- package/dist/services/codex-transcript.js.map +1 -1
- package/dist/services/federation-roster.d.ts +4 -0
- package/dist/services/federation-roster.d.ts.map +1 -1
- package/dist/services/federation-roster.js +3 -0
- package/dist/services/federation-roster.js.map +1 -1
- package/dist/services/federation-store.d.ts +6 -0
- package/dist/services/federation-store.d.ts.map +1 -1
- package/dist/services/federation-store.js.map +1 -1
- package/dist/services/grant-policy.d.ts +13 -0
- package/dist/services/grant-policy.d.ts.map +1 -0
- package/dist/services/grant-policy.js +40 -0
- package/dist/services/grant-policy.js.map +1 -0
- package/dist/services/grant-store.d.ts +24 -3
- package/dist/services/grant-store.d.ts.map +1 -1
- package/dist/services/grant-store.js +196 -12
- package/dist/services/grant-store.js.map +1 -1
- package/dist/services/groups-store.d.ts +36 -0
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +123 -1
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/jsonl-cursor.d.ts.map +1 -1
- package/dist/services/jsonl-cursor.js +8 -2
- package/dist/services/jsonl-cursor.js.map +1 -1
- package/dist/services/local-cli-opener.d.ts.map +1 -1
- package/dist/services/local-cli-opener.js +32 -0
- package/dist/services/local-cli-opener.js.map +1 -1
- package/dist/services/message-listener-run-preview-store.d.ts +43 -0
- package/dist/services/message-listener-run-preview-store.d.ts.map +1 -0
- package/dist/services/message-listener-run-preview-store.js +152 -0
- package/dist/services/message-listener-run-preview-store.js.map +1 -0
- package/dist/services/message-listener-store.d.ts +26 -0
- package/dist/services/message-listener-store.d.ts.map +1 -0
- package/dist/services/message-listener-store.js +138 -0
- package/dist/services/message-listener-store.js.map +1 -0
- package/dist/services/message-listener.d.ts +112 -0
- package/dist/services/message-listener.d.ts.map +1 -0
- package/dist/services/message-listener.js +391 -0
- package/dist/services/message-listener.js.map +1 -0
- package/dist/services/overload-alert-migration.d.ts +15 -0
- package/dist/services/overload-alert-migration.d.ts.map +1 -0
- package/dist/services/overload-alert-migration.js +115 -0
- package/dist/services/overload-alert-migration.js.map +1 -0
- package/dist/services/send-policy.d.ts +38 -0
- package/dist/services/send-policy.d.ts.map +1 -1
- package/dist/services/send-policy.js +34 -0
- package/dist/services/send-policy.js.map +1 -1
- package/dist/services/session-store.d.ts +12 -1
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +44 -2
- package/dist/services/session-store.js.map +1 -1
- package/dist/services/skill-registry-store.d.ts.map +1 -1
- package/dist/services/skill-registry-store.js +87 -12
- package/dist/services/skill-registry-store.js.map +1 -1
- package/dist/services/substitute-mode-normalize.d.ts +3 -0
- package/dist/services/substitute-mode-normalize.d.ts.map +1 -1
- package/dist/services/substitute-mode-normalize.js +8 -0
- package/dist/services/substitute-mode-normalize.js.map +1 -1
- package/dist/services/substitute-mode-store.d.ts.map +1 -1
- package/dist/services/substitute-mode-store.js +8 -1
- package/dist/services/substitute-mode-store.js.map +1 -1
- package/dist/services/transcript-resolver.d.ts +6 -1
- package/dist/services/transcript-resolver.d.ts.map +1 -1
- package/dist/services/transcript-resolver.js +145 -29
- package/dist/services/transcript-resolver.js.map +1 -1
- package/dist/services/trigger-types.d.ts +16 -0
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js +6 -0
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/setup/bot-config-editor.js +2 -2
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/setup/ensure-zmx.d.ts +31 -0
- package/dist/setup/ensure-zmx.d.ts.map +1 -0
- package/dist/setup/ensure-zmx.js +161 -0
- package/dist/setup/ensure-zmx.js.map +1 -0
- package/dist/setup/index.d.ts +1 -1
- package/dist/setup/index.js +1 -1
- package/dist/setup/open-platform-automation.d.ts +9 -0
- package/dist/setup/open-platform-automation.d.ts.map +1 -1
- package/dist/setup/open-platform-automation.js +15 -2
- package/dist/setup/open-platform-automation.js.map +1 -1
- package/dist/setup/setup-args.d.ts +1 -1
- package/dist/setup/setup-args.d.ts.map +1 -1
- package/dist/setup/setup-args.js +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +32 -0
- package/dist/skills/definitions.js.map +1 -1
- package/dist/skills/injection-mode.d.ts +6 -2
- package/dist/skills/injection-mode.d.ts.map +1 -1
- package/dist/skills/injection-mode.js +10 -5
- package/dist/skills/injection-mode.js.map +1 -1
- package/dist/types.d.ts +79 -3
- 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 +4 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/utils/effort-confirm-dialog.d.ts +40 -0
- package/dist/utils/effort-confirm-dialog.d.ts.map +1 -0
- package/dist/utils/effort-confirm-dialog.js +74 -0
- package/dist/utils/effort-confirm-dialog.js.map +1 -0
- package/dist/utils/pending-input-queue.d.ts +56 -0
- package/dist/utils/pending-input-queue.d.ts.map +1 -1
- package/dist/utils/pending-input-queue.js +80 -0
- package/dist/utils/pending-input-queue.js.map +1 -1
- package/dist/utils/render-dimensions.d.ts +8 -0
- package/dist/utils/render-dimensions.d.ts.map +1 -1
- package/dist/utils/render-dimensions.js +10 -0
- package/dist/utils/render-dimensions.js.map +1 -1
- package/dist/utils/stuck-key-guard.d.ts +3 -2
- package/dist/utils/stuck-key-guard.d.ts.map +1 -1
- package/dist/utils/stuck-key-guard.js +3 -3
- package/dist/utils/stuck-key-guard.js.map +1 -1
- package/dist/utils/terminal-renderer.d.ts +6 -0
- package/dist/utils/terminal-renderer.d.ts.map +1 -1
- package/dist/utils/terminal-renderer.js +15 -0
- package/dist/utils/terminal-renderer.js.map +1 -1
- package/dist/utils/tui-input-delivery.d.ts +32 -0
- package/dist/utils/tui-input-delivery.d.ts.map +1 -0
- package/dist/utils/tui-input-delivery.js +53 -0
- package/dist/utils/tui-input-delivery.js.map +1 -0
- package/dist/utils/xml.d.ts +19 -0
- package/dist/utils/xml.d.ts.map +1 -0
- package/dist/utils/xml.js +26 -0
- package/dist/utils/xml.js.map +1 -0
- package/dist/worker.js +1852 -374
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +8 -8
- package/dist/workflows/events/payloads.d.ts +8 -8
- package/dist/workflows/events/schema.d.ts +160 -160
- package/dist/workflows/v3/session-relay-client.js +1 -1
- package/dist/workflows/v3/session-relay-client.js.map +1 -1
- package/dist/workflows/v3/worker-fence.d.ts.map +1 -1
- package/dist/workflows/v3/worker-fence.js +15 -10
- package/dist/workflows/v3/worker-fence.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-UKA7K4JV.js +0 -10
- package/dist/dashboard-web/chunks/chunk-DFLNXQNT.js +0 -1
- package/dist/dashboard-web/chunks/connectors-page-A77YAB5Y.js +0 -1
- package/dist/dashboard-web/chunks/groups-page-RUZNVXQB.js +0 -8
- package/dist/dashboard-web/chunks/insights-page-RF7U7RTA.js +0 -22
- package/dist/dashboard-web/chunks/roles-page-BUMKD7Y4.js +0 -1
- package/dist/dashboard-web/chunks/sessions-page-6HLSEAB7.js +0 -2
- package/dist/dashboard-web/chunks/settings-page-MJDZHQ4X.js +0 -2
|
@@ -12,17 +12,18 @@ import * as sessionStore from '../services/session-store.js';
|
|
|
12
12
|
import * as messageQueue from '../services/message-queue.js';
|
|
13
13
|
import { downloadMessageResource, listChatBotMembers, UserTokenMissingError } from '../im/lark/client.js';
|
|
14
14
|
import { logger } from '../utils/logger.js';
|
|
15
|
-
import { forkWorker, sendWorkerInput, forkAdoptWorker, adoptSandboxBlocked, killStalePids, getCurrentCliVersion, restoreUsageLimitRuntimeState, setActiveSessionSafe, isRelayableRealSession, closeSession, getActiveSessionsRegistry, suspendWorker } from './worker-pool.js';
|
|
15
|
+
import { forkWorker, sendWorkerInput, forkAdoptWorker, adoptSandboxBlocked, killStalePids, sweepDeadPidMarkers, getCurrentCliVersion, restoreUsageLimitRuntimeState, setActiveSessionIfActive, setActiveSessionSafe, isDisposableCommandScratch, isRelayableRealSession, closeSession, getActiveSessionsRegistry, suspendWorker, isSessionTransferring, deferUntilSessionTransferSettled } from './worker-pool.js';
|
|
16
16
|
import { createCliAdapterSync } from '../adapters/cli/registry.js';
|
|
17
17
|
import { buildBotmuxShellHints } from '../adapters/cli/shared-hints.js';
|
|
18
18
|
import { resolveSkillInjectionModeForApp, builtinSkillEntries, buildBuiltinSkillCatalogBlock, builtinSkillHelpPointer, } from '../skills/injection-mode.js';
|
|
19
|
-
import { getSessionPersistentBackendType, persistentBackendTargetForSession, probePersistentBackendTarget,
|
|
19
|
+
import { getSessionPersistentBackendType, persistentBackendTargetForSession, probePersistentBackendServer, probePersistentBackendTarget, probePersistentSessions, killPersistentBackendTarget, } from './persistent-backend.js';
|
|
20
20
|
import { adoptTargetLabel, validateAdoptTargetState } from './session-discovery.js';
|
|
21
21
|
import { getBot, getAllBots, getOwnerOpenId, findOncallChat, effectiveDefaultWorkingDir } from '../bot-registry.js';
|
|
22
22
|
import { dashboardEventBus } from './dashboard-events.js';
|
|
23
|
-
import { composeRowFromActive } from './dashboard-rows.js';
|
|
23
|
+
import { composeRowFromActive, composeRowFromPersistedActive } from './dashboard-rows.js';
|
|
24
24
|
import { composeSpawnCodexAppContext, composeSpawnUserContent, deriveSessionTitleFromContent, } from './session-create.js';
|
|
25
25
|
import { validateZellijAdoptTarget } from './zellij-adopt-discovery.js';
|
|
26
|
+
import { backendSupportsWebTerminal } from '../adapters/backend/capabilities.js';
|
|
26
27
|
import { addCodexAppContext } from '../utils/codex-app-context.js';
|
|
27
28
|
import { activeSessionKey, sessionKey, sessionAnchorId, storedSessionAnchorId } from './types.js';
|
|
28
29
|
import { announceSessionRow, markSessionActivity, announcePendingRepoSession } from './session-activity.js';
|
|
@@ -40,6 +41,7 @@ import { getAttachmentsDir } from './attachment-path.js';
|
|
|
40
41
|
import { resolveRegularGroupMode } from '../services/chat-reply-mode-store.js';
|
|
41
42
|
import { beginReplyTargetTurn } from './reply-target.js';
|
|
42
43
|
import { readDeferredTopicBinding, removeDeferredTopicBinding } from './deferred-topic-binding.js';
|
|
44
|
+
import { escapeXmlTagLikeTokens } from '../utils/xml.js';
|
|
43
45
|
export { getAttachmentsDir } from './attachment-path.js';
|
|
44
46
|
function sessionCreatedAtMs(session) {
|
|
45
47
|
return session.createdAt ? (Date.parse(session.createdAt) || Date.now()) : Date.now();
|
|
@@ -47,6 +49,31 @@ function sessionCreatedAtMs(session) {
|
|
|
47
49
|
function sessionLastMessageAtMs(session) {
|
|
48
50
|
return session.lastMessageAt ? (Date.parse(session.lastMessageAt) || sessionCreatedAtMs(session)) : sessionCreatedAtMs(session);
|
|
49
51
|
}
|
|
52
|
+
function quarantineUnregisteredRestoreSession(session, reason) {
|
|
53
|
+
session.restoreQuarantinedAt ??= new Date().toISOString();
|
|
54
|
+
try {
|
|
55
|
+
sessionStore.updateSession(session);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
logger.error(`[${session.sessionId.substring(0, 8)}] Could not persist restore quarantine (${reason}): `
|
|
59
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
60
|
+
}
|
|
61
|
+
// This row was intentionally never announced as a runtime DaemonSession.
|
|
62
|
+
// Upsert a dormant persisted projection so an already-open dashboard does not
|
|
63
|
+
// lose it merely because its exact backing teardown was inconclusive.
|
|
64
|
+
try {
|
|
65
|
+
dashboardEventBus.publish({
|
|
66
|
+
type: 'session.spawned',
|
|
67
|
+
body: { session: composeRowFromPersistedActive(session) },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
// Visibility projection is best-effort; it must never turn one isolated
|
|
72
|
+
// teardown failure into a daemon restore failure for every later session.
|
|
73
|
+
logger.error(`[${session.sessionId.substring(0, 8)}] Could not publish restore quarantine row: `
|
|
74
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
50
77
|
function sameUsageLimit(a, b) {
|
|
51
78
|
if (!a && !b)
|
|
52
79
|
return true;
|
|
@@ -82,11 +109,32 @@ function sessionBotCliMismatch(ds) {
|
|
|
82
109
|
}
|
|
83
110
|
return null;
|
|
84
111
|
}
|
|
112
|
+
const cliMismatchResweepArmed = new WeakSet();
|
|
113
|
+
function armCliMismatchResweep(ds) {
|
|
114
|
+
if (cliMismatchResweepArmed.has(ds))
|
|
115
|
+
return;
|
|
116
|
+
cliMismatchResweepArmed.add(ds);
|
|
117
|
+
const rerun = () => {
|
|
118
|
+
cliMismatchResweepArmed.delete(ds);
|
|
119
|
+
void closeCliMismatchedSessionsForBot(ds.larkAppId).catch((err) => {
|
|
120
|
+
logger.error(`[${ds.session.sessionId.substring(0, 8)}] Deferred CLI mismatch resweep failed: `
|
|
121
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
if (!deferUntilSessionTransferSettled(ds, rerun)) {
|
|
125
|
+
// The gate settled between the caller's guard and registration.
|
|
126
|
+
queueMicrotask(rerun);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
85
129
|
async function closeActiveSessionIfCliMismatch(ds) {
|
|
86
130
|
const mismatch = sessionBotCliMismatch(ds);
|
|
87
131
|
if (!mismatch)
|
|
88
|
-
return
|
|
132
|
+
return 'not_mismatched';
|
|
89
133
|
const tag = ds.session.sessionId.substring(0, 8);
|
|
134
|
+
if (isSessionTransferring(ds)) {
|
|
135
|
+
logger.warn(`[${tag}] CLI mismatch close deferred until routing transfer settles`);
|
|
136
|
+
return 'transfer_deferred';
|
|
137
|
+
}
|
|
90
138
|
const backendType = getSessionPersistentBackendType(ds);
|
|
91
139
|
// 仅在没有活 worker 时预杀 backing pane:restore 守卫处 ds 尚未进 registry,
|
|
92
140
|
// closeSession→killWorker 摸不到 pane,必须在这里亲手杀;而活 worker(运行时
|
|
@@ -95,13 +143,25 @@ async function closeActiveSessionIfCliMismatch(ds) {
|
|
|
95
143
|
if (backendType && (!ds.worker || ds.worker.killed)) {
|
|
96
144
|
const target = persistentBackendTargetForSession(ds);
|
|
97
145
|
logger.warn(`[${tag}] CLI mismatch (session=${mismatch.sessionCli}, bot=${mismatch.botCli}), closing active session and killing ${backendType} ${target.sessionName}${target.backendType === 'herdr' && target.agentName ? `/${target.agentName}` : ''}`);
|
|
98
|
-
|
|
146
|
+
try {
|
|
147
|
+
killPersistentBackendTarget(target, ds.session.sessionId);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
// ZMX destruction is deliberately identity-verified and can fail on an
|
|
151
|
+
// inconclusive probe or a generation change. Never close the persisted
|
|
152
|
+
// row in that state: doing so would orphan a possibly-live CLI with no
|
|
153
|
+
// retryable ownership record. The caller skips this row and continues
|
|
154
|
+
// restoring/sweeping unrelated sessions.
|
|
155
|
+
logger.error(`[${tag}] CLI mismatch backing teardown failed; keeping active row: `
|
|
156
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
157
|
+
return 'teardown_failed';
|
|
158
|
+
}
|
|
99
159
|
}
|
|
100
160
|
else {
|
|
101
161
|
logger.warn(`[${tag}] CLI mismatch (session=${mismatch.sessionCli}, bot=${mismatch.botCli}), closing active session`);
|
|
102
162
|
}
|
|
103
163
|
await closeSession(ds.session.sessionId);
|
|
104
|
-
return
|
|
164
|
+
return 'closed';
|
|
105
165
|
}
|
|
106
166
|
/**
|
|
107
167
|
* Runtime counterpart of the restore-time CLI-mismatch guard(#346 只堵了重启
|
|
@@ -126,8 +186,21 @@ export async function closeCliMismatchedSessionsForBot(larkAppId) {
|
|
|
126
186
|
continue;
|
|
127
187
|
if (ds.adoptedFrom || ds.session.adoptedFrom || ds.session.title?.startsWith('Adopt:'))
|
|
128
188
|
continue;
|
|
129
|
-
if (
|
|
130
|
-
|
|
189
|
+
if (isSessionTransferring(ds)) {
|
|
190
|
+
armCliMismatchResweep(ds);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
const result = await closeActiveSessionIfCliMismatch(ds);
|
|
195
|
+
if (result === 'closed')
|
|
196
|
+
closed++;
|
|
197
|
+
else if (result === 'transfer_deferred')
|
|
198
|
+
armCliMismatchResweep(ds);
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
logger.error(`[${ds.session.sessionId.substring(0, 8)}] CLI mismatch close failed; continuing sweep: `
|
|
202
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
203
|
+
}
|
|
131
204
|
}
|
|
132
205
|
return closed;
|
|
133
206
|
}
|
|
@@ -153,6 +226,10 @@ export async function suspendActiveSessionsForBot(larkAppId) {
|
|
|
153
226
|
continue;
|
|
154
227
|
if (ds.adoptedFrom || ds.session.adoptedFrom || ds.session.title?.startsWith('Adopt:'))
|
|
155
228
|
continue;
|
|
229
|
+
if (isSessionTransferring(ds)) {
|
|
230
|
+
logger.warn(`[${ds.session.sessionId.substring(0, 8)}] Read-isolation cold restart deferred during routing transfer`);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
156
233
|
// Prefer suspend (keeps the session; --resume continues context on the next
|
|
157
234
|
// message). But suspendWorker no-ops for a NON-suspendable backend (explicit
|
|
158
235
|
// PTY) — leaving the old unisolated process running would silently defeat the
|
|
@@ -502,35 +579,13 @@ function renderWhiteboardBlock(opts) {
|
|
|
502
579
|
return [
|
|
503
580
|
`<whiteboard id="${id}">`,
|
|
504
581
|
'本地项目上下文;读取:`botmux whiteboard read --id ' + id + ' --json`(拿到 content 与 updatedAt)。',
|
|
505
|
-
'更新状态:`botmux whiteboard update --id ' + id + ' --expected-updated-at <上次 read 的 updatedAt> <内容>`。',
|
|
582
|
+
escapeXmlTagLikeTokens('更新状态:`botmux whiteboard update --id ' + id + ' --expected-updated-at <上次 read 的 updatedAt> <内容>`。'),
|
|
506
583
|
'更新前先用 `read --json` 拿到当前内容与 updatedAt,融合新信息后整体重写为一份完整的当前状态(默认中文;代码标识/命令/错误信息可保留原文),并用 `--expected-updated-at` 回传 read 到的版本号做并发冲突检测。',
|
|
507
584
|
'若更新报 `whiteboard_cas_mismatch`,说明期间有其它 agent 改过白板——重新 `read --json` 拿最新内容与 updatedAt,再次融合重写。',
|
|
508
585
|
'不要直接读写本地文件;不要写密钥/隐私;用户可见结论仍必须 `botmux send`。',
|
|
509
586
|
'</whiteboard>',
|
|
510
587
|
].join('\n');
|
|
511
588
|
}
|
|
512
|
-
function buildHermesBotmuxHints(locale) {
|
|
513
|
-
if (locale === 'en') {
|
|
514
|
-
return [
|
|
515
|
-
'You are running in a Feishu/Lark chat through botmux. For ordinary text replies, write the user-facing answer as your final assistant message; botmux automatically forwards that final output to Feishu/Lark.',
|
|
516
|
-
'Do not call `botmux send` for normal text answers. Use `botmux send` only for special delivery needs: files/images/videos, voice, cross-chat/top-level sends, or explicit mention routing to another person/bot.',
|
|
517
|
-
'`botmux send` / `botmux history` / `botmux quoted` / `botmux bots` are shell commands installed in $PATH; run them via Bash/terminal tools when needed.',
|
|
518
|
-
'If you already used `botmux send` for special delivery in this turn, do not put a second copy of the answer, messageId, or send-success receipt in the final assistant message.',
|
|
519
|
-
];
|
|
520
|
-
}
|
|
521
|
-
return [
|
|
522
|
-
'你运行在飞书(Lark)聊天中。普通文字回复请直接写在 assistant final 里,botmux 会自动把 final_output 转发到飞书。',
|
|
523
|
-
'普通文本答案不要调用 `botmux send`。只有需要图片/文件/视频/语音、跨群或顶层发送、显式 @ 某人/某 bot 等特殊投递能力时,才使用 `botmux send`。',
|
|
524
|
-
'`botmux send` / `botmux history` / `botmux quoted` / `botmux bots` 是已安装在 $PATH 的 shell 命令;需要时通过 Bash/terminal 工具执行。',
|
|
525
|
-
'如果本轮已经为了特殊投递调用过 `botmux send`,final 里不要再写第二份正文、messageId 或“发送成功/已处理”回执。',
|
|
526
|
-
];
|
|
527
|
-
}
|
|
528
|
-
function hermesFollowupReminder(locale) {
|
|
529
|
-
if (locale === 'en') {
|
|
530
|
-
return 'For ordinary text replies, do not call `botmux send`; put the user-facing answer in final and botmux will forward it to Feishu/Lark. Use `botmux send` only for special delivery such as files/images/videos, voice, cross-chat/top-level sends, or explicit mention routing. If already used, do not add a second answer or send-success receipt in final.';
|
|
531
|
-
}
|
|
532
|
-
return '普通文字回复不要调用 `botmux send`;直接把给用户看的答案写在 final,botmux 会自动转发到飞书。只有图片/文件/视频/语音、跨群/顶层发送、特殊 @ 路由等特殊投递才用 `botmux send`;如果本轮已经用过,不要在 final 里再写第二份答案或发送成功回执。';
|
|
533
|
-
}
|
|
534
589
|
/**
|
|
535
590
|
* Peer count at/below which the `<available_bots>` block inlines the full
|
|
536
591
|
* roster (name + open_id). Above it the block collapses to a one-line pointer
|
|
@@ -591,7 +646,7 @@ export function buildNewTopicPrompt(userMessage, sessionId, cliId, cliPathOverri
|
|
|
591
646
|
// (Claude Code builds its own via --append-system-prompt). Source hints
|
|
592
647
|
// freshly from i18n so they respect the resolved locale instead of the
|
|
593
648
|
// static `adapter.systemHints` array that was baked at module load.
|
|
594
|
-
const hints = adapter.injectsSessionContext ? [] :
|
|
649
|
+
const hints = adapter.injectsSessionContext ? [] : buildBotmuxShellHints(locale);
|
|
595
650
|
const routingBlock = hints.length > 0
|
|
596
651
|
? `<botmux_routing>\n${hints.join('\n')}\n</botmux_routing>`
|
|
597
652
|
: '';
|
|
@@ -622,7 +677,7 @@ export function buildNewTopicPrompt(userMessage, sessionId, cliId, cliPathOverri
|
|
|
622
677
|
'<identity>',
|
|
623
678
|
` <name>${xmlEscape(botIdentity.name ?? unknown)}</name>`,
|
|
624
679
|
` <open_id>${xmlEscape(botIdentity.openId ?? unknown)}</open_id>`,
|
|
625
|
-
` <routing_rules>${t('ai.identity.short_routing', undefined, locale)}</routing_rules>`,
|
|
680
|
+
` <routing_rules>${escapeXmlTagLikeTokens(t('ai.identity.short_routing', undefined, locale))}</routing_rules>`,
|
|
626
681
|
'</identity>',
|
|
627
682
|
].join('\n');
|
|
628
683
|
}
|
|
@@ -744,9 +799,13 @@ export function buildFollowUpContent(content, sessionId, opts) {
|
|
|
744
799
|
if (roleBlock)
|
|
745
800
|
parts.push(roleBlock);
|
|
746
801
|
if (opts?.cliId !== 'mira') {
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
802
|
+
// All non-Mira CLIs — including Hermes, which no longer gets reverse
|
|
803
|
+
// send-first guidance (#653) and now shares this standard path — get the
|
|
804
|
+
// anti-resend variant only when the experimental dashboard toggle is on
|
|
805
|
+
// (config.noVisibleOutputHint, default OFF); otherwise the reminder is
|
|
806
|
+
// byte-for-byte the pre-feature baseline. Live-read so a Settings flip
|
|
807
|
+
// applies to the next follow-up turn without a daemon restart.
|
|
808
|
+
const reminder = t(config.noVisibleOutputHint ? 'ai.followup.reminder_no_resend' : 'ai.followup.reminder', undefined, opts?.locale);
|
|
750
809
|
parts.push(`<botmux_reminder>${reminder}</botmux_reminder>`);
|
|
751
810
|
}
|
|
752
811
|
if (whiteboardBlock)
|
|
@@ -1013,7 +1072,7 @@ export function rememberLastCliInput(ds, userPrompt, cliInput, opts) {
|
|
|
1013
1072
|
// ─── Session restore ─────────────────────────────────────────────────────────
|
|
1014
1073
|
/**
|
|
1015
1074
|
* Whether daemon restore should eagerly re-fork a worker to re-attach a
|
|
1016
|
-
* surviving backing pane. True for every persistent backend (tmux/herdr/zellij);
|
|
1075
|
+
* surviving backing pane. True for every persistent backend (tmux/herdr/zellij/zmx);
|
|
1017
1076
|
* the pty backend has nothing to re-attach to, so it stays lazy.
|
|
1018
1077
|
*
|
|
1019
1078
|
* Eager re-attach is what makes a session actually come back after a restart —
|
|
@@ -1054,15 +1113,53 @@ export async function staggeredRecoveryFork(sessions, fork, batchSize = RECOVERY
|
|
|
1054
1113
|
}
|
|
1055
1114
|
export async function restoreActiveSessions(activeSessions) {
|
|
1056
1115
|
const sessions = sessionStore.listSessions();
|
|
1057
|
-
const
|
|
1116
|
+
const restorePriority = (session) => {
|
|
1117
|
+
if (session.adoptedFrom || session.cliId || session.lastCliInput || session.backendType)
|
|
1118
|
+
return 2;
|
|
1119
|
+
if (session.queued)
|
|
1120
|
+
return 1;
|
|
1121
|
+
return 0; // disposable daemon-command scratch
|
|
1122
|
+
};
|
|
1123
|
+
// Deterministic winner policy for corrupt/legacy same-anchor duplicates:
|
|
1124
|
+
// real CLI/adopt rows first, queued intent second, command scratches last.
|
|
1125
|
+
// Registration itself is CAS, so a fresh runtime occupant always wins over
|
|
1126
|
+
// every startup candidate regardless of this disk ordering.
|
|
1127
|
+
const active = sessions
|
|
1128
|
+
.filter(s => s.status === 'active')
|
|
1129
|
+
.sort((a, b) => restorePriority(b) - restorePriority(a));
|
|
1130
|
+
// Sweep dead CLI-pid markers regardless of whether we have sessions to restore:
|
|
1131
|
+
// the landmine files (recycled-PID misroute source) accumulate across every
|
|
1132
|
+
// daemon run, and a run with zero active sessions is still a fresh start that
|
|
1133
|
+
// should clean up after prior crashes/SIGKILLs.
|
|
1134
|
+
sweepDeadPidMarkers();
|
|
1058
1135
|
if (active.length === 0) {
|
|
1059
1136
|
logger.info('No active sessions to restore');
|
|
1060
1137
|
return;
|
|
1061
1138
|
}
|
|
1062
1139
|
// Kill any stale CLI processes from previous daemon run
|
|
1063
|
-
|
|
1140
|
+
// Dispatcher/IPC are already live while restore runs. Pass the runtime map
|
|
1141
|
+
// so the stale-PID sweep cannot kill a fresh worker that won the startup
|
|
1142
|
+
// race for the same logical session. The full snapshot still reaches backend
|
|
1143
|
+
// cleanup, preserving its backing-session name as active.
|
|
1144
|
+
killStalePids(active, activeSessions);
|
|
1064
1145
|
logger.info(`Registering ${active.length} active session(s) (no CLI spawn until new messages arrive)...`);
|
|
1146
|
+
const runtimeWinnerFor = (sessionId, candidate) => [...activeSessions.values()].find(ds => ds !== candidate && ds.session.sessionId === sessionId);
|
|
1147
|
+
// Persistent recovery below must only inspect rows registered by THIS
|
|
1148
|
+
// restore pass. The dispatcher/IPC are already live and may add a fresh
|
|
1149
|
+
// runtime session while one of the CAS registrations awaits; sweeping the
|
|
1150
|
+
// whole live Map would then mistake that new session for a startup snapshot
|
|
1151
|
+
// row and could close it while its backing pane is still being created.
|
|
1152
|
+
const restoredByThisInvocation = [];
|
|
1153
|
+
const stillOwnsRestoreRegistration = (ds) => ds.session.status === 'active'
|
|
1154
|
+
&& activeSessions.get(activeSessionKey(ds)) === ds;
|
|
1065
1155
|
for (const session of active) {
|
|
1156
|
+
// Dispatcher/IPC may create and register this exact persisted row while
|
|
1157
|
+
// startup restore is running. The runtime object is authoritative; never
|
|
1158
|
+
// rebuild or later close it as a collision loser.
|
|
1159
|
+
if (runtimeWinnerFor(session.sessionId)) {
|
|
1160
|
+
logger.debug(`[${session.sessionId.substring(0, 8)}] Already registered by live runtime during restore; skipping snapshot row`);
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1066
1163
|
// Restored sessions persisted before the scope field was added default to
|
|
1067
1164
|
// 'thread' — that matches the legacy thread-only behaviour.
|
|
1068
1165
|
const scope = session.scope === 'chat' ? 'chat' : 'thread';
|
|
@@ -1086,7 +1183,7 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1086
1183
|
}
|
|
1087
1184
|
catch { /* unknown bot → only the frozen decision matters */ }
|
|
1088
1185
|
if (adoptSandboxBlocked(adoptBotCfg, session)) {
|
|
1089
|
-
logger.warn(`[${session.sessionId.substring(0, 8)}]
|
|
1186
|
+
logger.warn(`[${session.sessionId.substring(0, 8)}] isolated/no-transport session persisted as adopt — converting to cold-start (a sandbox / apiOnly bot can't wrap a live external CLI)`);
|
|
1090
1187
|
session.adoptedFrom = undefined;
|
|
1091
1188
|
if (session.title?.startsWith('Adopt:')) {
|
|
1092
1189
|
const project = session.title.slice('Adopt:'.length).trim();
|
|
@@ -1108,7 +1205,8 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1108
1205
|
continue;
|
|
1109
1206
|
}
|
|
1110
1207
|
if (validation === 'unknown') {
|
|
1111
|
-
logger.warn(`Keeping adopt session ${session.sessionId}
|
|
1208
|
+
logger.warn(`Keeping adopt session ${session.sessionId} active but quarantined until the target can be verified (target validation failed: ${adoptTargetLabel(adopted)})`);
|
|
1209
|
+
quarantineUnregisteredRestoreSession(session, 'adopt_target_validation_unknown');
|
|
1112
1210
|
continue;
|
|
1113
1211
|
}
|
|
1114
1212
|
// Original CLI still alive — re-register and fork adopt worker
|
|
@@ -1154,9 +1252,21 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1154
1252
|
// Same-key collision guard: if a prior iteration already set an entry
|
|
1155
1253
|
// at this key (legitimately possible if disk holds two active sessions
|
|
1156
1254
|
// resolving to the same chat-scope key — e.g. a leaked scratch +
|
|
1157
|
-
// relayed real session from a prior buggy run), close the
|
|
1158
|
-
// rather than silently overwriting
|
|
1159
|
-
|
|
1255
|
+
// relayed real session from a prior buggy run), reject and close the
|
|
1256
|
+
// incoming loser rather than silently overwriting the runtime winner.
|
|
1257
|
+
const adoptKey = activeSessionKey(ds);
|
|
1258
|
+
const adoptRegistered = await setActiveSessionSafe(activeSessions, adoptKey, ds);
|
|
1259
|
+
const adoptCurrent = activeSessions.get(adoptKey);
|
|
1260
|
+
if (runtimeWinnerFor(session.sessionId, ds)) {
|
|
1261
|
+
logger.debug(`[${session.sessionId.substring(0, 8)}] Live runtime won adopt restore registration`);
|
|
1262
|
+
continue;
|
|
1263
|
+
}
|
|
1264
|
+
if (!adoptRegistered || session.status !== 'active' || adoptCurrent !== ds) {
|
|
1265
|
+
logger.warn(`[${session.sessionId.substring(0, 8)}] Adopt restore was cancelled while resolving a routing collision`);
|
|
1266
|
+
await closeSession(session.sessionId);
|
|
1267
|
+
continue;
|
|
1268
|
+
}
|
|
1269
|
+
restoredByThisInvocation.push(ds);
|
|
1160
1270
|
announceSessionRow(ds);
|
|
1161
1271
|
forkAdoptWorker(ds, { restoredFromMetadata: true });
|
|
1162
1272
|
logger.info(`[${session.sessionId.substring(0, 8)}] Restored adopt session (target: ${adoptTargetLabel(adopted)}, scope: ${scope})`);
|
|
@@ -1198,7 +1308,19 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1198
1308
|
};
|
|
1199
1309
|
const anchor = sessionAnchorId(ds);
|
|
1200
1310
|
messageQueue.ensureQueue(anchor);
|
|
1201
|
-
|
|
1311
|
+
const queuedKey = activeSessionKey(ds);
|
|
1312
|
+
const queuedRegistered = await setActiveSessionSafe(activeSessions, queuedKey, ds);
|
|
1313
|
+
const queuedCurrent = activeSessions.get(queuedKey);
|
|
1314
|
+
if (runtimeWinnerFor(session.sessionId, ds)) {
|
|
1315
|
+
logger.debug(`[${session.sessionId.substring(0, 8)}] Live runtime won queued restore registration`);
|
|
1316
|
+
continue;
|
|
1317
|
+
}
|
|
1318
|
+
if (!queuedRegistered || session.status !== 'active' || queuedCurrent !== ds) {
|
|
1319
|
+
logger.warn(`[${session.sessionId.substring(0, 8)}] Queued restore was cancelled while resolving a routing collision`);
|
|
1320
|
+
await closeSession(session.sessionId);
|
|
1321
|
+
continue;
|
|
1322
|
+
}
|
|
1323
|
+
restoredByThisInvocation.push(ds);
|
|
1202
1324
|
// 重启后把待办池卡片重新广播给 dashboard,否则会从看板消失(#277 同款修复,
|
|
1203
1325
|
// 我这条 queued 分支提前 continue 绕过了下面的 announceSessionRow,要自己补)。
|
|
1204
1326
|
announceSessionRow(ds);
|
|
@@ -1250,7 +1372,19 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1250
1372
|
// instead of resurrecting an invisible worker.
|
|
1251
1373
|
const backendType = getSessionPersistentBackendType(ds);
|
|
1252
1374
|
if (backendType) {
|
|
1253
|
-
|
|
1375
|
+
try {
|
|
1376
|
+
killPersistentBackendTarget(persistentBackendTargetForSession(ds), session.sessionId);
|
|
1377
|
+
}
|
|
1378
|
+
catch (err) {
|
|
1379
|
+
// Keep the row active when destruction cannot be proved. Closing it
|
|
1380
|
+
// would strand a possibly-live hidden run with no ownership record;
|
|
1381
|
+
// skipping registration also prevents this restore pass from
|
|
1382
|
+
// accidentally reattaching it. Continue with the remaining rows.
|
|
1383
|
+
logger.error(`[${session.sessionId.substring(0, 8)}] Could not tear down unmaterialized deferred run; keeping active row: `
|
|
1384
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
1385
|
+
quarantineUnregisteredRestoreSession(session, 'deferred_run_teardown_unverified');
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1254
1388
|
}
|
|
1255
1389
|
sessionStore.closeSession(session.sessionId);
|
|
1256
1390
|
removeDeferredTopicBinding(config.session.dataDir, session.sessionId);
|
|
@@ -1268,14 +1402,49 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1268
1402
|
ds.replyThreadAliases = aliases;
|
|
1269
1403
|
sessionStore.updateSession(session);
|
|
1270
1404
|
}
|
|
1271
|
-
|
|
1405
|
+
try {
|
|
1406
|
+
const mismatchClose = await closeActiveSessionIfCliMismatch(ds);
|
|
1407
|
+
if (mismatchClose !== 'not_mismatched') {
|
|
1408
|
+
if (mismatchClose === 'teardown_failed') {
|
|
1409
|
+
quarantineUnregisteredRestoreSession(session, 'cli_mismatch_teardown_unverified');
|
|
1410
|
+
}
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
catch (err) {
|
|
1415
|
+
logger.error(`[${session.sessionId.substring(0, 8)}] CLI mismatch close failed during restore; keeping active row: `
|
|
1416
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
1417
|
+
quarantineUnregisteredRestoreSession(session, 'cli_mismatch_close_failed');
|
|
1272
1418
|
continue;
|
|
1419
|
+
}
|
|
1273
1420
|
const anchor = sessionAnchorId(ds);
|
|
1274
1421
|
messageQueue.ensureQueue(anchor);
|
|
1275
1422
|
if (ds.usageLimit)
|
|
1276
1423
|
restoreUsageLimitRuntimeState(ds);
|
|
1277
1424
|
// Same-key collision guard — see adopt-branch comment above.
|
|
1278
|
-
|
|
1425
|
+
const restoreKey = activeSessionKey(ds);
|
|
1426
|
+
const restored = await setActiveSessionSafe(activeSessions, restoreKey, ds);
|
|
1427
|
+
const restoreCurrent = activeSessions.get(restoreKey);
|
|
1428
|
+
if (runtimeWinnerFor(session.sessionId, ds)) {
|
|
1429
|
+
logger.debug(`[${session.sessionId.substring(0, 8)}] Live runtime won restore registration`);
|
|
1430
|
+
continue;
|
|
1431
|
+
}
|
|
1432
|
+
if (!restored || session.status !== 'active' || restoreCurrent !== ds) {
|
|
1433
|
+
logger.warn(`[${session.sessionId.substring(0, 8)}] Restore was cancelled while resolving a routing collision`);
|
|
1434
|
+
try {
|
|
1435
|
+
await closeSession(session.sessionId);
|
|
1436
|
+
}
|
|
1437
|
+
catch (err) {
|
|
1438
|
+
// A rejected ZMX ownership/generation probe means teardown was not
|
|
1439
|
+
// proven. Keep the persisted row active for a later retry and continue
|
|
1440
|
+
// restoring unrelated sessions instead of crash-looping daemon boot.
|
|
1441
|
+
logger.error(`[${session.sessionId.substring(0, 8)}] Could not close restore collision loser; keeping active row: `
|
|
1442
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
1443
|
+
quarantineUnregisteredRestoreSession(session, 'restore_collision_close_failed');
|
|
1444
|
+
}
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
restoredByThisInvocation.push(ds);
|
|
1279
1448
|
announceSessionRow(ds);
|
|
1280
1449
|
if (session.initialUserTurnPending) {
|
|
1281
1450
|
// `hasHistory: true` above means "there may be a CLI process/transcript to
|
|
@@ -1292,6 +1461,51 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1292
1461
|
// actual re-fork is deferred into `toReattach` and staggered below so a box
|
|
1293
1462
|
// with dozens of surviving sessions doesn't spike on restart.
|
|
1294
1463
|
const toReattach = [];
|
|
1464
|
+
const restoreCandidates = [];
|
|
1465
|
+
const namesByBackend = new Map();
|
|
1466
|
+
for (const ds of restoredByThisInvocation) {
|
|
1467
|
+
// A later restore CAS awaited after this row was registered. During that
|
|
1468
|
+
// yield the user may have closed/resumed/replaced it; never carry the stale
|
|
1469
|
+
// object into a session-id based close that could hit its fresh successor.
|
|
1470
|
+
// A real message may also have synchronously forked this exact ds while the
|
|
1471
|
+
// registration Promise yielded. Its backing pane can still be starting, so
|
|
1472
|
+
// a missing probe is not zombie evidence and restore must leave it alone.
|
|
1473
|
+
if (!stillOwnsRestoreRegistration(ds) || ds.worker)
|
|
1474
|
+
continue;
|
|
1475
|
+
// External /adopt sessions use their discovered source target rather than
|
|
1476
|
+
// Botmux's deterministic managed backing name. They were already restored
|
|
1477
|
+
// through the adopt path above and must not enter the managed probe batch.
|
|
1478
|
+
if (ds.adoptedFrom)
|
|
1479
|
+
continue;
|
|
1480
|
+
// Queued(待办池)会话从没起过 CLI,没有任何后端会话——别去探它,否则 tmux 后端
|
|
1481
|
+
// 会把「找不到 backing」误判成僵尸而关掉它。
|
|
1482
|
+
if (ds.session.queued)
|
|
1483
|
+
continue;
|
|
1484
|
+
const backendType = getSessionPersistentBackendType(ds);
|
|
1485
|
+
if (!backendType || !shouldAutoForkOnRestore(backendType))
|
|
1486
|
+
continue;
|
|
1487
|
+
// Honour the worker-selected target (Herdr may own an agent inside a shared
|
|
1488
|
+
// host session) rather than assuming the deterministic whole-session name.
|
|
1489
|
+
const backendTarget = persistentBackendTargetForSession(ds);
|
|
1490
|
+
const backendName = backendTarget.backendType === 'herdr' && backendTarget.agentName
|
|
1491
|
+
? `${backendTarget.sessionName}/${backendTarget.agentName}`
|
|
1492
|
+
: backendTarget.sessionName;
|
|
1493
|
+
restoreCandidates.push({ ds, backendType, backendTarget, backendName });
|
|
1494
|
+
// Only session-name-addressable targets can be answered from a batch
|
|
1495
|
+
// snapshot; agent-scoped Herdr rows fall back to their per-target probe.
|
|
1496
|
+
if (backendTarget.backendType === 'herdr' && backendTarget.agentName)
|
|
1497
|
+
continue;
|
|
1498
|
+
const names = namesByBackend.get(backendType) ?? new Set();
|
|
1499
|
+
names.add(backendTarget.sessionName);
|
|
1500
|
+
namesByBackend.set(backendType, names);
|
|
1501
|
+
}
|
|
1502
|
+
// ZMX/Zellij can classify every requested name from one control-plane list.
|
|
1503
|
+
// This is both a consistent restore snapshot and avoids an O(N²) ZMX restart
|
|
1504
|
+
// when each per-row probe would otherwise scan every per-session daemon.
|
|
1505
|
+
const probeSnapshots = new Map();
|
|
1506
|
+
for (const [backendType, names] of namesByBackend) {
|
|
1507
|
+
probeSnapshots.set(backendType, probePersistentSessions(backendType, names));
|
|
1508
|
+
}
|
|
1295
1509
|
// Server-liveness is sampled ONCE per backend type (cached): a single
|
|
1296
1510
|
// `tmux list-sessions` answers for all of that backend's sessions, and a
|
|
1297
1511
|
// consistent snapshot avoids a mid-loop race where an early lazy fork could
|
|
@@ -1305,27 +1519,18 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1305
1519
|
}
|
|
1306
1520
|
return s;
|
|
1307
1521
|
};
|
|
1308
|
-
for (const
|
|
1309
|
-
//
|
|
1310
|
-
//
|
|
1311
|
-
|
|
1522
|
+
for (const { ds, backendType, backendTarget, backendName } of restoreCandidates) {
|
|
1523
|
+
// An earlier candidate's mismatch close can await document cleanup, so
|
|
1524
|
+
// revalidate exact ownership and worker state for every row before any
|
|
1525
|
+
// destructive action. A message can wake a later candidate during that
|
|
1526
|
+
// await, while its persistent backing is still being created.
|
|
1527
|
+
if (!stillOwnsRestoreRegistration(ds) || ds.worker)
|
|
1312
1528
|
continue;
|
|
1313
|
-
//
|
|
1314
|
-
//
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
continue;
|
|
1319
|
-
const backendType = getSessionPersistentBackendType(ds);
|
|
1320
|
-
if (!backendType)
|
|
1321
|
-
continue;
|
|
1322
|
-
if (!shouldAutoForkOnRestore(backendType))
|
|
1323
|
-
continue;
|
|
1324
|
-
const backendTarget = persistentBackendTargetForSession(ds);
|
|
1325
|
-
const backendName = backendTarget.backendType === 'herdr' && backendTarget.agentName
|
|
1326
|
-
? `${backendTarget.sessionName}/${backendTarget.agentName}`
|
|
1327
|
-
: backendTarget.sessionName;
|
|
1328
|
-
const probe = probePersistentBackendTarget(backendTarget);
|
|
1529
|
+
// Agent-scoped Herdr targets are not addressable by session name, so they
|
|
1530
|
+
// never joined the batch and keep the per-target probe.
|
|
1531
|
+
const probe = backendTarget.backendType === 'herdr' && backendTarget.agentName
|
|
1532
|
+
? probePersistentBackendTarget(backendTarget)
|
|
1533
|
+
: probeSnapshots.get(backendType)?.get(backendTarget.sessionName) ?? 'unknown';
|
|
1329
1534
|
if (probe === 'missing') {
|
|
1330
1535
|
const tag = ds.session.sessionId.substring(0, 8);
|
|
1331
1536
|
// Intentionally cold-resume-suspended (idle-worker sweeper killed the
|
|
@@ -1337,6 +1542,14 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1337
1542
|
logger.info(`[${tag}] ${backendType} session was cap-suspended — keeping active for lazy cold-resume`);
|
|
1338
1543
|
continue;
|
|
1339
1544
|
}
|
|
1545
|
+
// ZMX is not a shared multiplexer server: every session owns an
|
|
1546
|
+
// independent daemon. Another live ZMX session says nothing about this
|
|
1547
|
+
// one, so "missing" cannot safely drive a destructive zombie close.
|
|
1548
|
+
// Preserve the transcript-backed row and cold-resume on the next message.
|
|
1549
|
+
if (backendType === 'zmx') {
|
|
1550
|
+
logger.warn(`[${tag}] zmx backing session "${backendName}" is missing — keeping active for lazy resume`);
|
|
1551
|
+
continue;
|
|
1552
|
+
}
|
|
1340
1553
|
// 'missing' is ambiguous: it means EITHER this one pane is gone while the
|
|
1341
1554
|
// server runs (a true solo zombie) OR the whole multiplexer server is down
|
|
1342
1555
|
// (e.g. machine reboot) and every pane vanished at once. Only the former is
|
|
@@ -1371,8 +1584,16 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1371
1584
|
// check on the reattach path too — persistent-backend reattach ignores the
|
|
1372
1585
|
// bin/args handed to backend.spawn(), so anything that slips through here
|
|
1373
1586
|
// would silently resurrect the old frozen CLI.
|
|
1374
|
-
|
|
1587
|
+
try {
|
|
1588
|
+
const mismatchClose = await closeActiveSessionIfCliMismatch(ds);
|
|
1589
|
+
if (mismatchClose !== 'not_mismatched')
|
|
1590
|
+
continue;
|
|
1591
|
+
}
|
|
1592
|
+
catch (err) {
|
|
1593
|
+
logger.error(`[${ds.session.sessionId.substring(0, 8)}] CLI mismatch close failed during reattach; keeping active row: `
|
|
1594
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
1375
1595
|
continue;
|
|
1596
|
+
}
|
|
1376
1597
|
const tag = ds.session.sessionId.substring(0, 8);
|
|
1377
1598
|
logger.info(`[${tag}] ${backendType} session alive, queued for re-attach`);
|
|
1378
1599
|
toReattach.push(ds);
|
|
@@ -1404,6 +1625,10 @@ export async function restoreActiveSessions(activeSessions) {
|
|
|
1404
1625
|
* only the first forks; the rest just await the same `ds.workerPort`.
|
|
1405
1626
|
*/
|
|
1406
1627
|
export async function ensureTerminalWorkerPort(ds) {
|
|
1628
|
+
const frozenBackendType = ds.initConfig?.backendType ?? ds.session.backendType;
|
|
1629
|
+
if (frozenBackendType !== undefined && !backendSupportsWebTerminal(frozenBackendType)) {
|
|
1630
|
+
return undefined;
|
|
1631
|
+
}
|
|
1407
1632
|
if (ds.workerPort)
|
|
1408
1633
|
return ds.workerPort;
|
|
1409
1634
|
if (ds.session.status !== 'active')
|
|
@@ -1455,6 +1680,7 @@ export async function ensureTerminalWorkerPort(ds) {
|
|
|
1455
1680
|
* manual resume could resurrect a stale member epoch
|
|
1456
1681
|
* - 'deferred_unmaterialized' — a silent fresh-topic run finished without
|
|
1457
1682
|
* publishing, so it has no conversation to resume
|
|
1683
|
+
* - 'resume_cancelled' — a concurrent close won while resume was committing
|
|
1458
1684
|
*/
|
|
1459
1685
|
export async function resumeSession(sessionId, activeSessions) {
|
|
1460
1686
|
const session = sessionStore.getSession(sessionId);
|
|
@@ -1506,10 +1732,14 @@ export async function resumeSession(sessionId, activeSessions) {
|
|
|
1506
1732
|
// throwaway command container, so clobbering it would lose real intent.
|
|
1507
1733
|
const existing = activeSessions.get(key);
|
|
1508
1734
|
if (existing) {
|
|
1509
|
-
if (
|
|
1735
|
+
if (!isDisposableCommandScratch(existing)) {
|
|
1510
1736
|
return { ok: false, error: 'anchor_occupied', activeSessionId: existing.session.sessionId };
|
|
1511
1737
|
}
|
|
1512
1738
|
await closeSession(existing.session.sessionId);
|
|
1739
|
+
const replacement = activeSessions.get(key);
|
|
1740
|
+
if (replacement) {
|
|
1741
|
+
return { ok: false, error: 'anchor_occupied', activeSessionId: replacement.session.sessionId };
|
|
1742
|
+
}
|
|
1513
1743
|
}
|
|
1514
1744
|
// Belt-and-suspenders: also scan persisted sessions for any *other* active
|
|
1515
1745
|
// session pinned to the same (larkAppId, scope, anchor). The in-memory Map
|
|
@@ -1541,13 +1771,21 @@ export async function resumeSession(sessionId, activeSessions) {
|
|
|
1541
1771
|
&& (s.larkAppId ?? '') === larkAppId
|
|
1542
1772
|
&& (s.scope === 'chat' ? 'chat' : 'thread') === scope
|
|
1543
1773
|
&& storedSessionAnchorId(s) === anchor);
|
|
1544
|
-
const realConflict = conflicts.find(s => !!s.cliId || !!s.lastCliInput);
|
|
1774
|
+
const realConflict = conflicts.find(s => !!s.cliId || !!s.lastCliInput || !!s.backendType || !!s.adoptedFrom || s.queued === true);
|
|
1545
1775
|
if (realConflict) {
|
|
1546
1776
|
return { ok: false, error: 'anchor_occupied', activeSessionId: realConflict.sessionId };
|
|
1547
1777
|
}
|
|
1548
1778
|
for (const scratch of conflicts) {
|
|
1549
1779
|
await closeSession(scratch.sessionId);
|
|
1550
1780
|
}
|
|
1781
|
+
const replacementAfterDiskCleanup = activeSessions.get(key);
|
|
1782
|
+
if (replacementAfterDiskCleanup) {
|
|
1783
|
+
return {
|
|
1784
|
+
ok: false,
|
|
1785
|
+
error: 'anchor_occupied',
|
|
1786
|
+
activeSessionId: replacementAfterDiskCleanup.session.sessionId,
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1551
1789
|
// Reactivate in store — clear closedAt so dashboard rows don't keep showing
|
|
1552
1790
|
// the stale close timestamp on the now-active session.
|
|
1553
1791
|
session.status = 'active';
|
|
@@ -1584,9 +1822,18 @@ export async function resumeSession(sessionId, activeSessions) {
|
|
|
1584
1822
|
};
|
|
1585
1823
|
messageQueue.ensureQueue(anchor);
|
|
1586
1824
|
// setActiveSessionSafe over a bare Map.set: the scratch-eviction above
|
|
1587
|
-
// should already have freed `key`, but
|
|
1588
|
-
//
|
|
1589
|
-
await setActiveSessionSafe(activeSessions, key, ds);
|
|
1825
|
+
// should already have freed `key`, but its compare-and-set gate lets any
|
|
1826
|
+
// concurrent runtime occupant win rather than silently orphaning it.
|
|
1827
|
+
const registered = await setActiveSessionSafe(activeSessions, key, ds);
|
|
1828
|
+
if (!registered || session.status !== 'active' || activeSessions.get(key) !== ds) {
|
|
1829
|
+
const occupant = activeSessions.get(key);
|
|
1830
|
+
if (session.status === 'active')
|
|
1831
|
+
await closeSession(sessionId);
|
|
1832
|
+
if (occupant && occupant !== ds) {
|
|
1833
|
+
return { ok: false, error: 'anchor_occupied', activeSessionId: occupant.session.sessionId };
|
|
1834
|
+
}
|
|
1835
|
+
return { ok: false, error: 'resume_cancelled' };
|
|
1836
|
+
}
|
|
1590
1837
|
logger.info(`Resumed session ${sessionId.substring(0, 8)} (scope: ${scope}, anchor: ${anchor.substring(0, 12)})`);
|
|
1591
1838
|
return { ok: true, ds };
|
|
1592
1839
|
}
|
|
@@ -1811,41 +2058,73 @@ export async function executeScheduledTask(task, activeSessions, refreshCliVersi
|
|
|
1811
2058
|
const firePrompt = silent
|
|
1812
2059
|
? `${buildSilentScheduleHint(task.name, localeForBot(larkAppId))}\n\n${task.prompt}`
|
|
1813
2060
|
: task.prompt;
|
|
1814
|
-
//
|
|
1815
|
-
|
|
1816
|
-
|
|
2061
|
+
// Continue through the session already registered at this anchor. A
|
|
2062
|
+
// deliberately suspended session keeps its active registration with
|
|
2063
|
+
// worker=null; scheduled turns must cold-resume that same session instead of
|
|
2064
|
+
// creating a competing row that the registration CAS will reject.
|
|
2065
|
+
const activeKey = sessionKey(anchor, larkAppId);
|
|
2066
|
+
const existing = activeSessions.get(activeKey);
|
|
2067
|
+
const blockedDeferredSetup = existing && (existing.session.queued
|
|
2068
|
+
|| existing.pendingRepo
|
|
2069
|
+
|| existing.pendingRepoCommitInFlight
|
|
2070
|
+
|| existing.worktreeCreating);
|
|
2071
|
+
if (isContinuation && blockedDeferredSetup) {
|
|
2072
|
+
// Dashboard backlog and repo/worktree setup rows deliberately own the route
|
|
2073
|
+
// while keeping their worker null. Cold-resuming one with the scheduled
|
|
2074
|
+
// prompt either consumes queuedPrompt or races the later deferred fork.
|
|
2075
|
+
// Reject observably while preserving the user's pending setup intact.
|
|
2076
|
+
const setupKind = existing.session.queued ? 'queued' : 'pending setup';
|
|
2077
|
+
throw new Error(`scheduled continuation ${task.id} is blocked by ${setupKind} session ${existing.session.sessionId}; ` +
|
|
2078
|
+
'the deferred task was preserved');
|
|
2079
|
+
}
|
|
2080
|
+
if (isContinuation && existing) {
|
|
1817
2081
|
markSessionActivity(existing);
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
2082
|
+
ensureSessionWhiteboard(existing);
|
|
2083
|
+
if (sharedTopicRootId) {
|
|
2084
|
+
beginReplyTargetTurn(existing, sharedTopicRootId, scheduledTurnId);
|
|
2085
|
+
sessionStore.updateSession(existing.session);
|
|
2086
|
+
}
|
|
2087
|
+
const input = buildFollowUpCliInput(firePrompt, existing.session.sessionId, {
|
|
2088
|
+
isAdoptMode: false,
|
|
2089
|
+
cliId: existing.session.cliId ?? bot.config.cliId,
|
|
2090
|
+
cliPathOverride: existing.session.cliPathOverride ?? bot.config.cliPathOverride,
|
|
2091
|
+
locale: localeForBot(larkAppId),
|
|
2092
|
+
larkAppId,
|
|
2093
|
+
chatId: task.chatId,
|
|
2094
|
+
whiteboardId: existing.session.whiteboardId,
|
|
2095
|
+
});
|
|
2096
|
+
rememberLastCliInput(existing, task.prompt, input);
|
|
2097
|
+
if (silent)
|
|
2098
|
+
armSilentScheduledTurn(existing, scheduledTurnId);
|
|
2099
|
+
if (existing.worker && !existing.worker.killed) {
|
|
2100
|
+
try {
|
|
2101
|
+
if (sendWorkerInput(existing, input, scheduledTurnId)) {
|
|
2102
|
+
logger.info(`[scheduler] Task "${task.name}" injected into live session ${existing.session.sessionId}${silent ? ' (silent)' : ''}`);
|
|
2103
|
+
return;
|
|
2104
|
+
}
|
|
1823
2105
|
}
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
cliId: existing.session.cliId ?? bot.config.cliId,
|
|
1827
|
-
cliPathOverride: existing.session.cliPathOverride ?? bot.config.cliPathOverride,
|
|
1828
|
-
locale: localeForBot(larkAppId),
|
|
1829
|
-
larkAppId,
|
|
1830
|
-
chatId: task.chatId,
|
|
1831
|
-
whiteboardId: existing.session.whiteboardId,
|
|
1832
|
-
});
|
|
1833
|
-
rememberLastCliInput(existing, task.prompt, input);
|
|
1834
|
-
if (silent)
|
|
1835
|
-
armSilentScheduledTurn(existing, scheduledTurnId);
|
|
1836
|
-
if (!sendWorkerInput(existing, input, scheduledTurnId)) {
|
|
1837
|
-
if (silent)
|
|
1838
|
-
disarmSilentScheduledTurn(existing, scheduledTurnId);
|
|
1839
|
-
throw new Error('worker unavailable');
|
|
2106
|
+
catch (err) {
|
|
2107
|
+
logger.warn(`[scheduler] Live injection threw (${err.message}); cold-resuming registered session`);
|
|
1840
2108
|
}
|
|
1841
|
-
|
|
1842
|
-
|
|
2109
|
+
}
|
|
2110
|
+
if (activeSessions.get(activeKey) !== existing || existing.session.status !== 'active') {
|
|
2111
|
+
if (silent)
|
|
2112
|
+
disarmSilentScheduledTurn(existing, scheduledTurnId);
|
|
2113
|
+
throw new Error(`scheduled continuation lost active session ${existing.session.sessionId}`);
|
|
2114
|
+
}
|
|
2115
|
+
try {
|
|
2116
|
+
forkWorker(existing, input, {
|
|
2117
|
+
resume: existing.hasHistory,
|
|
2118
|
+
turnId: scheduledTurnId,
|
|
2119
|
+
});
|
|
1843
2120
|
}
|
|
1844
2121
|
catch (err) {
|
|
1845
2122
|
if (silent)
|
|
1846
2123
|
disarmSilentScheduledTurn(existing, scheduledTurnId);
|
|
1847
|
-
|
|
2124
|
+
throw err;
|
|
1848
2125
|
}
|
|
2126
|
+
logger.info(`[scheduler] Task "${task.name}" cold-resumed session ${existing.session.sessionId}${silent ? ' (silent)' : ''}`);
|
|
2127
|
+
return;
|
|
1849
2128
|
}
|
|
1850
2129
|
// Spawn a fresh session bound to the chosen anchor.
|
|
1851
2130
|
// Thread-scope: rootMessageId = anchor. Chat-scope: rootMessageId stores the
|
|
@@ -1893,7 +2172,12 @@ export async function executeScheduledTask(task, activeSessions, refreshCliVersi
|
|
|
1893
2172
|
}
|
|
1894
2173
|
ensureSessionWhiteboard(ds);
|
|
1895
2174
|
const prompt = buildNewTopicCliInput(firePrompt, session.sessionId, bot.config.cliId, bot.config.cliPathOverride, undefined, undefined, undefined, undefined, { name: bot.botName, openId: bot.botOpenId }, localeForBot(larkAppId), undefined, { larkAppId, chatId: task.chatId, whiteboardId: ds.session.whiteboardId });
|
|
1896
|
-
|
|
2175
|
+
if (!setActiveSessionIfActive(activeSessions, activeKey, ds)) {
|
|
2176
|
+
const winner = activeSessions.get(activeKey);
|
|
2177
|
+
await closeSession(session.sessionId);
|
|
2178
|
+
throw new Error(`scheduled task ${task.id} lost active-session registration for ${anchor}` +
|
|
2179
|
+
(winner ? ` to ${winner.session.sessionId}` : ''));
|
|
2180
|
+
}
|
|
1897
2181
|
rememberLastCliInput(ds, task.prompt, prompt);
|
|
1898
2182
|
if (silent)
|
|
1899
2183
|
armSilentScheduledTurn(ds, scheduledTurnId);
|
|
@@ -2031,6 +2315,13 @@ export async function spawnDashboardSession(activeSessions, refreshCliVersion, a
|
|
|
2031
2315
|
if (existing && (existing.worker || existing.session.queued || isRelayableRealSession(existing))) {
|
|
2032
2316
|
return { ok: false, error: 'session_exists' };
|
|
2033
2317
|
}
|
|
2318
|
+
const quarantinedPersisted = sessionStore.listSessions().find(session => session.status === 'active'
|
|
2319
|
+
&& !!session.restoreQuarantinedAt
|
|
2320
|
+
&& (session.larkAppId ?? larkAppId) === larkAppId
|
|
2321
|
+
&& storedSessionAnchorId(session) === anchor);
|
|
2322
|
+
if (quarantinedPersisted) {
|
|
2323
|
+
return { ok: false, error: 'session_exists' };
|
|
2324
|
+
}
|
|
2034
2325
|
refreshCliVersion?.(bot.config.cliId, bot.config.cliPathOverride);
|
|
2035
2326
|
// 可见任务横幅:只由 creator/lead 那次 spawn 发一条,给群成员交代这群是干嘛的。
|
|
2036
2327
|
// 纯文本、不 @ 任何 bot,不会误触发其它 bot。rootMessageId 存它仅为留痕(chat-scope
|
|
@@ -2110,7 +2401,10 @@ export async function spawnDashboardSession(activeSessions, refreshCliVersion, a
|
|
|
2110
2401
|
pendingCodexAppMessageContext: codexAppMessageContext,
|
|
2111
2402
|
pendingAttachments: args.attachments,
|
|
2112
2403
|
};
|
|
2113
|
-
activeSessions
|
|
2404
|
+
if (!setActiveSessionIfActive(activeSessions, sessionKey(anchor, larkAppId), ds)) {
|
|
2405
|
+
await closeSession(session.sessionId);
|
|
2406
|
+
return { ok: false, error: 'session_exists' };
|
|
2407
|
+
}
|
|
2114
2408
|
if (column === 'backlog') {
|
|
2115
2409
|
// Parked:不起 CLI。手动广播 session.spawned,让 dashboard 立刻显示待办池卡片
|
|
2116
2410
|
// (forkWorker 才会自动发这个事件,parked 路径要自己发)。
|