botmux 3.16.1 → 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/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/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 +21 -4
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +415 -100
- 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 +45 -11
- 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 +22 -8
- 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 +12 -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 +12 -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 +70 -16
- 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 +11 -1
- package/dist/platform/binding.d.ts.map +1 -1
- package/dist/platform/binding.js +3 -28
- package/dist/platform/binding.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 +542 -106
- 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;
|
|
@@ -4404,7 +4442,7 @@ export function teardownAuthoritativePersistentBackingBeforeClose(target) {
|
|
|
4404
4442
|
export function buildStreamingCardJson(ds, status) {
|
|
4405
4443
|
const botCfg = getBot(ds.larkAppId).config;
|
|
4406
4444
|
const effectiveCliId = sessionCliId(ds, botCfg);
|
|
4407
|
-
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,
|
|
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));
|
|
4408
4446
|
}
|
|
4409
4447
|
/**
|
|
4410
4448
|
* Close from a BACKGROUND path — one with no user surface to report to
|
|
@@ -4449,9 +4487,8 @@ export async function closeSession(sessionId, opts) {
|
|
|
4449
4487
|
const stored = sessionStore.getOwnedSession(sessionId);
|
|
4450
4488
|
const closeFrozenBackendType = ds?.initConfig?.backendType
|
|
4451
4489
|
?? ds?.session.backendType ?? stored?.backendType;
|
|
4452
|
-
const isOwnedClose = !ds
|
|
4453
|
-
&& !
|
|
4454
|
-
&& !stored?.adoptedFrom;
|
|
4490
|
+
const isOwnedClose = !(ds && isSharedAdoptSession(ds))
|
|
4491
|
+
&& !(stored && isSharedAdoptPersistedSession(stored));
|
|
4455
4492
|
const isOwnedRiffClose = isOwnedClose && closeFrozenBackendType === 'riff';
|
|
4456
4493
|
const isOwnedMojoClose = isOwnedClose && closeFrozenBackendType === 'mojo';
|
|
4457
4494
|
const closeJournal = ds?.session.mojoCloseJournal ?? stored?.mojoCloseJournal;
|
|
@@ -4851,10 +4888,11 @@ function removeInactiveRegistration(map, key, ds) {
|
|
|
4851
4888
|
}
|
|
4852
4889
|
function persistedActiveSessionKey(session, fallbackLarkAppId) {
|
|
4853
4890
|
const larkAppId = session.larkAppId ?? fallbackLarkAppId;
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
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);
|
|
4858
4896
|
}
|
|
4859
4897
|
/**
|
|
4860
4898
|
* Return an active persisted row that deliberately reserves this routing key
|
|
@@ -5080,7 +5118,14 @@ function failOrdinaryImDelivery(record, reason) {
|
|
|
5080
5118
|
requireOrdinaryTurnRecoveryAttention(record.ds.session, record.turnId, 'recovery_delivery_failed');
|
|
5081
5119
|
return;
|
|
5082
5120
|
}
|
|
5083
|
-
|
|
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))
|
|
5084
5129
|
return;
|
|
5085
5130
|
const loc = botLocale(getBot(record.ds.larkAppId).config);
|
|
5086
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: `
|
|
@@ -5170,8 +5215,13 @@ function shouldTrackOrdinaryImDelivery(ds, message) {
|
|
|
5170
5215
|
&& (message.turnId.startsWith('om_') || message.turnId.startsWith('bmx-recovery-'))
|
|
5171
5216
|
&& message.dispatchAttempt === undefined
|
|
5172
5217
|
&& (message.type !== 'init' || (!!message.prompt && !message.adoptMode))
|
|
5173
|
-
&& !ds
|
|
5174
|
-
|
|
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)
|
|
5175
5225
|
&& Number.isSafeInteger(ds.workerGeneration)
|
|
5176
5226
|
&& (ds.workerGeneration ?? 0) > 0
|
|
5177
5227
|
&& ds.session.workerGeneration === ds.workerGeneration;
|
|
@@ -5490,8 +5540,6 @@ targetScope, opts) {
|
|
|
5490
5540
|
runtimeWorkingDir: ds.workingDir,
|
|
5491
5541
|
runtimeAdoptedFrom: ds.adoptedFrom,
|
|
5492
5542
|
});
|
|
5493
|
-
if (ds.session.vcMeetingReceiver)
|
|
5494
|
-
return { ok: false, error: 'vc_receiver_not_relayable' };
|
|
5495
5543
|
if (hasProtectedSessionMutationOwnership(ds)) {
|
|
5496
5544
|
return { ok: false, error: 'codex_app_dispatch_pending' };
|
|
5497
5545
|
}
|
|
@@ -5565,7 +5613,7 @@ targetScope, opts) {
|
|
|
5565
5613
|
// Adopt sessions wrap a CLI process that botmux didn't spawn — the user
|
|
5566
5614
|
// owns it inside their own tmux pane, so moving routing here would be
|
|
5567
5615
|
// surprising and we don't control the tmux session's lifecycle. Refuse.
|
|
5568
|
-
if (ds
|
|
5616
|
+
if (isSharedAdoptSession(ds))
|
|
5569
5617
|
return { ok: false, error: 'adopt_not_relayable' };
|
|
5570
5618
|
// Busy worker: refuse immediately rather than waiting. An idle-wait loop
|
|
5571
5619
|
// (previously 60s) created an asymmetry with the peer-dispatch HTTP
|
|
@@ -5646,7 +5694,6 @@ targetScope, opts) {
|
|
|
5646
5694
|
const persistedTargetConflicts = sessionStore.listSessions().filter(item => item.sessionId !== ds.session.sessionId
|
|
5647
5695
|
&& item.status === 'active'
|
|
5648
5696
|
&& (!item.larkAppId || item.larkAppId === ds.larkAppId)
|
|
5649
|
-
&& !item.vcMeetingReceiver
|
|
5650
5697
|
&& (item.scope === 'chat' ? item.chatId : item.rootMessageId) === targetAnchor
|
|
5651
5698
|
&& !runtimeIds.has(item.sessionId));
|
|
5652
5699
|
if (persistedTargetConflicts.some(item => hasProtectedSessionMutationOwnership(item)
|
|
@@ -5836,6 +5883,12 @@ export function isForkCapableSession(ds) {
|
|
|
5836
5883
|
const cliId = sessionCliId(ds, botCfg);
|
|
5837
5884
|
if (!FORK_CAPABLE_CLI_IDS.has(cliId))
|
|
5838
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;
|
|
5839
5892
|
// Codex terminal mode is forkable; Codex under Hybrid RPC input is not (the
|
|
5840
5893
|
// thread is an app-server live session, no local rollout to `codex fork`).
|
|
5841
5894
|
//
|
|
@@ -5883,13 +5936,11 @@ export async function forkSession(sessionId, targetChatId, targetRootMessageId,
|
|
|
5883
5936
|
return { ok: false, error: 'fork_unsupported_backend' };
|
|
5884
5937
|
// ── Front guards (mirror transferSession; a fork needs a clean, complete
|
|
5885
5938
|
// source node exactly as a relay does) ──
|
|
5886
|
-
if (ds.session.vcMeetingReceiver)
|
|
5887
|
-
return { ok: false, error: 'vc_receiver_not_forkable' };
|
|
5888
5939
|
if (ds.pendingRepo)
|
|
5889
5940
|
return { ok: false, error: 'not_started_yet' };
|
|
5890
5941
|
if (!isRelayableRealSession(ds))
|
|
5891
5942
|
return { ok: false, error: 'not_started_yet' };
|
|
5892
|
-
if (ds
|
|
5943
|
+
if (isSharedAdoptSession(ds))
|
|
5893
5944
|
return { ok: false, error: 'adopt_not_forkable' };
|
|
5894
5945
|
if (isSessionLifecycleInFlight(ds))
|
|
5895
5946
|
return { ok: false, error: 'worker_busy' };
|
|
@@ -6747,7 +6798,18 @@ export function mojoLivePatchForSession(ds) {
|
|
|
6747
6798
|
/** Sessions with a quarantine notice send in flight, so it cannot double-post. */
|
|
6748
6799
|
const mojoQuarantineNoticeInFlight = new Set();
|
|
6749
6800
|
/**
|
|
6750
|
-
*
|
|
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.
|
|
6751
6813
|
*
|
|
6752
6814
|
* Previously setupWorkerHandlers held this as a closure and the quarantine notice
|
|
6753
6815
|
* COPIED three of its four checks — dropping `ordinaryManagedSuppression`, so a
|
|
@@ -7339,6 +7401,32 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
7339
7401
|
const cb = requireCallbacks();
|
|
7340
7402
|
const bot = getBot(ds.larkAppId);
|
|
7341
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
|
+
}
|
|
7342
7430
|
// A bare /repo placeholder (and a non-Codex empty group-join setup) owns no
|
|
7343
7431
|
// model turn. Starting its CLI with an empty prompt must not mint a queued
|
|
7344
7432
|
// activation token: the worker has nothing to submit and could never ACK it.
|
|
@@ -7490,7 +7578,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
7490
7578
|
// worker left parked but couldn't clean (hard-killed while parked, daemon
|
|
7491
7579
|
// still alive → next message reforks here). The fresh CLI spawns under the
|
|
7492
7580
|
// real bmx-<sid>; without this, bmx-diag-<sid> + its .ansi file would leak.
|
|
7493
|
-
if (!ds
|
|
7581
|
+
if (!isSharedAdoptSession(ds))
|
|
7494
7582
|
reclaimParkedCrashDiagnostic(ds);
|
|
7495
7583
|
agentCfg = sessionAgentConfig(ds, botCfg);
|
|
7496
7584
|
if (!initTurnId && prompt.length > 0 && agentCfg.cliId === 'codex-app') {
|
|
@@ -7499,7 +7587,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
7499
7587
|
ensureCliEnv(agentCfg.cliId, agentCfg.cliPathOverride);
|
|
7500
7588
|
let nativeSessionTitle;
|
|
7501
7589
|
let nativeSessionTitlePrompt;
|
|
7502
|
-
if (agentCfg.cliId === 'codex' && !ds
|
|
7590
|
+
if (agentCfg.cliId === 'codex' && !isSharedAdoptSession(ds)) {
|
|
7503
7591
|
const isFreshNativeSession = !resume && !ds.session.cliSessionId;
|
|
7504
7592
|
const titlePrompt = extractBotmuxLarkNativeSessionTitlePrompt(promptPayload.codexAppInput?.text ?? prompt, bot.botName ? [{ name: bot.botName }] : undefined);
|
|
7505
7593
|
if (isFreshNativeSession && !ds.session.nativeSessionTitleUserDefined) {
|
|
@@ -7735,17 +7823,28 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
7735
7823
|
reason: 'worker_fork_error',
|
|
7736
7824
|
message: reason,
|
|
7737
7825
|
});
|
|
7738
|
-
//
|
|
7739
|
-
//
|
|
7740
|
-
//
|
|
7741
|
-
|
|
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)) {
|
|
7742
7838
|
logger.info(`[${t}] Managed/silent fork failure kept out of auxiliary Lark UI `
|
|
7743
7839
|
+ `turn=${initAttributionTurnId?.slice(0, 12) ?? '-'} attempt=${initDispatchAttempt}: ${reason}`);
|
|
7744
7840
|
return;
|
|
7745
7841
|
}
|
|
7746
7842
|
const cliName = sessionCliDisplayName(ds, botCfg);
|
|
7747
7843
|
const message = tr('worker.start_failed', { cliName, reason }, botLocale(botCfg));
|
|
7748
|
-
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
|
|
7749
7848
|
? { sourceSessionId: ds.session.sessionId }
|
|
7750
7849
|
: undefined).catch(replyErr => logger.error(`[${t}] Failed to deliver worker fork error to Lark: ${replyErr}`));
|
|
7751
7850
|
});
|
|
@@ -7788,8 +7887,16 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
7788
7887
|
// dsh runner turn timeout: read live from bot config so tuning bots.json
|
|
7789
7888
|
// takes effect on the next worker fork without recreating the session.
|
|
7790
7889
|
turnTimeoutMs: botCfg.turnTimeoutMs,
|
|
7890
|
+
// dsh runtime variant (official runner vs dsh-tui PTY TUI).
|
|
7891
|
+
dshRuntime: botCfg.dshRuntime,
|
|
7791
7892
|
disableCliBypass: botCfg.disableCliBypass === true,
|
|
7792
|
-
|
|
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 } : {}),
|
|
7793
7900
|
// Startup commands run on every fresh spawn (incl. resume) so session-only
|
|
7794
7901
|
// settings like `/effort ultracode` are re-established. Adopt sessions are
|
|
7795
7902
|
// observed, not driven — forkAdoptWorker intentionally omits this.
|
|
@@ -8153,6 +8260,24 @@ function invalidateTuiPrompt(ds, reason, outcome = 'failed') {
|
|
|
8153
8260
|
clearTuiPromptAuthority(ds);
|
|
8154
8261
|
publishAttentionPatch(ds);
|
|
8155
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
|
+
}
|
|
8156
8281
|
function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureNotified: false }, reservedWorkerGeneration) {
|
|
8157
8282
|
const cb = requireCallbacks();
|
|
8158
8283
|
const t = tag(ds);
|
|
@@ -8218,13 +8343,35 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8218
8343
|
};
|
|
8219
8344
|
/** Auxiliary worker UI is never an authorized output channel for a dedicated
|
|
8220
8345
|
* VC receiver. Dashboard/audit state is still updated before these guards. */
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8226
|
-
|
|
8227
|
-
|
|
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
|
+
};
|
|
8228
8375
|
/** final_output is the sole exception: listener_thread and exact IM replies
|
|
8229
8376
|
* may proceed into the durable action ledger; silent/stale attempts do not. */
|
|
8230
8377
|
const managedFinalOutputSuppressed = (turnId, dispatchAttempt) => {
|
|
@@ -8232,13 +8379,16 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8232
8379
|
return true;
|
|
8233
8380
|
if (isTriggerFinalSuppressed(ds, turnId))
|
|
8234
8381
|
return true;
|
|
8235
|
-
|
|
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)) {
|
|
8236
8386
|
return ordinaryManagedSuppression(turnId, dispatchAttempt);
|
|
8237
8387
|
}
|
|
8238
8388
|
// Resolve every Lark-facing worker event against durable origin state. The
|
|
8239
8389
|
// receipt freezes responseMode, so terminal→idle updates and daemon restore
|
|
8240
8390
|
// cannot become loud merely because an in-memory suppression map was
|
|
8241
|
-
// cleared/lost. Missing attribution on a
|
|
8391
|
+
// cleared/lost. Missing attribution on a meeting-driven turn fails closed.
|
|
8242
8392
|
const decision = evaluateVcMeetingManagedSend(config.session.dataDir, {
|
|
8243
8393
|
receiverSessionId: ds.session.sessionId,
|
|
8244
8394
|
receiverSession: true,
|
|
@@ -8280,9 +8430,75 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8280
8430
|
logger.error(`[${t}] Failed to deliver worker startup failure to Lark: ${err?.message ?? err}`);
|
|
8281
8431
|
}
|
|
8282
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
|
+
};
|
|
8283
8499
|
// Adopt mode flags — computed once, used in all buildStreamingCard calls.
|
|
8284
8500
|
// Bridge mode (the v3 default for /adopt) hides the legacy takeover button.
|
|
8285
|
-
const isAdopt =
|
|
8501
|
+
const isAdopt = isSharedAdoptSession(ds);
|
|
8286
8502
|
const showTakeover = false;
|
|
8287
8503
|
worker.on('message', async (msg) => {
|
|
8288
8504
|
// Every IPC message is scoped to the child generation that emitted it.
|
|
@@ -8436,6 +8652,14 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8436
8652
|
}
|
|
8437
8653
|
startupState.ready = true;
|
|
8438
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
|
+
}
|
|
8439
8663
|
// Restore the daemon-owned display mode BEFORE any card handling: a
|
|
8440
8664
|
// fresh worker always boots with displayMode='hidden', and every early
|
|
8441
8665
|
// break below (managed / replyAlreadySent / streamingCardDisabled /
|
|
@@ -8606,8 +8830,10 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8606
8830
|
// POST a second card; the in-flight POST becomes this turn's card.
|
|
8607
8831
|
if (ds.streamCardId === CARD_POSTING_SENTINEL)
|
|
8608
8832
|
break;
|
|
8833
|
+
const pendingNewTurn = !!ds.streamCardPending;
|
|
8609
8834
|
const postingGeneration = ds.streamCardTurnGeneration ?? 0;
|
|
8610
8835
|
const cardReplyTarget = captureStreamingCardReplyTarget(ds, msg.turnId);
|
|
8836
|
+
const statusRevisionAtPost = ds.streamCardStatusRevision ?? 0;
|
|
8611
8837
|
ds.streamCardId = CARD_POSTING_SENTINEL;
|
|
8612
8838
|
try {
|
|
8613
8839
|
ds.streamCardNonce = randomBytes(4).toString('hex');
|
|
@@ -8615,7 +8841,9 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8615
8841
|
// See PATCH-branch comment above re: lastScreenStatus preference.
|
|
8616
8842
|
// For relay (kill+fork with surviving tmux/CLI), this avoids the
|
|
8617
8843
|
// jarring "启动中" right after the M1 "已接力" announcement.
|
|
8618
|
-
const initStatus =
|
|
8844
|
+
const initStatus = pendingNewTurn
|
|
8845
|
+
? turnStartingCardStatus(ds, effectiveCliId)
|
|
8846
|
+
: (ds.usageLimit ? 'limited' : (ds.lastScreenStatus ?? 'starting'));
|
|
8619
8847
|
const streamCardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readOnlyUrl, initTitle,
|
|
8620
8848
|
// For /relay resume, ds.lastScreenContent is the cached pane
|
|
8621
8849
|
// from before the kill+fork — using it avoids a blank flash
|
|
@@ -8656,6 +8884,9 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8656
8884
|
// resume where the CLI kept running), arm here — same authorized arm
|
|
8657
8885
|
// point as the reuse branch, now that streamCardId is the real id.
|
|
8658
8886
|
syncUsageRefreshTimer(ds);
|
|
8887
|
+
if (!superseded) {
|
|
8888
|
+
reconcilePostedStartingCard(ds, cardReplyTarget.turnId, statusRevisionAtPost);
|
|
8889
|
+
}
|
|
8659
8890
|
if (superseded && ds.streamCardPendingTurnId) {
|
|
8660
8891
|
void postTurnStartingCard(ds, cb.sessionReply, ds.streamCardPendingTurnId);
|
|
8661
8892
|
}
|
|
@@ -8676,7 +8907,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8676
8907
|
// Fallback: send static session card
|
|
8677
8908
|
try {
|
|
8678
8909
|
const localCliReadyAtBuild = isLocalCliOpenReady(ds, { cliId: effectiveCliId });
|
|
8679
|
-
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));
|
|
8680
8911
|
const fallbackCardId = await scopedReply(cardJson, 'interactive', msg.turnId);
|
|
8681
8912
|
if (!ownsLifecycleMutation()) {
|
|
8682
8913
|
void deleteMessage(ds.larkAppId, fallbackCardId).catch(() => { });
|
|
@@ -8684,7 +8915,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8684
8915
|
}
|
|
8685
8916
|
if (!localCliReadyAtBuild && isLocalCliOpenEnabled()
|
|
8686
8917
|
&& isLocalCliOpenReady(ds, { cliId: effectiveCliId })) {
|
|
8687
|
-
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));
|
|
8688
8919
|
try {
|
|
8689
8920
|
await updateMessage(ds.larkAppId, fallbackCardId, readyCardJson);
|
|
8690
8921
|
}
|
|
@@ -8878,7 +9109,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
8878
9109
|
const prevStatus = ds.lastScreenStatus;
|
|
8879
9110
|
updateUsageLimitState(ds, msg.usageLimit);
|
|
8880
9111
|
ds.lastScreenContent = msg.content;
|
|
8881
|
-
ds.lastScreenStatus = (msg.
|
|
9112
|
+
ds.lastScreenStatus = resolveUsageAwareScreenStatus(ds, msg.status, msg.usageLimit);
|
|
8882
9113
|
bumpStreamCardStatusRevision(ds);
|
|
8883
9114
|
// A suspend that arrived mid-turn parked itself here. Defer until this
|
|
8884
9115
|
// screen_update has finished using process state — suspendWorker nulls
|
|
@@ -9073,7 +9304,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
9073
9304
|
break;
|
|
9074
9305
|
const prevStatus = ds.lastScreenStatus;
|
|
9075
9306
|
updateUsageLimitState(ds, msg.usageLimit);
|
|
9076
|
-
ds.lastScreenStatus = (msg.
|
|
9307
|
+
ds.lastScreenStatus = resolveUsageAwareScreenStatus(ds, msg.status, msg.usageLimit);
|
|
9077
9308
|
bumpStreamCardStatusRevision(ds);
|
|
9078
9309
|
// Same deferred-suspend checkpoint as the screen_update branch, and
|
|
9079
9310
|
// deferred for the same reason (see runPendingSuspendIfSettled).
|
|
@@ -9433,9 +9664,45 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
9433
9664
|
break;
|
|
9434
9665
|
}
|
|
9435
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
|
+
}
|
|
9436
9703
|
// Do NOT auto-restart in adopt mode — there's nothing to restart
|
|
9437
|
-
if (ds
|
|
9438
|
-
logger.info(`[${t}]
|
|
9704
|
+
if (isSharedAdoptSession(ds)) {
|
|
9705
|
+
logger.info(`[${t}] Shared adopted session ended`);
|
|
9439
9706
|
// Freeze the streaming card
|
|
9440
9707
|
if (!suppressExitUi && ds.streamCardId && workerHasInitialized(ds)) {
|
|
9441
9708
|
const readUrl = readableTerminalUrlFor(ds);
|
|
@@ -9571,7 +9838,12 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
9571
9838
|
logger.error(`[${t}] Worker error: ${msg.message}`);
|
|
9572
9839
|
// `error` is a fatal launch-generation signal. It normally arrives
|
|
9573
9840
|
// during init, but can also follow a previously-ready worker whose CLI
|
|
9574
|
-
// 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;
|
|
9575
9847
|
await notifyStartupFailure(msg.message, msg.turnId, msg.dispatchAttempt);
|
|
9576
9848
|
break;
|
|
9577
9849
|
}
|
|
@@ -10403,14 +10675,19 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
10403
10675
|
ds.worker = null;
|
|
10404
10676
|
ds.workerReady = false;
|
|
10405
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;
|
|
10406
10686
|
// A queued suspend for THIS generation is now moot — the worker it was
|
|
10407
10687
|
// about is gone. Leaving it set would suspend the replacement on its
|
|
10408
10688
|
// first idle (the deferred checkpoint's own "worker gone" branch cannot
|
|
10409
10689
|
// help: it only runs on a screen update that will never arrive).
|
|
10410
10690
|
clearPendingSuspendClaim(ds, 'worker exited');
|
|
10411
|
-
// Dead worker generation — stop the periodic usage refresh immediately
|
|
10412
|
-
// instead of waiting a tick for it to self-clear on !workerHasInitialized.
|
|
10413
|
-
clearUsageRefreshTimer(ds);
|
|
10414
10691
|
ds.workerToken = null;
|
|
10415
10692
|
ds.workerViewToken = null;
|
|
10416
10693
|
ds.managedTurnOrigin = undefined;
|
|
@@ -10438,6 +10715,13 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
10438
10715
|
sessionStore.updateSession(ds.session);
|
|
10439
10716
|
if (exitedPreviewTarget !== undefined)
|
|
10440
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
|
+
});
|
|
10441
10725
|
}
|
|
10442
10726
|
if (!transferRetirement) {
|
|
10443
10727
|
try {
|
|
@@ -10568,10 +10852,11 @@ async function finishTurnReactions(ds) {
|
|
|
10568
10852
|
return;
|
|
10569
10853
|
// Detach the batch first so a second idle edge can't double-flip it.
|
|
10570
10854
|
ds.pendingAckReactions = [];
|
|
10571
|
-
//
|
|
10572
|
-
//
|
|
10573
|
-
|
|
10574
|
-
|
|
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.
|
|
10575
10860
|
const silent = silentTurnReactions(ds);
|
|
10576
10861
|
const doneEmoji = doneReactionEmojiFor(ds);
|
|
10577
10862
|
for (const ack of list) {
|
|
@@ -10755,7 +11040,14 @@ function deliverFinalOutput(ds, msg, t, attempt, onComplete, isStillOwned = () =
|
|
|
10755
11040
|
const imOrigin = msg.dispatchAttempt === undefined
|
|
10756
11041
|
? resolveVcMeetingImTurnOrigin(ds.session, msg.turnId)
|
|
10757
11042
|
: undefined;
|
|
10758
|
-
|
|
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)
|
|
10759
11051
|
? evaluateVcMeetingManagedSend(config.session.dataDir, {
|
|
10760
11052
|
receiverSessionId: ds.session.sessionId,
|
|
10761
11053
|
receiverSession: true,
|
|
@@ -10773,7 +11065,7 @@ function deliverFinalOutput(ds, msg, t, attempt, onComplete, isStillOwned = () =
|
|
|
10773
11065
|
return;
|
|
10774
11066
|
}
|
|
10775
11067
|
const revalidateManagedSend = () => {
|
|
10776
|
-
if (!ds.
|
|
11068
|
+
if (!isMeetingDrivenTurn(ds, msg.turnId, msg.dispatchAttempt))
|
|
10777
11069
|
return;
|
|
10778
11070
|
const current = evaluateVcMeetingManagedSend(config.session.dataDir, {
|
|
10779
11071
|
receiverSessionId: ds.session.sessionId,
|
|
@@ -10851,11 +11143,14 @@ function deliverFinalOutput(ds, msg, t, attempt, onComplete, isStillOwned = () =
|
|
|
10851
11143
|
const feedbackRequesterSubjectId = recipientOpenId;
|
|
10852
11144
|
const feedback = feedbackPolicy && feedbackRequesterSubjectId ? { policy: feedbackPolicy } : undefined;
|
|
10853
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));
|
|
10854
11151
|
const cardJson = msg.kind === 'local-turn' || msg.kind === 'local-turn-headless'
|
|
10855
11152
|
? buildContextualReplyCard({
|
|
10856
|
-
title:
|
|
10857
|
-
? tr('card.local_turn_resumed', undefined, localeForBot(ds.larkAppId))
|
|
10858
|
-
: tr('card.local_turn', undefined, localeForBot(ds.larkAppId)),
|
|
11153
|
+
title: localTurnTitle,
|
|
10859
11154
|
userText: msg.kind === 'local-turn' ? safeUserText ?? '' : undefined,
|
|
10860
11155
|
assistantText: safeAssistantText,
|
|
10861
11156
|
assistantLabel: storedSessionCliDisplayName(ds),
|
|
@@ -11314,6 +11609,7 @@ export function forkAdoptWorker(ds, opts) {
|
|
|
11314
11609
|
cliSessionId: isStructuredBridge ? adopted.sessionId : undefined,
|
|
11315
11610
|
model: agentCfg.model,
|
|
11316
11611
|
turnTimeoutMs: botCfg.turnTimeoutMs,
|
|
11612
|
+
dshRuntime: botCfg.dshRuntime,
|
|
11317
11613
|
disableCliBypass: botCfg.disableCliBypass === true,
|
|
11318
11614
|
codexRpcInput: botCfg.codexRpcInput === true || config.codexRpcInputDefault,
|
|
11319
11615
|
// Adopt is normally observe-only (prompt=''), driven later by 'message'
|
|
@@ -11528,7 +11824,20 @@ export function killStalePids(activeSessions_, runtimeSessions) {
|
|
|
11528
11824
|
try {
|
|
11529
11825
|
// Check if process exists (signal 0 doesn't kill, just checks)
|
|
11530
11826
|
process.kill(session.pid, 0);
|
|
11531
|
-
//
|
|
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.
|
|
11532
11841
|
logger.info(`Killing stale CLI process (pid: ${session.pid}, session: ${session.sessionId})`);
|
|
11533
11842
|
try {
|
|
11534
11843
|
process.kill(-session.pid, 'SIGTERM');
|
|
@@ -11794,6 +12103,12 @@ function cleanupPersistentBackendSessions(backendType, activeSessions_, runtimeS
|
|
|
11794
12103
|
for (const session of activeSessions_) {
|
|
11795
12104
|
if (!belongsToBackend(session))
|
|
11796
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;
|
|
11797
12112
|
const sessionCliId = session.cliId;
|
|
11798
12113
|
if (!sessionCliId || !session.larkAppId)
|
|
11799
12114
|
continue;
|