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/worker.js
CHANGED
|
@@ -15,15 +15,16 @@
|
|
|
15
15
|
import { createHash, randomBytes } from 'node:crypto';
|
|
16
16
|
import { accessSync, chmodSync, mkdirSync, writeFileSync, unlinkSync, rmdirSync, existsSync, statSync, lstatSync, readdirSync, readlinkSync, readFileSync, realpathSync, copyFileSync, watch as fsWatch, createWriteStream, constants as fsConstants } from 'node:fs';
|
|
17
17
|
import { atomicWriteFileSync } from './utils/atomic-write.js';
|
|
18
|
-
import { join, basename, dirname, delimiter } from 'node:path';
|
|
18
|
+
import { join, basename, dirname, delimiter, relative } from 'node:path';
|
|
19
19
|
import { resolveBotmuxWrapperBinDir, prependBotmuxBin } from './core/botmux-wrapper.js';
|
|
20
20
|
import { homedir, tmpdir, userInfo } from 'node:os';
|
|
21
21
|
import { spawnSync } from 'node:child_process';
|
|
22
22
|
import { evaluateCredentialOnlyIsolationGate, credentialIsolationRequired, deviceCredentialIsolationMarkerPath, isCredentialIsolationReservedBasename, buildCredentialIsolationRules, buildSeatbeltProfile, isolatedPaneOriginChannel, isolatedPaneReattachSafe, evaluatePersistentPaneMigration, executePersistentPaneMigration, persistentTeardownKillKind, isolationPaneMarkerPath, policyOffTombstonePath, policyOffTombstoneContent, policyOffTombstoneValid, provenancePendingContent, provenancePendingNonce, sendCredFilePath, botHomePath, buildCliExecutableReadCarveOuts, isolationPaneMarkerContent, isolationPanePolicyDigest, } from './adapters/cli/read-isolation.js';
|
|
23
|
-
import { buildFsPolicy, compileToSeatbelt, migrateLegacySandboxFields, resolveRedirectedAdapterAuthPaths, FsPolicyConfigError } from './adapters/cli/fs-policy.js';
|
|
23
|
+
import { buildFsPolicy, compileToSeatbelt, migrateLegacySandboxFields, resolveRedirectedAdapterAuthPaths, resolveLarkCliLinuxStoreDir, larkCliChildDataRoot, FsPolicyConfigError } from './adapters/cli/fs-policy.js';
|
|
24
24
|
import { buildCloseResultMessage, normalizeDestroyResult, mayRestoreWriteAdmission, interpretAbortOutcome, classifyRestartTeardown } from './adapters/backend/destroy-result.js';
|
|
25
25
|
import { isRemoteBackendType, killPersistentBackendTarget, killPersistentSession, probePersistentBackendTarget, probePersistentSession, shouldRejectPersistentPostKillProbe } from './core/persistent-backend.js';
|
|
26
26
|
import { finalizeRawCommandDelivery, writeRawCommandLine } from './core/raw-command-writer.js';
|
|
27
|
+
import { decodeTerminalWriteFrameSource, terminalWriteFrame, } from './core/terminal-write-frame.js';
|
|
27
28
|
import { rawCommandWriteOptionsFor } from './core/raw-command-write-options.js';
|
|
28
29
|
import { publishCliSessionIdToDaemon } from './core/cli-session-id-publisher.js';
|
|
29
30
|
import { readProcessStartIdentity } from './core/session-marker.js';
|
|
@@ -46,6 +47,7 @@ import { remoteWorkerShutdownInputBlocker } from './core/remote-worker-shutdown-
|
|
|
46
47
|
import { ReadyGate, shouldArmReadyGate } from './utils/ready-gate.js';
|
|
47
48
|
import { shouldRunStartupCommandsOnSpawn, shouldDeferInitialPromptForStartup } from './core/startup-commands.js';
|
|
48
49
|
import { sanitizePerBotEnv } from './core/per-bot-env.js';
|
|
50
|
+
import { normalizeExistingAppServerEndpoint } from './core/existing-app-server.js';
|
|
49
51
|
import { resolveChildBotsConfig } from './core/config-dir.js';
|
|
50
52
|
import { evaluateVcMeetingManagedSend, } from './services/vc-meeting-send-policy.js';
|
|
51
53
|
import { TurnTerminalDeduper } from './services/turn-terminal-deduper.js';
|
|
@@ -58,7 +60,7 @@ import { readSessionMcpRuntimeManifest, sessionMcpRuntimeHostOnlyPaths, } from '
|
|
|
58
60
|
import { prepareCliPluginGeneration } from './core/plugins/cli-generation.js';
|
|
59
61
|
import { loadBotConfigs, resolveBrandLabel, resolveUsageDisplay, } from './bot-registry.js';
|
|
60
62
|
import { readGlobalConfig, isWorkflowFeatureEnabled } from './global-config.js';
|
|
61
|
-
import { deriveTerminalWriteToken, resolveTerminalAccessForRequest, safeTerminalTokenEqual, } from './core/terminal-write-auth.js';
|
|
63
|
+
import { deriveTerminalWriteToken, resolveTerminalAccessForRequest, safeTerminalTokenEqual, TERMINAL_PLATFORM_READONLY_HINT_HEADER, } from './core/terminal-write-auth.js';
|
|
62
64
|
import { deriveWorkerViewGeneration, looksLikeTerminalControlGrant, TERMINAL_VIEW_FORWARD_HEADER, verifyTerminalControlGrant, verifyTerminalViewForward, } from './core/terminal-control-grant.js';
|
|
63
65
|
import { appendControlAudit, controlAuditRecord } from './dashboard/control-audit.js';
|
|
64
66
|
import { readPlatformBinding } from './platform/binding.js';
|
|
@@ -81,6 +83,7 @@ import { currentHermesStateOffset, drainHermesStateDb, resolveHermesStateDbPath
|
|
|
81
83
|
import { filterHermesEventsForBotmuxSession } from './services/hermes-session-filter.js';
|
|
82
84
|
import { currentMtrSessionOffset, drainMtrSession, findLatestMtrSessionByDirectory, findMtrSessionById } from './services/mtr-transcript.js';
|
|
83
85
|
import { drainPiTranscript } from './services/pi-transcript.js';
|
|
86
|
+
import { drainOmpTranscript } from './services/omp-transcript.js';
|
|
84
87
|
import { drainGrokUpdates, findGrokSessionByPid, grokSessionIdFromPath, } from './services/grok-transcript.js';
|
|
85
88
|
import { resolveFileBridgePath } from './services/file-bridge-path.js';
|
|
86
89
|
import { isStructuredBridgeAdoptIdleCli, isStructuredBridgeAdoptInputCli, isStructuredBridgeFallbackActive, isStructuredBridgeLifecycleBlockingCli, } from './services/structured-bridge-clis.js';
|
|
@@ -96,6 +99,7 @@ import { listenWebTerminalWithFallback } from './utils/web-terminal-listen.js';
|
|
|
96
99
|
import { HerdrWebTerminalBinding } from './utils/herdr-web-terminal-binding.js';
|
|
97
100
|
import { TERMINAL_FAVICON_DATA_URI } from './utils/terminal-favicon.js';
|
|
98
101
|
import { t, setDefaultLocale } from './i18n/index.js';
|
|
102
|
+
import { registerPromptOverrideResolver } from './skills/effective-builtins.js';
|
|
99
103
|
import { TerminalRenderer } from './utils/terminal-renderer.js';
|
|
100
104
|
import { DEFAULT_RENDER_COLS, DEFAULT_RENDER_ROWS, MAX_RENDER_COLS, MAX_RENDER_ROWS, MIN_RENDER_COLS, MIN_RENDER_ROWS, clamp, resolveRenderDimensions, } from './utils/render-dimensions.js';
|
|
101
105
|
import { createCliAdapterSync, locateOnPath } from './adapters/cli/registry.js';
|
|
@@ -107,6 +111,7 @@ import { delay } from './utils/timing.js';
|
|
|
107
111
|
import { claudeJsonlPathForSession, resolveJsonlFromPid, findOpenClaudeSessionIds, syncClaudeResumeTargetToCwd, DEFAULT_CLAUDE_DATA_DIR } from './adapters/cli/claude-code.js';
|
|
108
112
|
import { sessionReadyHookCommand } from './adapters/hook-command.js';
|
|
109
113
|
import { mtrSessionIdForBotmuxSession } from './adapters/cli/mtr.js';
|
|
114
|
+
import { ompSessionDir } from './adapters/cli/oh-my-pi.js';
|
|
110
115
|
import { strictInputHandle } from './adapters/cli/strict-input-handle.js';
|
|
111
116
|
import { HerdrBackend } from './adapters/backend/herdr-backend.js';
|
|
112
117
|
import { TmuxBackend } from './adapters/backend/tmux-backend.js';
|
|
@@ -130,7 +135,7 @@ import { tmuxEnv, probeTmuxFunctionalWithRetry } from './setup/ensure-tmux.js';
|
|
|
130
135
|
import { probeZmxVersion } from './setup/ensure-zmx.js';
|
|
131
136
|
import { tmuxRestartJitterMs } from './core/tmux-recovery.js';
|
|
132
137
|
import { IdleDetector } from './utils/idle-detector.js';
|
|
133
|
-
import { StuckDetector, matchHookReviewScreen } from './utils/stuck-detector.js';
|
|
138
|
+
import { StuckDetector, matchHookReviewScreen, shouldHoldInputForHookReview, } from './utils/stuck-detector.js';
|
|
134
139
|
import { processStuckWarningTuiKeys, shouldRearmStuckDetector } from './utils/stuck-key-guard.js';
|
|
135
140
|
import { sendTuiKeySequence, submitTuiTextInput } from './utils/tui-input-delivery.js';
|
|
136
141
|
import { captureToPng } from './utils/screenshot-renderer.js';
|
|
@@ -138,7 +143,8 @@ import { snapshotToPng, snapshotToText, shouldCaptureScreen, isScreenSelfDriven
|
|
|
138
143
|
import { chooseWebTerminalSeed } from './utils/web-terminal-seed.js';
|
|
139
144
|
import { mergeHerdrWebSnapshot, renderHerdrWebHistory, } from './utils/herdr-web-history.js';
|
|
140
145
|
import { parseWorkerRequestUrl } from './utils/worker-http.js';
|
|
141
|
-
import {
|
|
146
|
+
import { structuredRateLimitState, isStructuredRateLimitAuthoritative } from './utils/cli-usage-limit.js';
|
|
147
|
+
import { createUsageLimitTracker } from './utils/usage-limit-tracker.js';
|
|
142
148
|
import { uploadImageBuffer } from './utils/lark-upload.js';
|
|
143
149
|
import { applySessionOwnerEnv, redactChildEnv, scrubClaudeSessionMarkerEnv, scrubSessionCliHomeEnv } from './utils/child-env.js';
|
|
144
150
|
import { decideSubmitConfirmationAction, settleDeferredSubmitConfirmation, settleStaleWriteContinuation, } from './services/submit-confirmation.js';
|
|
@@ -153,7 +159,7 @@ import { fetchDaemonIpc } from './core/daemon-ipc-auth.js';
|
|
|
153
159
|
import { withCodexAppContext } from './utils/codex-app-context.js';
|
|
154
160
|
import { resolveCodexAppFinalTurnIdentity } from './adapters/cli/codex-app-turn.js';
|
|
155
161
|
import { RunnerControlDecoder } from './adapters/cli/runner-control-channel.js';
|
|
156
|
-
import { normalizeCodexAppLifecycleEvent, normalizeFinalUsage, } from './services/codex-app-runner-protocol.js';
|
|
162
|
+
import { CODEX_APP_ACTIVE_WRITER_EXIT_CODE, normalizeCodexAppLifecycleEvent, normalizeFinalUsage, } from './services/codex-app-runner-protocol.js';
|
|
157
163
|
import { hasMatchingManagedOriginCapability, ensureManagedOriginAttestationDirectory, ensureManagedOriginCapabilityLeafSafe, ensureManagedOriginDataRootProbe, ensureManagedOriginIsolationSentinel, ensureManagedOriginRootLocator, managedOriginLegacyIsolationProbeAccess, managedOriginDataRootProbeAccess, managedOriginIsolationSentinelAccess, managedOriginAttestationDirectory, managedOriginCapabilityDirectory, managedOriginCapabilityPath, managedOriginRootLocatorPath, readManagedOriginAuthorityFile, RELAY_ORIGIN_CAPABILITY_BASENAME, replaceManagedOriginCapabilityFile, sweepManagedOriginAttestationProofs, } from './core/managed-origin-capability.js';
|
|
158
164
|
import { CodexRpcEngine, } from './codex-rpc-engine.js';
|
|
159
165
|
import { beginRuntimeWriteCycle, isCliBackendGenerationCurrent, PtyOutputGeneration, projectRuntimeScreenStatus, } from './utils/runtime-screen-status.js';
|
|
@@ -1410,6 +1416,7 @@ const adoptWriteQueue = new AsyncSerialQueue();
|
|
|
1410
1416
|
* adapter's hermesBridgeAttach reads the correct mode. */
|
|
1411
1417
|
let lastSpawnEffectiveResume = false;
|
|
1412
1418
|
let lastSpawnEffectiveCliSessionId;
|
|
1419
|
+
let lastSpawnEffectiveAdapterSessionId;
|
|
1413
1420
|
let lastSpawnDeferInitialPrompt = false;
|
|
1414
1421
|
let lastSpawnQueuedInitialPrompt;
|
|
1415
1422
|
let lastSpawnQueuedInitialPromptLogicalContent;
|
|
@@ -1678,7 +1685,7 @@ let closeRequested = false;
|
|
|
1678
1685
|
let capturedSpawnCommand = null;
|
|
1679
1686
|
let deferredTopicOutputTail = '';
|
|
1680
1687
|
const reportedDeferredTopicRoots = new Set();
|
|
1681
|
-
const CLI_DISPLAY_NAMES = { 'claude-code': 'Claude', seed: 'Seed', relay: 'Relay', aiden: 'Aiden', coco: 'CoCo', codex: 'Codex', 'codex-app': 'Codex App', cursor: 'Cursor', gemini: 'Gemini', genius: 'Genius', opencode: 'OpenCode', opencode2: 'OpenCode 2', antigravity: 'Antigravity', mtr: 'MTR', hermes: 'Hermes', mira: 'Mira', mir: 'Mir CLI', traex: 'TRAE', pi: 'Pi', copilot: 'Copilot', 'oh-my-pi': 'Oh My Pi', kimi: 'Kimi', grok: 'Grok Build', 'kiro-cli': 'Kiro', riff: 'Riff', reasonix: 'Reasonix', dsh: 'DeepSeek Harness', mojo: 'Mojo' };
|
|
1688
|
+
const CLI_DISPLAY_NAMES = { 'claude-code': 'Claude', seed: 'Seed', relay: 'Relay', aiden: 'Aiden', coco: 'CoCo', codex: 'Codex', 'codex-app': 'Codex App', cursor: 'Cursor', gemini: 'Gemini', genius: 'Genius', opencode: 'OpenCode', opencode2: 'OpenCode 2', antigravity: 'Antigravity', mtr: 'MTR', hermes: 'Hermes', mira: 'Mira', mir: 'Mir CLI', traex: 'TRAE', pi: 'Pi', copilot: 'Copilot', 'oh-my-pi': 'Oh My Pi', kimi: 'Kimi', grok: 'Grok Build', 'kiro-cli': 'Kiro', riff: 'Riff', reasonix: 'Reasonix', dsh: 'DeepSeek Harness', 'dsh-tui': 'DeepSeek Harness TUI', mojo: 'Mojo' };
|
|
1682
1689
|
function cliName() {
|
|
1683
1690
|
return (lastInitConfig?.cliRuntime?.source === 'configured'
|
|
1684
1691
|
? (lastInitConfig.cliRuntime.displayName?.trim() || lastInitConfig.cliRuntime.id)
|
|
@@ -1793,6 +1800,38 @@ let readyPatternSeenDuringHold = false;
|
|
|
1793
1800
|
let awaitingPostSessionStartPromptEvidence = false;
|
|
1794
1801
|
/** Scoped marker set only by IdleDetector's screen-driven callback. */
|
|
1795
1802
|
let postSessionStartPromptEvidenceInFlight = false;
|
|
1803
|
+
/** A visible Codex Hook-review screen owns Enter/arrow keys. Keep queued IM
|
|
1804
|
+
* input out of that menu so an automated submit cannot accidentally select
|
|
1805
|
+
* “Trust all” or get consumed as menu navigation. */
|
|
1806
|
+
let hookReviewInputHold = false;
|
|
1807
|
+
let hookReviewInputHoldNotified = false;
|
|
1808
|
+
function refreshHookReviewInputHold(snapshot) {
|
|
1809
|
+
if (!snapshot.trim())
|
|
1810
|
+
return;
|
|
1811
|
+
const blocked = shouldHoldInputForHookReview(snapshot);
|
|
1812
|
+
if (blocked === hookReviewInputHold)
|
|
1813
|
+
return;
|
|
1814
|
+
hookReviewInputHold = blocked;
|
|
1815
|
+
if (blocked) {
|
|
1816
|
+
log('Hook review menu visible — holding queued input until the menu is resolved');
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
hookReviewInputHoldNotified = false;
|
|
1820
|
+
log('Hook review menu cleared — releasing queued input');
|
|
1821
|
+
if (hasPendingInputForFlush())
|
|
1822
|
+
queueMicrotask(() => { void flushPending(); });
|
|
1823
|
+
}
|
|
1824
|
+
function notifyHookReviewInputHold() {
|
|
1825
|
+
if (hookReviewInputHoldNotified)
|
|
1826
|
+
return;
|
|
1827
|
+
hookReviewInputHoldNotified = true;
|
|
1828
|
+
send({
|
|
1829
|
+
type: 'user_notify',
|
|
1830
|
+
turnId: currentBotmuxTurnId,
|
|
1831
|
+
message: '⚠️ Codex 正在显示 Hook 审核菜单。为避免消息误触“Trust all”或被菜单吞掉,'
|
|
1832
|
+
+ 'BotMux 已暂存后续消息;请先在终端完成 Hook 选择,恢复普通输入框后会自动投递。',
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1796
1835
|
/** Wait until the PTY has been quiet for READY_FLUSH_SETTLE_MS (Ink render
|
|
1797
1836
|
* drained), capped at READY_FLUSH_SETTLE_CAP_MS, then flush the held prompt.
|
|
1798
1837
|
* An authoritative direct ready command (Hermes) can mark prompt readiness;
|
|
@@ -2028,6 +2067,15 @@ let pendingSessionRename = null;
|
|
|
2028
2067
|
let nativeSessionTitleSyncInFlight;
|
|
2029
2068
|
let nativeSessionTitleAppliedThreadId;
|
|
2030
2069
|
let nativeSessionTitleRevision = 0;
|
|
2070
|
+
/** Every user-controlled input class that flushPending can deliver. Keep this
|
|
2071
|
+
* shared with review-menu release handling so a queued raw command, adopt
|
|
2072
|
+
* message, or native rename is not stranded after the menu disappears. */
|
|
2073
|
+
function hasPendingInputForFlush() {
|
|
2074
|
+
return pendingMessages.length > 0
|
|
2075
|
+
|| pendingAdoptMessages.length > 0
|
|
2076
|
+
|| pendingRawInputs.length > 0
|
|
2077
|
+
|| pendingSessionRename !== null;
|
|
2078
|
+
}
|
|
2031
2079
|
let nativeSessionTitleResumeUpdatedAt;
|
|
2032
2080
|
let nativeSessionTitleCurrentGenerationResume = false;
|
|
2033
2081
|
const nativeSessionTitleSyncAbortControllers = new Set();
|
|
@@ -3416,59 +3464,27 @@ function codexAppLivenessStatus(base, nowMs = Date.now()) {
|
|
|
3416
3464
|
function structuredRateLimitAuthoritative() {
|
|
3417
3465
|
return isStructuredRateLimitAuthoritative(cliAdapter);
|
|
3418
3466
|
}
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
: undefined;
|
|
3441
|
-
return turnSeq;
|
|
3442
|
-
},
|
|
3443
|
-
// Map a runtime status to a usage-limit-aware status, recording whether this
|
|
3444
|
-
// turn hit a limit (read back via detectedThisTurn).
|
|
3445
|
-
classify(content, status) {
|
|
3446
|
-
const detected = detectCliUsageLimit(content, undefined, { suppressRateKind: structuredRateLimitAuthoritative() });
|
|
3447
|
-
if (!detected.limited)
|
|
3448
|
-
return { status };
|
|
3449
|
-
const key = usageLimitStateKey(detected);
|
|
3450
|
-
if (detected.retryReady && key === suppressedRetryReadyKey) {
|
|
3451
|
-
return { status };
|
|
3452
|
-
}
|
|
3453
|
-
suppressedRetryReadyKey = undefined;
|
|
3454
|
-
detectedTurn = turnSeq;
|
|
3455
|
-
return { status: 'limited', usageLimit: detected };
|
|
3456
|
-
},
|
|
3457
|
-
detectedThisTurn(seq) {
|
|
3458
|
-
return detectedTurn === seq;
|
|
3459
|
-
},
|
|
3460
|
-
// Record a limit that came from a STRUCTURED signal (transcript error
|
|
3461
|
-
// record) rather than screen text. Mirrors classify()'s state writes so
|
|
3462
|
-
// the tracker stays coherent: mark this turn as having hit a limit (read
|
|
3463
|
-
// by detectedThisTurn for the submit-confirmation recheck) and clear any
|
|
3464
|
-
// stale retry-ready suppression. The actual emit is done by the caller.
|
|
3465
|
-
noteStructuredLimit() {
|
|
3466
|
-
suppressedRetryReadyKey = undefined;
|
|
3467
|
-
detectedTurn = turnSeq;
|
|
3468
|
-
},
|
|
3469
|
-
};
|
|
3470
|
-
}
|
|
3471
|
-
const usageLimitTracker = createUsageLimitTracker();
|
|
3467
|
+
/**
|
|
3468
|
+
* PTY-quiescence window for the usage-limit active-work gate. A `working`
|
|
3469
|
+
* status alone does not prove output is progressing (it is the default
|
|
3470
|
+
* projection whenever promptReady === false), so the screen-scan suppression
|
|
3471
|
+
* only applies while PTY output is this fresh. A non-structured CLI blocked at
|
|
3472
|
+
* a rate/quota error screen that never renders its ready prompt goes silent
|
|
3473
|
+
* past this window and is correctly detected instead of being suppressed
|
|
3474
|
+
* forever. Aligns with the StuckDetector's PTY-quiescence threshold.
|
|
3475
|
+
*/
|
|
3476
|
+
const USAGE_LIMIT_OUTPUT_ACTIVE_WINDOW_MS = 15_000;
|
|
3477
|
+
function isOutputActivelyProgressing() {
|
|
3478
|
+
return Date.now() - lastPtyActivityAtMs < USAGE_LIMIT_OUTPUT_ACTIVE_WINDOW_MS;
|
|
3479
|
+
}
|
|
3480
|
+
// Per-turn usage-limit state machine (extracted to utils/usage-limit-tracker
|
|
3481
|
+
// for unit testing). The structured-limit stickiness it owns is load-bearing:
|
|
3482
|
+
// a one-shot transcript emit must survive the daemon's working-frame self-heal
|
|
3483
|
+
// while the CLI stays genuinely blocked.
|
|
3484
|
+
const usageLimitTracker = createUsageLimitTracker({
|
|
3485
|
+
isRateKindSuppressed: structuredRateLimitAuthoritative,
|
|
3486
|
+
isOutputActive: isOutputActivelyProgressing,
|
|
3487
|
+
});
|
|
3472
3488
|
function currentUsageLimitSnapshot() {
|
|
3473
3489
|
if (!backendScreenEvidenceIsAuthoritativeForMutation())
|
|
3474
3490
|
return '';
|
|
@@ -3588,6 +3604,10 @@ let activeRuntimePublished = false;
|
|
|
3588
3604
|
const codexBridgeQueue = new CodexBridgeQueue();
|
|
3589
3605
|
let codexBridgeWatcher = null;
|
|
3590
3606
|
let codexBridgeTimer = null;
|
|
3607
|
+
let ompBridgeState = {};
|
|
3608
|
+
let ompQuietCandidateKey;
|
|
3609
|
+
let ompQuietCandidateCompleteOffset;
|
|
3610
|
+
const ompRetiredTranscriptPaths = new Set();
|
|
3591
3611
|
/** Settings are observed on the same append-only cursor as bridge output.
|
|
3592
3612
|
* The tracker owns rollout-generation clear/update semantics and publishes a
|
|
3593
3613
|
* dedicated IPC event, independent of PTY redraw frequency. */
|
|
@@ -4766,7 +4786,7 @@ function maybeEmitStructuredRateLimit(events) {
|
|
|
4766
4786
|
// Prefer a clock parsed from the record's own text ("... resets 10:40pm");
|
|
4767
4787
|
// fall back to the shared bucketed cooldown when it carries none.
|
|
4768
4788
|
const usageLimit = structuredRateLimitState(apiErrorMessageText(ev));
|
|
4769
|
-
usageLimitTracker.noteStructuredLimit();
|
|
4789
|
+
usageLimitTracker.noteStructuredLimit(usageLimit);
|
|
4770
4790
|
send({
|
|
4771
4791
|
type: 'screen_update',
|
|
4772
4792
|
content: currentUsageLimitSnapshot(),
|
|
@@ -5132,6 +5152,11 @@ function emitReadyTurns(opts = {}) {
|
|
|
5132
5152
|
const fields = formatLocalTurnFields(rawUserText, postText);
|
|
5133
5153
|
if (!fields)
|
|
5134
5154
|
continue;
|
|
5155
|
+
// A harvested answer proves the CLI recovered from any structured
|
|
5156
|
+
// limit that parked it — mirror the daemon's final_output self-heal
|
|
5157
|
+
// (worker-pool clears ds.usageLimit there) by dropping the tracker's
|
|
5158
|
+
// re-emit latch so the next classify() does not re-pin the card.
|
|
5159
|
+
usageLimitTracker.noteTurnCompleted();
|
|
5135
5160
|
send({
|
|
5136
5161
|
type: 'final_output',
|
|
5137
5162
|
content: fields.content,
|
|
@@ -5147,6 +5172,7 @@ function emitReadyTurns(opts = {}) {
|
|
|
5147
5172
|
const headlessContent = formatHeadlessLocalTurnContent(postText);
|
|
5148
5173
|
if (!headlessContent)
|
|
5149
5174
|
continue;
|
|
5175
|
+
usageLimitTracker.noteTurnCompleted();
|
|
5150
5176
|
send({
|
|
5151
5177
|
type: 'final_output',
|
|
5152
5178
|
content: headlessContent,
|
|
@@ -5164,6 +5190,7 @@ function emitReadyTurns(opts = {}) {
|
|
|
5164
5190
|
const deliveredText = turn.restoredFromJournal
|
|
5165
5191
|
? `${t('worker.bridge_restored_turn_notice')}\n\n${postText}`
|
|
5166
5192
|
: postText;
|
|
5193
|
+
usageLimitTracker.noteTurnCompleted();
|
|
5167
5194
|
send({
|
|
5168
5195
|
type: 'final_output',
|
|
5169
5196
|
content: deliveredText,
|
|
@@ -5209,6 +5236,19 @@ function codexBridgeFallbackActive() {
|
|
|
5209
5236
|
// services/structured-bridge-clis.ts (single source of the allowlist).
|
|
5210
5237
|
return isStructuredBridgeFallbackActive(lastInitConfig?.cliId, lastInitConfig?.adoptMode === true);
|
|
5211
5238
|
}
|
|
5239
|
+
/** A Codex App shared-adopt starts a SECOND official `codex --remote` client
|
|
5240
|
+
* against an external App Server/thread. It has the same transcript shape as
|
|
5241
|
+
* terminal `/adopt`, but is deliberately NOT `adoptMode`: BotMux still owns
|
|
5242
|
+
* the remote TUI and its normal Lark-input reliability rules. */
|
|
5243
|
+
function isExistingAppServerSharedBridge() {
|
|
5244
|
+
return !!lastInitConfig?.existingAppServerEndpoint;
|
|
5245
|
+
}
|
|
5246
|
+
/** `/adopt`'s split-live attach is also required for the App Server share:
|
|
5247
|
+
* existing history is absorbed while new, App-side turns after the attach
|
|
5248
|
+
* boundary can be synthesised back into the Lark conversation. */
|
|
5249
|
+
function codexBridgeUsesSplitLiveAttach() {
|
|
5250
|
+
return lastInitConfig?.adoptMode === true || isExistingAppServerSharedBridge();
|
|
5251
|
+
}
|
|
5212
5252
|
/** Only drivers with a complete normal-final + interrupted-terminal contract
|
|
5213
5253
|
* may let a transcript-started turn override the screen-ready heuristic. */
|
|
5214
5254
|
function hasStructuredLifecycleBlock() {
|
|
@@ -5241,6 +5281,9 @@ function structuredBridgeIsMtr() {
|
|
|
5241
5281
|
function structuredBridgeIsPi() {
|
|
5242
5282
|
return lastInitConfig?.cliId === 'pi';
|
|
5243
5283
|
}
|
|
5284
|
+
function structuredBridgeIsOmp() {
|
|
5285
|
+
return lastInitConfig?.cliId === 'oh-my-pi';
|
|
5286
|
+
}
|
|
5244
5287
|
function structuredBridgeIsGrok() {
|
|
5245
5288
|
return lastInitConfig?.cliId === 'grok';
|
|
5246
5289
|
}
|
|
@@ -5250,7 +5293,7 @@ function codexBridgeIsCursor() {
|
|
|
5250
5293
|
function currentHermesBridgeDbPath() {
|
|
5251
5294
|
return hermesBridgeDbPath ?? resolveHermesStateDbPath();
|
|
5252
5295
|
}
|
|
5253
|
-
function structuredBridgeIngestPath(path, offset) {
|
|
5296
|
+
function structuredBridgeIngestPath(path, offset, opts = {}) {
|
|
5254
5297
|
if (structuredBridgeIsCodex())
|
|
5255
5298
|
return drainCodexRollout(path, offset);
|
|
5256
5299
|
// adoptMode gates the drainer's bare-sentinel synthesis: adopt posts
|
|
@@ -5262,6 +5305,13 @@ function structuredBridgeIngestPath(path, offset) {
|
|
|
5262
5305
|
return drainCursorTranscript(path, offset);
|
|
5263
5306
|
if (structuredBridgeIsPi())
|
|
5264
5307
|
return drainPiTranscript(path, offset);
|
|
5308
|
+
if (structuredBridgeIsOmp()) {
|
|
5309
|
+
const result = drainOmpTranscript(path, offset, ompBridgeState, {
|
|
5310
|
+
flushTrailingFinal: opts.flushOmpTrailingFinal,
|
|
5311
|
+
});
|
|
5312
|
+
ompBridgeState = result.state;
|
|
5313
|
+
return result;
|
|
5314
|
+
}
|
|
5265
5315
|
if (structuredBridgeIsGrok())
|
|
5266
5316
|
return drainGrokUpdates(path, offset);
|
|
5267
5317
|
if (structuredBridgeIsHermes()) {
|
|
@@ -5353,12 +5403,14 @@ function codexBridgeStartTimer() {
|
|
|
5353
5403
|
// fd is process-scoped, so following that fd cannot select a sibling
|
|
5354
5404
|
// TRAE process merely because it shares the working directory.
|
|
5355
5405
|
maybeFollowTraexSessionRotationViaPid();
|
|
5406
|
+
maybeFollowOmpTranscriptRotation();
|
|
5356
5407
|
if (!codexBridgeRolloutPath) {
|
|
5357
5408
|
// Late-attach: cliSessionId (writeInput / daemon probe) then adopt
|
|
5358
5409
|
// pid. Path lookup is centralized in resolveFileBridgePath so
|
|
5359
5410
|
// adding a file-backed CLI does not grow this if-tree.
|
|
5360
|
-
//
|
|
5361
|
-
//
|
|
5411
|
+
// Terminal `/adopt` and an existing-App-Server share both need
|
|
5412
|
+
// split-live: absorb history before the attach boundary while keeping
|
|
5413
|
+
// new local/App-side turns after it available for forwarding.
|
|
5362
5414
|
const path = resolveFileBridgePath(lastInitConfig?.cliId, {
|
|
5363
5415
|
sessionId: codexBridgePendingSessionId,
|
|
5364
5416
|
cwd: lastInitConfig?.workingDir,
|
|
@@ -5387,11 +5439,12 @@ function codexBridgeStartTimer() {
|
|
|
5387
5439
|
}
|
|
5388
5440
|
codexBridgePendingSessionId = undefined;
|
|
5389
5441
|
codexAdoptPendingPid = undefined;
|
|
5390
|
-
const mode =
|
|
5442
|
+
const mode = codexBridgeUsesSplitLiveAttach() ? 'split-live' : 'fresh-empty';
|
|
5391
5443
|
codexBridgeAttach(path, mode);
|
|
5392
5444
|
}
|
|
5393
5445
|
}
|
|
5394
5446
|
codexBridgeIngest();
|
|
5447
|
+
maybeFlushOmpTrailingFinalOnQuietTick();
|
|
5395
5448
|
if (isPromptReady)
|
|
5396
5449
|
emitReadyCodexTurns();
|
|
5397
5450
|
}
|
|
@@ -5493,6 +5546,9 @@ function mtrBridgeIngest() {
|
|
|
5493
5546
|
}
|
|
5494
5547
|
}
|
|
5495
5548
|
function codexBridgeAttach(rolloutPath, mode) {
|
|
5549
|
+
ompBridgeState = {};
|
|
5550
|
+
ompQuietCandidateKey = undefined;
|
|
5551
|
+
ompQuietCandidateCompleteOffset = undefined;
|
|
5496
5552
|
codexBridgeRolloutPath = rolloutPath;
|
|
5497
5553
|
if (structuredBridgeIsCodex())
|
|
5498
5554
|
codexServiceTierTracker.bind(rolloutPath);
|
|
@@ -5676,6 +5732,9 @@ function codexBridgeDetachFile() {
|
|
|
5676
5732
|
codexBridgeOffset = 0;
|
|
5677
5733
|
codexBridgePendingTail = '';
|
|
5678
5734
|
codexBridgeBaselineDone = false;
|
|
5735
|
+
ompBridgeState = {};
|
|
5736
|
+
ompQuietCandidateKey = undefined;
|
|
5737
|
+
ompQuietCandidateCompleteOffset = undefined;
|
|
5679
5738
|
}
|
|
5680
5739
|
/** Resolve the pid of the Codex process this worker observes (spawned child or
|
|
5681
5740
|
* adopted pane), mirroring the grok/traex pid-follow resolution order. */
|
|
@@ -5796,7 +5855,7 @@ function codexBridgeNotifyCliSessionId(cliSessionId) {
|
|
|
5796
5855
|
const pid = currentCodexObservedPid();
|
|
5797
5856
|
const next = resolveFileBridgePath('codex', { sessionId: cliSessionId });
|
|
5798
5857
|
if (next && next !== codexBridgeRolloutPath) {
|
|
5799
|
-
const attachMode =
|
|
5858
|
+
const attachMode = codexBridgeUsesSplitLiveAttach() ? 'split-live' : 'fresh-empty';
|
|
5800
5859
|
log(`Codex session binding corrected ${currentSid ?? '?'} → ${cliSessionId} (pid ${pid} owns it); re-attaching bridge to ${next}`);
|
|
5801
5860
|
codexBridgeDetachFile();
|
|
5802
5861
|
codexBridgePendingSessionId = undefined;
|
|
@@ -5925,7 +5984,7 @@ function codexBridgeNotifyCliSessionId(cliSessionId) {
|
|
|
5925
5984
|
}
|
|
5926
5985
|
return;
|
|
5927
5986
|
}
|
|
5928
|
-
// Codex INITIAL attach (no prior rollout bound). The multi-fd adopt case
|
|
5987
|
+
// Codex INITIAL attach (no prior rollout bound). The multi-fd terminal-adopt case
|
|
5929
5988
|
// reaches here with codexBridgeRolloutPath still unset: findCodexRolloutByPid
|
|
5930
5989
|
// returned undefined (ambiguous parent+sibling), so the adopt block armed the
|
|
5931
5990
|
// poller instead of attaching. The cliSessionId is normally already
|
|
@@ -5963,7 +6022,7 @@ function codexBridgeNotifyCliSessionId(cliSessionId) {
|
|
|
5963
6022
|
});
|
|
5964
6023
|
if (path) {
|
|
5965
6024
|
codexBridgePendingSessionId = undefined;
|
|
5966
|
-
codexBridgeAttach(path, 'fresh-empty');
|
|
6025
|
+
codexBridgeAttach(path, codexBridgeUsesSplitLiveAttach() ? 'split-live' : 'fresh-empty');
|
|
5967
6026
|
}
|
|
5968
6027
|
else {
|
|
5969
6028
|
codexBridgePendingSessionId = cliSessionId;
|
|
@@ -6015,6 +6074,30 @@ function maybeFollowTraexSessionRotationViaPid() {
|
|
|
6015
6074
|
persistCliSessionId(observed.cliSessionId);
|
|
6016
6075
|
codexBridgeNotifyCliSessionId(observed.cliSessionId);
|
|
6017
6076
|
}
|
|
6077
|
+
/** OMP may create a new JSONL in the same isolated Botmux session directory.
|
|
6078
|
+
* Follow only that exact directory; no pid/global-session discovery is used. */
|
|
6079
|
+
function maybeFollowOmpTranscriptRotation() {
|
|
6080
|
+
if (!structuredBridgeIsOmp() || !codexBridgeRolloutPath
|
|
6081
|
+
|| !lastSpawnEffectiveAdapterSessionId)
|
|
6082
|
+
return;
|
|
6083
|
+
const next = resolveFileBridgePath('oh-my-pi', {
|
|
6084
|
+
sessionId: lastSpawnEffectiveAdapterSessionId,
|
|
6085
|
+
});
|
|
6086
|
+
if (!next || next === codexBridgeRolloutPath || ompRetiredTranscriptPaths.has(next))
|
|
6087
|
+
return;
|
|
6088
|
+
const retired = codexBridgeRolloutPath;
|
|
6089
|
+
try {
|
|
6090
|
+
codexBridgeIngest();
|
|
6091
|
+
emitReadyCodexTurns();
|
|
6092
|
+
}
|
|
6093
|
+
catch (err) {
|
|
6094
|
+
log(`OMP pre-rotation bridge drain failed: ${err.message}`);
|
|
6095
|
+
}
|
|
6096
|
+
log(`OMP transcript rotated: ${codexBridgeRolloutPath} → ${next}`);
|
|
6097
|
+
ompRetiredTranscriptPaths.add(retired);
|
|
6098
|
+
codexBridgeDetachFile();
|
|
6099
|
+
codexBridgeAttach(next, 'fresh-empty');
|
|
6100
|
+
}
|
|
6018
6101
|
function codexBridgeIngest(opts = {}) {
|
|
6019
6102
|
// Follow-up RPC turns install their exact bridge mark only after the
|
|
6020
6103
|
// turn/start ACK passes the generation fence. Ordinary ingest must not
|
|
@@ -6033,7 +6116,9 @@ function codexBridgeIngest(opts = {}) {
|
|
|
6033
6116
|
}
|
|
6034
6117
|
if (!codexBridgeRolloutPath || !codexBridgeBaselineDone)
|
|
6035
6118
|
return;
|
|
6036
|
-
const result = structuredBridgeIngestPath(codexBridgeRolloutPath, codexBridgeOffset
|
|
6119
|
+
const result = structuredBridgeIngestPath(codexBridgeRolloutPath, codexBridgeOffset, {
|
|
6120
|
+
flushOmpTrailingFinal: opts.flushOmpTrailingFinal,
|
|
6121
|
+
});
|
|
6037
6122
|
codexBridgeOffset = result.newOffset;
|
|
6038
6123
|
codexBridgePendingTail = result.pendingTail;
|
|
6039
6124
|
if (structuredBridgeIsTraex()) {
|
|
@@ -6069,6 +6154,42 @@ function codexBridgeIngest(opts = {}) {
|
|
|
6069
6154
|
idleDetector?.fireIdle();
|
|
6070
6155
|
}
|
|
6071
6156
|
}
|
|
6157
|
+
/** Confirm an OMP file-tail terminal only after one complete quiet bridge tick
|
|
6158
|
+
* and an authoritative current viewport with no busy marker. This deliberately
|
|
6159
|
+
* runs while lifecycle blocking is asserted; ingesting the final releases it. */
|
|
6160
|
+
function maybeFlushOmpTrailingFinalOnQuietTick() {
|
|
6161
|
+
if (!structuredBridgeIsOmp())
|
|
6162
|
+
return;
|
|
6163
|
+
const candidate = ompBridgeState.provisionalFinal;
|
|
6164
|
+
if (!candidate) {
|
|
6165
|
+
ompQuietCandidateKey = undefined;
|
|
6166
|
+
ompQuietCandidateCompleteOffset = undefined;
|
|
6167
|
+
return;
|
|
6168
|
+
}
|
|
6169
|
+
const key = candidate.event.uuid;
|
|
6170
|
+
if (ompQuietCandidateKey !== key
|
|
6171
|
+
|| ompQuietCandidateCompleteOffset !== codexBridgeOffset) {
|
|
6172
|
+
ompQuietCandidateKey = key;
|
|
6173
|
+
ompQuietCandidateCompleteOffset = codexBridgeOffset;
|
|
6174
|
+
return;
|
|
6175
|
+
}
|
|
6176
|
+
if (codexBridgePendingTail || !hasStructuredLifecycleBlock() || !backend
|
|
6177
|
+
|| !backendScreenEvidenceIsAuthoritativeForMutation()
|
|
6178
|
+
|| !cliAdapter?.busyPattern)
|
|
6179
|
+
return;
|
|
6180
|
+
try {
|
|
6181
|
+
const screen = captureBackendScreen(backend);
|
|
6182
|
+
if (!screen || cliAdapter.busyPattern.test(busyProbeRegion(screen)))
|
|
6183
|
+
return;
|
|
6184
|
+
}
|
|
6185
|
+
catch (err) {
|
|
6186
|
+
log(`OMP quiet-final viewport capture failed: ${err.message}`);
|
|
6187
|
+
return;
|
|
6188
|
+
}
|
|
6189
|
+
ompQuietCandidateKey = undefined;
|
|
6190
|
+
ompQuietCandidateCompleteOffset = undefined;
|
|
6191
|
+
codexBridgeIngest({ flushOmpTrailingFinal: true });
|
|
6192
|
+
}
|
|
6072
6193
|
/** 将 Codex 的结构化 429 终态同步为既有的限流状态。 */
|
|
6073
6194
|
function maybeEmitCodexStructuredRateLimit(events) {
|
|
6074
6195
|
for (const ev of events) {
|
|
@@ -6076,7 +6197,7 @@ function maybeEmitCodexStructuredRateLimit(events) {
|
|
|
6076
6197
|
continue;
|
|
6077
6198
|
emittedRateLimitUuids.add(ev.uuid);
|
|
6078
6199
|
const usageLimit = structuredRateLimitState(ev.terminalErrorSummary ?? '429 Too Many Requests');
|
|
6079
|
-
usageLimitTracker.noteStructuredLimit();
|
|
6200
|
+
usageLimitTracker.noteStructuredLimit(usageLimit);
|
|
6080
6201
|
send({
|
|
6081
6202
|
type: 'screen_update',
|
|
6082
6203
|
content: currentUsageLimitSnapshot(),
|
|
@@ -6453,11 +6574,12 @@ function emitReadyCodexTurns() {
|
|
|
6453
6574
|
// that flag to settle completed-with-empty; a bare `completed` terminal (e.g.
|
|
6454
6575
|
// the RPC-hydration timeout path) must never be read as silence.
|
|
6455
6576
|
const nothingToSendTurns = new Set();
|
|
6456
|
-
const
|
|
6577
|
+
const terminalAdoptMode = lastInitConfig?.adoptMode === true;
|
|
6578
|
+
const sharedAppServerBridge = isExistingAppServerSharedBridge();
|
|
6457
6579
|
// Adopt mode: model is the user's external Codex, no botmux send to
|
|
6458
6580
|
// gate against — every assistant turn (Lark-driven OR locally typed)
|
|
6459
6581
|
// should reach the thread. Skip marker IO entirely.
|
|
6460
|
-
const markers =
|
|
6582
|
+
const markers = terminalAdoptMode ? [] : readSendMarkers();
|
|
6461
6583
|
const remaining = codexBridgeQueue.peek();
|
|
6462
6584
|
// Only a STARTED pending turn can bound the last ready turn's send window.
|
|
6463
6585
|
// An unstarted turn hasn't been dequeued yet (its user event hasn't landed),
|
|
@@ -6472,6 +6594,13 @@ function emitReadyCodexTurns() {
|
|
|
6472
6594
|
: undefined;
|
|
6473
6595
|
for (let i = 0; i < ready.length; i++) {
|
|
6474
6596
|
const turn = ready[i];
|
|
6597
|
+
// A shared App Server session still owns the BotMux remote TUI, so its
|
|
6598
|
+
// Lark-originated turns retain normal send-marker deduplication. Only a
|
|
6599
|
+
// turn synthesized from Codex App input is external/local: that side has
|
|
6600
|
+
// no pending Lark fingerprint and must be forwarded like terminal
|
|
6601
|
+
// `/adopt`, including both the App prompt and its final reply.
|
|
6602
|
+
const adoptMode = terminalAdoptMode
|
|
6603
|
+
|| (sharedAppServerBridge && turn.isLocal === true);
|
|
6475
6604
|
const sourceHermesSessionId = structuredBridgeIsHermes() ? turn.sourceSessionId : undefined;
|
|
6476
6605
|
const nextBoundaryMs = (i + 1 < ready.length ? ready[i + 1].markTimeMs : nextPendingMarkTimeMs);
|
|
6477
6606
|
const gateInput = {
|
|
@@ -6527,6 +6656,9 @@ function emitReadyCodexTurns() {
|
|
|
6527
6656
|
const fields = formatLocalTurnFields(turn.userText ?? '', postContent);
|
|
6528
6657
|
if (!fields)
|
|
6529
6658
|
continue;
|
|
6659
|
+
// Harvested answer → drop the structured-limit re-emit latch (mirrors
|
|
6660
|
+
// the daemon's final_output self-heal; see emitReadyTurns).
|
|
6661
|
+
usageLimitTracker.noteTurnCompleted();
|
|
6530
6662
|
send({
|
|
6531
6663
|
type: 'final_output',
|
|
6532
6664
|
...(sourceHermesSessionId ? { sourceHermesSessionId } : {}),
|
|
@@ -6539,6 +6671,7 @@ function emitReadyCodexTurns() {
|
|
|
6539
6671
|
});
|
|
6540
6672
|
continue;
|
|
6541
6673
|
}
|
|
6674
|
+
usageLimitTracker.noteTurnCompleted();
|
|
6542
6675
|
send({
|
|
6543
6676
|
type: 'final_output',
|
|
6544
6677
|
...(sourceHermesSessionId ? { sourceHermesSessionId } : {}),
|
|
@@ -6577,6 +6710,10 @@ function stopCodexBridge() {
|
|
|
6577
6710
|
codexBridgeOffset = 0;
|
|
6578
6711
|
codexBridgePendingTail = '';
|
|
6579
6712
|
codexBridgeBaselineDone = false;
|
|
6713
|
+
ompBridgeState = {};
|
|
6714
|
+
ompQuietCandidateKey = undefined;
|
|
6715
|
+
ompQuietCandidateCompleteOffset = undefined;
|
|
6716
|
+
ompRetiredTranscriptPaths.clear();
|
|
6580
6717
|
hermesBridgeOffset = 0;
|
|
6581
6718
|
hermesBridgeBaselineDone = false;
|
|
6582
6719
|
hermesBridgeSourceSessionId = undefined;
|
|
@@ -8011,6 +8148,13 @@ function handleVisibleStartupInteraction(data) {
|
|
|
8011
8148
|
// row; never let its selection marker reach the first-prompt idle detector.
|
|
8012
8149
|
if (dismissAidenCodexUpdateDialog(data))
|
|
8013
8150
|
return true;
|
|
8151
|
+
// An existing App Server viewer shares the user's Codex App trust context.
|
|
8152
|
+
// Never auto-accept an interactive trust prompt here: it can include
|
|
8153
|
+
// project/plugin hooks owned outside BotMux. The operator can safely choose
|
|
8154
|
+
// "Continue without trusting" through the Web Terminal when Codex presents
|
|
8155
|
+
// its Hooks review menu.
|
|
8156
|
+
if (lastInitConfig?.existingAppServerEndpoint)
|
|
8157
|
+
return false;
|
|
8014
8158
|
if (trustHandled)
|
|
8015
8159
|
return false;
|
|
8016
8160
|
const stripped = data.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
@@ -8820,6 +8964,7 @@ function onPtyData(data) {
|
|
|
8820
8964
|
}
|
|
8821
8965
|
}
|
|
8822
8966
|
renderer?.write(data);
|
|
8967
|
+
refreshHookReviewInputHold(`${renderer?.rawSnapshot() ?? ''}\n${data}`);
|
|
8823
8968
|
// In tmux-attach mode, each web client has its own tmux attach PTY —
|
|
8824
8969
|
// no relay needed. In non-tmux mode AND in pipe mode (adopt-bridge),
|
|
8825
8970
|
// broadcast through the shared scrollback so all connected web clients
|
|
@@ -8929,6 +9074,7 @@ async function onBackendScreenResync(snapshot) {
|
|
|
8929
9074
|
// current viewport: a prior local attach may have changed the real geometry.
|
|
8930
9075
|
const visibleSnapshot = nextRenderer?.rawSnapshot() ?? '';
|
|
8931
9076
|
lastAnalyzerSnapshot = visibleSnapshot;
|
|
9077
|
+
refreshHookReviewInputHold(visibleSnapshot);
|
|
8932
9078
|
// ZMX history does not carry the authoritative current PTY dimensions. A
|
|
8933
9079
|
// local `zmx attach` can resize the session below our default 120x24 and that
|
|
8934
9080
|
// size persists after detach, so even the rendered tail may include rows just
|
|
@@ -9638,12 +9784,9 @@ function scheduleSubmitFailureNotify(msg, recheck, transcriptLabel, bridgeTurnId
|
|
|
9638
9784
|
case 'suppress-active':
|
|
9639
9785
|
redriveRejectedStructuredReady();
|
|
9640
9786
|
log(`Deferred recheck missing but later ${action.evidence} shows ${cliName()} is active — suppressing submit warning. preview="${preview}"`);
|
|
9641
|
-
//
|
|
9642
|
-
//
|
|
9643
|
-
|
|
9644
|
-
if (turnIdentity?.dispatchAttempt !== undefined) {
|
|
9645
|
-
scheduleSubmitFailureNotify(msg, recheck, transcriptLabel, bridgeTurnId, undefined, turnSeq, turnIdentity, durableTerminalStatus);
|
|
9646
|
-
}
|
|
9787
|
+
// activity 证据只能说明 CLI 仍在动,不能证明本次输入已提交
|
|
9788
|
+
// 普通 IM 和 durable turn 都继续重查,直到 transcript 命中或安静窗口落到 submit_unconfirmed
|
|
9789
|
+
scheduleSubmitFailureNotify(msg, recheck, transcriptLabel, bridgeTurnId, undefined, turnSeq, turnIdentity, durableTerminalStatus, structuredTarget);
|
|
9647
9790
|
return;
|
|
9648
9791
|
case 'notify-hard-failure':
|
|
9649
9792
|
// failureReason is handled synchronously above.
|
|
@@ -9817,7 +9960,7 @@ async function flushPending() {
|
|
|
9817
9960
|
}
|
|
9818
9961
|
if (ambiguousSubmissionRecoveryHold?.backend === backend)
|
|
9819
9962
|
return;
|
|
9820
|
-
if (
|
|
9963
|
+
if (!hasPendingInputForFlush())
|
|
9821
9964
|
return; // nothing to flush — keep isPromptReady
|
|
9822
9965
|
if (sessionRenameInFlight())
|
|
9823
9966
|
return; // wait for /rename to finish before any user input
|
|
@@ -9836,6 +9979,14 @@ async function flushPending() {
|
|
|
9836
9979
|
return;
|
|
9837
9980
|
if (bareShellLaunchBlocked)
|
|
9838
9981
|
return; // launch is held at a bare shell — don't type prompts into it
|
|
9982
|
+
// A Hook-review page has an active selection and consumes Enter/arrow keys.
|
|
9983
|
+
// Re-check the rendered viewport right before a literal write; PTY chunks
|
|
9984
|
+
// can arrive between idle detection and this queue drain.
|
|
9985
|
+
refreshHookReviewInputHold(lastAnalyzerSnapshot || renderer?.rawSnapshot() || '');
|
|
9986
|
+
if (hookReviewInputHold) {
|
|
9987
|
+
notifyHookReviewInputHold();
|
|
9988
|
+
return;
|
|
9989
|
+
}
|
|
9839
9990
|
// Screen-idle is not a durable receipt. A permission/AskUser prompt can look
|
|
9840
9991
|
// idle while the logical delivery is unresolved, so no following IM or
|
|
9841
9992
|
// meeting turn may cross this boundary until an explicit terminal releases
|
|
@@ -11161,6 +11312,27 @@ function scheduleBusyPatternIdleProbe(source) {
|
|
|
11161
11312
|
}
|
|
11162
11313
|
async function spawnCli(cfg, opts = {}) {
|
|
11163
11314
|
const spawnGeneration = ++cliSpawnGeneration;
|
|
11315
|
+
// Experimental external App Server attachment: BotMux owns only this TUI
|
|
11316
|
+
// client, never the server or its JSON-RPC input stream. Re-establish the
|
|
11317
|
+
// remote argv state on EVERY spawn because killCli() deliberately clears the
|
|
11318
|
+
// generic RPC remote variables before a restart.
|
|
11319
|
+
if (cfg.existingAppServerEndpoint !== undefined) {
|
|
11320
|
+
if (cfg.cliId !== 'codex') {
|
|
11321
|
+
throw new Error('existing App Server attachment requires cliId "codex"');
|
|
11322
|
+
}
|
|
11323
|
+
if (!cfg.cliSessionId) {
|
|
11324
|
+
throw new Error('existing App Server attachment requires an already-selected Codex thread id');
|
|
11325
|
+
}
|
|
11326
|
+
if (cfg.codexRpcInput === true) {
|
|
11327
|
+
throw new Error('existing App Server attachment cannot enable BotMux codexRpcInput');
|
|
11328
|
+
}
|
|
11329
|
+
if (codexRpcEngine) {
|
|
11330
|
+
throw new Error('existing App Server attachment cannot share a BotMux-owned Codex RPC engine');
|
|
11331
|
+
}
|
|
11332
|
+
remoteWsUrl = normalizeExistingAppServerEndpoint(cfg.existingAppServerEndpoint, `worker existingAppServerEndpoint for session ${cfg.sessionId}`);
|
|
11333
|
+
remoteThreadId = cfg.cliSessionId;
|
|
11334
|
+
log(`External Codex App Server attach prepared for thread ${cfg.cliSessionId.substring(0, 12)}`);
|
|
11335
|
+
}
|
|
11164
11336
|
// Prefer force-clear so a half-finished rename cannot block the new generation.
|
|
11165
11337
|
forceClearSessionRenameInFlight();
|
|
11166
11338
|
currentCliCredentialIsolated = false;
|
|
@@ -11330,7 +11502,16 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
11330
11502
|
log(`Adopt mode (${effectiveBackendType}): observing ${target} (${cols}x${rows})`);
|
|
11331
11503
|
return;
|
|
11332
11504
|
}
|
|
11333
|
-
|
|
11505
|
+
// dsh runtime variant: when the bot selected dsh-tui (dshRuntime='tui'),
|
|
11506
|
+
// resolve the PTY-driven dsh-tui adapter instead of the headless JSON-RPC
|
|
11507
|
+
// runner. The bot's cliId stays 'dsh' (the toggle is a per-bot config field),
|
|
11508
|
+
// so every other dsh-specific branch (OSC decode, turn timeout, etc.) is
|
|
11509
|
+
// unaffected — dsh-tui simply doesn't emit OSC frames, making the decoder a
|
|
11510
|
+
// no-op for it.
|
|
11511
|
+
const effectiveCliId = cfg.cliId === 'dsh' && cfg.dshRuntime === 'tui'
|
|
11512
|
+
? 'dsh-tui'
|
|
11513
|
+
: cfg.cliId;
|
|
11514
|
+
cliAdapter = createCliAdapterSync(effectiveCliId, cfg.cliPathOverride);
|
|
11334
11515
|
// backendType trust-but-verify + HARD GATE (PTY 退役): an explicit per-bot
|
|
11335
11516
|
// config (or BACKEND_TYPE env override) bypasses config.ts's default, so the
|
|
11336
11517
|
// worker re-probes the requested persistent backend here. A requested
|
|
@@ -12283,6 +12464,8 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
12283
12464
|
bareShellLaunchBlocked = false;
|
|
12284
12465
|
bareShellChecked = false;
|
|
12285
12466
|
bareShellCheckInProgress = false;
|
|
12467
|
+
hookReviewInputHold = false;
|
|
12468
|
+
hookReviewInputHoldNotified = false;
|
|
12286
12469
|
// ── Resume pre-flight check + two-tier fallback ──────────────────────────
|
|
12287
12470
|
// Tier 0 (adapter capability): adapters whose buildArgs can only resume a
|
|
12288
12471
|
// PRECISE cliSessionId (cursor/copilot/kimi — no --continue/latest fallback,
|
|
@@ -12404,6 +12587,8 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
12404
12587
|
// the fresh session's first turn.
|
|
12405
12588
|
lastSpawnEffectiveResume = effectiveResume;
|
|
12406
12589
|
lastSpawnEffectiveCliSessionId = effectiveCliSessionId;
|
|
12590
|
+
lastSpawnEffectiveAdapterSessionId = effectiveAdapterSessionId;
|
|
12591
|
+
ompRetiredTranscriptPaths.clear();
|
|
12407
12592
|
// ttadk 网关:模型走 ttadk 自己的 `-m`(启动期注入到 ttadk 前缀,见下方 wrapperCli
|
|
12408
12593
|
// 分支),不能再把 cfg.model 透给底层适配器,否则真实 CLI 会再吃一个 --model 重复。
|
|
12409
12594
|
const ttadkGateway = isTtadkWrapper(cfg.wrapperCli);
|
|
@@ -12900,6 +13085,47 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
12900
13085
|
}
|
|
12901
13086
|
return out;
|
|
12902
13087
|
};
|
|
13088
|
+
// Canonicalize a path whose LEAF may not exist yet (e.g. a lark-cli keystore dir
|
|
13089
|
+
// lark-cli will create on first write): realpath the DEEPEST EXISTING ancestor
|
|
13090
|
+
// (resolving any symlinked prefix — symlinked $HOME, a symlinked data root) and
|
|
13091
|
+
// re-append the non-existent tail. A plain full-path realpath THROWS on the
|
|
13092
|
+
// missing leaf and returns the raw lexical value, so a symlinked ancestor would
|
|
13093
|
+
// stay unresolved and the policy could anchor a DIFFERENT namespace than the one
|
|
13094
|
+
// lark-cli opens after IT canonicalizes (a canonicalization TOCTOU). Input must already be
|
|
13095
|
+
// lexically clean (no `..`); resolveLarkCliLinuxStoreDir Cleans it first.
|
|
13096
|
+
const canonicalNearestAncestor = (p) => {
|
|
13097
|
+
if (!p.startsWith('/'))
|
|
13098
|
+
return p;
|
|
13099
|
+
const segs = p.split('/').filter(Boolean);
|
|
13100
|
+
for (let i = segs.length; i >= 0; i--) {
|
|
13101
|
+
const prefix = '/' + segs.slice(0, i).join('/');
|
|
13102
|
+
try {
|
|
13103
|
+
const real = realpathSync(prefix);
|
|
13104
|
+
return i === segs.length ? real : `${real}/${segs.slice(i).join('/')}`;
|
|
13105
|
+
}
|
|
13106
|
+
catch { /* ancestor missing → try a shallower prefix */ }
|
|
13107
|
+
}
|
|
13108
|
+
return p; // not even '/' resolved (impossible) → lexical fallback
|
|
13109
|
+
};
|
|
13110
|
+
// OMP keeps all transcripts under one shared state root. Materialize this
|
|
13111
|
+
// Botmux session's exact directory before allow-path existence filtering;
|
|
13112
|
+
// the policy below masks the shared sessions parent and re-opens only this
|
|
13113
|
+
// deeper directory read-write.
|
|
13114
|
+
const ompCurrentSessionDir = cliAdapter.id === 'oh-my-pi'
|
|
13115
|
+
? ompSessionDir(effectiveAdapterSessionId)
|
|
13116
|
+
: undefined;
|
|
13117
|
+
if (ompCurrentSessionDir)
|
|
13118
|
+
mkdirSync(ompCurrentSessionDir, { recursive: true, mode: 0o700 });
|
|
13119
|
+
const canonicalOmpSessionsRoot = ompCurrentSessionDir
|
|
13120
|
+
? canonical(dirname(dirname(ompCurrentSessionDir)))
|
|
13121
|
+
: undefined;
|
|
13122
|
+
const canonicalOmpSessionDir = ompCurrentSessionDir
|
|
13123
|
+
? canonical(ompCurrentSessionDir)
|
|
13124
|
+
: undefined;
|
|
13125
|
+
if (canonicalOmpSessionsRoot && canonicalOmpSessionDir
|
|
13126
|
+
&& relative(canonicalOmpSessionsRoot, canonicalOmpSessionDir) !== join('botmux', effectiveAdapterSessionId)) {
|
|
13127
|
+
throw new Error('[sandbox] OMP session directory escapes its managed sessions root');
|
|
13128
|
+
}
|
|
12903
13129
|
// User three-tier lists: the new sandboxPaths field, or a pre-migration
|
|
12904
13130
|
// session/config's legacy fields mapped through the SAME lossless mapping
|
|
12905
13131
|
// the startup migration uses.
|
|
@@ -13019,6 +13245,9 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
13019
13245
|
if (process.platform === 'linux') {
|
|
13020
13246
|
mandatoryDenyPaths.push(join(canonical(dataDir), 'sandboxes', cfg.sessionId));
|
|
13021
13247
|
}
|
|
13248
|
+
if (canonicalOmpSessionsRoot) {
|
|
13249
|
+
mandatoryDenyPaths.push(canonicalOmpSessionsRoot);
|
|
13250
|
+
}
|
|
13022
13251
|
if (process.platform === 'darwin') {
|
|
13023
13252
|
const osUserHomeDir = userInfo().homedir;
|
|
13024
13253
|
if (!osUserHomeDir) {
|
|
@@ -13150,6 +13379,23 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
13150
13379
|
// arbitrary parent dir (`/tmp`, `/etc`, a project root) and bricking the
|
|
13151
13380
|
// core CLI (codex P1). Canonicalized so it shares the roots' namespace.
|
|
13152
13381
|
loadedBotsConfigPath: cfg.loadedBotsConfigPath ? canonical(cfg.loadedBotsConfigPath) : undefined,
|
|
13382
|
+
// Linux lark-cli keystore (holds every bot's appsecret ciphertext + the shared
|
|
13383
|
+
// master key). Resolve it EXACTLY as the in-sandbox lark-cli will:
|
|
13384
|
+
// `<clean(LARKSUITE_CLI_DATA_DIR)>/lark-cli` when that env var is a valid ABSOLUTE
|
|
13385
|
+
// path (lexically Cleaned — `..`/`.`/`//` resolved, control chars rejected), else
|
|
13386
|
+
// `$HOME/.local/share/lark-cli` — lark-cli does NOT read XDG_DATA_HOME (verified by
|
|
13387
|
+
// strace on v1.0.76 + its keychain_other.go::StorageDir / SafeEnvDirPath). The
|
|
13388
|
+
// Clean happens INSIDE resolveLarkCliLinuxStoreDir (a `..`-bearing value
|
|
13389
|
+
// must be normalized here, or realpath throws on the nonexistent leaf, the raw `..`
|
|
13390
|
+
// survives, normalizeFsPath rejects it, and the policy anchors the DEFAULT store
|
|
13391
|
+
// while lark-cli opens the Clean'd real one → leak). canonicalNearestAncestor then
|
|
13392
|
+
// resolves any SYMLINKED ancestor even when the store leaf does not exist yet (a
|
|
13393
|
+
// full-path realpath would throw and skip symlink resolution → policy/CLI namespace
|
|
13394
|
+
// divergence). bwrap has no --clearenv, so the child inherits this bot's
|
|
13395
|
+
// LARKSUITE_CLI_DATA_DIR from the worker's own (frozen, non-agent-controllable)
|
|
13396
|
+
// process.env, and sandbox.ts pins the child to the SAME cleaned resolution
|
|
13397
|
+
// (--setenv/--unsetenv) so policy == CLI by construction. Ignored on darwin.
|
|
13398
|
+
larkCliLinuxStore: canonicalNearestAncestor(resolveLarkCliLinuxStoreDir(process.env.LARKSUITE_CLI_DATA_DIR, lexicalHome)),
|
|
13153
13399
|
redirectedCliData: willRedirectCliData,
|
|
13154
13400
|
cliDataPaths: willRedirectCliData ? undefined : keepExisting([
|
|
13155
13401
|
cliAdapter.claudeDataDir,
|
|
@@ -13201,7 +13447,7 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
13201
13447
|
]),
|
|
13202
13448
|
botmuxInstallRoot,
|
|
13203
13449
|
outbox,
|
|
13204
|
-
extraWritePaths: keepExisting([process.env.TMPDIR]),
|
|
13450
|
+
extraWritePaths: keepExisting([process.env.TMPDIR, canonicalOmpSessionDir]),
|
|
13205
13451
|
userPaths,
|
|
13206
13452
|
mandatoryDenyPaths,
|
|
13207
13453
|
mandatoryDenyRegexes,
|
|
@@ -13288,6 +13534,25 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
13288
13534
|
}
|
|
13289
13535
|
}
|
|
13290
13536
|
else {
|
|
13537
|
+
// Canonical lark-cli data root pinned into the child so the in-sandbox lark-cli
|
|
13538
|
+
// resolves the SAME physical keystore the policy denied/carved-out (avoiding a
|
|
13539
|
+
// symlinked-data-root namespace split). fsPolicyCtx.larkCliLinuxStore is the FULLY-canonical store dir (leaf
|
|
13540
|
+
// symlinks followed) — the physical location the policy protects. The child opens
|
|
13541
|
+
// `<pin>/lark-cli`, so pin = dirname(store) is same-source ONLY when the canonical
|
|
13542
|
+
// store's basename is still `lark-cli` (the common case, incl. a symlink that
|
|
13543
|
+
// resolves to another `lark-cli` dir). If the `lark-cli` leaf was a symlink to a
|
|
13544
|
+
// DIFFERENTLY-named dir (basename !== 'lark-cli'), dirname(store)+'/lark-cli' would
|
|
13545
|
+
// NOT equal the canonical store → child would open an unbound path (auth breaks) OR
|
|
13546
|
+
// a foreign one. There is no leak either way (the policy still denies the real dir,
|
|
13547
|
+
// and both candidate stores are locked), so degrade SAFELY: pin nothing → child
|
|
13548
|
+
// falls back to the default store (also locked), and log the exotic layout.
|
|
13549
|
+
const canonStore = fsPolicyCtx.larkCliLinuxStore;
|
|
13550
|
+
const childLarkDataRoot = larkCliChildDataRoot(canonStore);
|
|
13551
|
+
if (canonStore && childLarkDataRoot === null) {
|
|
13552
|
+
log(`[sandbox] lark-cli keystore ${canonStore} does not end in 'lark-cli' (symlinked leaf?) — `
|
|
13553
|
+
+ `not pinning a custom LARKSUITE_CLI_DATA_DIR into the sandbox; the in-sandbox lark-cli will `
|
|
13554
|
+
+ `use the default store. The real keystore is still denied/carve-out'd by policy (no leak).`);
|
|
13555
|
+
}
|
|
13291
13556
|
const sbx = prepareDirectSandbox({
|
|
13292
13557
|
sessionId: cfg.sessionId,
|
|
13293
13558
|
dataDir,
|
|
@@ -13298,6 +13563,7 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
13298
13563
|
cliArgs: args,
|
|
13299
13564
|
trustedBotmuxCommandPaths: [defaultGatewayEntry().command],
|
|
13300
13565
|
mcpGatewaySocketPath: sessionMcpGatewayHost?.socketPath,
|
|
13566
|
+
larkCliDataDir: childLarkDataRoot,
|
|
13301
13567
|
});
|
|
13302
13568
|
if (!sbx) {
|
|
13303
13569
|
// FAIL-SAFE: never silently run unsandboxed.
|
|
@@ -13839,6 +14105,13 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
13839
14105
|
// can verify they were spawned inside a botmux session by walking the
|
|
13840
14106
|
// process tree and looking for a matching pid file in this directory.
|
|
13841
14107
|
const cliPid = backend.getChildPid?.();
|
|
14108
|
+
if (cfg.existingAppServerEndpoint !== undefined) {
|
|
14109
|
+
// The local `codex --remote` client does not own the selected rollout fd;
|
|
14110
|
+
// writeInput must accept only the explicitly bound remote thread in the
|
|
14111
|
+
// shared history.jsonl instead of applying its normal local-PID ownership
|
|
14112
|
+
// filter (which would reject the correct App Server submission).
|
|
14113
|
+
backend.expectedCodexSessionId = cfg.cliSessionId;
|
|
14114
|
+
}
|
|
13842
14115
|
publishLocalProcessAttestation(cliPid ?? undefined);
|
|
13843
14116
|
if (cliPid && process.env.SESSION_DATA_DIR) {
|
|
13844
14117
|
const markersDir = join(process.env.SESSION_DATA_DIR, '.botmux-cli-pids');
|
|
@@ -14042,10 +14315,21 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
14042
14315
|
hermesBridgeAttach(effectiveResume ? 'baseline-existing' : 'fresh-empty');
|
|
14043
14316
|
}
|
|
14044
14317
|
else if (cfg.cliId === 'codex') {
|
|
14318
|
+
if (cfg.existingAppServerEndpoint) {
|
|
14319
|
+
// A shared Codex App thread has two legitimate writers: the App and
|
|
14320
|
+
// BotMux's official `codex --remote` client. Enable local-turn
|
|
14321
|
+
// synthesis only from this share's attach boundary, so older thread
|
|
14322
|
+
// history is absorbed while future App-side turns are mirrored to Lark.
|
|
14323
|
+
codexAdoptStartMs = Date.now();
|
|
14324
|
+
codexBridgeQueue.setLocalTurns(true, codexAdoptStartMs);
|
|
14325
|
+
log('Codex bridge enabled App Server shared-turn forwarding');
|
|
14326
|
+
}
|
|
14045
14327
|
if (effectiveCliSessionId) {
|
|
14046
14328
|
const rolloutPath = findCodexRolloutBySessionId(effectiveCliSessionId);
|
|
14047
14329
|
if (rolloutPath) {
|
|
14048
|
-
codexBridgeAttach(rolloutPath,
|
|
14330
|
+
codexBridgeAttach(rolloutPath, cfg.existingAppServerEndpoint
|
|
14331
|
+
? 'split-live'
|
|
14332
|
+
: effectiveResume ? 'baseline-existing' : 'fresh-empty');
|
|
14049
14333
|
}
|
|
14050
14334
|
else {
|
|
14051
14335
|
codexBridgePendingSessionId = effectiveCliSessionId;
|
|
@@ -14091,7 +14375,7 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
14091
14375
|
codexBridgeStartTimer();
|
|
14092
14376
|
}
|
|
14093
14377
|
}
|
|
14094
|
-
else if (cfg.cliId === 'pi' || cfg.cliId === 'grok') {
|
|
14378
|
+
else if (cfg.cliId === 'pi' || cfg.cliId === 'grok' || cfg.cliId === 'oh-my-pi') {
|
|
14095
14379
|
// File-backed: pin path when known (pi session id / grok --session-id
|
|
14096
14380
|
// UUID), else arm the poller. Grok collision-fallback (dir already
|
|
14097
14381
|
// exists → no --session-id → grok mints id) is recovered via writeInput
|
|
@@ -14245,7 +14529,8 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
14245
14529
|
// itself fail-opens on non-authoritative screens (ZMX), so a stale
|
|
14246
14530
|
// `Working...` in ZMX history can never block a structured terminal.
|
|
14247
14531
|
const busyGuardedIdle = evidenceSource === 'screen'
|
|
14248
|
-
|| (evidenceSource === 'external'
|
|
14532
|
+
|| (evidenceSource === 'external'
|
|
14533
|
+
&& (structuredBridgeIsPi() || structuredBridgeIsOmp()));
|
|
14249
14534
|
if (busyGuardedIdle && idleBackend
|
|
14250
14535
|
&& deferPromptReadyWhileBusy(`${cliName()} ${evidenceSource}-idle`, idleBackend))
|
|
14251
14536
|
return;
|
|
@@ -14487,7 +14772,7 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
14487
14772
|
log('Suppressed claude_exit for intentional in-worker restart');
|
|
14488
14773
|
}
|
|
14489
14774
|
else {
|
|
14490
|
-
send({ type: 'claude_exit', code, signal, logTail, canParkDiagnostic, turnId: exitedTurnId, dispatchAttempt: exitedDispatchAttempt });
|
|
14775
|
+
send({ type: 'claude_exit', code, signal, logTail, canParkDiagnostic, turnId: exitedTurnId, dispatchAttempt: exitedDispatchAttempt, ...(lastInitConfig?.cliId === 'codex-app' && code === CODEX_APP_ACTIVE_WRITER_EXIT_CODE ? { codexAppActiveWriter: true } : {}) });
|
|
14491
14776
|
}
|
|
14492
14777
|
});
|
|
14493
14778
|
const isPersistentBackendReattach = actuallyReattachedPersistent;
|
|
@@ -14757,8 +15042,8 @@ function killCli(opts = {}) {
|
|
|
14757
15042
|
appRunnerControlDecoder.reset();
|
|
14758
15043
|
}
|
|
14759
15044
|
async function restartCliProcess(reason, opts = {}) {
|
|
14760
|
-
if (lastInitConfig?.adoptMode) {
|
|
14761
|
-
log(`Restart ignored in adopt mode (${reason})`);
|
|
15045
|
+
if (lastInitConfig?.adoptMode || lastInitConfig?.existingAppServerEndpoint) {
|
|
15046
|
+
log(`Restart ignored in shared-adopt mode (${reason})`);
|
|
14762
15047
|
return;
|
|
14763
15048
|
}
|
|
14764
15049
|
// Invalidate queued/deferred callbacks before async teardown begins. Waiting
|
|
@@ -14951,6 +15236,12 @@ function startWebServer(host, preferredPort) {
|
|
|
14951
15236
|
res.end('Forbidden');
|
|
14952
15237
|
return;
|
|
14953
15238
|
}
|
|
15239
|
+
// #933 回归修复:平台注入的 Cookie/Role 会被中央前门剥掉(P1-6 / 内部 grant),
|
|
15240
|
+
// 此时 platformReadonly 判不出来;前门改用这个展示层提示头把「平台认证过的只读
|
|
15241
|
+
// 访客」这一事实带过来。只影响只读页渲染哪条横幅(SSO 登录引导 vs 纯只读提示),
|
|
15242
|
+
// 不参与任何授权判定——hasRead/hasWrite 在它之前就已定死。
|
|
15243
|
+
const platformReadonlyHint = !hasWrite
|
|
15244
|
+
&& req.headers[TERMINAL_PLATFORM_READONLY_HINT_HEADER] !== undefined;
|
|
14954
15245
|
const loginHdr = req.headers['x-botmux-login-url'];
|
|
14955
15246
|
// The central front proxy only injects X-Botmux-Login-Url on the SPA 401
|
|
14956
15247
|
// path, never on `/s/` terminal requests — so a read-only web terminal
|
|
@@ -14991,7 +15282,7 @@ function startWebServer(host, preferredPort) {
|
|
|
14991
15282
|
|| effectiveBackendType === 'tmux'
|
|
14992
15283
|
|| effectiveBackendType === 'zellij';
|
|
14993
15284
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
14994
|
-
res.end(getTerminalHtml(hasWrite, platformReadonly, loginUrl, forceRemoteScroll, localTerminalBackend, allowReadOnlyRemoteScroll));
|
|
15285
|
+
res.end(getTerminalHtml(hasWrite, platformReadonly || platformReadonlyHint, loginUrl, forceRemoteScroll, localTerminalBackend, allowReadOnlyRemoteScroll));
|
|
14995
15286
|
});
|
|
14996
15287
|
wss = new WebSocketServer({
|
|
14997
15288
|
server: httpServer,
|
|
@@ -15047,6 +15338,26 @@ function startWebServer(host, preferredPort) {
|
|
|
15047
15338
|
if (hasWrite)
|
|
15048
15339
|
authedClients.add(ws);
|
|
15049
15340
|
log(`WS client connected (total: ${wsClients.size}, write: ${hasWrite})`);
|
|
15341
|
+
// The page only knows the verdict of its own HTTP GET. This upgrade was a
|
|
15342
|
+
// SECOND, independent authorization — and an iOS WebView does not send
|
|
15343
|
+
// cookies on it, so the very same page can be judged writable over HTTP
|
|
15344
|
+
// and read-only here. Tell the page what THIS socket actually got: it
|
|
15345
|
+
// stops forwarding input it knows will be dropped, and reports the truth
|
|
15346
|
+
// up to whatever embeds it (the Workbench terminal pane reads it to label
|
|
15347
|
+
// the channel).
|
|
15348
|
+
//
|
|
15349
|
+
// It used to ride the PTY byte stream as an OSC sequence, which the page
|
|
15350
|
+
// then scanned for on EVERY frame — so any process running inside a
|
|
15351
|
+
// read-only terminal could print the same bytes and flip the page's own
|
|
15352
|
+
// write verdict (or DoS itself into read-only). It is now an out-of-band
|
|
15353
|
+
// control frame that MUST be the first message on this socket, sent in the
|
|
15354
|
+
// same synchronous tick that registered it: the page only ever decodes
|
|
15355
|
+
// frame #1 of a connection as control, so PTY output can never be mistaken
|
|
15356
|
+
// for it. Nothing awaits between `wsClients.add` above and this send.
|
|
15357
|
+
try {
|
|
15358
|
+
ws.send(terminalWriteFrame(hasWrite));
|
|
15359
|
+
}
|
|
15360
|
+
catch { /* already closing */ }
|
|
15050
15361
|
// A signed Dashboard grant is fixed-expiry — for READ scope as much as
|
|
15051
15362
|
// for write (P1-5). Even if the central proxy's socket invalidation is
|
|
15052
15363
|
// delayed or bypassed, the worker independently removes any granted
|
|
@@ -15502,6 +15813,18 @@ ${loginUrl ? `<a id="login-banner" href="${loginUrl}" target="_top" rel="noopene
|
|
|
15502
15813
|
var isTouch='ontouchstart'in window||navigator.maxTouchPoints>0;
|
|
15503
15814
|
if(isTouch){document.body.classList.add('touch');}
|
|
15504
15815
|
var hasToken=${hasWrite};
|
|
15816
|
+
// hasToken 是**这一次 HTTP GET** 的判定,只当初值。真正说了算的是下面这条:worker 在
|
|
15817
|
+
// WebSocket 握手完成时把「这条连接实际拿到什么权限」发下来(OSC 1989 write),落成
|
|
15818
|
+
// wsHasWrite。两者会不一致——iOS WebView 的 WS 升级不带 Cookie,页面 HTTP 判可写、
|
|
15819
|
+
// WS 却只读,照抄 hasToken 就会让人对着一个不收字的终端打字。null = 还没连上。
|
|
15820
|
+
var wsHasWrite=null;
|
|
15821
|
+
// 「这条连接拿到什么权限」的解码器 —— 与 worker 侧同一份实现,原样嵌进来(这段页面
|
|
15822
|
+
// 代码住在模板字符串里,import 不进来,所以只能一份源码两处跑)。
|
|
15823
|
+
// 规则:只在**本连接的第一帧**上尝试,且要求整帧精确匹配。PTY 里跑的进程打出一模
|
|
15824
|
+
// 一样的字节也翻不动权限——它永远不可能是第一帧。
|
|
15825
|
+
var _wbDecodeWriteFrame=${decodeTerminalWriteFrameSource};
|
|
15826
|
+
// 本连接还没消费过首帧。每次 connect() 重置:上一条连接的结论套不到新连接上。
|
|
15827
|
+
var _wbFirstFrame=true;
|
|
15505
15828
|
var platformReadonly=${platformReadonly};
|
|
15506
15829
|
var remoteScroll=${forceRemoteScroll};
|
|
15507
15830
|
var localTerminalBackend=${localTerminalBackend};
|
|
@@ -15579,6 +15902,39 @@ function _wbApplyAutoFontSize(){
|
|
|
15579
15902
|
return true;
|
|
15580
15903
|
}
|
|
15581
15904
|
fit.fit();
|
|
15905
|
+
// 这条通道到底能不能写,以**已经建立的 WS** 为准(见页面顶部 wsHasWrite 那段注释)。
|
|
15906
|
+
// 结论一到手就做两件事:① 页面自己收起输入,别把注定被丢掉的字发出去;② 上抛给嵌入
|
|
15907
|
+
// 方(工作台终端面板据此显示「可输入 / 只读」,而不是照抄 HTTP 的 hasToken)。
|
|
15908
|
+
// 上抛的目标 origin 绝不用星号:优先取 location.ancestorOrigins(WebKit / Blink 都有,
|
|
15909
|
+
// iOS WebView 正是 WebKit),取不到就退回「父页给我们发外观消息时带的那个 origin」;
|
|
15910
|
+
// 两者都没有就不发——宁可不上抛,也不广播出去。
|
|
15911
|
+
// 注意:这一整段在 worker.ts 的模板字符串里,注释和代码都不能出现反引号,
|
|
15912
|
+
// 也不能出现「美元号 + 左花括号」的插值起始序列,否则会把模板字符串截断。
|
|
15913
|
+
var _wbParentOrigin=null;
|
|
15914
|
+
try{
|
|
15915
|
+
var _wbAo=window.location.ancestorOrigins;
|
|
15916
|
+
if(_wbAo&&_wbAo.length&&_wbAo[0]&&_wbAo[0]!=='null')_wbParentOrigin=_wbAo[0];
|
|
15917
|
+
}catch(_e){}
|
|
15918
|
+
function _wbPostWrite(){
|
|
15919
|
+
if(window.parent===window||!_wbParentOrigin)return;
|
|
15920
|
+
// wsHasWrite===null 也要上抛:那是「这条连接退回未知」的一次明确通知(重连开始时
|
|
15921
|
+
// 发),嵌入方据此把上一条连接的结论清掉,而不是继续显示一个过期的判定。
|
|
15922
|
+
try{window.parent.postMessage({type:'botmux:wb-terminal-write',write:wsHasWrite},_wbParentOrigin)}catch(_e){}
|
|
15923
|
+
}
|
|
15924
|
+
function _wbSetWsWrite(v){
|
|
15925
|
+
var changed=wsHasWrite!==v;
|
|
15926
|
+
wsHasWrite=v;
|
|
15927
|
+
if(changed){
|
|
15928
|
+
var _wb=document.getElementById('readonly-banner');
|
|
15929
|
+
// 页面以为自己可写、这条连接其实只读:把只读横幅亮出来。反过来(连上后确认可写)
|
|
15930
|
+
// 就把它收掉,别留一条与事实相反的提示。null(未知)两边都不动:还没有依据。
|
|
15931
|
+
if(_wb&&!platformReadonly){
|
|
15932
|
+
if(v===false&&hasToken)_wb.classList.add('show');
|
|
15933
|
+
else if(v===true)_wb.classList.remove('show');
|
|
15934
|
+
}
|
|
15935
|
+
}
|
|
15936
|
+
_wbPostWrite();
|
|
15937
|
+
}
|
|
15582
15938
|
// 工作台下发的终端外观(消息类型 botmux:wb-appearance)。终端画布住在这个跨文档
|
|
15583
15939
|
// iframe 里,父页换 class / 换 CSS 变量都传不进来,配色只能靠 postMessage 递过来。
|
|
15584
15940
|
// 注意:这一整段在 worker.ts 的模板字符串里,注释和代码都不能出现反引号,
|
|
@@ -15602,17 +15958,22 @@ window.addEventListener('message',function(_ev){
|
|
|
15602
15958
|
if(_ev.source===window||_ev.source!==window.parent)return;
|
|
15603
15959
|
var _d=_ev.data;
|
|
15604
15960
|
if(!_d||_d.type!=='botmux:wb-appearance')return;
|
|
15961
|
+
// 这一条消息本身就证明了父页的 origin(来源已经校验过是 window.parent)。
|
|
15962
|
+
// ancestorOrigins 拿不到时(Firefox)靠它补上,并把攒着的写权限结论补发一次。
|
|
15963
|
+
if(!_wbParentOrigin&&_ev.origin&&_ev.origin!=='null'){_wbParentOrigin=_ev.origin;_wbPostWrite();}
|
|
15605
15964
|
var _theme=_wbSanitizeTheme(_d.theme);
|
|
15606
15965
|
if(!_theme)return;
|
|
15607
15966
|
try{
|
|
15608
15967
|
term.options.theme=_theme;
|
|
15609
|
-
//
|
|
15968
|
+
// 阅读风走 1.15 的行距;经典保持 xterm 默认的 1,「经典 = 原样」的一部分。
|
|
15610
15969
|
// 经典那套色值与上面写死的字面量逐色相同,所以没开阅读风的用户零变化。
|
|
15611
15970
|
// 这两个数字的唯一出处是 WORKBENCH_TERM_LINE_HEIGHTS(agent-workbench-appearance.ts),
|
|
15612
15971
|
// 接缝测试按那张表比对这一行的字面量:改一处必须两处一起改。
|
|
15613
15972
|
// 曾经是 1.55,单元格高约 24.5px,CLI 底部那块固定 8 行的 chrome(提示 + 输入框 +
|
|
15614
|
-
// 状态条)就要占掉约 196px —— 一屏四分之一全是 chrome
|
|
15615
|
-
|
|
15973
|
+
// 状态条)就要占掉约 196px —— 一屏四分之一全是 chrome,正文被挤走,所以先降到 1.3。
|
|
15974
|
+
// 1.3 对中文密集的 TUI 内容还是偏松:汉字撑满 em 框,行间空隙观感接近隔行,于是
|
|
15975
|
+
// 再收一档到 1.15(单元格高约 18.2px),比经典松一成半,读起来才连成一段。
|
|
15976
|
+
term.options.lineHeight=_d.termStyle==='reader'?1.15:1;
|
|
15616
15977
|
// 行距变了可视行数就变,必须复算一次。几何契约不动:只有画布内重绘。
|
|
15617
15978
|
fit.fit();
|
|
15618
15979
|
}catch(_e){}
|
|
@@ -15798,10 +16159,17 @@ function _sendInput(d){if(ws_&&ws_.readyState===1)ws_.send(JSON.stringify({type:
|
|
|
15798
16159
|
var _MOTION_RE=/^(?:\\x1b\\[<(?:35|39|43|47|51|55|59|63);\\d+;\\d+[Mm])+$/;
|
|
15799
16160
|
var _motionPend=null,_motionT=0;
|
|
15800
16161
|
term.onData(function(d){
|
|
15801
|
-
|
|
16162
|
+
// 只有已建立的 WS 明确回报「可写」(wsHasWrite===true) 才放行输入。null(还没确认,
|
|
16163
|
+
// 含重连中)和 false(这条连接判成只读)一律不发——桌面也要等首帧。照抄 hasToken
|
|
16164
|
+
// (这一次 HTTP GET 的判定)会在 WS 不带 Cookie 的 iOS WebView 上让人对着一个不收字
|
|
16165
|
+
// 的终端打字,按键石沉大海最难自查(第 17 点)。
|
|
16166
|
+
if(wsHasWrite!==true){
|
|
15802
16167
|
// Mouse escape sequences are input too: a TUI can bind clicks or wheel
|
|
15803
16168
|
// events to actions. View links never forward terminal bytes.
|
|
15804
|
-
|
|
16169
|
+
// 已经确定只读(这条连接判 false,或 HTTP GET 本来就没给写)才弹只读提示;null 是
|
|
16170
|
+
// 还没连上的短暂过渡,此时 ws 多半也没 OPEN,静默丢弃即可,别闪一条与事实相反的只读。
|
|
16171
|
+
if(!hasToken||wsHasWrite===false)_showReadonlyToast();
|
|
16172
|
+
return;
|
|
15805
16173
|
}
|
|
15806
16174
|
if(_MOTION_RE.test(d)){
|
|
15807
16175
|
// Trailing throttle: keep only the LATEST motion, flush every 90ms. Hover
|
|
@@ -15862,6 +16230,10 @@ if(typeof ResizeObserver!=='undefined'){
|
|
|
15862
16230
|
var proto=location.protocol==='https:'?'wss':'ws';
|
|
15863
16231
|
var ws=new WebSocket(proto+'://'+location.host+base+'/'+location.search);
|
|
15864
16232
|
ws_=ws;ws.binaryType='arraybuffer';
|
|
16233
|
+
// 新连接 = 新的一次鉴权。上一条连接拿到的权限说明不了这一条,先退回未知(并把这
|
|
16234
|
+
// 个「未知」上抛给嵌入方),等这条连接自己的首帧说话。
|
|
16235
|
+
_wbFirstFrame=true;
|
|
16236
|
+
_wbSetWsWrite(null);
|
|
15865
16237
|
// Force a resize on every (re)connect: clear the dedup memory first. On
|
|
15866
16238
|
// reconnect the browser grid is usually unchanged, so without this the
|
|
15867
16239
|
// dedup in sendResize() would suppress the resize — but a reconnect often
|
|
@@ -15871,6 +16243,14 @@ if(typeof ResizeObserver!=='undefined'){
|
|
|
15871
16243
|
// (status-line update bleeds into the line below). Always re-assert size.
|
|
15872
16244
|
ws.onopen=function(){el.textContent='connected';el.className='ok';_lastC=_lastR=0;sendResize()};
|
|
15873
16245
|
ws.onmessage=function(e){
|
|
16246
|
+
// 带外控制帧只可能是**本连接的第一帧**,而且必须整帧精确匹配(见页面顶部
|
|
16247
|
+
// _wbDecodeWriteFrame 那段注释)。首帧一旦消费掉,这条连接上之后的每一个字节
|
|
16248
|
+
// 就都只是 PTY 输出 —— 里面的进程再怎么打印控制帧的字面量也翻不动权限。
|
|
16249
|
+
if(_wbFirstFrame){
|
|
16250
|
+
_wbFirstFrame=false;
|
|
16251
|
+
var _ctl=_wbDecodeWriteFrame(e.data,true);
|
|
16252
|
+
if(_ctl!==null){_wbSetWsWrite(_ctl);return;}
|
|
16253
|
+
}
|
|
15874
16254
|
var data=typeof e.data==='string'?e.data:new TextDecoder().decode(e.data);
|
|
15875
16255
|
// Snapshot-aware Herdr history replaces the buffer instead of appending a
|
|
15876
16256
|
// mostly-overlapping full screen. Preserve the reader's anchor when older
|
|
@@ -15911,7 +16291,15 @@ if(typeof ResizeObserver!=='undefined'){
|
|
|
15911
16291
|
if(m){try{_clipBuf=new TextDecoder().decode(Uint8Array.from(atob(m[1]),function(c){return c.charCodeAt(0)}));_doCopy(_clipBuf);_showCopied()}catch(ex){}}
|
|
15912
16292
|
term.write(data,_settleInitialBottom);
|
|
15913
16293
|
};
|
|
15914
|
-
ws.onclose=function(){
|
|
16294
|
+
ws.onclose=function(){
|
|
16295
|
+
ws_=null;el.textContent='disconnected';el.className='err';
|
|
16296
|
+
// 关闭当下就把这条连接的写权限退回未知:先复位首帧标志(重连后要等新首帧才恢复
|
|
16297
|
+
// 结论),再 _wbSetWsWrite(null) —— 它同时收起输入(term.onData/toolbar/_fwdScroll
|
|
16298
|
+
// 的门禁都以 wsHasWrite===true 为准)并向嵌入方上抛 write:null。不这样做的话,断线
|
|
16299
|
+
// 到 2 秒后重连的空窗里,页面仍以上一条连接的旧判定放行输入、父页也还显示旧的可写。
|
|
16300
|
+
_wbFirstFrame=true;_wbSetWsWrite(null);
|
|
16301
|
+
setTimeout(connect,2000);
|
|
16302
|
+
};
|
|
15915
16303
|
ws.onerror=function(){ws.close()};
|
|
15916
16304
|
})();
|
|
15917
16305
|
|
|
@@ -15982,6 +16370,12 @@ function _cellAt(clientX,clientY){
|
|
|
15982
16370
|
}
|
|
15983
16371
|
function _fwdScroll(px,coord){
|
|
15984
16372
|
if((!hasToken&&!readOnlyRemoteScroll)||!ws_||ws_.readyState!==1||!px)return;
|
|
16373
|
+
// 写链路(hasToken)转发的是 type:'input',与 term.onData / toolbar 同一条判据:只有已
|
|
16374
|
+
// 建立的 WS 明确回报可写(wsHasWrite===true)才放行;null(未确认,含重连中)与 false
|
|
16375
|
+
// (这条连接只读)一律不发——照抄 hasToken 会在 WS 不带 Cookie 时把输入打进一个原地
|
|
16376
|
+
// 丢字的终端(第 17 点)。只读远程滚动(hasToken=false)发的是 type:'scroll',不是输入,
|
|
16377
|
+
// 不受这道门约束(服务端另有校验)。
|
|
16378
|
+
if(hasToken&&wsHasWrite!==true)return;
|
|
15985
16379
|
coord=coord||(((term.cols>>1)+1)+';'+((term.rows>>1)+1)); // never (1,1)
|
|
15986
16380
|
var dir=px<0?-1:1;
|
|
15987
16381
|
if(_scrollBurstDir&&dir!==_scrollBurstDir){_scrollAccum=0;_scrollBurstTicks=0;}
|
|
@@ -16226,6 +16620,9 @@ if(isTouch&&hasToken){
|
|
|
16226
16620
|
var press=null;
|
|
16227
16621
|
function fire(){
|
|
16228
16622
|
if(!ws_||ws_.readyState!==1)return;
|
|
16623
|
+
// 与 term.onData 同一条判据:只有 WS 明确回报可写(wsHasWrite===true)才发;null
|
|
16624
|
+
// (还没确认)与 false(只读)都不发,桌面也等首帧(第 17 点)。
|
|
16625
|
+
if(wsHasWrite!==true){if(wsHasWrite===false)_showReadonlyToast();return;}
|
|
16229
16626
|
var k=km[btn.getAttribute('data-k')];
|
|
16230
16627
|
if(k)ws_.send(JSON.stringify({type:'input',data:k}));
|
|
16231
16628
|
}
|
|
@@ -16598,6 +16995,10 @@ process.on('message', async (raw) => {
|
|
|
16598
16995
|
if (msg.locale === 'zh' || msg.locale === 'en') {
|
|
16599
16996
|
setDefaultLocale(msg.locale);
|
|
16600
16997
|
}
|
|
16998
|
+
// Wire user prompt-key overrides into t() so worker-side prompt building
|
|
16999
|
+
// (claude-family `--append-system-prompt` via buildBotmuxSystemPromptText,
|
|
17000
|
+
// mojo catalog, etc.) honours customizations. Idempotent per worker.
|
|
17001
|
+
registerPromptOverrideResolver();
|
|
16601
17002
|
// Scope session store to this bot's per-bot file.
|
|
16602
17003
|
// Slice C0: workflow-spawned workers (BOTMUX_WORKFLOW=1) skip this —
|
|
16603
17004
|
// their `sessionId` is synthetic (`wf-<runId>-<activityId>-...`) and
|
|
@@ -17048,12 +17449,23 @@ process.on('message', async (raw) => {
|
|
|
17048
17449
|
// shell。bareShellCheckInProgress 覆盖“检查进行中”、bareShellLaunchBlocked
|
|
17049
17450
|
// 覆盖“仍停在裸 shell 的安全 hold”两种状态,一并入队;若该进程随后出现
|
|
17050
17451
|
// 真实 PTY prompt 且 leaf 已变为非 shell,markPromptReady 会恢复排空。
|
|
17452
|
+
// Hook-review menus are another direct-write hazard: raw_input normally
|
|
17453
|
+
// preserves busy delivery, but its Enter/arrow sequence must never drive
|
|
17454
|
+
// the menu's current selection.
|
|
17455
|
+
refreshHookReviewInputHold(lastAnalyzerSnapshot || renderer?.rawSnapshot() || '');
|
|
17051
17456
|
if (cliRestartInProgress || rawInputRestartGate || sessionRenameInFlight()
|
|
17052
17457
|
|| shouldHoldCodexRunnerInput(codexRunnerFreshness)
|
|
17053
17458
|
|| injectionFlushing || shouldDeferUserFlush(pendingInjections)
|
|
17054
|
-
|| bareShellCheckInProgress || bareShellLaunchBlocked
|
|
17459
|
+
|| bareShellCheckInProgress || bareShellLaunchBlocked
|
|
17460
|
+
|| hookReviewInputHold) {
|
|
17055
17461
|
freshnessInputQueue.enqueueRaw(msg);
|
|
17056
|
-
|
|
17462
|
+
if (hookReviewInputHold) {
|
|
17463
|
+
notifyHookReviewInputHold();
|
|
17464
|
+
log(`Deferred passthrough slash command while Hook review is visible: ${msg.content}`);
|
|
17465
|
+
}
|
|
17466
|
+
else {
|
|
17467
|
+
log(`Deferred passthrough slash command until CLI input gate settles: ${msg.content}`);
|
|
17468
|
+
}
|
|
17057
17469
|
}
|
|
17058
17470
|
else {
|
|
17059
17471
|
await deliverRawInput(msg);
|
|
@@ -17446,6 +17858,15 @@ process.on('message', async (raw) => {
|
|
|
17446
17858
|
log('Late ready signal after timeout fallback — marking prompt ready now');
|
|
17447
17859
|
markPromptReady();
|
|
17448
17860
|
}
|
|
17861
|
+
// CLI 启动确证(SessionStart hook 已跑):复位连续重启计数——resume 目标
|
|
17862
|
+
// 显然存在且加载成功。这和 prompt 侧复位是同一语义,但更早:2026-08-20
|
|
17863
|
+
// 事故里 resume 后的 CLI 已 ready 二十多秒、还没等到 prompt 复位就被共享
|
|
17864
|
+
// server 抖动误杀,第二次重启因此被 tier-2 强制 FRESH 白丢上下文。到过
|
|
17865
|
+
// ready 的 spawn 崩溃不是「resume 目标不存在」问题,下一轮应有全新预算。
|
|
17866
|
+
if (consecutiveInWorkerRestarts > 0) {
|
|
17867
|
+
log(`SessionStart ready — resetting consecutive restart count (was ${consecutiveInWorkerRestarts})`);
|
|
17868
|
+
consecutiveInWorkerRestarts = 0;
|
|
17869
|
+
}
|
|
17449
17870
|
if (msg.requestId) {
|
|
17450
17871
|
send({ type: 'session_ready_ack', requestId: msg.requestId });
|
|
17451
17872
|
}
|
|
@@ -17946,10 +18367,29 @@ function cleanup() {
|
|
|
17946
18367
|
// cleanup retires the lease. SIGKILL residue is reclaimed by the next worker.
|
|
17947
18368
|
releaseCodexAppPosixOwnerLease();
|
|
17948
18369
|
}
|
|
17949
|
-
|
|
17950
|
-
|
|
18370
|
+
/**
|
|
18371
|
+
* A shared existing-App-Server session owns this worker and its bmx-* remote
|
|
18372
|
+
* TUI, but never owns the App Server/thread behind that TUI. On daemon
|
|
18373
|
+
* restart, preserve the remote TUI so the replacement daemon can reattach it;
|
|
18374
|
+
* only retire this worker's HTTP/WebSocket observers. Ordinary managed sessions
|
|
18375
|
+
* retain the historical killCli shutdown path.
|
|
18376
|
+
*/
|
|
18377
|
+
function shutdownWorkerForParentExit(reason) {
|
|
18378
|
+
stopScreenshotLoop();
|
|
18379
|
+
if (lastInitConfig?.existingAppServerEndpoint) {
|
|
18380
|
+
log(`Preserving existing-App-Server remote TUI during ${reason}`);
|
|
18381
|
+
cleanup();
|
|
18382
|
+
process.exit(0);
|
|
18383
|
+
return;
|
|
18384
|
+
}
|
|
18385
|
+
killCli();
|
|
18386
|
+
cleanup();
|
|
18387
|
+
process.exit(0);
|
|
18388
|
+
}
|
|
18389
|
+
process.on('SIGTERM', () => shutdownWorkerForParentExit('SIGTERM'));
|
|
18390
|
+
process.on('SIGINT', () => shutdownWorkerForParentExit('SIGINT'));
|
|
17951
18391
|
// If parent daemon dies, IPC channel closes — clean up
|
|
17952
|
-
process.on('disconnect', () => { log('Daemon disconnected');
|
|
18392
|
+
process.on('disconnect', () => { log('Daemon disconnected'); shutdownWorkerForParentExit('IPC disconnect'); });
|
|
17953
18393
|
// Watchdog: belt-and-braces parent-death detection. SIGTERM and 'disconnect'
|
|
17954
18394
|
// should both reach us when the daemon dies, but if main thread is stuck in
|
|
17955
18395
|
// a sync path V8 silently buffers the signal and we end up as a ppid=1
|
|
@@ -17966,16 +18406,12 @@ setInterval(() => {
|
|
|
17966
18406
|
const currentPpid = process.ppid;
|
|
17967
18407
|
if (currentPpid !== ORIGINAL_PARENT_PID || currentPpid === 1) {
|
|
17968
18408
|
log(`Watchdog: parent pid changed (${ORIGINAL_PARENT_PID} → ${currentPpid}) — daemon died, exiting`);
|
|
17969
|
-
stopScreenshotLoop();
|
|
17970
18409
|
try {
|
|
17971
|
-
|
|
18410
|
+
shutdownWorkerForParentExit('parent watchdog');
|
|
17972
18411
|
}
|
|
17973
|
-
catch {
|
|
17974
|
-
|
|
17975
|
-
cleanup();
|
|
18412
|
+
catch {
|
|
18413
|
+
process.exit(0); /* best-effort exit if teardown itself faults */
|
|
17976
18414
|
}
|
|
17977
|
-
catch { /* best-effort */ }
|
|
17978
|
-
process.exit(0);
|
|
17979
18415
|
}
|
|
17980
18416
|
}, 30_000).unref();
|
|
17981
18417
|
// ─── Sandbox crash-time teardown ─────────────────────────────────────────────
|