botmux 3.16.0 → 3.17.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/riff-backend.d.ts +84 -1
- package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
- package/dist/adapters/backend/riff-backend.js +202 -19
- package/dist/adapters/backend/riff-backend.js.map +1 -1
- package/dist/adapters/backend/sandbox.d.ts +8 -0
- package/dist/adapters/backend/sandbox.d.ts.map +1 -1
- package/dist/adapters/backend/sandbox.js +15 -0
- package/dist/adapters/backend/sandbox.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +61 -7
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +80 -9
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts +62 -4
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +240 -27
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +4 -2
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/coco.d.ts.map +1 -1
- package/dist/adapters/cli/coco.js +27 -0
- package/dist/adapters/cli/coco.js.map +1 -1
- package/dist/adapters/cli/codex-app.d.ts.map +1 -1
- package/dist/adapters/cli/codex-app.js +28 -0
- 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 +51 -23
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/dsh-tui.d.ts +24 -0
- package/dist/adapters/cli/dsh-tui.d.ts.map +1 -0
- package/dist/adapters/cli/dsh-tui.js +99 -0
- package/dist/adapters/cli/dsh-tui.js.map +1 -0
- package/dist/adapters/cli/dsh.d.ts.map +1 -1
- package/dist/adapters/cli/dsh.js +15 -9
- package/dist/adapters/cli/dsh.js.map +1 -1
- package/dist/adapters/cli/fs-policy.d.ts +98 -1
- package/dist/adapters/cli/fs-policy.d.ts.map +1 -1
- package/dist/adapters/cli/fs-policy.js +182 -7
- package/dist/adapters/cli/fs-policy.js.map +1 -1
- package/dist/adapters/cli/grok.js +16 -18
- package/dist/adapters/cli/grok.js.map +1 -1
- package/dist/adapters/cli/model-catalog-json.d.ts +17 -0
- package/dist/adapters/cli/model-catalog-json.d.ts.map +1 -0
- package/dist/adapters/cli/model-catalog-json.js +43 -0
- package/dist/adapters/cli/model-catalog-json.js.map +1 -0
- package/dist/adapters/cli/oh-my-pi.d.ts +2 -0
- package/dist/adapters/cli/oh-my-pi.d.ts.map +1 -1
- package/dist/adapters/cli/oh-my-pi.js +45 -13
- package/dist/adapters/cli/oh-my-pi.js.map +1 -1
- package/dist/adapters/cli/opencode.js +1 -1
- package/dist/adapters/cli/opencode.js.map +1 -1
- package/dist/adapters/cli/registry.d.ts +2 -1
- package/dist/adapters/cli/registry.d.ts.map +1 -1
- package/dist/adapters/cli/registry.js +6 -1
- package/dist/adapters/cli/registry.js.map +1 -1
- package/dist/adapters/cli/relay.d.ts.map +1 -1
- package/dist/adapters/cli/relay.js +7 -0
- package/dist/adapters/cli/relay.js.map +1 -1
- package/dist/adapters/cli/seed.d.ts.map +1 -1
- package/dist/adapters/cli/seed.js +6 -0
- package/dist/adapters/cli/seed.js.map +1 -1
- package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
- package/dist/adapters/cli/shared-hints.js +20 -14
- package/dist/adapters/cli/shared-hints.js.map +1 -1
- package/dist/adapters/cli/traex.d.ts +4 -0
- package/dist/adapters/cli/traex.d.ts.map +1 -1
- package/dist/adapters/cli/traex.js +153 -0
- package/dist/adapters/cli/traex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +42 -1
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/autostart.d.ts +6 -0
- package/dist/autostart.d.ts.map +1 -1
- package/dist/autostart.js +21 -1
- package/dist/autostart.js.map +1 -1
- package/dist/bot-registry.d.ts +27 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +62 -2
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/vc-agent.d.ts.map +1 -1
- package/dist/cli/vc-agent.js +21 -3
- package/dist/cli/vc-agent.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +92 -1
- package/dist/cli.js.map +1 -1
- package/dist/codex-app-runner.js +28 -0
- package/dist/codex-app-runner.js.map +1 -1
- package/dist/core/bot-steer-directive.d.ts +25 -0
- package/dist/core/bot-steer-directive.d.ts.map +1 -0
- package/dist/core/bot-steer-directive.js +47 -0
- package/dist/core/bot-steer-directive.js.map +1 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +102 -18
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +53 -13
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dashboard-rows.d.ts +3 -0
- package/dist/core/dashboard-rows.d.ts.map +1 -1
- package/dist/core/dashboard-rows.js +26 -5
- package/dist/core/dashboard-rows.js.map +1 -1
- package/dist/core/dashboard-url.d.ts +10 -1
- package/dist/core/dashboard-url.d.ts.map +1 -1
- package/dist/core/dashboard-url.js +12 -2
- package/dist/core/dashboard-url.js.map +1 -1
- package/dist/core/dispatch.d.ts.map +1 -1
- package/dist/core/dispatch.js +5 -4
- package/dist/core/dispatch.js.map +1 -1
- package/dist/core/existing-app-server.d.ts +22 -0
- package/dist/core/existing-app-server.d.ts.map +1 -0
- package/dist/core/existing-app-server.js +85 -0
- package/dist/core/existing-app-server.js.map +1 -0
- package/dist/core/native-topic-id.d.ts +9 -0
- package/dist/core/native-topic-id.d.ts.map +1 -0
- package/dist/core/native-topic-id.js +14 -0
- package/dist/core/native-topic-id.js.map +1 -0
- package/dist/core/per-bot-env.d.ts.map +1 -1
- package/dist/core/per-bot-env.js +10 -0
- package/dist/core/per-bot-env.js.map +1 -1
- package/dist/core/plugins/mcp/gateway.d.ts +27 -3
- package/dist/core/plugins/mcp/gateway.d.ts.map +1 -1
- package/dist/core/plugins/mcp/gateway.js +272 -26
- package/dist/core/plugins/mcp/gateway.js.map +1 -1
- package/dist/core/plugins/mcp/host.d.ts +18 -0
- package/dist/core/plugins/mcp/host.d.ts.map +1 -1
- package/dist/core/plugins/mcp/host.js +80 -16
- package/dist/core/plugins/mcp/host.js.map +1 -1
- package/dist/core/plugins/mcp/launch-record.d.ts +14 -0
- package/dist/core/plugins/mcp/launch-record.d.ts.map +1 -0
- package/dist/core/plugins/mcp/launch-record.js +63 -0
- package/dist/core/plugins/mcp/launch-record.js.map +1 -0
- package/dist/core/session-activity.d.ts +11 -0
- package/dist/core/session-activity.d.ts.map +1 -1
- package/dist/core/session-activity.js +23 -0
- package/dist/core/session-activity.js.map +1 -1
- package/dist/core/session-group-birth.d.ts.map +1 -1
- package/dist/core/session-group-birth.js +23 -8
- package/dist/core/session-group-birth.js.map +1 -1
- package/dist/core/session-manager.d.ts +1 -4
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +16 -20
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/shared-adopt.d.ts +20 -0
- package/dist/core/shared-adopt.d.ts.map +1 -0
- package/dist/core/shared-adopt.js +26 -0
- package/dist/core/shared-adopt.js.map +1 -0
- package/dist/core/skills/customize-command.d.ts +7 -0
- package/dist/core/skills/customize-command.d.ts.map +1 -0
- package/dist/core/skills/customize-command.js +303 -0
- package/dist/core/skills/customize-command.js.map +1 -0
- 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 +15 -0
- package/dist/core/terminal-write-auth.js.map +1 -1
- package/dist/core/terminal-write-frame.d.ts +30 -0
- package/dist/core/terminal-write-frame.d.ts.map +1 -0
- package/dist/core/terminal-write-frame.js +44 -0
- package/dist/core/terminal-write-frame.js.map +1 -0
- package/dist/core/types.d.ts +14 -4
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +6 -8
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +31 -6
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +444 -102
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/core/worker-startup-retry.d.ts +52 -0
- package/dist/core/worker-startup-retry.d.ts.map +1 -0
- package/dist/core/worker-startup-retry.js +68 -0
- package/dist/core/worker-startup-retry.js.map +1 -0
- package/dist/daemon.d.ts +9 -8
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +467 -171
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/autostart-api.d.ts +26 -0
- package/dist/dashboard/autostart-api.d.ts.map +1 -0
- package/dist/dashboard/autostart-api.js +88 -0
- package/dist/dashboard/autostart-api.js.map +1 -0
- package/dist/dashboard/bot-payload.d.ts +5 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +2 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/control-csrf.d.ts +29 -1
- package/dist/dashboard/control-csrf.d.ts.map +1 -1
- package/dist/dashboard/control-csrf.js +49 -10
- package/dist/dashboard/control-csrf.js.map +1 -1
- package/dist/dashboard/customization-api.d.ts +23 -0
- package/dist/dashboard/customization-api.d.ts.map +1 -0
- package/dist/dashboard/customization-api.js +198 -0
- package/dist/dashboard/customization-api.js.map +1 -0
- package/dist/dashboard/debug-terminal.d.ts +13 -1
- package/dist/dashboard/debug-terminal.d.ts.map +1 -1
- package/dist/dashboard/debug-terminal.js +18 -4
- package/dist/dashboard/debug-terminal.js.map +1 -1
- package/dist/dashboard/settings-write-applier.d.ts +0 -23
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +7 -28
- package/dist/dashboard/settings-write-applier.js.map +1 -1
- package/dist/dashboard/terminal-control-route.d.ts +32 -0
- package/dist/dashboard/terminal-control-route.d.ts.map +1 -0
- package/dist/dashboard/terminal-control-route.js +70 -0
- package/dist/dashboard/terminal-control-route.js.map +1 -0
- package/dist/dashboard/terminal-control.d.ts +64 -13
- package/dist/dashboard/terminal-control.d.ts.map +1 -1
- package/dist/dashboard/terminal-control.js +77 -14
- package/dist/dashboard/terminal-control.js.map +1 -1
- package/dist/dashboard/terminal-front-proxy.d.ts.map +1 -1
- package/dist/dashboard/terminal-front-proxy.js +41 -9
- package/dist/dashboard/terminal-front-proxy.js.map +1 -1
- package/dist/dashboard/vc-consumer-profiles-api.d.ts +69 -18
- package/dist/dashboard/vc-consumer-profiles-api.d.ts.map +1 -1
- package/dist/dashboard/vc-consumer-profiles-api.js +149 -29
- package/dist/dashboard/vc-consumer-profiles-api.js.map +1 -1
- package/dist/dashboard/web/agent-workbench-api.d.ts +34 -2
- package/dist/dashboard/web/agent-workbench-api.d.ts.map +1 -1
- package/dist/dashboard/web/agent-workbench-api.js +39 -4
- package/dist/dashboard/web/agent-workbench-api.js.map +1 -1
- package/dist/dashboard/web/agent-workbench-appearance.d.ts +6 -3
- package/dist/dashboard/web/agent-workbench-appearance.d.ts.map +1 -1
- package/dist/dashboard/web/agent-workbench-appearance.js +7 -4
- package/dist/dashboard/web/agent-workbench-appearance.js.map +1 -1
- package/dist/dashboard/web/agent-workbench-model.d.ts +2 -0
- package/dist/dashboard/web/agent-workbench-model.d.ts.map +1 -1
- package/dist/dashboard/web/agent-workbench-model.js.map +1 -1
- package/dist/dashboard/web/agent-workbench-page.js +2 -2
- package/dist/dashboard/web/agent-workbench-page.js.map +1 -1
- package/dist/dashboard/web/agent-workbench-panes.d.ts +59 -4
- package/dist/dashboard/web/agent-workbench-panes.d.ts.map +1 -1
- package/dist/dashboard/web/agent-workbench-panes.js +493 -85
- package/dist/dashboard/web/agent-workbench-panes.js.map +1 -1
- package/dist/dashboard/web/agent-workbench-session-list.d.ts +11 -7
- package/dist/dashboard/web/agent-workbench-session-list.d.ts.map +1 -1
- package/dist/dashboard/web/agent-workbench-session-list.js +24 -31
- package/dist/dashboard/web/agent-workbench-session-list.js.map +1 -1
- package/dist/dashboard/web/agent-workbench-terminal-control.d.ts +198 -0
- package/dist/dashboard/web/agent-workbench-terminal-control.d.ts.map +1 -0
- package/dist/dashboard/web/agent-workbench-terminal-control.js +292 -0
- package/dist/dashboard/web/agent-workbench-terminal-control.js.map +1 -0
- package/dist/dashboard/web/agent-workbench-view.d.ts +3 -2
- package/dist/dashboard/web/agent-workbench-view.d.ts.map +1 -1
- package/dist/dashboard/web/agent-workbench-view.js +69 -50
- package/dist/dashboard/web/agent-workbench-view.js.map +1 -1
- package/dist/dashboard/web/app.d.ts.map +1 -1
- package/dist/dashboard/web/app.js +58 -7
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +24 -0
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +191 -16
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +17 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +51 -2
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/bot-onboarding.d.ts.map +1 -1
- package/dist/dashboard/web/bot-onboarding.js +32 -8
- package/dist/dashboard/web/bot-onboarding.js.map +1 -1
- package/dist/dashboard/web/confirm-modal.d.ts +35 -0
- package/dist/dashboard/web/confirm-modal.d.ts.map +1 -0
- package/dist/dashboard/web/confirm-modal.js +111 -0
- package/dist/dashboard/web/confirm-modal.js.map +1 -0
- package/dist/dashboard/web/connectors-page.d.ts.map +1 -1
- package/dist/dashboard/web/connectors-page.js +2 -1
- package/dist/dashboard/web/connectors-page.js.map +1 -1
- package/dist/dashboard/web/customization-page.d.ts +3 -0
- package/dist/dashboard/web/customization-page.d.ts.map +1 -0
- package/dist/dashboard/web/customization-page.js +315 -0
- package/dist/dashboard/web/customization-page.js.map +1 -0
- package/dist/dashboard/web/dashboard-routes.d.ts.map +1 -1
- package/dist/dashboard/web/dashboard-routes.js +1 -0
- package/dist/dashboard/web/dashboard-routes.js.map +1 -1
- package/dist/dashboard/web/groups-page.d.ts.map +1 -1
- package/dist/dashboard/web/groups-page.js +11 -9
- 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 +226 -42
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/monitor-room.d.ts.map +1 -1
- package/dist/dashboard/web/monitor-room.js +3 -2
- package/dist/dashboard/web/monitor-room.js.map +1 -1
- package/dist/dashboard/web/overview-page.d.ts.map +1 -1
- package/dist/dashboard/web/overview-page.js +98 -63
- package/dist/dashboard/web/overview-page.js.map +1 -1
- package/dist/dashboard/web/roles-page.d.ts.map +1 -1
- package/dist/dashboard/web/roles-page.js +4 -3
- package/dist/dashboard/web/roles-page.js.map +1 -1
- package/dist/dashboard/web/schedules-page.d.ts.map +1 -1
- package/dist/dashboard/web/schedules-page.js +165 -18
- package/dist/dashboard/web/schedules-page.js.map +1 -1
- package/dist/dashboard/web/sessions-kanban.d.ts +2 -0
- package/dist/dashboard/web/sessions-kanban.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-kanban.js +142 -24
- 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 +177 -94
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +5 -4
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/settings-page.d.ts +0 -8
- package/dist/dashboard/web/settings-page.d.ts.map +1 -1
- package/dist/dashboard/web/settings-page.js +185 -65
- package/dist/dashboard/web/settings-page.js.map +1 -1
- package/dist/dashboard/web/skills/skill-library-tab.d.ts +2 -0
- package/dist/dashboard/web/skills/skill-library-tab.d.ts.map +1 -1
- package/dist/dashboard/web/skills/skill-library-tab.js +15 -1
- package/dist/dashboard/web/skills/skill-library-tab.js.map +1 -1
- package/dist/dashboard/web/skills-page.d.ts.map +1 -1
- package/dist/dashboard/web/skills-page.js +5 -4
- package/dist/dashboard/web/skills-page.js.map +1 -1
- package/dist/dashboard/web/store.d.ts +5 -0
- package/dist/dashboard/web/store.d.ts.map +1 -1
- package/dist/dashboard/web/store.js +4 -0
- package/dist/dashboard/web/store.js.map +1 -1
- package/dist/dashboard/web/team-federation-page.d.ts.map +1 -1
- package/dist/dashboard/web/team-federation-page.js +3 -2
- package/dist/dashboard/web/team-federation-page.js.map +1 -1
- package/dist/dashboard/web/toast.d.ts +15 -0
- package/dist/dashboard/web/toast.d.ts.map +1 -0
- package/dist/dashboard/web/toast.js +70 -0
- package/dist/dashboard/web/toast.js.map +1 -0
- package/dist/dashboard/web/v3-components.d.ts.map +1 -1
- package/dist/dashboard/web/v3-components.js +2 -1
- package/dist/dashboard/web/v3-components.js.map +1 -1
- package/dist/dashboard/web/vc-consumer-profiles-section.d.ts +10 -12
- package/dist/dashboard/web/vc-consumer-profiles-section.d.ts.map +1 -1
- package/dist/dashboard/web/vc-consumer-profiles-section.js +219 -200
- package/dist/dashboard/web/vc-consumer-profiles-section.js.map +1 -1
- package/dist/dashboard/web/whiteboards-page.d.ts.map +1 -1
- package/dist/dashboard/web/whiteboards-page.js +2 -1
- package/dist/dashboard/web/whiteboards-page.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/agent-workbench-dock-page-LZSUOCEQ.js +1 -0
- package/dist/dashboard-web/chunks/agent-workbench-page-YONDRJU7.js +1 -0
- package/dist/dashboard-web/chunks/bot-defaults-page-X7LV6373.js +1 -0
- package/dist/dashboard-web/chunks/chunk-2PVRDXEL.js +1 -0
- package/dist/dashboard-web/chunks/chunk-53JUKBU2.js +10 -0
- package/dist/dashboard-web/chunks/{chunk-5CJMHTT4.js → chunk-5QX4OYOR.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-NACP7BMR.js → chunk-5VRVJFQR.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-FDZ7WOBW.js → chunk-6ROOZWYV.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-F6KNPF4R.js → chunk-CK22743G.js} +15 -19
- package/dist/dashboard-web/chunks/chunk-CVNIMUPY.js +25 -0
- package/dist/dashboard-web/chunks/chunk-GJC2TMWS.js +2 -0
- package/dist/dashboard-web/chunks/chunk-OT6L6G7R.js +1 -0
- package/dist/dashboard-web/chunks/chunk-QITAMHW6.js +1 -0
- package/dist/dashboard-web/chunks/chunk-RHVFZC5Z.js +1 -0
- package/dist/dashboard-web/chunks/chunk-SOBMN2VY.js +1 -0
- package/dist/dashboard-web/chunks/chunk-SZOB6VK2.js +1 -0
- package/dist/dashboard-web/chunks/chunk-XVIXNQGC.js +44 -0
- package/dist/dashboard-web/chunks/chunk-YUJF34ME.js +1 -0
- package/dist/dashboard-web/chunks/connectors-page-LB2HEJ7F.js +1 -0
- package/dist/dashboard-web/chunks/customization-page-4GXBYLG6.js +105 -0
- package/dist/dashboard-web/chunks/feedback-page-IZQ7VLDG.js +1 -0
- package/dist/dashboard-web/chunks/groups-page-4WCP5OEV.js +8 -0
- package/dist/dashboard-web/chunks/{insights-page-HJFDYR5N.js → insights-page-TXQHIPQL.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-V7PCP47V.js → monitor-room-VBRVWXCM.js} +8 -8
- package/dist/dashboard-web/chunks/monitoring-page-QDDZWERE.js +2 -0
- package/dist/dashboard-web/chunks/office-page-PENFNYPG.js +1 -0
- package/dist/dashboard-web/chunks/overview-page-BZQDP6MP.js +1 -0
- package/dist/dashboard-web/chunks/plugin-page-JX4GSWZS.js +1 -0
- package/dist/dashboard-web/chunks/roles-page-VPNINZNK.js +1 -0
- package/dist/dashboard-web/chunks/schedules-page-BITO6HEA.js +1 -0
- package/dist/dashboard-web/chunks/sessions-page-ZKCOVTD2.js +8 -0
- package/dist/dashboard-web/chunks/settings-page-UT6JDTS6.js +2 -0
- package/dist/dashboard-web/chunks/skills-page-GULXVAJY.js +4 -0
- package/dist/dashboard-web/chunks/team-federation-page-CUUJWGEG.js +1 -0
- package/dist/dashboard-web/chunks/v3-page-5WVJ5AUR.js +94 -0
- package/dist/dashboard-web/chunks/whiteboards-page-FZ2CME4H.js +1 -0
- package/dist/dashboard-web/design-tokens.css +236 -0
- package/dist/dashboard-web/index.html +1 -0
- package/dist/dashboard-web/style.css +1930 -114
- package/dist/dashboard.js +360 -279
- package/dist/dashboard.js.map +1 -1
- package/dist/desktop/renderer/app.js +6 -0
- package/dist/desktop/renderer/app.js.map +1 -1
- package/dist/dsh-runner.js +139 -17
- package/dist/dsh-runner.js.map +1 -1
- package/dist/global-config.d.ts +33 -2
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +62 -0
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +13 -4
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/index.d.ts +27 -3
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +33 -4
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +13 -4
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +2 -0
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +7 -4
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +135 -20
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +38 -3
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +163 -13
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/v3-blocked-card-handler.d.ts.map +1 -1
- package/dist/im/lark/v3-blocked-card-handler.js +2 -1
- package/dist/im/lark/v3-blocked-card-handler.js.map +1 -1
- package/dist/im/lark/v3-blocked-card.d.ts +1 -1
- package/dist/im/lark/v3-blocked-card.d.ts.map +1 -1
- package/dist/im/lark/v3-blocked-card.js +14 -0
- package/dist/im/lark/v3-blocked-card.js.map +1 -1
- package/dist/index-core-only.js +2 -0
- package/dist/index-core-only.js.map +1 -1
- package/dist/index-daemon.js +3 -0
- package/dist/index-daemon.js.map +1 -1
- package/dist/platform/binding.d.ts +37 -0
- package/dist/platform/binding.d.ts.map +1 -1
- package/dist/platform/binding.js +15 -0
- package/dist/platform/binding.js.map +1 -1
- package/dist/services/bridge-turn-journal.d.ts +63 -0
- package/dist/services/bridge-turn-journal.d.ts.map +1 -0
- package/dist/services/bridge-turn-journal.js +149 -0
- package/dist/services/bridge-turn-journal.js.map +1 -0
- package/dist/services/bridge-turn-queue.d.ts +8 -1
- package/dist/services/bridge-turn-queue.d.ts.map +1 -1
- package/dist/services/bridge-turn-queue.js +2 -1
- package/dist/services/bridge-turn-queue.js.map +1 -1
- package/dist/services/codex-app-runner-protocol.d.ts +5 -0
- package/dist/services/codex-app-runner-protocol.d.ts.map +1 -1
- package/dist/services/codex-app-runner-protocol.js +5 -0
- package/dist/services/codex-app-runner-protocol.js.map +1 -1
- package/dist/services/customization-store.d.ts +93 -0
- package/dist/services/customization-store.d.ts.map +1 -0
- package/dist/services/customization-store.js +542 -0
- package/dist/services/customization-store.js.map +1 -0
- package/dist/services/file-bridge-path.d.ts.map +1 -1
- package/dist/services/file-bridge-path.js +3 -0
- package/dist/services/file-bridge-path.js.map +1 -1
- package/dist/services/grok-transcript.d.ts.map +1 -1
- package/dist/services/grok-transcript.js +44 -9
- package/dist/services/grok-transcript.js.map +1 -1
- package/dist/services/local-cli-opener.d.ts.map +1 -1
- package/dist/services/local-cli-opener.js +12 -3
- package/dist/services/local-cli-opener.js.map +1 -1
- package/dist/services/model-catalog.d.ts +54 -0
- package/dist/services/model-catalog.d.ts.map +1 -0
- package/dist/services/model-catalog.js +147 -0
- package/dist/services/model-catalog.js.map +1 -0
- package/dist/services/omp-transcript.d.ts +27 -0
- package/dist/services/omp-transcript.d.ts.map +1 -0
- package/dist/services/omp-transcript.js +214 -0
- package/dist/services/omp-transcript.js.map +1 -0
- package/dist/services/platform-team-store.d.ts +16 -8
- package/dist/services/platform-team-store.d.ts.map +1 -1
- package/dist/services/platform-team-store.js +17 -10
- package/dist/services/platform-team-store.js.map +1 -1
- package/dist/services/project-scanner.d.ts.map +1 -1
- package/dist/services/project-scanner.js +13 -8
- package/dist/services/project-scanner.js.map +1 -1
- package/dist/services/resumable-session-discovery.d.ts.map +1 -1
- package/dist/services/resumable-session-discovery.js +117 -35
- package/dist/services/resumable-session-discovery.js.map +1 -1
- package/dist/services/structured-bridge-clis.d.ts +5 -3
- package/dist/services/structured-bridge-clis.d.ts.map +1 -1
- package/dist/services/structured-bridge-clis.js +5 -1
- package/dist/services/structured-bridge-clis.js.map +1 -1
- package/dist/services/vc-meeting-action-gate.d.ts.map +1 -1
- package/dist/services/vc-meeting-action-gate.js +15 -4
- package/dist/services/vc-meeting-action-gate.js.map +1 -1
- package/dist/services/vc-meeting-consumer-profile-bootstrap.d.ts +46 -66
- package/dist/services/vc-meeting-consumer-profile-bootstrap.d.ts.map +1 -1
- package/dist/services/vc-meeting-consumer-profile-bootstrap.js +33 -292
- package/dist/services/vc-meeting-consumer-profile-bootstrap.js.map +1 -1
- package/dist/services/vc-meeting-consumer-profile-templates.js +3 -3
- package/dist/services/vc-meeting-consumer-profile-templates.js.map +1 -1
- package/dist/services/vc-meeting-delivery-store.d.ts +18 -0
- package/dist/services/vc-meeting-delivery-store.d.ts.map +1 -1
- package/dist/services/vc-meeting-delivery-store.js +55 -0
- package/dist/services/vc-meeting-delivery-store.js.map +1 -1
- package/dist/services/vc-meeting-send-policy.d.ts +21 -1
- package/dist/services/vc-meeting-send-policy.d.ts.map +1 -1
- package/dist/services/vc-meeting-send-policy.js +30 -2
- package/dist/services/vc-meeting-send-policy.js.map +1 -1
- package/dist/services/vc-meeting-shared-consumer-catalog-store.d.ts +78 -0
- package/dist/services/vc-meeting-shared-consumer-catalog-store.d.ts.map +1 -0
- package/dist/services/vc-meeting-shared-consumer-catalog-store.js +227 -0
- package/dist/services/vc-meeting-shared-consumer-catalog-store.js.map +1 -0
- package/dist/services/vc-meeting-shared-consumer-catalog.d.ts +53 -0
- package/dist/services/vc-meeting-shared-consumer-catalog.d.ts.map +1 -0
- package/dist/services/vc-meeting-shared-consumer-catalog.js +161 -0
- package/dist/services/vc-meeting-shared-consumer-catalog.js.map +1 -0
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +1 -0
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/setup/open-platform-automation.d.ts +20 -0
- package/dist/setup/open-platform-automation.d.ts.map +1 -1
- package/dist/setup/open-platform-automation.js +27 -0
- package/dist/setup/open-platform-automation.js.map +1 -1
- package/dist/skills/customization-bundle.d.ts +83 -0
- package/dist/skills/customization-bundle.d.ts.map +1 -0
- package/dist/skills/customization-bundle.js +214 -0
- package/dist/skills/customization-bundle.js.map +1 -0
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +10 -3
- package/dist/skills/definitions.js.map +1 -1
- package/dist/skills/effective-builtins.d.ts +38 -0
- package/dist/skills/effective-builtins.d.ts.map +1 -0
- package/dist/skills/effective-builtins.js +96 -0
- package/dist/skills/effective-builtins.js.map +1 -0
- package/dist/skills/injection-mode.d.ts +4 -1
- package/dist/skills/injection-mode.d.ts.map +1 -1
- package/dist/skills/injection-mode.js +11 -3
- package/dist/skills/injection-mode.js.map +1 -1
- package/dist/skills/installer.d.ts.map +1 -1
- package/dist/skills/installer.js +24 -1
- package/dist/skills/installer.js.map +1 -1
- package/dist/skills/prompt-fragments.d.ts +58 -0
- package/dist/skills/prompt-fragments.d.ts.map +1 -0
- package/dist/skills/prompt-fragments.js +88 -0
- package/dist/skills/prompt-fragments.js.map +1 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/cli-usage-limit.d.ts +50 -0
- package/dist/utils/cli-usage-limit.d.ts.map +1 -1
- package/dist/utils/cli-usage-limit.js +43 -0
- package/dist/utils/cli-usage-limit.js.map +1 -1
- package/dist/utils/idle-detector.d.ts +12 -0
- package/dist/utils/idle-detector.d.ts.map +1 -1
- package/dist/utils/idle-detector.js +88 -2
- package/dist/utils/idle-detector.js.map +1 -1
- package/dist/utils/stuck-detector.d.ts +5 -0
- package/dist/utils/stuck-detector.d.ts.map +1 -1
- package/dist/utils/stuck-detector.js +41 -0
- package/dist/utils/stuck-detector.js.map +1 -1
- package/dist/utils/usage-limit-tracker.d.ts +46 -0
- package/dist/utils/usage-limit-tracker.d.ts.map +1 -0
- package/dist/utils/usage-limit-tracker.js +91 -0
- package/dist/utils/usage-limit-tracker.js.map +1 -0
- package/dist/utils/user-token.js +1 -1
- package/dist/utils/user-token.js.map +1 -1
- package/dist/worker.js +812 -125
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +14 -14
- package/dist/workflows/events/payloads.d.ts +12 -12
- package/dist/workflows/events/schema.d.ts +164 -164
- package/dist/workflows/migration/v2-to-v3.js +1 -1
- package/dist/workflows/migration/v2-to-v3.js.map +1 -1
- package/dist/workflows/v3/architect.d.ts.map +1 -1
- package/dist/workflows/v3/architect.js +11 -5
- package/dist/workflows/v3/architect.js.map +1 -1
- package/dist/workflows/v3/artifact-contract-declarations.d.ts +22 -0
- package/dist/workflows/v3/artifact-contract-declarations.d.ts.map +1 -0
- package/dist/workflows/v3/artifact-contract-declarations.js +95 -0
- package/dist/workflows/v3/artifact-contract-declarations.js.map +1 -0
- package/dist/workflows/v3/contract.d.ts +1 -0
- package/dist/workflows/v3/contract.d.ts.map +1 -1
- package/dist/workflows/v3/contract.js.map +1 -1
- package/dist/workflows/v3/daemon-run.d.ts +2 -2
- package/dist/workflows/v3/daemon-run.d.ts.map +1 -1
- package/dist/workflows/v3/daemon-run.js +4 -0
- package/dist/workflows/v3/daemon-run.js.map +1 -1
- package/dist/workflows/v3/dag.d.ts +7 -0
- package/dist/workflows/v3/dag.d.ts.map +1 -1
- package/dist/workflows/v3/dag.js +69 -8
- package/dist/workflows/v3/dag.js.map +1 -1
- package/dist/workflows/v3/ephemeral-pool.js +1 -1
- package/dist/workflows/v3/ephemeral-pool.js.map +1 -1
- package/dist/workflows/v3/event-contract.d.ts +3 -1
- package/dist/workflows/v3/event-contract.d.ts.map +1 -1
- package/dist/workflows/v3/host.d.ts.map +1 -1
- package/dist/workflows/v3/host.js +5 -1
- package/dist/workflows/v3/host.js.map +1 -1
- package/dist/workflows/v3/journal.d.ts +1 -1
- package/dist/workflows/v3/journal.d.ts.map +1 -1
- package/dist/workflows/v3/journal.js.map +1 -1
- package/dist/workflows/v3/library-materialize.d.ts.map +1 -1
- package/dist/workflows/v3/library-materialize.js +9 -1
- package/dist/workflows/v3/library-materialize.js.map +1 -1
- package/dist/workflows/v3/library-schema.d.ts +3 -1
- package/dist/workflows/v3/library-schema.d.ts.map +1 -1
- package/dist/workflows/v3/library-schema.js +20 -4
- package/dist/workflows/v3/library-schema.js.map +1 -1
- package/dist/workflows/v3/library-store.d.ts.map +1 -1
- package/dist/workflows/v3/library-store.js +15 -3
- package/dist/workflows/v3/library-store.js.map +1 -1
- package/dist/workflows/v3/run-envelope.d.ts +16 -16
- package/dist/workflows/v3/shared-node-runtime.d.ts +2 -1
- package/dist/workflows/v3/shared-node-runtime.d.ts.map +1 -1
- package/dist/workflows/v3/shared-node-runtime.js +48 -7
- package/dist/workflows/v3/shared-node-runtime.js.map +1 -1
- package/package.json +2 -1
- package/dist/dashboard-web/chunks/agent-workbench-dock-page-IKAC5CFL.js +0 -1
- package/dist/dashboard-web/chunks/agent-workbench-page-M53UN5EB.js +0 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-YKE5SDZF.js +0 -10
- package/dist/dashboard-web/chunks/chunk-22WIADUT.js +0 -2
- package/dist/dashboard-web/chunks/chunk-24ONYQMR.js +0 -1
- package/dist/dashboard-web/chunks/chunk-2W6SCN3M.js +0 -1
- package/dist/dashboard-web/chunks/chunk-4XNJX6B3.js +0 -1
- package/dist/dashboard-web/chunks/chunk-5THX4DI2.js +0 -66
- package/dist/dashboard-web/chunks/chunk-7SZRL6FY.js +0 -1
- package/dist/dashboard-web/chunks/chunk-AGCPF6MT.js +0 -1
- package/dist/dashboard-web/chunks/chunk-FXW3V4JR.js +0 -1
- package/dist/dashboard-web/chunks/chunk-SYC6D5XU.js +0 -1
- package/dist/dashboard-web/chunks/connectors-page-VOISXXII.js +0 -1
- package/dist/dashboard-web/chunks/feedback-page-RN6HRZMS.js +0 -1
- package/dist/dashboard-web/chunks/groups-page-UQT6DCB7.js +0 -8
- package/dist/dashboard-web/chunks/monitoring-page-NKEKVQAP.js +0 -2
- package/dist/dashboard-web/chunks/office-page-GVJF73XW.js +0 -1
- package/dist/dashboard-web/chunks/overview-page-KHLV6VTI.js +0 -1
- package/dist/dashboard-web/chunks/plugin-page-J3OF5IY4.js +0 -1
- package/dist/dashboard-web/chunks/roles-page-WQIRSOLZ.js +0 -1
- package/dist/dashboard-web/chunks/schedules-page-J2DEY2X6.js +0 -1
- package/dist/dashboard-web/chunks/sessions-page-7BQFEA5P.js +0 -8
- package/dist/dashboard-web/chunks/settings-page-BJEIOFJH.js +0 -2
- package/dist/dashboard-web/chunks/skills-page-HGIFR3IU.js +0 -4
- package/dist/dashboard-web/chunks/team-federation-page-MR5ZPL4B.js +0 -1
- package/dist/dashboard-web/chunks/v3-page-GBR5JWPE.js +0 -94
- package/dist/dashboard-web/chunks/whiteboards-page-QAZM4BN3.js +0 -1
- package/dist/services/vc-meeting-consumer-profile-store.d.ts +0 -39
- package/dist/services/vc-meeting-consumer-profile-store.d.ts.map +0 -1
- package/dist/services/vc-meeting-consumer-profile-store.js +0 -191
- package/dist/services/vc-meeting-consumer-profile-store.js.map +0 -1
package/dist/core/worker-pool.js
CHANGED
|
@@ -36,6 +36,8 @@ import { hasUnprovenContainment } from './mojo-containment.js';
|
|
|
36
36
|
import { MOJO_EXPLICIT_CLOSE_RESULT_TIMEOUT_MS } from '../adapters/backend/mojo-budgets.js';
|
|
37
37
|
import { buildEffectiveMojoConfig, diffMojoSessionIdentity, MOJO_IDENTITY_KEYS, pickMojoLivePatch, isMojoRemoteGone, mojoUnprovableEnvKeys, pickMojoSessionIdentity, } from '../adapters/backend/mojo-types.js';
|
|
38
38
|
import { sanitizePerBotEnv } from './per-bot-env.js';
|
|
39
|
+
import { normalizeExistingAppServerEndpoint } from './existing-app-server.js';
|
|
40
|
+
import { isExistingAppServerSharedAdoptPersistedSession, isSharedAdoptPersistedSession, isSharedAdoptSession, } from './shared-adopt.js';
|
|
39
41
|
import { closeResidualClause } from './close-residual.js';
|
|
40
42
|
import { logger } from '../utils/logger.js';
|
|
41
43
|
import { createCliAdapterSync } from '../adapters/cli/registry.js';
|
|
@@ -308,7 +310,7 @@ import { hasProtectedSessionMutationOwnership } from './session-mutation-guard.j
|
|
|
308
310
|
import { DONE_REACTION_EMOJI_TYPE } from './pending-response.js';
|
|
309
311
|
import { buildTerminalUrl } from './terminal-url.js';
|
|
310
312
|
import { prependBotmuxBin, resolveBotmuxWrapperBinDir } from './botmux-wrapper.js';
|
|
311
|
-
import { usageLimitStateKey } from '../utils/cli-usage-limit.js';
|
|
313
|
+
import { usageLimitStateKey, isActiveWorkRuntimeStatus } from '../utils/cli-usage-limit.js';
|
|
312
314
|
import { evaluateVcMeetingManagedSend, resolveVcMeetingImTurnOrigin, } from '../services/vc-meeting-send-policy.js';
|
|
313
315
|
import { finishVcMeetingImReply, prepareVcMeetingDeliveryReply, prepareVcMeetingImReply, } from '../services/vc-meeting-im-reply.js';
|
|
314
316
|
import { neutralizeLarkAtTags } from '../services/send-policy.js';
|
|
@@ -327,6 +329,7 @@ import { recordDispatchInputCommit } from './dispatch.js';
|
|
|
327
329
|
import { sendWorkerIpc } from './worker-ipc.js';
|
|
328
330
|
import { cleanupExplicitSessionBacking } from './explicit-session-backing-cleanup.js';
|
|
329
331
|
import { REMOTE_ADMISSION_RESTORE_TIMEOUT_MS } from './shutdown-budgets.js';
|
|
332
|
+
import { MAX_STARTUP_AUTO_RETRIES, isTransientStartupFailure, startupAutoRetryDelayMs, } from './worker-startup-retry.js';
|
|
330
333
|
import { managedOriginCapabilityPath, replaceManagedOriginCapabilityFile, } from './managed-origin-capability.js';
|
|
331
334
|
const __filename = fileURLToPath(import.meta.url);
|
|
332
335
|
const __dirname = dirname(__filename);
|
|
@@ -570,7 +573,7 @@ function scheduleLocalCliOpenReadinessPatch(ds) {
|
|
|
570
573
|
const botCfg = getBot(ds.larkAppId).config;
|
|
571
574
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
572
575
|
const status = ds.usageLimit ? 'limited' : (ds.lastScreenStatus ?? 'starting');
|
|
573
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey,
|
|
576
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isSharedAdoptSession(ds), false, localeForBot(ds.larkAppId), status === 'limited' ? ds.usageLimit : undefined, writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId), sessionRuntimeDisplayName(ds, botCfg), codexServiceTierBadge(effectiveCliId, ds.codexServiceTier));
|
|
574
577
|
scheduleCardPatch(ds, cardJson);
|
|
575
578
|
}
|
|
576
579
|
function flushPendingLocalCliOpenReadinessPatch(ds) {
|
|
@@ -582,7 +585,7 @@ function flushPendingLocalCliOpenReadinessPatch(ds) {
|
|
|
582
585
|
/** PATCH the live card when the executor reports a different active runtime.
|
|
583
586
|
* Runtime identity stays attached to the streaming usage line. */
|
|
584
587
|
function scheduleActiveRuntimePatch(ds) {
|
|
585
|
-
if (
|
|
588
|
+
if (streamingCardDisabled(ds) || ds.suppressRecoveryCard) {
|
|
586
589
|
ds.pendingActiveRuntimeCardRefresh = undefined;
|
|
587
590
|
return;
|
|
588
591
|
}
|
|
@@ -602,7 +605,7 @@ function scheduleActiveRuntimePatch(ds) {
|
|
|
602
605
|
const botCfg = getBot(ds.larkAppId).config;
|
|
603
606
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
604
607
|
const status = ds.usageLimit ? 'limited' : (ds.lastScreenStatus ?? 'starting');
|
|
605
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey,
|
|
608
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isSharedAdoptSession(ds), false, localeForBot(ds.larkAppId), status === 'limited' ? ds.usageLimit : undefined, writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId), sessionRuntimeDisplayName(ds, botCfg), codexServiceTierBadge(effectiveCliId, ds.codexServiceTier));
|
|
606
609
|
scheduleCardPatch(ds, cardJson);
|
|
607
610
|
}
|
|
608
611
|
function flushPendingActiveRuntimePatch(ds) {
|
|
@@ -613,7 +616,7 @@ function flushPendingActiveRuntimePatch(ds) {
|
|
|
613
616
|
}
|
|
614
617
|
/** PATCH a live card when rollout settings change, even if the PTY is static. */
|
|
615
618
|
function scheduleCodexServiceTierPatch(ds) {
|
|
616
|
-
if (
|
|
619
|
+
if (streamingCardDisabled(ds) || ds.suppressRecoveryCard) {
|
|
617
620
|
ds.pendingCodexTierCardRefresh = undefined;
|
|
618
621
|
return;
|
|
619
622
|
}
|
|
@@ -633,7 +636,7 @@ function scheduleCodexServiceTierPatch(ds) {
|
|
|
633
636
|
const botCfg = getBot(ds.larkAppId).config;
|
|
634
637
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
635
638
|
const status = ds.usageLimit ? 'limited' : (ds.lastScreenStatus ?? 'starting');
|
|
636
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), buildTerminalUrl(ds), ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId), ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey,
|
|
639
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), buildTerminalUrl(ds), ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId), ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isSharedAdoptSession(ds), false, localeForBot(ds.larkAppId), status === 'limited' ? ds.usageLimit : undefined, writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId), sessionRuntimeDisplayName(ds, botCfg), codexServiceTierBadge(effectiveCliId, ds.codexServiceTier));
|
|
637
640
|
scheduleCardPatch(ds, cardJson);
|
|
638
641
|
}
|
|
639
642
|
/** How often the live streaming card is re-PATCHed with fresh usage while a
|
|
@@ -692,7 +695,7 @@ export function refreshStreamingCardUsage(ds) {
|
|
|
692
695
|
// run for a port=0 backend (ZMX reports ready without a Web Terminal), a raw
|
|
693
696
|
// URL would render a fake `:undefined`/backend-less link. Mirror every other
|
|
694
697
|
// card path — empty string when there is no real terminal.
|
|
695
|
-
readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', ds.lastScreenStatus ?? 'working', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey,
|
|
698
|
+
readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', ds.lastScreenStatus ?? 'working', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isSharedAdoptSession(ds), false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }),
|
|
696
699
|
// fresh:true — the whole point of the tick is to break the 15s throttle so
|
|
697
700
|
// the total/turn usage actually climbs on-screen every interval.
|
|
698
701
|
getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId, { fresh: true }), sessionRuntimeDisplayName(ds, botCfg),
|
|
@@ -764,7 +767,7 @@ export function scheduleRiffAccessUrlPatch(ds) {
|
|
|
764
767
|
const botCfg = getBot(ds.larkAppId).config;
|
|
765
768
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
766
769
|
const status = ds.usageLimit ? 'limited' : (ds.lastScreenStatus ?? 'starting');
|
|
767
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), buildTerminalUrl(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey,
|
|
770
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), buildTerminalUrl(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isSharedAdoptSession(ds), false, localeForBot(ds.larkAppId), status === 'limited' ? ds.usageLimit : undefined, writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId), sessionRuntimeDisplayName(ds, botCfg), codexServiceTierBadge(effectiveCliId, ds.codexServiceTier));
|
|
768
771
|
scheduleCardPatch(ds, cardJson);
|
|
769
772
|
}
|
|
770
773
|
function flushPendingRiffUrlPatch(ds) {
|
|
@@ -786,9 +789,7 @@ function sessionCliId(ds, botCfg) {
|
|
|
786
789
|
function ordinaryTurnRecoveryEligible(ds, botCfg = getBot(ds.larkAppId).config) {
|
|
787
790
|
return sessionCliId(ds, botCfg) === 'claude-code'
|
|
788
791
|
&& ds.session.status === 'active'
|
|
789
|
-
&& !ds
|
|
790
|
-
&& !ds.session.adoptedFrom
|
|
791
|
-
&& ds.initConfig?.adoptMode !== true
|
|
792
|
+
&& !isSharedAdoptSession(ds)
|
|
792
793
|
&& !ds.session.vcMeetingReceiver
|
|
793
794
|
&& larkTransportEnabled({ chatId: ds.chatId, apiOnly: botCfg.apiOnly });
|
|
794
795
|
}
|
|
@@ -1267,11 +1268,6 @@ export function cardUsageLimit(ds) {
|
|
|
1267
1268
|
return ds.lastScreenStatus === 'limited' ? ds.usageLimit : undefined;
|
|
1268
1269
|
}
|
|
1269
1270
|
function scheduleUsageLimitCardPatch(ds) {
|
|
1270
|
-
// Dedicated VC receivers keep limit state for dashboard/audit only. A timer
|
|
1271
|
-
// must never revive or mutate an old/manual Lark card after the synchronous
|
|
1272
|
-
// screen_update path has already suppressed auxiliary UI.
|
|
1273
|
-
if (ds.session.vcMeetingReceiver)
|
|
1274
|
-
return;
|
|
1275
1271
|
if (ds.lastScreenStatus !== 'limited')
|
|
1276
1272
|
return;
|
|
1277
1273
|
if (!ds.streamCardId || ds.streamCardId === CARD_POSTING_SENTINEL || !workerHasInitialized(ds))
|
|
@@ -1280,7 +1276,7 @@ function scheduleUsageLimitCardPatch(ds) {
|
|
|
1280
1276
|
const effectiveCliId = sessionCliId(ds, bot.config);
|
|
1281
1277
|
const readUrl = readableTerminalUrlFor(ds);
|
|
1282
1278
|
const turnTitle = ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, bot.config);
|
|
1283
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'limited', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey,
|
|
1279
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'limited', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isSharedAdoptSession(ds), false, localeForBot(ds.larkAppId), ds.usageLimit, writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId), sessionRuntimeDisplayName(ds, bot.config), codexServiceTierBadge(effectiveCliId, ds.codexServiceTier));
|
|
1284
1280
|
scheduleCardPatch(ds, cardJson);
|
|
1285
1281
|
}
|
|
1286
1282
|
function armUsageLimitRetryTimer(ds, previous) {
|
|
@@ -1328,6 +1324,31 @@ function updateUsageLimitState(ds, usageLimit) {
|
|
|
1328
1324
|
persistStreamCardState(ds);
|
|
1329
1325
|
armUsageLimitRetryTimer(ds, previous);
|
|
1330
1326
|
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Resolve the daemon-side screen status in the presence of a usage limit.
|
|
1329
|
+
*
|
|
1330
|
+
* Limit state is intentionally sticky across ticks that carry no fresh verdict
|
|
1331
|
+
* (the worker re-emits only while the limit text stays on screen; screenshot
|
|
1332
|
+
* ticks arrive every 10s), so a recorded limit survives ordinary idle frames
|
|
1333
|
+
* and the cooldown card stays up until the user retries. But it must
|
|
1334
|
+
* self-heal on evidence of active work: a blocked CLI does not produce
|
|
1335
|
+
* `working`/`analyzing` frames, so a limit that survives such a frame was a
|
|
1336
|
+
* false positive (a flicker-frame screen scan, or a structured emit the CLI
|
|
1337
|
+
* recovered from). Without this the card keeps showing 「限额已达」 for the
|
|
1338
|
+
* rest of the turn even though the CLI is demonstrably still running — the
|
|
1339
|
+
* reported 误报. The worker-side status gate (detectScreenUsageLimit) prevents
|
|
1340
|
+
* the detection while active; this is the daemon-side net for limits recorded
|
|
1341
|
+
* during an idle flicker before work resumed.
|
|
1342
|
+
*/
|
|
1343
|
+
function resolveUsageAwareScreenStatus(ds, workerStatus, freshUsageLimit) {
|
|
1344
|
+
if (freshUsageLimit)
|
|
1345
|
+
return 'limited';
|
|
1346
|
+
if (ds.usageLimit && isActiveWorkRuntimeStatus(workerStatus)) {
|
|
1347
|
+
clearUsageLimitState(ds);
|
|
1348
|
+
return workerStatus;
|
|
1349
|
+
}
|
|
1350
|
+
return ds.usageLimit ? 'limited' : workerStatus;
|
|
1351
|
+
}
|
|
1331
1352
|
const WORKER_ERROR_MARKER = '[botmux-worker-error]';
|
|
1332
1353
|
function logWorkerStderr(t, line) {
|
|
1333
1354
|
if (!line)
|
|
@@ -1475,7 +1496,7 @@ function reconcilePostedStartingCard(ds, turnId, revisionAtPost) {
|
|
|
1475
1496
|
const botCfg = getBot(ds.larkAppId).config;
|
|
1476
1497
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
1477
1498
|
const status = ds.usageLimit ? 'limited' : (ds.lastScreenStatus ?? turnStartingCardStatus(ds, effectiveCliId));
|
|
1478
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey,
|
|
1499
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isSharedAdoptSession(ds), false, localeForBot(ds.larkAppId), status === 'limited' ? ds.usageLimit : undefined, writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId, { fresh: status === 'idle' }), sessionRuntimeDisplayName(ds, botCfg), codexServiceTierBadge(effectiveCliId, ds.codexServiceTier));
|
|
1479
1500
|
scheduleCardPatch(ds, cardJson, turnId);
|
|
1480
1501
|
}
|
|
1481
1502
|
/**
|
|
@@ -1495,7 +1516,7 @@ export async function postTurnStartingCard(ds, sessionReply, turnId) {
|
|
|
1495
1516
|
return false;
|
|
1496
1517
|
if (ds.streamCardId === CARD_POSTING_SENTINEL)
|
|
1497
1518
|
return false;
|
|
1498
|
-
if (
|
|
1519
|
+
if (streamingCardDisabled(ds, turnId))
|
|
1499
1520
|
return false;
|
|
1500
1521
|
if (!workerHasInitialized(ds))
|
|
1501
1522
|
return false;
|
|
@@ -1519,7 +1540,7 @@ export async function postTurnStartingCard(ds, sessionReply, turnId) {
|
|
|
1519
1540
|
const nonce = randomBytes(4).toString('hex');
|
|
1520
1541
|
const status = turnStartingCardStatus(ds, effectiveCliId);
|
|
1521
1542
|
const statusRevisionAtPost = ds.streamCardStatusRevision ?? 0;
|
|
1522
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), '', status, effectiveCliId, ds.displayMode ?? 'hidden', nonce, undefined,
|
|
1543
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), '', status, effectiveCliId, ds.displayMode ?? 'hidden', nonce, undefined, isSharedAdoptSession(ds), false, localeForBot(ds.larkAppId), ds.usageLimit, writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId), sessionRuntimeDisplayName(ds, botCfg), codexServiceTierBadge(effectiveCliId, ds.codexServiceTier));
|
|
1523
1544
|
ds.streamCardNonce = nonce;
|
|
1524
1545
|
ds.streamCardId = CARD_POSTING_SENTINEL;
|
|
1525
1546
|
const ownsPostIdentity = () => ds.session === sessionAtPost
|
|
@@ -1600,10 +1621,6 @@ export async function postTurnStartingCard(ds, sessionReply, turnId) {
|
|
|
1600
1621
|
* that so the card keeps live-patching afterwards even when the bot opted out.
|
|
1601
1622
|
*/
|
|
1602
1623
|
export async function postFreshStreamingCard(ds, sessionReply) {
|
|
1603
|
-
// Receiver terminals can contain meeting-derived private context. Never
|
|
1604
|
-
// publish one into the listener chat as a streaming-card side channel.
|
|
1605
|
-
if (ds.session.vcMeetingReceiver)
|
|
1606
|
-
return false;
|
|
1607
1624
|
if (isDocNativeSession(ds))
|
|
1608
1625
|
return false;
|
|
1609
1626
|
if (!workerHasInitialized(ds))
|
|
@@ -1624,7 +1641,7 @@ export async function postFreshStreamingCard(ds, sessionReply) {
|
|
|
1624
1641
|
const prevPending = ds.streamCardPending;
|
|
1625
1642
|
const cardReplyTarget = captureStreamingCardReplyTarget(ds);
|
|
1626
1643
|
ds.streamCardNonce = randomBytes(4).toString('hex');
|
|
1627
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, title, ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey,
|
|
1644
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, title, ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isSharedAdoptSession(ds), false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId), sessionRuntimeDisplayName(ds, botCfg), codexServiceTierBadge(effectiveCliId, ds.codexServiceTier));
|
|
1628
1645
|
ds.streamCardId = CARD_POSTING_SENTINEL;
|
|
1629
1646
|
try {
|
|
1630
1647
|
ds.streamCardId = await sessionReply(sessionAnchorId(ds), cardJson, 'interactive', ds.larkAppId, cardReplyTarget.turnId);
|
|
@@ -1773,7 +1790,7 @@ export function buildWritableTerminalCard(ds) {
|
|
|
1773
1790
|
if (ds.riffAccessUrl) {
|
|
1774
1791
|
const botCfg = getBot(ds.larkAppId).config;
|
|
1775
1792
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
1776
|
-
return buildSessionCard(ds.session.sessionId, sessionAnchorId(ds), ds.riffAccessUrl, ds.session.title || sessionCliDisplayName(ds, botCfg), effectiveCliId, true,
|
|
1793
|
+
return buildSessionCard(ds.session.sessionId, sessionAnchorId(ds), ds.riffAccessUrl, ds.session.title || sessionCliDisplayName(ds, botCfg), effectiveCliId, true, isSharedAdoptSession(ds), localeForBot(ds.larkAppId), false, sessionRuntimeDisplayName(ds, botCfg));
|
|
1777
1794
|
}
|
|
1778
1795
|
const port = ds.workerPort ?? ds.session.webPort;
|
|
1779
1796
|
if (!port || !ds.workerToken)
|
|
@@ -1781,7 +1798,7 @@ export function buildWritableTerminalCard(ds) {
|
|
|
1781
1798
|
const botCfg = getBot(ds.larkAppId).config;
|
|
1782
1799
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
1783
1800
|
return buildSessionCard(ds.session.sessionId, sessionAnchorId(ds), buildTerminalUrl(ds, { write: true }), ds.session.title || sessionCliDisplayName(ds, botCfg), effectiveCliId, true, // showManageButtons — write-link card includes restart & close
|
|
1784
|
-
|
|
1801
|
+
isSharedAdoptSession(ds), // shared adopt — disconnect, never close the source conversation
|
|
1785
1802
|
localeForBot(ds.larkAppId), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), sessionRuntimeDisplayName(ds, botCfg));
|
|
1786
1803
|
}
|
|
1787
1804
|
/**
|
|
@@ -1852,7 +1869,7 @@ function buildSubstituteControlCard(ds) {
|
|
|
1852
1869
|
const botCfg = getBot(ds.larkAppId).config;
|
|
1853
1870
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
1854
1871
|
return buildSessionCard(ds.session.sessionId, sessionAnchorId(ds), '', // Manage-only: this backend intentionally has no Web Terminal URL.
|
|
1855
|
-
ds.session.title || sessionCliDisplayName(ds, botCfg), effectiveCliId, true,
|
|
1872
|
+
ds.session.title || sessionCliDisplayName(ds, botCfg), effectiveCliId, true, isSharedAdoptSession(ds), localeForBot(ds.larkAppId), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), sessionRuntimeDisplayName(ds, botCfg));
|
|
1856
1873
|
}
|
|
1857
1874
|
/**
|
|
1858
1875
|
* DM a control card to the bot's owner(s) for a substitute-mode session.
|
|
@@ -2052,8 +2069,14 @@ export const restartCounts = new Map();
|
|
|
2052
2069
|
/** Track which CLI adapters have had skills installed this daemon lifecycle */
|
|
2053
2070
|
const skillsInstalledCliIds = new Set();
|
|
2054
2071
|
/**
|
|
2055
|
-
* Ensure built-in skills are installed for a given CLI.
|
|
2056
|
-
*
|
|
2072
|
+
* Ensure built-in skills are installed for a given CLI. Synchronous and
|
|
2073
|
+
* idempotent. Two cadences:
|
|
2074
|
+
* - Skill FILES (whiteboard / workflow family / built-in skills) are
|
|
2075
|
+
* re-evaluated on EVERY spawn, before the `skillsInstalledCliIds` gate, so
|
|
2076
|
+
* runtime switches (whiteboard, workflow) and user customization (skill body
|
|
2077
|
+
* override / disable) take effect on the next session without a restart.
|
|
2078
|
+
* - Hook install + botmux-ask fallback do not vary at runtime, so they run
|
|
2079
|
+
* once per CLI per daemon lifecycle (behind the gate).
|
|
2057
2080
|
*/
|
|
2058
2081
|
export function ensureCliSkills(cliId, cliPathOverride) {
|
|
2059
2082
|
const adapter = createCliAdapterSync(cliId, cliPathOverride);
|
|
@@ -2074,9 +2097,15 @@ export function ensureCliSkills(cliId, cliPathOverride) {
|
|
|
2074
2097
|
// Workflow skill 家族同理:跟随机器级 workflow 开关,每次 spawn 重新评估,
|
|
2075
2098
|
// 关闭时删除已装的三个 skill,下一个会话即生效。
|
|
2076
2099
|
ensureWorkflowSkills(cliId, pluginSkillsDir, isWorkflowFeatureEnabled());
|
|
2100
|
+
// 内置 skill 文件跟随用户自定义(正文覆盖 / 停用),必须每次 spawn 重新落盘
|
|
2101
|
+
// ——与上面白板 / workflow 同样放在 once-cache 之前,否则改了 skill 正文或
|
|
2102
|
+
// 停用某 skill 后,claude 家族(--plugin-dir)的新会话仍读旧文件、要等 daemon
|
|
2103
|
+
// 重启才生效,和自定义页「下一个会话即生效」不符。ensureSkills 幂等(内容相同
|
|
2104
|
+
// 则跳过),每次 spawn 的代价只是几个小文件的读比对。
|
|
2105
|
+
ensurePluginSkills(cliId, adapter.pluginDir);
|
|
2077
2106
|
if (skillsInstalledCliIds.has(cliId))
|
|
2078
2107
|
return;
|
|
2079
|
-
|
|
2108
|
+
// Hook 接管与 botmux-ask 兜底 skill 不随自定义变化 → 每个 daemon 生命周期装一次即可。
|
|
2080
2109
|
if (adapter.hookInstall) {
|
|
2081
2110
|
try {
|
|
2082
2111
|
installHook(cliId, adapter.hookInstall, hookCommandFor(cliId));
|
|
@@ -2118,12 +2147,15 @@ export function ensureCliSkills(cliId, cliPathOverride) {
|
|
|
2118
2147
|
ensureWorkflowSkills(cliId, skillsDir, globalInstall && isWorkflowFeatureEnabled());
|
|
2119
2148
|
if (!globalInstall && skillsDir)
|
|
2120
2149
|
removeGlobalBotmuxSkills(skillsDir);
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
//
|
|
2150
|
+
// 内置 skill 文件跟随用户自定义(正文覆盖 / 停用),必须每次 spawn 重新落盘
|
|
2151
|
+
// ——同白板 / workflow 放在 once-cache 之前。global 模式落全局盘;幂等(内容相同
|
|
2152
|
+
// 则跳过),停用的 skill 目录会被 ensureSkills 内部清掉,下一个会话即生效。
|
|
2124
2153
|
if (globalInstall)
|
|
2125
2154
|
ensureSkills(cliId, skillsDir);
|
|
2126
|
-
|
|
2155
|
+
if (skillsInstalledCliIds.has(cliId))
|
|
2156
|
+
return;
|
|
2157
|
+
// 以下都不随自定义变化 → 每个 daemon 生命周期装一次即可。
|
|
2158
|
+
// askUserQuestion 接管策略与 skill 文件无关:hook 该装还得装(Codex 无 hook,
|
|
2127
2159
|
// 靠 botmux-ask skill / catalog 兜底)。
|
|
2128
2160
|
if (adapter.hookInstall) {
|
|
2129
2161
|
try {
|
|
@@ -2518,7 +2550,7 @@ export async function detachWorkerForTransfer(ds, opts = {}) {
|
|
|
2518
2550
|
* adopt-skip decision is unit-testable without spawning a worker.
|
|
2519
2551
|
*/
|
|
2520
2552
|
export function shouldDestroyPaneBeforeRestart(ds) {
|
|
2521
|
-
return !ds
|
|
2553
|
+
return !isSharedAdoptSession(ds);
|
|
2522
2554
|
}
|
|
2523
2555
|
/**
|
|
2524
2556
|
* Destroy a still-alive persistent backing pane (tmux/herdr/zellij/zmx) before a
|
|
@@ -2818,7 +2850,12 @@ function resolveMojoCancelLaunch(ds, remoteId, label) {
|
|
|
2818
2850
|
};
|
|
2819
2851
|
}
|
|
2820
2852
|
function destroyOrphanedBackingSession(ds, opts = {}) {
|
|
2821
|
-
|
|
2853
|
+
// Traditional `/adopt` observes a user-owned tmux/zellij/Herdr target and
|
|
2854
|
+
// must never destroy it. An App Server shared adopt is different: its
|
|
2855
|
+
// persistent bmx-* pane is BotMux's own `codex --remote` client, so reclaim
|
|
2856
|
+
// that pane while leaving the external App Server untouched.
|
|
2857
|
+
if (!isExistingAppServerSharedAdoptPersistedSession(ds.session)
|
|
2858
|
+
&& isSharedAdoptSession(ds))
|
|
2822
2859
|
return;
|
|
2823
2860
|
reclaimParkedCrashDiagnostic(ds);
|
|
2824
2861
|
// Riff cancellation is asynchronous and cannot be made safe from this
|
|
@@ -3464,7 +3501,8 @@ async function prepareRiffExplicitClose(ds, stored) {
|
|
|
3464
3501
|
}
|
|
3465
3502
|
if (backendType !== 'riff')
|
|
3466
3503
|
return { ok: true };
|
|
3467
|
-
if (ds
|
|
3504
|
+
if ((ds && isSharedAdoptSession(ds))
|
|
3505
|
+
|| isSharedAdoptPersistedSession(session))
|
|
3468
3506
|
return { ok: true };
|
|
3469
3507
|
if (ds?.worker && !ds.worker.killed) {
|
|
3470
3508
|
if (!stored || stored.status !== 'active') {
|
|
@@ -3562,7 +3600,8 @@ async function prepareMojoExplicitClose(ds, stored) {
|
|
|
3562
3600
|
const session = ds?.session ?? stored;
|
|
3563
3601
|
if (!session)
|
|
3564
3602
|
return { ok: true };
|
|
3565
|
-
if (ds
|
|
3603
|
+
if ((ds && isSharedAdoptSession(ds))
|
|
3604
|
+
|| isSharedAdoptPersistedSession(session))
|
|
3566
3605
|
return { ok: true };
|
|
3567
3606
|
// A row whose lineage was PARKED (restore-time quarantine moves the id here and
|
|
3568
3607
|
// clears the active slot) still has a remote session running. It is reported as
|
|
@@ -4385,9 +4424,8 @@ function teardownAuthoritativePersistentBackingBeforeCloseImpl(target, closeWins
|
|
|
4385
4424
|
}
|
|
4386
4425
|
const backendType = ds ? getSessionPersistentBackendType(ds) : session.backendType;
|
|
4387
4426
|
if (backendType !== 'zmx'
|
|
4388
|
-
|| ds
|
|
4389
|
-
||
|
|
4390
|
-
|| session.adoptedFrom
|
|
4427
|
+
|| (ds && isSharedAdoptSession(ds))
|
|
4428
|
+
|| isSharedAdoptPersistedSession(session)
|
|
4391
4429
|
|| session.queued
|
|
4392
4430
|
|| session.status === 'closed')
|
|
4393
4431
|
return;
|
|
@@ -4396,6 +4434,16 @@ function teardownAuthoritativePersistentBackingBeforeCloseImpl(target, closeWins
|
|
|
4396
4434
|
export function teardownAuthoritativePersistentBackingBeforeClose(target) {
|
|
4397
4435
|
teardownAuthoritativePersistentBackingBeforeCloseImpl(target, false);
|
|
4398
4436
|
}
|
|
4437
|
+
/** Render the live streaming-card JSON for `ds` (🖥️ header + usage line +
|
|
4438
|
+
* 显示输出/终端/关闭会话 buttons). Factored so callers outside the normal
|
|
4439
|
+
* screen-update flow — notably the Lark 恢复会话 button — can restore the SAME
|
|
4440
|
+
* card the session had while running, instead of a stripped-down variant.
|
|
4441
|
+
* `status` defaults to the session's last known screen status. */
|
|
4442
|
+
export function buildStreamingCardJson(ds, status) {
|
|
4443
|
+
const botCfg = getBot(ds.larkAppId).config;
|
|
4444
|
+
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
4445
|
+
return buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readableTerminalUrlFor(ds), ds.currentTurnTitle || ds.session.title || sessionCliDisplayName(ds, botCfg), ds.lastScreenContent ?? '', status ?? ds.lastScreenStatus ?? 'starting', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isSharedAdoptSession(ds), false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId), sessionRuntimeDisplayName(ds, botCfg), codexServiceTierBadge(effectiveCliId, ds.codexServiceTier));
|
|
4446
|
+
}
|
|
4399
4447
|
/**
|
|
4400
4448
|
* Close from a BACKGROUND path — one with no user surface to report to
|
|
4401
4449
|
* (trigger-session cleanup, deferred-schedule settlement, sweeps).
|
|
@@ -4425,14 +4473,22 @@ export async function closeSessionForBackgroundCleanup(sessionId, context) {
|
|
|
4425
4473
|
}
|
|
4426
4474
|
return result;
|
|
4427
4475
|
}
|
|
4428
|
-
export async function closeSession(sessionId) {
|
|
4476
|
+
export async function closeSession(sessionId, opts) {
|
|
4477
|
+
// `awaitWorkerExit` (default true): whether to block on the worker process
|
|
4478
|
+
// actually exiting before returning. A busy CLI wedges in node-pty teardown
|
|
4479
|
+
// and only dies at the ~7s SIGKILL backstop, so callers behind a tight ACK
|
|
4480
|
+
// window (the Lark card "关闭会话" button, whose callback must ACK inside ~3s
|
|
4481
|
+
// or the client surfaces the "code: 300000" toast) pass false: the logical
|
|
4482
|
+
// close below is fully synchronous, and killWorker already armed the kill
|
|
4483
|
+
// backstop, so the worker WILL die in the background — the caller need not
|
|
4484
|
+
// wait for it. Bridge-marker cleanup still runs, deferred behind the fence.
|
|
4485
|
+
const awaitWorkerExit = opts?.awaitWorkerExit ?? true;
|
|
4429
4486
|
const ds = findActiveBySessionId(sessionId);
|
|
4430
4487
|
const stored = sessionStore.getOwnedSession(sessionId);
|
|
4431
4488
|
const closeFrozenBackendType = ds?.initConfig?.backendType
|
|
4432
4489
|
?? ds?.session.backendType ?? stored?.backendType;
|
|
4433
|
-
const isOwnedClose = !ds
|
|
4434
|
-
&& !
|
|
4435
|
-
&& !stored?.adoptedFrom;
|
|
4490
|
+
const isOwnedClose = !(ds && isSharedAdoptSession(ds))
|
|
4491
|
+
&& !(stored && isSharedAdoptPersistedSession(stored));
|
|
4436
4492
|
const isOwnedRiffClose = isOwnedClose && closeFrozenBackendType === 'riff';
|
|
4437
4493
|
const isOwnedMojoClose = isOwnedClose && closeFrozenBackendType === 'mojo';
|
|
4438
4494
|
const closeJournal = ds?.session.mojoCloseJournal ?? stored?.mojoCloseJournal;
|
|
@@ -4625,8 +4681,16 @@ export async function closeSession(sessionId) {
|
|
|
4625
4681
|
publishClosedSessionPatch(sessionId, after?.closedAt ? Date.parse(after.closedAt) : undefined, { tokenUsage: after ? composeRowFromClosed(after).tokenUsage : null });
|
|
4626
4682
|
}
|
|
4627
4683
|
if (wasOpen && hadLiveWorker) {
|
|
4628
|
-
|
|
4629
|
-
|
|
4684
|
+
if (awaitWorkerExit) {
|
|
4685
|
+
await closeFenceFor(sessionId, closeWorkerGeneration);
|
|
4686
|
+
sessionStore.cleanupSessionBridgeSendMarkersNow(sessionId);
|
|
4687
|
+
}
|
|
4688
|
+
else {
|
|
4689
|
+
// Don't block the caller on the worker exiting (killWorker already armed
|
|
4690
|
+
// the SIGKILL backstop). Defer bridge-marker cleanup behind the same
|
|
4691
|
+
// fence so a mid-flight send is still credited until the worker ACKs/exits.
|
|
4692
|
+
sessionStore.cleanupSessionBridgeSendMarkers(sessionId);
|
|
4693
|
+
}
|
|
4630
4694
|
}
|
|
4631
4695
|
// All authoritative map/status/store/event state above transitions
|
|
4632
4696
|
// synchronously, before the first await. Lark reaction/unsubscribe cleanup is
|
|
@@ -4824,10 +4888,11 @@ function removeInactiveRegistration(map, key, ds) {
|
|
|
4824
4888
|
}
|
|
4825
4889
|
function persistedActiveSessionKey(session, fallbackLarkAppId) {
|
|
4826
4890
|
const larkAppId = session.larkAppId ?? fallbackLarkAppId;
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4891
|
+
// Plan B: a VC meeting agent is an ordinary chat-scope session keyed by its
|
|
4892
|
+
// normal chat anchor — this must stay in lockstep with activeSessionKey()
|
|
4893
|
+
// (core/types.ts) so a restored meeting row re-registers at the SAME slot the
|
|
4894
|
+
// live path uses. The vcMeetingReceiver marker is delivery metadata only.
|
|
4895
|
+
return sessionKey(storedSessionAnchorId(session), larkAppId);
|
|
4831
4896
|
}
|
|
4832
4897
|
/**
|
|
4833
4898
|
* Return an active persisted row that deliberately reserves this routing key
|
|
@@ -5053,7 +5118,14 @@ function failOrdinaryImDelivery(record, reason) {
|
|
|
5053
5118
|
requireOrdinaryTurnRecoveryAttention(record.ds.session, record.turnId, 'recovery_delivery_failed');
|
|
5054
5119
|
return;
|
|
5055
5120
|
}
|
|
5056
|
-
|
|
5121
|
+
// Plan B: a meeting agent is an ordinary chat-scope session. This tracker only
|
|
5122
|
+
// ever holds ORDINARY IM turns (durable transcript deliveries use the receipt
|
|
5123
|
+
// path, not this map), so a plain user turn's delivery failure must be reported
|
|
5124
|
+
// like any session — otherwise the user's message is silently dropped. Only a
|
|
5125
|
+
// stamped meeting @mention follow-up stays fenced (isMeetingDrivenTurn); a
|
|
5126
|
+
// silent scheduled turn stays suppressed as before. (isMeetingDrivenTurn
|
|
5127
|
+
// supersedes the pre-Plan-B `vcMeetingReceiver` blanket check.)
|
|
5128
|
+
if (isMeetingDrivenTurn(record.ds, record.turnId) || isSilentScheduledTurn(record.ds, record.turnId))
|
|
5057
5129
|
return;
|
|
5058
5130
|
const loc = botLocale(getBot(record.ds.larkAppId).config);
|
|
5059
5131
|
void requireCallbacks().sessionReply(sessionAnchorId(record.ds), tr('worker.input_delivery_failed', { turnId: record.turnId.substring(0, 16) }, loc), 'text', record.ds.larkAppId, record.turnId).catch(err => logger.error(`[${tag(record.ds)}] Failed to report ordinary IM worker delivery failure: `
|
|
@@ -5143,8 +5215,13 @@ function shouldTrackOrdinaryImDelivery(ds, message) {
|
|
|
5143
5215
|
&& (message.turnId.startsWith('om_') || message.turnId.startsWith('bmx-recovery-'))
|
|
5144
5216
|
&& message.dispatchAttempt === undefined
|
|
5145
5217
|
&& (message.type !== 'init' || (!!message.prompt && !message.adoptMode))
|
|
5146
|
-
&& !ds
|
|
5147
|
-
|
|
5218
|
+
&& !isSharedAdoptSession(ds)
|
|
5219
|
+
// Plan B: a meeting agent is an ordinary chat-scope session. A plain user
|
|
5220
|
+
// turn on it IS an ordinary IM delivery (track it for receipt-ACK + retry so
|
|
5221
|
+
// its failure is reported). Only a meeting-driven turn — a stamped @mention
|
|
5222
|
+
// follow-up here, since durable deliveries are already excluded by the
|
|
5223
|
+
// dispatchAttempt===undefined guard above — stays on the receipt/lease path.
|
|
5224
|
+
&& !isMeetingDrivenTurn(ds, message.turnId, message.dispatchAttempt)
|
|
5148
5225
|
&& Number.isSafeInteger(ds.workerGeneration)
|
|
5149
5226
|
&& (ds.workerGeneration ?? 0) > 0
|
|
5150
5227
|
&& ds.session.workerGeneration === ds.workerGeneration;
|
|
@@ -5463,8 +5540,6 @@ targetScope, opts) {
|
|
|
5463
5540
|
runtimeWorkingDir: ds.workingDir,
|
|
5464
5541
|
runtimeAdoptedFrom: ds.adoptedFrom,
|
|
5465
5542
|
});
|
|
5466
|
-
if (ds.session.vcMeetingReceiver)
|
|
5467
|
-
return { ok: false, error: 'vc_receiver_not_relayable' };
|
|
5468
5543
|
if (hasProtectedSessionMutationOwnership(ds)) {
|
|
5469
5544
|
return { ok: false, error: 'codex_app_dispatch_pending' };
|
|
5470
5545
|
}
|
|
@@ -5538,7 +5613,7 @@ targetScope, opts) {
|
|
|
5538
5613
|
// Adopt sessions wrap a CLI process that botmux didn't spawn — the user
|
|
5539
5614
|
// owns it inside their own tmux pane, so moving routing here would be
|
|
5540
5615
|
// surprising and we don't control the tmux session's lifecycle. Refuse.
|
|
5541
|
-
if (ds
|
|
5616
|
+
if (isSharedAdoptSession(ds))
|
|
5542
5617
|
return { ok: false, error: 'adopt_not_relayable' };
|
|
5543
5618
|
// Busy worker: refuse immediately rather than waiting. An idle-wait loop
|
|
5544
5619
|
// (previously 60s) created an asymmetry with the peer-dispatch HTTP
|
|
@@ -5619,7 +5694,6 @@ targetScope, opts) {
|
|
|
5619
5694
|
const persistedTargetConflicts = sessionStore.listSessions().filter(item => item.sessionId !== ds.session.sessionId
|
|
5620
5695
|
&& item.status === 'active'
|
|
5621
5696
|
&& (!item.larkAppId || item.larkAppId === ds.larkAppId)
|
|
5622
|
-
&& !item.vcMeetingReceiver
|
|
5623
5697
|
&& (item.scope === 'chat' ? item.chatId : item.rootMessageId) === targetAnchor
|
|
5624
5698
|
&& !runtimeIds.has(item.sessionId));
|
|
5625
5699
|
if (persistedTargetConflicts.some(item => hasProtectedSessionMutationOwnership(item)
|
|
@@ -5809,6 +5883,12 @@ export function isForkCapableSession(ds) {
|
|
|
5809
5883
|
const cliId = sessionCliId(ds, botCfg);
|
|
5810
5884
|
if (!FORK_CAPABLE_CLI_IDS.has(cliId))
|
|
5811
5885
|
return false;
|
|
5886
|
+
// An external App Server thread is not a local rollout. `codex fork` would
|
|
5887
|
+
// resolve against the terminal client's own CODEX_HOME and could fork an
|
|
5888
|
+
// unrelated/empty local session, so refuse rather than claiming a copy was
|
|
5889
|
+
// made. A future server-side thread/fork primitive can add an explicit path.
|
|
5890
|
+
if (ds.session.existingAppServerEndpoint)
|
|
5891
|
+
return false;
|
|
5812
5892
|
// Codex terminal mode is forkable; Codex under Hybrid RPC input is not (the
|
|
5813
5893
|
// thread is an app-server live session, no local rollout to `codex fork`).
|
|
5814
5894
|
//
|
|
@@ -5856,13 +5936,11 @@ export async function forkSession(sessionId, targetChatId, targetRootMessageId,
|
|
|
5856
5936
|
return { ok: false, error: 'fork_unsupported_backend' };
|
|
5857
5937
|
// ── Front guards (mirror transferSession; a fork needs a clean, complete
|
|
5858
5938
|
// source node exactly as a relay does) ──
|
|
5859
|
-
if (ds.session.vcMeetingReceiver)
|
|
5860
|
-
return { ok: false, error: 'vc_receiver_not_forkable' };
|
|
5861
5939
|
if (ds.pendingRepo)
|
|
5862
5940
|
return { ok: false, error: 'not_started_yet' };
|
|
5863
5941
|
if (!isRelayableRealSession(ds))
|
|
5864
5942
|
return { ok: false, error: 'not_started_yet' };
|
|
5865
|
-
if (ds
|
|
5943
|
+
if (isSharedAdoptSession(ds))
|
|
5866
5944
|
return { ok: false, error: 'adopt_not_forkable' };
|
|
5867
5945
|
if (isSessionLifecycleInFlight(ds))
|
|
5868
5946
|
return { ok: false, error: 'worker_busy' };
|
|
@@ -6720,7 +6798,18 @@ export function mojoLivePatchForSession(ds) {
|
|
|
6720
6798
|
/** Sessions with a quarantine notice send in flight, so it cannot double-post. */
|
|
6721
6799
|
const mojoQuarantineNoticeInFlight = new Set();
|
|
6722
6800
|
/**
|
|
6723
|
-
*
|
|
6801
|
+
* Auxiliary-UI suppression policy for the **mojo quarantine notice** path.
|
|
6802
|
+
*
|
|
6803
|
+
* NOTE (Plan B merge, 2026-08): setupWorkerHandlers' `managedAuxUiSuppressed`
|
|
6804
|
+
* no longer delegates here — it keeps its own inline copy that gates on
|
|
6805
|
+
* `isMeetingDrivenTurn` instead of this function's pre-Plan-B blanket
|
|
6806
|
+
* `ds.session.vcMeetingReceiver` check. That blanket check would re-suppress a
|
|
6807
|
+
* plain user turn on a meeting-agent chat session (the "手动@不回复" regression),
|
|
6808
|
+
* which is exactly why the streaming/aux path had to diverge. This function is
|
|
6809
|
+
* therefore currently the mojo-quarantine caller's policy only; migrating it to
|
|
6810
|
+
* `isMeetingDrivenTurn` (so the two converge again without the regression) is a
|
|
6811
|
+
* tracked follow-up. ordinaryTurnRecoveryEligible (below) carries the same
|
|
6812
|
+
* pre-Plan-B blanket and is on the same follow-up.
|
|
6724
6813
|
*
|
|
6725
6814
|
* Previously setupWorkerHandlers held this as a closure and the quarantine notice
|
|
6726
6815
|
* COPIED three of its four checks — dropping `ordinaryManagedSuppression`, so a
|
|
@@ -7312,6 +7401,32 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
7312
7401
|
const cb = requireCallbacks();
|
|
7313
7402
|
const bot = getBot(ds.larkAppId);
|
|
7314
7403
|
const botCfg = bot.config;
|
|
7404
|
+
if (botCfg.existingAppServer
|
|
7405
|
+
&& botCfg.cliId === 'codex'
|
|
7406
|
+
&& ds.session.existingAppServerEndpoint === undefined) {
|
|
7407
|
+
throw new Error('this BotMux bot attaches only to an existing Codex App conversation; '
|
|
7408
|
+
+ 'use /adopt to select the thread before sending task input');
|
|
7409
|
+
}
|
|
7410
|
+
let existingAppServerEndpoint;
|
|
7411
|
+
if (ds.session.existingAppServerEndpoint !== undefined) {
|
|
7412
|
+
existingAppServerEndpoint = normalizeExistingAppServerEndpoint(ds.session.existingAppServerEndpoint, `session ${ds.session.sessionId} existingAppServerEndpoint`);
|
|
7413
|
+
if (ds.session.cliId !== 'codex') {
|
|
7414
|
+
throw new Error('existing Codex App Server session must use cliId "codex"; '
|
|
7415
|
+
+ 'refusing to launch another CLI against this thread');
|
|
7416
|
+
}
|
|
7417
|
+
if (!ds.session.cliSessionId) {
|
|
7418
|
+
throw new Error('existing Codex App Server session has no selected thread id; '
|
|
7419
|
+
+ 'use /adopt to select a Codex App conversation first');
|
|
7420
|
+
}
|
|
7421
|
+
if (ds.session.sandbox === true || botCfg.readIsolation === true) {
|
|
7422
|
+
throw new Error('existing Codex App Server attachment cannot run under sandbox/readIsolation; '
|
|
7423
|
+
+ 'the external app-server would remain outside that boundary');
|
|
7424
|
+
}
|
|
7425
|
+
// Remote TUI attach always resumes the explicitly selected remote thread.
|
|
7426
|
+
// Never let later cold-resume/restart policy demote it into a new local
|
|
7427
|
+
// Codex session.
|
|
7428
|
+
resume = true;
|
|
7429
|
+
}
|
|
7315
7430
|
// A bare /repo placeholder (and a non-Codex empty group-join setup) owns no
|
|
7316
7431
|
// model turn. Starting its CLI with an empty prompt must not mint a queued
|
|
7317
7432
|
// activation token: the worker has nothing to submit and could never ACK it.
|
|
@@ -7463,7 +7578,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
7463
7578
|
// worker left parked but couldn't clean (hard-killed while parked, daemon
|
|
7464
7579
|
// still alive → next message reforks here). The fresh CLI spawns under the
|
|
7465
7580
|
// real bmx-<sid>; without this, bmx-diag-<sid> + its .ansi file would leak.
|
|
7466
|
-
if (!ds
|
|
7581
|
+
if (!isSharedAdoptSession(ds))
|
|
7467
7582
|
reclaimParkedCrashDiagnostic(ds);
|
|
7468
7583
|
agentCfg = sessionAgentConfig(ds, botCfg);
|
|
7469
7584
|
if (!initTurnId && prompt.length > 0 && agentCfg.cliId === 'codex-app') {
|
|
@@ -7472,7 +7587,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
7472
7587
|
ensureCliEnv(agentCfg.cliId, agentCfg.cliPathOverride);
|
|
7473
7588
|
let nativeSessionTitle;
|
|
7474
7589
|
let nativeSessionTitlePrompt;
|
|
7475
|
-
if (agentCfg.cliId === 'codex' && !ds
|
|
7590
|
+
if (agentCfg.cliId === 'codex' && !isSharedAdoptSession(ds)) {
|
|
7476
7591
|
const isFreshNativeSession = !resume && !ds.session.cliSessionId;
|
|
7477
7592
|
const titlePrompt = extractBotmuxLarkNativeSessionTitlePrompt(promptPayload.codexAppInput?.text ?? prompt, bot.botName ? [{ name: bot.botName }] : undefined);
|
|
7478
7593
|
if (isFreshNativeSession && !ds.session.nativeSessionTitleUserDefined) {
|
|
@@ -7708,17 +7823,28 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
7708
7823
|
reason: 'worker_fork_error',
|
|
7709
7824
|
message: reason,
|
|
7710
7825
|
});
|
|
7711
|
-
//
|
|
7712
|
-
//
|
|
7713
|
-
//
|
|
7714
|
-
|
|
7826
|
+
// Plan B: a meeting agent is an ordinary chat-scope session. A fork
|
|
7827
|
+
// diagnostic must stay out of the chat only for a genuinely meeting-driven
|
|
7828
|
+
// opening — a durable transcript delivery (initDispatchAttempt set) or a
|
|
7829
|
+
// stamped meeting @mention follow-up — because that path is fenced to the
|
|
7830
|
+
// receipt/lease chain and could otherwise leak on a silent delivery. A plain
|
|
7831
|
+
// user turn on this session gets its fork failure surfaced like any session
|
|
7832
|
+
// (else the user's own turn fails silently). Silent scheduled turns stay
|
|
7833
|
+
// suppressed as before.
|
|
7834
|
+
const forkErrorMeetingDriven = !!ds.session.vcMeetingReceiver
|
|
7835
|
+
&& (initDispatchAttempt !== undefined
|
|
7836
|
+
|| resolveVcMeetingImTurnOrigin(ds.session, initAttributionTurnId) !== undefined);
|
|
7837
|
+
if (forkErrorMeetingDriven || isSilentScheduledTurn(ds, initAttributionTurnId)) {
|
|
7715
7838
|
logger.info(`[${t}] Managed/silent fork failure kept out of auxiliary Lark UI `
|
|
7716
7839
|
+ `turn=${initAttributionTurnId?.slice(0, 12) ?? '-'} attempt=${initDispatchAttempt}: ${reason}`);
|
|
7717
7840
|
return;
|
|
7718
7841
|
}
|
|
7719
7842
|
const cliName = sessionCliDisplayName(ds, botCfg);
|
|
7720
7843
|
const message = tr('worker.start_failed', { cliName, reason }, botLocale(botCfg));
|
|
7721
|
-
void cb.sessionReply(sessionAnchorId(ds), message, 'text', ds.larkAppId, fallbackTurnId(ds, initAttributionTurnId),
|
|
7844
|
+
void cb.sessionReply(sessionAnchorId(ds), message, 'text', ds.larkAppId, fallbackTurnId(ds, initAttributionTurnId),
|
|
7845
|
+
// A meeting-driven turn (listener_thread delivery) that DOES surface must
|
|
7846
|
+
// still attribute to the meeting session so the send policy resolves it.
|
|
7847
|
+
forkErrorMeetingDriven
|
|
7722
7848
|
? { sourceSessionId: ds.session.sessionId }
|
|
7723
7849
|
: undefined).catch(replyErr => logger.error(`[${t}] Failed to deliver worker fork error to Lark: ${replyErr}`));
|
|
7724
7850
|
});
|
|
@@ -7761,8 +7887,16 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
7761
7887
|
// dsh runner turn timeout: read live from bot config so tuning bots.json
|
|
7762
7888
|
// takes effect on the next worker fork without recreating the session.
|
|
7763
7889
|
turnTimeoutMs: botCfg.turnTimeoutMs,
|
|
7890
|
+
// dsh runtime variant (official runner vs dsh-tui PTY TUI).
|
|
7891
|
+
dshRuntime: botCfg.dshRuntime,
|
|
7764
7892
|
disableCliBypass: botCfg.disableCliBypass === true,
|
|
7765
|
-
|
|
7893
|
+
// Existing App Server attachment owns neither an app-server nor a JSON-RPC
|
|
7894
|
+
// input channel. It is a normal official remote TUI, so all user input goes
|
|
7895
|
+
// through its terminal and must never trigger BotMux's self-owned RPC engine.
|
|
7896
|
+
codexRpcInput: existingAppServerEndpoint
|
|
7897
|
+
? false
|
|
7898
|
+
: botCfg.codexRpcInput === true || config.codexRpcInputDefault,
|
|
7899
|
+
...(existingAppServerEndpoint ? { existingAppServerEndpoint } : {}),
|
|
7766
7900
|
// Startup commands run on every fresh spawn (incl. resume) so session-only
|
|
7767
7901
|
// settings like `/effort ultracode` are re-established. Adopt sessions are
|
|
7768
7902
|
// observed, not driven — forkAdoptWorker intentionally omits this.
|
|
@@ -8126,6 +8260,24 @@ function invalidateTuiPrompt(ds, reason, outcome = 'failed') {
|
|
|
8126
8260
|
clearTuiPromptAuthority(ds);
|
|
8127
8261
|
publishAttentionPatch(ds);
|
|
8128
8262
|
}
|
|
8263
|
+
/**
|
|
8264
|
+
* Plan B: a VC meeting agent is an ordinary chat-scope session that hosts BOTH
|
|
8265
|
+
* meeting transcript deliveries and plain user IM turns. A turn is
|
|
8266
|
+
* "meeting-driven" only when it is a durable transcript delivery (dispatchAttempt
|
|
8267
|
+
* set) or a stamped meeting @mention follow-up (vcMeetingImTurnOrigin). Only
|
|
8268
|
+
* meeting-driven turns are subject to the durable silent/listener_thread output
|
|
8269
|
+
* policy and the receipt-fenced auxiliary-UI suppression; a plain user turn on
|
|
8270
|
+
* this session is an ordinary turn and must post / notify normally (else the
|
|
8271
|
+
* user's own reply is silently swallowed — the "手动@机器人也不回复" bug). Shared
|
|
8272
|
+
* across setupWorkerHandlers and deliverFinalOutput so every gate stays aligned.
|
|
8273
|
+
*/
|
|
8274
|
+
function isMeetingDrivenTurn(ds, turnId, dispatchAttempt) {
|
|
8275
|
+
if (!ds.session.vcMeetingReceiver)
|
|
8276
|
+
return false;
|
|
8277
|
+
if (dispatchAttempt !== undefined)
|
|
8278
|
+
return true;
|
|
8279
|
+
return resolveVcMeetingImTurnOrigin(ds.session, turnId) !== undefined;
|
|
8280
|
+
}
|
|
8129
8281
|
function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureNotified: false }, reservedWorkerGeneration) {
|
|
8130
8282
|
const cb = requireCallbacks();
|
|
8131
8283
|
const t = tag(ds);
|
|
@@ -8191,13 +8343,35 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8191
8343
|
};
|
|
8192
8344
|
/** Auxiliary worker UI is never an authorized output channel for a dedicated
|
|
8193
8345
|
* VC receiver. Dashboard/audit state is still updated before these guards. */
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8346
|
+
const managedAuxUiSuppressed = (turnId, dispatchAttempt) => {
|
|
8347
|
+
// No-transport session (apiOnly bot or HTTP virtual chat): there is no real
|
|
8348
|
+
// Feishu chat to render a card/reaction into. Suppress ALL auxiliary UI at
|
|
8349
|
+
// the single source every aux-UI handler funnels through — this is the
|
|
8350
|
+
// authoritative fix, NOT a fake "success" message id from sessionReply (a
|
|
8351
|
+
// synthetic id would get stored as streamCardId and later scheduleCardPatch
|
|
8352
|
+
// → updateMessage would still dial Feishu). Dashboard/web-terminal state is
|
|
8353
|
+
// still updated before these guards, so the terminal view is unaffected.
|
|
8354
|
+
if (!larkTransportEnabled({ chatId: ds.chatId, apiOnly: getBot(ds.larkAppId).config.apiOnly }))
|
|
8355
|
+
return true;
|
|
8356
|
+
if (isSilentScheduledTurn(ds, turnId))
|
|
8357
|
+
return true;
|
|
8358
|
+
// Plan B: a VC meeting agent is an ordinary chat-scope session, so the live
|
|
8359
|
+
// STREAMING CARD is no longer gated here at all — it surfaces through the
|
|
8360
|
+
// normal post/patch path like any group session (that was the "看不到流式
|
|
8361
|
+
// 卡片" report; those sites dropped their VC guard). What remains funnelled
|
|
8362
|
+
// through managedAuxUiSuppressed is out-of-band auxiliary UI (startup-failure
|
|
8363
|
+
// notices, usage-limit patches, exit UI): for a genuinely meeting-driven turn
|
|
8364
|
+
// — a durable transcript delivery (dispatchAttempt set) or a stamped meeting
|
|
8365
|
+
// @mention follow-up — that diagnostic stays fenced to the receipt/retry
|
|
8366
|
+
// chain and must never leak out-of-band (a silent delivery especially). A
|
|
8367
|
+
// plain user turn on this session has neither marker and notifies normally.
|
|
8368
|
+
// (isMeetingDrivenTurn supersedes the pre-Plan-B per-bot-receiver blanket
|
|
8369
|
+
// check that master's auxUiSuppressedFor still uses — keeping this inline is
|
|
8370
|
+
// the load-bearing "手动@不回复" fix.)
|
|
8371
|
+
if (isMeetingDrivenTurn(ds, turnId, dispatchAttempt))
|
|
8372
|
+
return true;
|
|
8373
|
+
return ordinaryManagedSuppression(turnId, dispatchAttempt);
|
|
8374
|
+
};
|
|
8201
8375
|
/** final_output is the sole exception: listener_thread and exact IM replies
|
|
8202
8376
|
* may proceed into the durable action ledger; silent/stale attempts do not. */
|
|
8203
8377
|
const managedFinalOutputSuppressed = (turnId, dispatchAttempt) => {
|
|
@@ -8205,13 +8379,16 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8205
8379
|
return true;
|
|
8206
8380
|
if (isTriggerFinalSuppressed(ds, turnId))
|
|
8207
8381
|
return true;
|
|
8208
|
-
|
|
8382
|
+
// Only a genuinely meeting-driven turn is subject to the durable meeting-send
|
|
8383
|
+
// policy; a plain user turn on this session posts through the ordinary path
|
|
8384
|
+
// (see isMeetingDrivenTurn).
|
|
8385
|
+
if (!isMeetingDrivenTurn(ds, turnId, dispatchAttempt)) {
|
|
8209
8386
|
return ordinaryManagedSuppression(turnId, dispatchAttempt);
|
|
8210
8387
|
}
|
|
8211
8388
|
// Resolve every Lark-facing worker event against durable origin state. The
|
|
8212
8389
|
// receipt freezes responseMode, so terminal→idle updates and daemon restore
|
|
8213
8390
|
// cannot become loud merely because an in-memory suppression map was
|
|
8214
|
-
// cleared/lost. Missing attribution on a
|
|
8391
|
+
// cleared/lost. Missing attribution on a meeting-driven turn fails closed.
|
|
8215
8392
|
const decision = evaluateVcMeetingManagedSend(config.session.dataDir, {
|
|
8216
8393
|
receiverSessionId: ds.session.sessionId,
|
|
8217
8394
|
receiverSession: true,
|
|
@@ -8253,9 +8430,75 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8253
8430
|
logger.error(`[${t}] Failed to deliver worker startup failure to Lark: ${err?.message ?? err}`);
|
|
8254
8431
|
}
|
|
8255
8432
|
};
|
|
8433
|
+
/**
|
|
8434
|
+
* Daemon-side self-heal for TRANSIENT startup/relaunch failures (see
|
|
8435
|
+
* worker-startup-retry.ts; 2026-08-23 tmux restart-storm incident). Returns
|
|
8436
|
+
* true when a silent bounded retry was scheduled INSTEAD of a user-visible
|
|
8437
|
+
* notify. Scope is deliberately narrow:
|
|
8438
|
+
* - only turn-less failures — a turn-carrying failure has a waiting sender
|
|
8439
|
+
* and (for durable VC/at-most-once turns) its own delivery state machine
|
|
8440
|
+
* that must keep owning retries;
|
|
8441
|
+
* - only reasons the classifier deems transient host/backend pressure;
|
|
8442
|
+
* - bounded by MAX_STARTUP_AUTO_RETRIES per failing streak (reset on a
|
|
8443
|
+
* worker generation reaching `ready`), so a persistent failure still
|
|
8444
|
+
* surfaces to the user with the standard card.
|
|
8445
|
+
* The retry itself is a blank re-fork (`forkWorker(ds, '', true)`) — exactly
|
|
8446
|
+
* what a daemon restart or the next inbound message would do: re-attach when
|
|
8447
|
+
* the backing pane survived (the 08-23 case — pane and CLI were alive, only
|
|
8448
|
+
* the worker had died), cold `--resume` otherwise.
|
|
8449
|
+
*/
|
|
8450
|
+
const scheduleTransientStartupRetry = (reason, turnId, dispatchAttempt) => {
|
|
8451
|
+
if (turnId !== undefined || dispatchAttempt !== undefined)
|
|
8452
|
+
return false;
|
|
8453
|
+
if (startupState.failureNotified)
|
|
8454
|
+
return false;
|
|
8455
|
+
if (!isTransientStartupFailure(reason))
|
|
8456
|
+
return false;
|
|
8457
|
+
if (ds.session.status !== 'active')
|
|
8458
|
+
return false;
|
|
8459
|
+
const attempts = (ds.startupAutoRetry?.attempts ?? 0) + 1;
|
|
8460
|
+
if (attempts > MAX_STARTUP_AUTO_RETRIES)
|
|
8461
|
+
return false;
|
|
8462
|
+
// Mark this generation handled so the pre-ready exit guard cannot post a
|
|
8463
|
+
// duplicate start_exited_early card behind the scheduled retry.
|
|
8464
|
+
startupState.failureNotified = true;
|
|
8465
|
+
const delayMs = startupAutoRetryDelayMs(ds.session.sessionId, attempts);
|
|
8466
|
+
logger.warn(`[${t}] Transient worker startup failure (${reason}); `
|
|
8467
|
+
+ `auto-retry ${attempts}/${MAX_STARTUP_AUTO_RETRIES} in ${Math.round(delayMs / 1000)}s`);
|
|
8468
|
+
const timer = setTimeout(() => {
|
|
8469
|
+
if (ds.startupAutoRetry?.timer === timer)
|
|
8470
|
+
ds.startupAutoRetry.timer = undefined;
|
|
8471
|
+
// Lifecycle guards: the session may have been closed, replaced in the
|
|
8472
|
+
// registry, or revived by an inbound message while the timer slept.
|
|
8473
|
+
if (ds.session.status !== 'active')
|
|
8474
|
+
return;
|
|
8475
|
+
if (ds.worker && !ds.worker.killed)
|
|
8476
|
+
return;
|
|
8477
|
+
if (activeSessionsRegistry && activeSessionsRegistry.get(activeSessionKey(ds)) !== ds)
|
|
8478
|
+
return;
|
|
8479
|
+
logger.info(`[${t}] Auto-retrying worker start (${attempts}/${MAX_STARTUP_AUTO_RETRIES}) `
|
|
8480
|
+
+ 'after transient startup failure');
|
|
8481
|
+
try {
|
|
8482
|
+
forkWorker(ds, '', true);
|
|
8483
|
+
}
|
|
8484
|
+
catch (err) {
|
|
8485
|
+
logger.error(`[${t}] Startup auto-retry fork failed: ${err?.message ?? err}`);
|
|
8486
|
+
}
|
|
8487
|
+
}, delayMs);
|
|
8488
|
+
timer.unref?.();
|
|
8489
|
+
// An inbound message can revive the session between two failing
|
|
8490
|
+
// generations; if the replacement fails too, the previous generation's
|
|
8491
|
+
// still-pending timer must not survive as an orphan (its guards make a
|
|
8492
|
+
// double blank re-fork merely wasteful, not harmful — but one streak owns
|
|
8493
|
+
// exactly one pending retry).
|
|
8494
|
+
if (ds.startupAutoRetry?.timer)
|
|
8495
|
+
clearTimeout(ds.startupAutoRetry.timer);
|
|
8496
|
+
ds.startupAutoRetry = { attempts, timer };
|
|
8497
|
+
return true;
|
|
8498
|
+
};
|
|
8256
8499
|
// Adopt mode flags — computed once, used in all buildStreamingCard calls.
|
|
8257
8500
|
// Bridge mode (the v3 default for /adopt) hides the legacy takeover button.
|
|
8258
|
-
const isAdopt =
|
|
8501
|
+
const isAdopt = isSharedAdoptSession(ds);
|
|
8259
8502
|
const showTakeover = false;
|
|
8260
8503
|
worker.on('message', async (msg) => {
|
|
8261
8504
|
// Every IPC message is scoped to the child generation that emitted it.
|
|
@@ -8409,6 +8652,14 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8409
8652
|
}
|
|
8410
8653
|
startupState.ready = true;
|
|
8411
8654
|
ds.workerReady = true;
|
|
8655
|
+
// A generation reached ready: the transient-startup failing streak is
|
|
8656
|
+
// over. Clear the budget and any still-pending retry timer (an inbound
|
|
8657
|
+
// message may have revived the session while a retry slept).
|
|
8658
|
+
if (ds.startupAutoRetry) {
|
|
8659
|
+
if (ds.startupAutoRetry.timer)
|
|
8660
|
+
clearTimeout(ds.startupAutoRetry.timer);
|
|
8661
|
+
ds.startupAutoRetry = undefined;
|
|
8662
|
+
}
|
|
8412
8663
|
// Restore the daemon-owned display mode BEFORE any card handling: a
|
|
8413
8664
|
// fresh worker always boots with displayMode='hidden', and every early
|
|
8414
8665
|
// break below (managed / replyAlreadySent / streamingCardDisabled /
|
|
@@ -8579,8 +8830,10 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8579
8830
|
// POST a second card; the in-flight POST becomes this turn's card.
|
|
8580
8831
|
if (ds.streamCardId === CARD_POSTING_SENTINEL)
|
|
8581
8832
|
break;
|
|
8833
|
+
const pendingNewTurn = !!ds.streamCardPending;
|
|
8582
8834
|
const postingGeneration = ds.streamCardTurnGeneration ?? 0;
|
|
8583
8835
|
const cardReplyTarget = captureStreamingCardReplyTarget(ds, msg.turnId);
|
|
8836
|
+
const statusRevisionAtPost = ds.streamCardStatusRevision ?? 0;
|
|
8584
8837
|
ds.streamCardId = CARD_POSTING_SENTINEL;
|
|
8585
8838
|
try {
|
|
8586
8839
|
ds.streamCardNonce = randomBytes(4).toString('hex');
|
|
@@ -8588,7 +8841,9 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8588
8841
|
// See PATCH-branch comment above re: lastScreenStatus preference.
|
|
8589
8842
|
// For relay (kill+fork with surviving tmux/CLI), this avoids the
|
|
8590
8843
|
// jarring "启动中" right after the M1 "已接力" announcement.
|
|
8591
|
-
const initStatus =
|
|
8844
|
+
const initStatus = pendingNewTurn
|
|
8845
|
+
? turnStartingCardStatus(ds, effectiveCliId)
|
|
8846
|
+
: (ds.usageLimit ? 'limited' : (ds.lastScreenStatus ?? 'starting'));
|
|
8592
8847
|
const streamCardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readOnlyUrl, initTitle,
|
|
8593
8848
|
// For /relay resume, ds.lastScreenContent is the cached pane
|
|
8594
8849
|
// from before the kill+fork — using it avoids a blank flash
|
|
@@ -8629,6 +8884,9 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8629
8884
|
// resume where the CLI kept running), arm here — same authorized arm
|
|
8630
8885
|
// point as the reuse branch, now that streamCardId is the real id.
|
|
8631
8886
|
syncUsageRefreshTimer(ds);
|
|
8887
|
+
if (!superseded) {
|
|
8888
|
+
reconcilePostedStartingCard(ds, cardReplyTarget.turnId, statusRevisionAtPost);
|
|
8889
|
+
}
|
|
8632
8890
|
if (superseded && ds.streamCardPendingTurnId) {
|
|
8633
8891
|
void postTurnStartingCard(ds, cb.sessionReply, ds.streamCardPendingTurnId);
|
|
8634
8892
|
}
|
|
@@ -8649,7 +8907,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8649
8907
|
// Fallback: send static session card
|
|
8650
8908
|
try {
|
|
8651
8909
|
const localCliReadyAtBuild = isLocalCliOpenReady(ds, { cliId: effectiveCliId });
|
|
8652
|
-
const cardJson = buildSessionCard(ds.session.sessionId, sessionAnchorId(ds), readOnlyUrl, ds.session.title || sessionCliDisplayName(ds, botCfg), effectiveCliId, undefined,
|
|
8910
|
+
const cardJson = buildSessionCard(ds.session.sessionId, sessionAnchorId(ds), readOnlyUrl, ds.session.title || sessionCliDisplayName(ds, botCfg), effectiveCliId, undefined, isSharedAdoptSession(ds), loc, localCliReadyAtBuild, sessionRuntimeDisplayName(ds, botCfg));
|
|
8653
8911
|
const fallbackCardId = await scopedReply(cardJson, 'interactive', msg.turnId);
|
|
8654
8912
|
if (!ownsLifecycleMutation()) {
|
|
8655
8913
|
void deleteMessage(ds.larkAppId, fallbackCardId).catch(() => { });
|
|
@@ -8657,7 +8915,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8657
8915
|
}
|
|
8658
8916
|
if (!localCliReadyAtBuild && isLocalCliOpenEnabled()
|
|
8659
8917
|
&& isLocalCliOpenReady(ds, { cliId: effectiveCliId })) {
|
|
8660
|
-
const readyCardJson = buildSessionCard(ds.session.sessionId, sessionAnchorId(ds), readOnlyUrl, ds.session.title || sessionCliDisplayName(ds, botCfg), effectiveCliId, undefined,
|
|
8918
|
+
const readyCardJson = buildSessionCard(ds.session.sessionId, sessionAnchorId(ds), readOnlyUrl, ds.session.title || sessionCliDisplayName(ds, botCfg), effectiveCliId, undefined, isSharedAdoptSession(ds), loc, true, sessionRuntimeDisplayName(ds, botCfg));
|
|
8661
8919
|
try {
|
|
8662
8920
|
await updateMessage(ds.larkAppId, fallbackCardId, readyCardJson);
|
|
8663
8921
|
}
|
|
@@ -8851,7 +9109,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8851
9109
|
const prevStatus = ds.lastScreenStatus;
|
|
8852
9110
|
updateUsageLimitState(ds, msg.usageLimit);
|
|
8853
9111
|
ds.lastScreenContent = msg.content;
|
|
8854
|
-
ds.lastScreenStatus = (msg.
|
|
9112
|
+
ds.lastScreenStatus = resolveUsageAwareScreenStatus(ds, msg.status, msg.usageLimit);
|
|
8855
9113
|
bumpStreamCardStatusRevision(ds);
|
|
8856
9114
|
// A suspend that arrived mid-turn parked itself here. Defer until this
|
|
8857
9115
|
// screen_update has finished using process state — suspendWorker nulls
|
|
@@ -9046,7 +9304,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
9046
9304
|
break;
|
|
9047
9305
|
const prevStatus = ds.lastScreenStatus;
|
|
9048
9306
|
updateUsageLimitState(ds, msg.usageLimit);
|
|
9049
|
-
ds.lastScreenStatus = (msg.
|
|
9307
|
+
ds.lastScreenStatus = resolveUsageAwareScreenStatus(ds, msg.status, msg.usageLimit);
|
|
9050
9308
|
bumpStreamCardStatusRevision(ds);
|
|
9051
9309
|
// Same deferred-suspend checkpoint as the screen_update branch, and
|
|
9052
9310
|
// deferred for the same reason (see runPendingSuspendIfSettled).
|
|
@@ -9406,9 +9664,45 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
9406
9664
|
break;
|
|
9407
9665
|
}
|
|
9408
9666
|
const suppressExitUi = managedAuxUiSuppressed(msg.turnId, msg.dispatchAttempt);
|
|
9667
|
+
if (msg.codexAppActiveWriter === true && effectiveCliId === 'codex-app') {
|
|
9668
|
+
// `thread/resume` was explicitly rejected because another Codex
|
|
9669
|
+
// app-server currently owns the persisted thread. This is a safe,
|
|
9670
|
+
// expected handoff conflict — never spend restart budget or let the
|
|
9671
|
+
// generic second-restart policy drop cliSessionId and create a fresh
|
|
9672
|
+
// context. Park the logical Botmux session so its next message
|
|
9673
|
+
// retries the SAME thread after the external writer releases it.
|
|
9674
|
+
const thread = ds.session.cliSessionId ?? ds.session.sessionId;
|
|
9675
|
+
logger.warn(`[${t}] Codex App resume blocked by external active writer; preserving `
|
|
9676
|
+
+ `thread ${thread.substring(0, 12)}`);
|
|
9677
|
+
const suspended = suspendWorker(ds, 'codex_app_active_writer');
|
|
9678
|
+
if (!suspended) {
|
|
9679
|
+
// The runner process already exited and there is no safe live
|
|
9680
|
+
// backend suspension path. Retire only this Node worker; never
|
|
9681
|
+
// destroy the external app-server that owns the thread.
|
|
9682
|
+
retireWorkerProcessOnly(ds, 'codex_app_active_writer');
|
|
9683
|
+
ds.hasHistory = true;
|
|
9684
|
+
ds.session.suspendedColdResume = true;
|
|
9685
|
+
sessionStore.updateSession(ds.session);
|
|
9686
|
+
}
|
|
9687
|
+
if (!suppressExitUi) {
|
|
9688
|
+
const threadLabel = `${thread.substring(0, 16)}…`;
|
|
9689
|
+
const message = loc === 'zh'
|
|
9690
|
+
? `⚠️ Codex App 历史会话(${threadLabel})当前正由另一个 Codex App / app-server 持有。`
|
|
9691
|
+
+ 'BotMux 已保留原上下文,不会新建空会话;请在 App 侧释放该会话后,再发送一条消息重试恢复。'
|
|
9692
|
+
: `⚠️ Codex App thread (${threadLabel}) is currently held by another Codex App / app-server writer. `
|
|
9693
|
+
+ 'BotMux preserved the original context and did not create a fresh session; release it in the App, then send another message to retry.';
|
|
9694
|
+
try {
|
|
9695
|
+
await scopedReply(message, 'text', msg.turnId);
|
|
9696
|
+
}
|
|
9697
|
+
catch (replyErr) {
|
|
9698
|
+
logger.error(`[${t}] Failed to report Codex App active-writer conflict: ${replyErr}`);
|
|
9699
|
+
}
|
|
9700
|
+
}
|
|
9701
|
+
break;
|
|
9702
|
+
}
|
|
9409
9703
|
// Do NOT auto-restart in adopt mode — there's nothing to restart
|
|
9410
|
-
if (ds
|
|
9411
|
-
logger.info(`[${t}]
|
|
9704
|
+
if (isSharedAdoptSession(ds)) {
|
|
9705
|
+
logger.info(`[${t}] Shared adopted session ended`);
|
|
9412
9706
|
// Freeze the streaming card
|
|
9413
9707
|
if (!suppressExitUi && ds.streamCardId && workerHasInitialized(ds)) {
|
|
9414
9708
|
const readUrl = readableTerminalUrlFor(ds);
|
|
@@ -9544,7 +9838,12 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
9544
9838
|
logger.error(`[${t}] Worker error: ${msg.message}`);
|
|
9545
9839
|
// `error` is a fatal launch-generation signal. It normally arrives
|
|
9546
9840
|
// during init, but can also follow a previously-ready worker whose CLI
|
|
9547
|
-
// recovery/restart fails; that later failure must remain user-visible
|
|
9841
|
+
// recovery/restart fails; that later failure must remain user-visible —
|
|
9842
|
+
// UNLESS it is a turn-less transient (post-outage restart storm), where
|
|
9843
|
+
// a bounded silent re-fork self-heals without waking anyone (08-23:
|
|
9844
|
+
// idle sessions got "会话启动失败" cards while their panes were alive).
|
|
9845
|
+
if (scheduleTransientStartupRetry(msg.message, msg.turnId, msg.dispatchAttempt))
|
|
9846
|
+
break;
|
|
9548
9847
|
await notifyStartupFailure(msg.message, msg.turnId, msg.dispatchAttempt);
|
|
9549
9848
|
break;
|
|
9550
9849
|
}
|
|
@@ -10376,14 +10675,19 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
10376
10675
|
ds.worker = null;
|
|
10377
10676
|
ds.workerReady = false;
|
|
10378
10677
|
ds.workerPort = null;
|
|
10678
|
+
// A dead worker can no longer refresh its card/usage view.
|
|
10679
|
+
clearUsageRefreshTimer(ds);
|
|
10680
|
+
// A worker owns the terminal HTTP listener. Persisting its former port
|
|
10681
|
+
// after exit leaves the Dashboard proxy routing `/terminal/<session>` to
|
|
10682
|
+
// a dead socket until some later ready event overwrites it. Clear both
|
|
10683
|
+
// durable and aggregated state now so a persistent backend can be
|
|
10684
|
+
// re-attached on demand instead of returning a stale 502.
|
|
10685
|
+
ds.session.webPort = undefined;
|
|
10379
10686
|
// A queued suspend for THIS generation is now moot — the worker it was
|
|
10380
10687
|
// about is gone. Leaving it set would suspend the replacement on its
|
|
10381
10688
|
// first idle (the deferred checkpoint's own "worker gone" branch cannot
|
|
10382
10689
|
// help: it only runs on a screen update that will never arrive).
|
|
10383
10690
|
clearPendingSuspendClaim(ds, 'worker exited');
|
|
10384
|
-
// Dead worker generation — stop the periodic usage refresh immediately
|
|
10385
|
-
// instead of waiting a tick for it to self-clear on !workerHasInitialized.
|
|
10386
|
-
clearUsageRefreshTimer(ds);
|
|
10387
10691
|
ds.workerToken = null;
|
|
10388
10692
|
ds.workerViewToken = null;
|
|
10389
10693
|
ds.managedTurnOrigin = undefined;
|
|
@@ -10411,6 +10715,13 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
10411
10715
|
sessionStore.updateSession(ds.session);
|
|
10412
10716
|
if (exitedPreviewTarget !== undefined)
|
|
10413
10717
|
publishSessionPreviewCleared(ds.session.sessionId);
|
|
10718
|
+
dashboardEventBus.publish({
|
|
10719
|
+
type: 'session.update',
|
|
10720
|
+
body: {
|
|
10721
|
+
sessionId: ds.session.sessionId,
|
|
10722
|
+
patch: { webPort: null, workerPid: null },
|
|
10723
|
+
},
|
|
10724
|
+
});
|
|
10414
10725
|
}
|
|
10415
10726
|
if (!transferRetirement) {
|
|
10416
10727
|
try {
|
|
@@ -10541,10 +10852,11 @@ async function finishTurnReactions(ds) {
|
|
|
10541
10852
|
return;
|
|
10542
10853
|
// Detach the batch first so a second idle edge can't double-flip it.
|
|
10543
10854
|
ds.pendingAckReactions = [];
|
|
10544
|
-
//
|
|
10545
|
-
//
|
|
10546
|
-
|
|
10547
|
-
|
|
10855
|
+
// Plan B: a meeting agent is an ordinary chat-scope session. Pending ack
|
|
10856
|
+
// reactions only ever exist for a real inbound user message (the ✋ is placed
|
|
10857
|
+
// when that message arrives — transcript deliveries have no inbound message to
|
|
10858
|
+
// react to), so a non-empty list here always belongs to a plain user turn and
|
|
10859
|
+
// must settle to ✅ like any session. No VC special-case.
|
|
10548
10860
|
const silent = silentTurnReactions(ds);
|
|
10549
10861
|
const doneEmoji = doneReactionEmojiFor(ds);
|
|
10550
10862
|
for (const ack of list) {
|
|
@@ -10728,7 +11040,14 @@ function deliverFinalOutput(ds, msg, t, attempt, onComplete, isStillOwned = () =
|
|
|
10728
11040
|
const imOrigin = msg.dispatchAttempt === undefined
|
|
10729
11041
|
? resolveVcMeetingImTurnOrigin(ds.session, msg.turnId)
|
|
10730
11042
|
: undefined;
|
|
10731
|
-
|
|
11043
|
+
// Plan B: the meeting agent is an ordinary chat-scope session, so it also
|
|
11044
|
+
// delivers plain user turns. Apply the durable meeting-send policy ONLY to
|
|
11045
|
+
// a genuinely meeting-driven turn (a durable transcript delivery, or a
|
|
11046
|
+
// stamped meeting @mention follow-up). A plain user turn is not
|
|
11047
|
+
// meeting-driven → managedDecision stays undefined → it delivers through
|
|
11048
|
+
// the ordinary path. Without this gate a plain user reply resolves
|
|
11049
|
+
// `origin_unproven` and is silently dropped (the "手动@机器人也不回复" bug).
|
|
11050
|
+
const managedDecision = isMeetingDrivenTurn(ds, msg.turnId, msg.dispatchAttempt)
|
|
10732
11051
|
? evaluateVcMeetingManagedSend(config.session.dataDir, {
|
|
10733
11052
|
receiverSessionId: ds.session.sessionId,
|
|
10734
11053
|
receiverSession: true,
|
|
@@ -10746,7 +11065,7 @@ function deliverFinalOutput(ds, msg, t, attempt, onComplete, isStillOwned = () =
|
|
|
10746
11065
|
return;
|
|
10747
11066
|
}
|
|
10748
11067
|
const revalidateManagedSend = () => {
|
|
10749
|
-
if (!ds.
|
|
11068
|
+
if (!isMeetingDrivenTurn(ds, msg.turnId, msg.dispatchAttempt))
|
|
10750
11069
|
return;
|
|
10751
11070
|
const current = evaluateVcMeetingManagedSend(config.session.dataDir, {
|
|
10752
11071
|
receiverSessionId: ds.session.sessionId,
|
|
@@ -10824,11 +11143,14 @@ function deliverFinalOutput(ds, msg, t, attempt, onComplete, isStillOwned = () =
|
|
|
10824
11143
|
const feedbackRequesterSubjectId = recipientOpenId;
|
|
10825
11144
|
const feedback = feedbackPolicy && feedbackRequesterSubjectId ? { policy: feedbackPolicy } : undefined;
|
|
10826
11145
|
cardUsage ??= getDaemonReplyCardUsageSnapshot(ds, effectiveCliId);
|
|
11146
|
+
const localTurnTitle = msg.kind === 'local-turn-headless'
|
|
11147
|
+
? tr('card.local_turn_resumed', undefined, localeForBot(ds.larkAppId))
|
|
11148
|
+
: isExistingAppServerSharedAdoptPersistedSession(ds.session)
|
|
11149
|
+
? tr('card.codex_app_shared_turn', undefined, localeForBot(ds.larkAppId))
|
|
11150
|
+
: tr('card.local_turn', undefined, localeForBot(ds.larkAppId));
|
|
10827
11151
|
const cardJson = msg.kind === 'local-turn' || msg.kind === 'local-turn-headless'
|
|
10828
11152
|
? buildContextualReplyCard({
|
|
10829
|
-
title:
|
|
10830
|
-
? tr('card.local_turn_resumed', undefined, localeForBot(ds.larkAppId))
|
|
10831
|
-
: tr('card.local_turn', undefined, localeForBot(ds.larkAppId)),
|
|
11153
|
+
title: localTurnTitle,
|
|
10832
11154
|
userText: msg.kind === 'local-turn' ? safeUserText ?? '' : undefined,
|
|
10833
11155
|
assistantText: safeAssistantText,
|
|
10834
11156
|
assistantLabel: storedSessionCliDisplayName(ds),
|
|
@@ -11287,6 +11609,7 @@ export function forkAdoptWorker(ds, opts) {
|
|
|
11287
11609
|
cliSessionId: isStructuredBridge ? adopted.sessionId : undefined,
|
|
11288
11610
|
model: agentCfg.model,
|
|
11289
11611
|
turnTimeoutMs: botCfg.turnTimeoutMs,
|
|
11612
|
+
dshRuntime: botCfg.dshRuntime,
|
|
11290
11613
|
disableCliBypass: botCfg.disableCliBypass === true,
|
|
11291
11614
|
codexRpcInput: botCfg.codexRpcInput === true || config.codexRpcInputDefault,
|
|
11292
11615
|
// Adopt is normally observe-only (prompt=''), driven later by 'message'
|
|
@@ -11501,7 +11824,20 @@ export function killStalePids(activeSessions_, runtimeSessions) {
|
|
|
11501
11824
|
try {
|
|
11502
11825
|
// Check if process exists (signal 0 doesn't kill, just checks)
|
|
11503
11826
|
process.kill(session.pid, 0);
|
|
11504
|
-
//
|
|
11827
|
+
// Existing-App-Server share: the worker and its BotMux-owned remote TUI
|
|
11828
|
+
// can be in one process group. Reap only the stale worker PID; killing
|
|
11829
|
+
// the group would also terminate `codex --remote`, even though the
|
|
11830
|
+
// replacement daemon is expected to reattach that client. The external
|
|
11831
|
+
// App Server itself is never ours to signal.
|
|
11832
|
+
if (isExistingAppServerSharedAdoptPersistedSession(session)) {
|
|
11833
|
+
logger.info(`Killing stale shared-App-Server worker only (pid: ${session.pid}, session: ${session.sessionId})`);
|
|
11834
|
+
try {
|
|
11835
|
+
process.kill(session.pid, 'SIGTERM');
|
|
11836
|
+
}
|
|
11837
|
+
catch { /* already gone */ }
|
|
11838
|
+
continue;
|
|
11839
|
+
}
|
|
11840
|
+
// Ordinary managed sessions own their complete process group.
|
|
11505
11841
|
logger.info(`Killing stale CLI process (pid: ${session.pid}, session: ${session.sessionId})`);
|
|
11506
11842
|
try {
|
|
11507
11843
|
process.kill(-session.pid, 'SIGTERM');
|
|
@@ -11767,6 +12103,12 @@ function cleanupPersistentBackendSessions(backendType, activeSessions_, runtimeS
|
|
|
11767
12103
|
for (const session of activeSessions_) {
|
|
11768
12104
|
if (!belongsToBackend(session))
|
|
11769
12105
|
continue;
|
|
12106
|
+
// The remote TUI deliberately uses cliId=codex even when the Bot's
|
|
12107
|
+
// default/new-session runtime remains codex-app. This is a shared-adopt
|
|
12108
|
+
// binding, not a stale CLI selection, so keep its bmx-* client alive for
|
|
12109
|
+
// the restore path. Explicit /detach remains responsible for cleanup.
|
|
12110
|
+
if (isExistingAppServerSharedAdoptPersistedSession(session))
|
|
12111
|
+
continue;
|
|
11770
12112
|
const sessionCliId = session.cliId;
|
|
11771
12113
|
if (!sessionCliId || !session.larkAppId)
|
|
11772
12114
|
continue;
|