botmux 3.13.0 → 3.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -1
- package/README.md +25 -1
- package/dist/.runtime-build-id +1 -1
- package/dist/adapters/adopt-route.d.ts +58 -0
- package/dist/adapters/adopt-route.d.ts.map +1 -1
- package/dist/adapters/adopt-route.js +136 -0
- package/dist/adapters/adopt-route.js.map +1 -1
- package/dist/adapters/backend/capabilities.js +1 -1
- package/dist/adapters/backend/capabilities.js.map +1 -1
- package/dist/adapters/backend/herdr-backend.d.ts +1 -1
- package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
- package/dist/adapters/backend/herdr-backend.js +2 -1
- package/dist/adapters/backend/herdr-backend.js.map +1 -1
- package/dist/adapters/backend/riff-backend.d.ts +97 -0
- package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
- package/dist/adapters/backend/riff-backend.js +380 -66
- 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 +16 -2
- package/dist/adapters/backend/sandbox.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +12 -13
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +113 -53
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts +34 -3
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +72 -9
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +7 -0
- package/dist/adapters/backend/types.d.ts.map +1 -1
- package/dist/adapters/backend/types.js.map +1 -1
- package/dist/adapters/backend/zellij-backend.d.ts +2 -3
- package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zellij-backend.js +6 -13
- package/dist/adapters/backend/zellij-backend.js.map +1 -1
- package/dist/adapters/backend/zellij-observe-backend.d.ts +2 -1
- package/dist/adapters/backend/zellij-observe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zellij-observe-backend.js +6 -3
- package/dist/adapters/backend/zellij-observe-backend.js.map +1 -1
- package/dist/adapters/backend/zmx-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zmx-backend.js +40 -18
- package/dist/adapters/backend/zmx-backend.js.map +1 -1
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +12 -1
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +3 -2
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/cursor.d.ts.map +1 -1
- package/dist/adapters/cli/cursor.js +13 -1
- package/dist/adapters/cli/cursor.js.map +1 -1
- package/dist/adapters/cli/dsh.d.ts +4 -0
- package/dist/adapters/cli/dsh.d.ts.map +1 -0
- package/dist/adapters/cli/dsh.js +100 -0
- package/dist/adapters/cli/dsh.js.map +1 -0
- package/dist/adapters/cli/fs-policy.d.ts.map +1 -1
- package/dist/adapters/cli/fs-policy.js +5 -0
- package/dist/adapters/cli/fs-policy.js.map +1 -1
- package/dist/adapters/cli/opencode.d.ts +52 -1
- package/dist/adapters/cli/opencode.d.ts.map +1 -1
- package/dist/adapters/cli/opencode.js +117 -76
- package/dist/adapters/cli/opencode.js.map +1 -1
- package/dist/adapters/cli/opencode2.d.ts +4 -0
- package/dist/adapters/cli/opencode2.d.ts.map +1 -0
- package/dist/adapters/cli/opencode2.js +137 -0
- package/dist/adapters/cli/opencode2.js.map +1 -0
- package/dist/adapters/cli/pi.d.ts +14 -1
- package/dist/adapters/cli/pi.d.ts.map +1 -1
- package/dist/adapters/cli/pi.js +21 -1
- package/dist/adapters/cli/pi.js.map +1 -1
- package/dist/adapters/cli/read-isolation.d.ts +1 -1
- package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
- package/dist/adapters/cli/read-isolation.js +5 -2
- package/dist/adapters/cli/read-isolation.js.map +1 -1
- package/dist/adapters/cli/registry.d.ts +7 -1
- package/dist/adapters/cli/registry.d.ts.map +1 -1
- package/dist/adapters/cli/registry.js +19 -6
- package/dist/adapters/cli/registry.js.map +1 -1
- package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
- package/dist/adapters/cli/shared-hints.js +41 -23
- package/dist/adapters/cli/shared-hints.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +28 -3
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/adapters/hook-command.d.ts +8 -0
- package/dist/adapters/hook-command.d.ts.map +1 -1
- package/dist/adapters/hook-command.js +11 -0
- package/dist/adapters/hook-command.js.map +1 -1
- package/dist/adapters/hook-installer.d.ts +16 -1
- package/dist/adapters/hook-installer.d.ts.map +1 -1
- package/dist/adapters/hook-installer.js +232 -2
- package/dist/adapters/hook-installer.js.map +1 -1
- package/dist/bot-registry.d.ts +138 -8
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +76 -4
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/pm2-start-transaction.d.ts +14 -0
- package/dist/cli/pm2-start-transaction.d.ts.map +1 -1
- package/dist/cli/pm2-start-transaction.js +25 -9
- package/dist/cli/pm2-start-transaction.js.map +1 -1
- package/dist/cli/restart-failure-notification.d.ts +54 -0
- package/dist/cli/restart-failure-notification.d.ts.map +1 -0
- package/dist/cli/restart-failure-notification.js +142 -0
- package/dist/cli/restart-failure-notification.js.map +1 -0
- package/dist/cli/restart-failure-owner.d.ts +20 -0
- package/dist/cli/restart-failure-owner.d.ts.map +1 -0
- package/dist/cli/restart-failure-owner.js +27 -0
- package/dist/cli/restart-failure-owner.js.map +1 -0
- package/dist/cli/restart-shutdown-preflight.d.ts +24 -0
- package/dist/cli/restart-shutdown-preflight.d.ts.map +1 -0
- package/dist/cli/restart-shutdown-preflight.js +105 -0
- package/dist/cli/restart-shutdown-preflight.js.map +1 -0
- package/dist/cli/send-dispatch.d.ts +26 -0
- package/dist/cli/send-dispatch.d.ts.map +1 -1
- package/dist/cli/send-dispatch.js +30 -0
- package/dist/cli/send-dispatch.js.map +1 -1
- package/dist/cli.d.ts +4 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +673 -402
- package/dist/cli.js.map +1 -1
- package/dist/codex-app-runner.js +38 -15
- package/dist/codex-app-runner.js.map +1 -1
- package/dist/codex-rpc-lifecycle.d.ts +14 -2
- package/dist/codex-rpc-lifecycle.d.ts.map +1 -1
- package/dist/codex-rpc-lifecycle.js +13 -3
- package/dist/codex-rpc-lifecycle.js.map +1 -1
- package/dist/core/ask-broker.d.ts +3 -0
- package/dist/core/ask-broker.d.ts.map +1 -1
- package/dist/core/ask-broker.js +29 -7
- package/dist/core/ask-broker.js.map +1 -1
- package/dist/core/ask-hook/registry.d.ts.map +1 -1
- package/dist/core/ask-hook/registry.js +4 -0
- package/dist/core/ask-hook/registry.js.map +1 -1
- package/dist/core/ask-types.d.ts +5 -1
- package/dist/core/ask-types.d.ts.map +1 -1
- package/dist/core/ask-types.js.map +1 -1
- package/dist/core/browser-restart.d.ts +112 -0
- package/dist/core/browser-restart.d.ts.map +1 -0
- package/dist/core/browser-restart.js +233 -0
- package/dist/core/browser-restart.js.map +1 -0
- package/dist/core/command-discovery.d.ts.map +1 -1
- package/dist/core/command-discovery.js +4 -1
- package/dist/core/command-discovery.js.map +1 -1
- package/dist/core/command-handler.d.ts +19 -11
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +237 -43
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/current-turn-provenance.d.ts.map +1 -1
- package/dist/core/current-turn-provenance.js +7 -27
- package/dist/core/current-turn-provenance.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +359 -11
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dispatch-registry.d.ts.map +1 -1
- package/dist/core/dispatch-registry.js +6 -0
- package/dist/core/dispatch-registry.js.map +1 -1
- package/dist/core/dispatch-report-binding.d.ts +31 -0
- package/dist/core/dispatch-report-binding.d.ts.map +1 -0
- package/dist/core/dispatch-report-binding.js +88 -0
- package/dist/core/dispatch-report-binding.js.map +1 -0
- package/dist/core/doc-comment-prompt.d.ts +2 -0
- package/dist/core/doc-comment-prompt.d.ts.map +1 -1
- package/dist/core/doc-comment-prompt.js +6 -0
- package/dist/core/doc-comment-prompt.js.map +1 -1
- package/dist/core/host-overload-alert.d.ts +25 -1
- package/dist/core/host-overload-alert.d.ts.map +1 -1
- package/dist/core/host-overload-alert.js +69 -8
- package/dist/core/host-overload-alert.js.map +1 -1
- package/dist/core/local-terminal-opener.js +1 -1
- package/dist/core/local-terminal-opener.js.map +1 -1
- package/dist/core/managed-origin-capability.d.ts +7 -6
- package/dist/core/managed-origin-capability.d.ts.map +1 -1
- package/dist/core/managed-origin-capability.js +32 -1
- package/dist/core/managed-origin-capability.js.map +1 -1
- package/dist/core/passthrough-commands.d.ts.map +1 -1
- package/dist/core/passthrough-commands.js +3 -2
- package/dist/core/passthrough-commands.js.map +1 -1
- package/dist/core/plugins/materializer.d.ts.map +1 -1
- package/dist/core/plugins/materializer.js +1 -0
- package/dist/core/plugins/materializer.js.map +1 -1
- package/dist/core/producer-quiescence.d.ts +70 -0
- package/dist/core/producer-quiescence.d.ts.map +1 -0
- package/dist/core/producer-quiescence.js +90 -0
- package/dist/core/producer-quiescence.js.map +1 -0
- package/dist/core/raw-command-write-options.d.ts +10 -0
- package/dist/core/raw-command-write-options.d.ts.map +1 -0
- package/dist/core/raw-command-write-options.js +23 -0
- package/dist/core/raw-command-write-options.js.map +1 -0
- package/dist/core/raw-command-writer.d.ts +4 -0
- package/dist/core/raw-command-writer.d.ts.map +1 -1
- package/dist/core/raw-command-writer.js +15 -6
- package/dist/core/raw-command-writer.js.map +1 -1
- package/dist/core/reply-target.d.ts +11 -0
- package/dist/core/reply-target.d.ts.map +1 -1
- package/dist/core/reply-target.js +22 -7
- package/dist/core/reply-target.js.map +1 -1
- package/dist/core/report-session-relay.d.ts +53 -0
- package/dist/core/report-session-relay.d.ts.map +1 -0
- package/dist/core/report-session-relay.js +116 -0
- package/dist/core/report-session-relay.js.map +1 -0
- package/dist/core/session-discovery.d.ts +5 -0
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +13 -0
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-group-birth.d.ts +7 -0
- package/dist/core/session-group-birth.d.ts.map +1 -0
- package/dist/core/session-group-birth.js +145 -0
- package/dist/core/session-group-birth.js.map +1 -0
- package/dist/core/session-manager.d.ts +16 -21
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +140 -21
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/skills/frontmatter.d.ts.map +1 -1
- package/dist/core/skills/frontmatter.js +6 -1
- package/dist/core/skills/frontmatter.js.map +1 -1
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +18 -3
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +18 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +63 -11
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +262 -21
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts +33 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +904 -147
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/auth.d.ts +20 -0
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +86 -1
- package/dist/dashboard/auth.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +9 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +5 -1
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/federation-spoke-api.d.ts.map +1 -1
- package/dist/dashboard/federation-spoke-api.js +27 -3
- package/dist/dashboard/federation-spoke-api.js.map +1 -1
- package/dist/dashboard/feedback-analytics-api.d.ts +6 -0
- package/dist/dashboard/feedback-analytics-api.d.ts.map +1 -0
- package/dist/dashboard/feedback-analytics-api.js +39 -0
- package/dist/dashboard/feedback-analytics-api.js.map +1 -0
- package/dist/dashboard/session-action-timeout.d.ts +3 -0
- package/dist/dashboard/session-action-timeout.d.ts.map +1 -0
- package/dist/dashboard/session-action-timeout.js +12 -0
- package/dist/dashboard/session-action-timeout.js.map +1 -0
- package/dist/dashboard/vc-consumer-profiles-api.d.ts +9 -3
- package/dist/dashboard/vc-consumer-profiles-api.d.ts.map +1 -1
- package/dist/dashboard/vc-consumer-profiles-api.js +2 -1
- package/dist/dashboard/vc-consumer-profiles-api.js.map +1 -1
- package/dist/dashboard/web/app.d.ts.map +1 -1
- package/dist/dashboard/web/app.js +48 -5
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +11 -0
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +368 -36
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +8 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/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/feedback-page.d.ts +3 -0
- package/dist/dashboard/web/feedback-page.d.ts.map +1 -0
- package/dist/dashboard/web/feedback-page.js +34 -0
- package/dist/dashboard/web/feedback-page.js.map +1 -0
- package/dist/dashboard/web/groups-page.d.ts.map +1 -1
- package/dist/dashboard/web/groups-page.js +7 -2
- 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 +122 -22
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/skills-page.d.ts.map +1 -1
- package/dist/dashboard/web/skills-page.js +4 -2
- package/dist/dashboard/web/skills-page.js.map +1 -1
- package/dist/dashboard/web/team-federation-page.d.ts.map +1 -1
- package/dist/dashboard/web/team-federation-page.js +26 -2
- package/dist/dashboard/web/team-federation-page.js.map +1 -1
- package/dist/dashboard/web/team-federation.d.ts +19 -0
- package/dist/dashboard/web/team-federation.d.ts.map +1 -1
- package/dist/dashboard/web/team-federation.js +3 -0
- package/dist/dashboard/web/team-federation.js.map +1 -1
- package/dist/dashboard/web/update-action.d.ts +8 -0
- package/dist/dashboard/web/update-action.d.ts.map +1 -1
- package/dist/dashboard/web/update-action.js +10 -0
- package/dist/dashboard/web/update-action.js.map +1 -1
- package/dist/dashboard/web/vc-consumer-profiles-section.d.ts.map +1 -1
- package/dist/dashboard/web/vc-consumer-profiles-section.js +58 -9
- package/dist/dashboard/web/vc-consumer-profiles-section.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-65SMZ4QY.js +10 -0
- package/dist/dashboard-web/chunks/chunk-G5XGBCM6.js +1 -0
- package/dist/dashboard-web/chunks/chunk-IKN6WURK.js +1 -0
- package/dist/dashboard-web/chunks/{chunk-6JTZJRQJ.js → chunk-VMWNR3SV.js} +15 -15
- package/dist/dashboard-web/chunks/{chunk-CU4SUKK3.js → chunk-Z7B3E3US.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-5QJB24I2.js → chunk-Z7DO42GJ.js} +1 -1
- package/dist/dashboard-web/chunks/connectors-page-CPH6VVFY.js +1 -0
- package/dist/dashboard-web/chunks/feedback-page-J2FKQ3LK.js +1 -0
- package/dist/dashboard-web/chunks/groups-page-VDOOLS4M.js +8 -0
- package/dist/dashboard-web/chunks/{insights-page-EVGGJI5L.js → insights-page-A2LWTKJD.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-HRGR2ENP.js → monitor-room-2YR752E3.js} +1 -1
- package/dist/dashboard-web/chunks/monitoring-page-BGYSKZGM.js +2 -0
- package/dist/dashboard-web/chunks/office-page-ZZHTEAE4.js +1 -0
- package/dist/dashboard-web/chunks/overview-page-7NYPKQSB.js +1 -0
- package/dist/dashboard-web/chunks/roles-page-YUC4YLJG.js +1 -0
- package/dist/dashboard-web/chunks/schedules-page-2L2NKTDJ.js +1 -0
- package/dist/dashboard-web/chunks/{sessions-page-DXBCDWCS.js → sessions-page-DYZZMKFL.js} +1 -1
- package/dist/dashboard-web/chunks/settings-page-LIULUSMH.js +2 -0
- package/dist/dashboard-web/chunks/{skills-page-2QAVB2SA.js → skills-page-DQPBBI3T.js} +2 -2
- package/dist/dashboard-web/chunks/team-federation-page-LQKLDV4K.js +1 -0
- package/dist/dashboard-web/chunks/{v3-page-AY35LKCX.js → v3-page-RVKQIMAP.js} +1 -1
- package/dist/dashboard-web/chunks/whiteboards-page-S5K55HRZ.js +1 -0
- package/dist/dashboard-web/style.css +35 -0
- package/dist/dashboard.js +210 -13
- package/dist/dashboard.js.map +1 -1
- package/dist/desktop/shared/shell-path-probes.d.ts +1 -1
- package/dist/desktop/shared/shell-path-probes.js +2 -2
- package/dist/desktop/shared/shell-path-probes.js.map +1 -1
- package/dist/dsh-runner.d.ts +3 -0
- package/dist/dsh-runner.d.ts.map +1 -0
- package/dist/dsh-runner.js +673 -0
- package/dist/dsh-runner.js.map +1 -0
- package/dist/global-config.d.ts +24 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +35 -0
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +39 -31
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +39 -31
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/ask-card.d.ts +3 -1
- package/dist/im/lark/ask-card.d.ts.map +1 -1
- package/dist/im/lark/ask-card.js +61 -5
- package/dist/im/lark/ask-card.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +13 -6
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +2 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +145 -5
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +18 -0
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +41 -28
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/md-card.d.ts +18 -0
- package/dist/im/lark/md-card.d.ts.map +1 -1
- package/dist/im/lark/md-card.js +21 -0
- package/dist/im/lark/md-card.js.map +1 -1
- package/dist/im/lark/relay-target-routing.d.ts.map +1 -1
- package/dist/im/lark/relay-target-routing.js +3 -2
- package/dist/im/lark/relay-target-routing.js.map +1 -1
- package/dist/im/lark/reply-mode-command.d.ts.map +1 -1
- package/dist/im/lark/reply-mode-command.js +19 -8
- package/dist/im/lark/reply-mode-command.js.map +1 -1
- package/dist/im/lark/skill-feedback-card.d.ts +15 -0
- package/dist/im/lark/skill-feedback-card.d.ts.map +1 -0
- package/dist/im/lark/skill-feedback-card.js +149 -0
- package/dist/im/lark/skill-feedback-card.js.map +1 -0
- package/dist/services/bot-config-store.d.ts +10 -1
- package/dist/services/bot-config-store.d.ts.map +1 -1
- package/dist/services/bot-config-store.js +103 -3
- package/dist/services/bot-config-store.js.map +1 -1
- package/dist/services/bridge-fallback-gate.d.ts +29 -7
- package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
- package/dist/services/bridge-fallback-gate.js +58 -16
- package/dist/services/bridge-fallback-gate.js.map +1 -1
- package/dist/services/codex-reasoning-effort.d.ts +9 -0
- package/dist/services/codex-reasoning-effort.d.ts.map +1 -0
- package/dist/services/codex-reasoning-effort.js +23 -0
- package/dist/services/codex-reasoning-effort.js.map +1 -0
- package/dist/services/codex-service-tier.d.ts +1 -1
- package/dist/services/codex-service-tier.d.ts.map +1 -1
- package/dist/services/codex-transcript.d.ts +9 -0
- package/dist/services/codex-transcript.d.ts.map +1 -1
- package/dist/services/codex-transcript.js +13 -6
- package/dist/services/codex-transcript.js.map +1 -1
- package/dist/services/feed-group-tagger.d.ts +15 -0
- package/dist/services/feed-group-tagger.d.ts.map +1 -0
- package/dist/services/feed-group-tagger.js +348 -0
- package/dist/services/feed-group-tagger.js.map +1 -0
- package/dist/services/feedback-analytics.d.ts +82 -0
- package/dist/services/feedback-analytics.d.ts.map +1 -0
- package/dist/services/feedback-analytics.js +95 -0
- package/dist/services/feedback-analytics.js.map +1 -0
- package/dist/services/feedback-outbox.d.ts +24 -0
- package/dist/services/feedback-outbox.d.ts.map +1 -0
- package/dist/services/feedback-outbox.js +12 -0
- package/dist/services/feedback-outbox.js.map +1 -0
- package/dist/services/feedback-policy-resolver.d.ts +56 -0
- package/dist/services/feedback-policy-resolver.d.ts.map +1 -0
- package/dist/services/feedback-policy-resolver.js +156 -0
- package/dist/services/feedback-policy-resolver.js.map +1 -0
- package/dist/services/feedback-policy.d.ts +41 -0
- package/dist/services/feedback-policy.d.ts.map +1 -0
- package/dist/services/feedback-policy.js +102 -0
- package/dist/services/feedback-policy.js.map +1 -0
- package/dist/services/feedback-webhook-dispatcher.d.ts +76 -0
- package/dist/services/feedback-webhook-dispatcher.d.ts.map +1 -0
- package/dist/services/feedback-webhook-dispatcher.js +237 -0
- package/dist/services/feedback-webhook-dispatcher.js.map +1 -0
- package/dist/services/grant-prefs-store.d.ts +3 -0
- package/dist/services/grant-prefs-store.d.ts.map +1 -1
- package/dist/services/grant-prefs-store.js +30 -1
- package/dist/services/grant-prefs-store.js.map +1 -1
- package/dist/services/groups-store.d.ts +14 -0
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +23 -0
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/local-cli-opener.d.ts +1 -1
- package/dist/services/local-cli-opener.d.ts.map +1 -1
- package/dist/services/local-cli-opener.js +2 -0
- package/dist/services/local-cli-opener.js.map +1 -1
- package/dist/services/peer-cross-ref-store.d.ts +8 -0
- package/dist/services/peer-cross-ref-store.d.ts.map +1 -0
- package/dist/services/peer-cross-ref-store.js +95 -0
- package/dist/services/peer-cross-ref-store.js.map +1 -0
- package/dist/services/pi-transcript.d.ts.map +1 -1
- package/dist/services/pi-transcript.js +10 -6
- package/dist/services/pi-transcript.js.map +1 -1
- package/dist/services/prompt-context-store.d.ts +29 -0
- package/dist/services/prompt-context-store.d.ts.map +1 -0
- package/dist/services/prompt-context-store.js +205 -0
- package/dist/services/prompt-context-store.js.map +1 -0
- package/dist/services/session-group-avatar.d.ts +6 -0
- package/dist/services/session-group-avatar.d.ts.map +1 -0
- package/dist/services/session-group-avatar.js +96 -0
- package/dist/services/session-group-avatar.js.map +1 -0
- package/dist/services/session-group-title.d.ts +47 -0
- package/dist/services/session-group-title.d.ts.map +1 -0
- package/dist/services/session-group-title.js +226 -0
- package/dist/services/session-group-title.js.map +1 -0
- package/dist/services/session-groups-store.d.ts +30 -0
- package/dist/services/session-groups-store.d.ts.map +1 -0
- package/dist/services/session-groups-store.js +132 -0
- package/dist/services/session-groups-store.js.map +1 -0
- package/dist/services/session-store.d.ts +58 -0
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +174 -0
- package/dist/services/session-store.js.map +1 -1
- package/dist/services/skill-feedback-store.d.ts +252 -0
- package/dist/services/skill-feedback-store.d.ts.map +1 -0
- package/dist/services/skill-feedback-store.js +795 -0
- package/dist/services/skill-feedback-store.js.map +1 -0
- package/dist/services/skill-registry-store.d.ts +23 -0
- package/dist/services/skill-registry-store.d.ts.map +1 -1
- package/dist/services/skill-registry-store.js +93 -7
- package/dist/services/skill-registry-store.js.map +1 -1
- package/dist/services/structured-bridge-clis.d.ts +13 -4
- package/dist/services/structured-bridge-clis.d.ts.map +1 -1
- package/dist/services/structured-bridge-clis.js +13 -3
- package/dist/services/structured-bridge-clis.js.map +1 -1
- package/dist/services/team-store.d.ts +4 -0
- package/dist/services/team-store.d.ts.map +1 -1
- package/dist/services/team-store.js +16 -0
- package/dist/services/team-store.js.map +1 -1
- package/dist/services/traex-transcript.d.ts +32 -4
- package/dist/services/traex-transcript.d.ts.map +1 -1
- package/dist/services/traex-transcript.js +116 -8
- package/dist/services/traex-transcript.js.map +1 -1
- package/dist/services/trigger-types.d.ts +1 -1
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js +2 -2
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/services/turn-completion-events.d.ts +57 -0
- package/dist/services/turn-completion-events.d.ts.map +1 -0
- package/dist/services/turn-completion-events.js +179 -0
- package/dist/services/turn-completion-events.js.map +1 -0
- package/dist/services/vc-meeting-consumer-isolation.d.ts +21 -12
- package/dist/services/vc-meeting-consumer-isolation.d.ts.map +1 -1
- package/dist/services/vc-meeting-consumer-isolation.js +25 -19
- package/dist/services/vc-meeting-consumer-isolation.js.map +1 -1
- package/dist/services/vc-meeting-consumer-profile-bootstrap.d.ts +8 -2
- package/dist/services/vc-meeting-consumer-profile-bootstrap.d.ts.map +1 -1
- package/dist/services/vc-meeting-consumer-profile-bootstrap.js +18 -10
- package/dist/services/vc-meeting-consumer-profile-bootstrap.js.map +1 -1
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +6 -0
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/setup/cli-availability.d.ts +2 -1
- package/dist/setup/cli-availability.d.ts.map +1 -1
- package/dist/setup/cli-availability.js +2 -1
- package/dist/setup/cli-availability.js.map +1 -1
- package/dist/setup/cli-selection.d.ts +8 -0
- package/dist/setup/cli-selection.d.ts.map +1 -1
- package/dist/setup/cli-selection.js +31 -0
- package/dist/setup/cli-selection.js.map +1 -1
- package/dist/setup/ensure-fonts.d.ts +5 -0
- package/dist/setup/ensure-fonts.d.ts.map +1 -1
- package/dist/setup/ensure-fonts.js +26 -1
- package/dist/setup/ensure-fonts.js.map +1 -1
- package/dist/setup/lark-scopes.json +3 -1
- package/dist/setup/open-platform-automation.d.ts +1 -1
- package/dist/setup/open-platform-automation.d.ts.map +1 -1
- package/dist/setup/open-platform-automation.js +15 -3
- package/dist/setup/open-platform-automation.js.map +1 -1
- package/dist/skills/definitions.d.ts +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +9 -2
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +11 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/bot-routing.d.ts +1 -1
- package/dist/utils/bot-routing.d.ts.map +1 -1
- package/dist/utils/bot-routing.js.map +1 -1
- package/dist/utils/user-token.d.ts +38 -0
- package/dist/utils/user-token.d.ts.map +1 -1
- package/dist/utils/user-token.js +76 -1
- package/dist/utils/user-token.js.map +1 -1
- package/dist/utils/web-terminal-scroll.d.ts +27 -0
- package/dist/utils/web-terminal-scroll.d.ts.map +1 -0
- package/dist/utils/web-terminal-scroll.js +60 -0
- package/dist/utils/web-terminal-scroll.js.map +1 -0
- package/dist/vc-agent/polling-source.d.ts +70 -0
- package/dist/vc-agent/polling-source.d.ts.map +1 -1
- package/dist/vc-agent/polling-source.js +115 -0
- package/dist/vc-agent/polling-source.js.map +1 -1
- package/dist/worker.js +469 -86
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +22 -22
- package/dist/workflows/events/payloads.d.ts +8 -8
- package/dist/workflows/events/schema.d.ts +20 -20
- package/dist/workflows/migration/v2-run-archive-schema.d.ts +20 -20
- package/package.json +5 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-NQJFJ45Z.js +0 -10
- package/dist/dashboard-web/chunks/chunk-KEEYQ3WF.js +0 -1
- package/dist/dashboard-web/chunks/connectors-page-HADHZXXZ.js +0 -1
- package/dist/dashboard-web/chunks/groups-page-5TYF46RS.js +0 -8
- package/dist/dashboard-web/chunks/monitoring-page-LBU7TTTW.js +0 -2
- package/dist/dashboard-web/chunks/office-page-DF6RRBMN.js +0 -1
- package/dist/dashboard-web/chunks/overview-page-ZB6QH2EX.js +0 -1
- package/dist/dashboard-web/chunks/roles-page-ZBZGHIIF.js +0 -1
- package/dist/dashboard-web/chunks/schedules-page-NQVQC3QY.js +0 -1
- package/dist/dashboard-web/chunks/settings-page-E7NN4NVQ.js +0 -2
- package/dist/dashboard-web/chunks/team-federation-page-VGWL53AE.js +0 -1
- package/dist/dashboard-web/chunks/whiteboards-page-BNWUU4KG.js +0 -1
package/dist/worker.js
CHANGED
|
@@ -23,18 +23,20 @@ import { evaluateCredentialOnlyIsolationGate, credentialIsolationRequired, devic
|
|
|
23
23
|
import { buildFsPolicy, compileToSeatbelt, migrateLegacySandboxFields, resolveRedirectedAdapterAuthPaths, FsPolicyConfigError } from './adapters/cli/fs-policy.js';
|
|
24
24
|
import { killPersistentBackendTarget, killPersistentSession, probePersistentBackendTarget, probePersistentSession, shouldRejectPersistentPostKillProbe } from './core/persistent-backend.js';
|
|
25
25
|
import { finalizeRawCommandDelivery, writeRawCommandLine } from './core/raw-command-writer.js';
|
|
26
|
+
import { rawCommandWriteOptionsFor } from './core/raw-command-write-options.js';
|
|
26
27
|
import { publishCliSessionIdToDaemon } from './core/cli-session-id-publisher.js';
|
|
27
28
|
import { readProcessStartIdentity } from './core/session-marker.js';
|
|
28
29
|
import { roleLibraryRoot, roleLibrarySubtree } from './core/role-library.js';
|
|
29
30
|
import { drainTranscript, joinAssistantText, trailingAssistantText, findJsonlContainingFingerprint, findJsonlsContainingExactContent, findLatestJsonl, extractLastAssistantTurn, extractTurnStartText, splitTranscriptEventsByCutoff, isTranscriptRateLimitEvent, apiErrorMessageText } from './services/claude-transcript.js';
|
|
30
31
|
import { BridgeTurnQueue, makeFingerprint, normaliseForFingerprint } from './services/bridge-turn-queue.js';
|
|
31
|
-
import { bridgePostText, isBridgeNothingToSendFinal,
|
|
32
|
+
import { bridgePostText, isBridgeNothingToSendFinal, shouldSuppressBridgeEmit, structuredFallbackKind, stripTrailingOaiMemoryCitation } from './services/bridge-fallback-gate.js';
|
|
32
33
|
import { buildSubmitMessagePreview } from './services/submit-notification.js';
|
|
33
34
|
import { decideHardTimeoutAction, decidePostHookPromptEvidence, decideSettleMarkReady, shouldArmPostHookPromptEvidenceFallback, shouldReleaseFirstPromptTimeout, shouldWaitForPostSessionStartPromptEvidence, shouldWriteNow, POST_HOOK_EVIDENCE_FALLBACK_MS, POST_HOOK_EVIDENCE_RETRY_MS, } from './utils/input-gate.js';
|
|
34
35
|
import { decideCodexRunnerFreshness, CodexRunnerFreshnessInputQueue, shouldHoldCodexRunnerInput, } from './services/codex-runner-freshness.js';
|
|
35
36
|
import { canStartInjectionFlush, shouldDeferUserFlush, shouldFlushInjectionsFirst } from './core/inject-queue-policy.js';
|
|
36
37
|
import { decideRestartFollowup, settleDurableTurnForRestart } from './core/restart-followup-policy.js';
|
|
37
38
|
import { stripAnsiForLog, tailChars } from './utils/crash-log.js';
|
|
39
|
+
import { parseReadOnlyRemoteScrollPayload, READ_ONLY_REMOTE_SCROLL_SESSION_BUDGET, READ_ONLY_REMOTE_SCROLL_WINDOW_MS, ReadOnlyRemoteScrollLimiter, } from './utils/web-terminal-scroll.js';
|
|
38
40
|
import { CodexUpdateDialogGuard } from './utils/codex-update-dialog.js';
|
|
39
41
|
import { EffortConfirmDialogGuard } from './utils/effort-confirm-dialog.js';
|
|
40
42
|
import { installStdioEpipeGuard, isIgnorableStreamError } from './utils/stdio-epipe-guard.js';
|
|
@@ -63,7 +65,7 @@ import { CodexBridgeQueue, pruneExpiredPreStartHeadsAndEmit } from './services/c
|
|
|
63
65
|
import { detectCodexComposerState } from './services/codex-composer-state.js';
|
|
64
66
|
import { generateCodexAppThreadTitle, readCodexAppThreadMetadata, setCodexAppThreadName, } from './services/codex-app-threads.js';
|
|
65
67
|
import { buildBotmuxLarkNativeSessionTitle } from './core/session-title.js';
|
|
66
|
-
import { CODEX_AUTH_ERROR_CODE, CODEX_CONNECTION_ERROR_CODE, CODEX_INVALID_REQUEST_ERROR_CODE,
|
|
68
|
+
import { CODEX_AUTH_ERROR_CODE, CODEX_CONNECTION_ERROR_CODE, CODEX_INVALID_REQUEST_ERROR_CODE, drainCodexRollout, findCodexRolloutBySessionId, findCodexRolloutByPid, findCodexRolloutSetByPid, codexHistorySidIsOwned, splitCodexEventsByCutoff, extractLastCodexTurn, codexSessionIdFromRolloutPath, isCodexRateLimitEvent, scanCodexThreadSettings, readLatestCodexRuntime } from './services/codex-transcript.js';
|
|
67
69
|
import { CodexServiceTierTracker, resolveCodexServiceTierSnapshot } from './services/codex-service-tier.js';
|
|
68
70
|
import { WORKER_IPC_HANDLER_READY_EVENT } from './worker-ipc-preload.js';
|
|
69
71
|
import { drainTraexRollout, findTraexRolloutBySessionId, findTraexRolloutByPid, findTraexRolloutSetByPid, readLatestTraexRuntime, traexHistorySidIsOwned } from './services/traex-transcript.js';
|
|
@@ -91,9 +93,9 @@ import { t, setDefaultLocale } from './i18n/index.js';
|
|
|
91
93
|
import { TerminalRenderer } from './utils/terminal-renderer.js';
|
|
92
94
|
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';
|
|
93
95
|
import { createCliAdapterSync, locateOnPath } from './adapters/cli/registry.js';
|
|
94
|
-
import { buildWrappedLaunch, parseWrapperCli, isTtadkWrapper } from './setup/cli-selection.js';
|
|
96
|
+
import { buildWrappedLaunch, parseWrapperCli, isTtadkWrapper, wrapperLaunchEnv } from './setup/cli-selection.js';
|
|
95
97
|
import { cliUnavailableMessage } from './setup/cli-availability.js';
|
|
96
|
-
import { findLaunchedCliPid, scheduleWrapperRealCliPid, readComm, isBareShellComm, bareShellLaunchKind, settleLaunchComm, } from './core/session-discovery.js';
|
|
98
|
+
import { findLaunchedCliPid, scheduleWrapperRealCliPid, readComm, isBareShellComm, bareShellLaunchKind, bareShellLaunchGuidance, settleLaunchComm, } from './core/session-discovery.js';
|
|
97
99
|
import { CODEX_RPC_TERMINAL_HYDRATION_DELAYS_MS, RpcEngagementFence, codexRpcEligible, paneRunsRemoteTui, orchestrateCodexRpcInit, rolloutUserTurnMatches, decideStartupDialogAction, shouldQueueInitialPrompt, shouldPreMarkFirstTurn, killAndVerifyPersistentPane, rpcTranscriptIngestBlockedByAwaitingActivation } from './codex-rpc-lifecycle.js';
|
|
98
100
|
import { delay } from './utils/timing.js';
|
|
99
101
|
import { claudeJsonlPathForSession, resolveJsonlFromPid, findOpenClaudeSessionIds, syncClaudeResumeTargetToCwd, DEFAULT_CLAUDE_DATA_DIR } from './adapters/cli/claude-code.js';
|
|
@@ -143,7 +145,7 @@ import { withCodexAppContext } from './utils/codex-app-context.js';
|
|
|
143
145
|
import { resolveCodexAppFinalTurnIdentity } from './adapters/cli/codex-app-turn.js';
|
|
144
146
|
import { RunnerControlDecoder } from './adapters/cli/runner-control-channel.js';
|
|
145
147
|
import { normalizeCodexAppLifecycleEvent, normalizeFinalUsage, } from './services/codex-app-runner-protocol.js';
|
|
146
|
-
import { hasMatchingManagedOriginCapability, ensureManagedOriginAttestationDirectory, ensureManagedOriginCapabilityLeafSafe, ensureManagedOriginDataRootProbe, ensureManagedOriginIsolationSentinel, ensureManagedOriginRootLocator, managedOriginLegacyIsolationProbeAccess, managedOriginDataRootProbeAccess, managedOriginIsolationSentinelAccess, managedOriginAttestationDirectory, managedOriginCapabilityPath, managedOriginRootLocatorPath, readManagedOriginAuthorityFile, RELAY_ORIGIN_CAPABILITY_BASENAME, replaceManagedOriginCapabilityFile, sweepManagedOriginAttestationProofs, } from './core/managed-origin-capability.js';
|
|
148
|
+
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';
|
|
147
149
|
import { CodexRpcEngine, } from './codex-rpc-engine.js';
|
|
148
150
|
import { beginRuntimeWriteCycle, isCliBackendGenerationCurrent, PtyOutputGeneration, projectRuntimeScreenStatus, } from './utils/runtime-screen-status.js';
|
|
149
151
|
import { AsyncSerialQueue } from './utils/async-serial-queue.js';
|
|
@@ -422,14 +424,22 @@ function persistentPaneInfo(backendType, sessionId) {
|
|
|
422
424
|
: HerdrBackend.hasSession(name);
|
|
423
425
|
return { name, live };
|
|
424
426
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
427
|
+
/** Tri-state pane liveness, for callers that must not read "no answer" as an
|
|
428
|
+
* answer. The `hasSession()` helpers collapse an unanswered probe into
|
|
429
|
+
* `false`, which is safe where a wrong guess just means "treat as fresh" but
|
|
430
|
+
* NOT where the boolean is consumed as proof that a kill succeeded. Every
|
|
431
|
+
* backend already classifies this correctly; only the boolean wrapper threw
|
|
432
|
+
* the distinction away. */
|
|
433
|
+
function persistentPaneProbe(backendType, name) {
|
|
434
|
+
const probe = backendType === 'tmux' ? TmuxBackend.probeSession(name)
|
|
435
|
+
: backendType === 'zellij' ? ZellijBackend.probeSession(name)
|
|
436
|
+
: backendType === 'herdr' ? HerdrBackend.probeSession(name)
|
|
437
|
+
: ZmxBackend.probeSession(name);
|
|
438
|
+
if (probe === 'exists')
|
|
439
|
+
return 'live';
|
|
440
|
+
if (probe === 'missing')
|
|
441
|
+
return 'gone';
|
|
442
|
+
return 'unknown';
|
|
433
443
|
}
|
|
434
444
|
function probeOwnedZmxSession(name, sessionId, expectedPid) {
|
|
435
445
|
const managed = ZmxBackend.probeManagedSession(name, sessionId);
|
|
@@ -459,7 +469,7 @@ function probeOwnedZmxSession(name, sessionId, expectedPid) {
|
|
|
459
469
|
async function killPersistentSessionVerified(backendType, name, sessionId) {
|
|
460
470
|
return killAndVerifyPersistentPane(name, {
|
|
461
471
|
kill: (resolvedName) => killPersistentSession(backendType, resolvedName, sessionId),
|
|
462
|
-
|
|
472
|
+
probeLive: (resolvedName) => persistentPaneProbe(backendType, resolvedName),
|
|
463
473
|
wait: delay,
|
|
464
474
|
});
|
|
465
475
|
}
|
|
@@ -1389,6 +1399,25 @@ let spawnArgvInitialPromptBusy = false;
|
|
|
1389
1399
|
* arm above; quiescence argv adapters seed working then idle at first ready.
|
|
1390
1400
|
*/
|
|
1391
1401
|
let spawnArgvNeedsWorkingSeed = false;
|
|
1402
|
+
/**
|
|
1403
|
+
* Startup-window evidence for an argv-baked first prompt: true once the turn
|
|
1404
|
+
* has VISIBLY started — busyPattern seen in the raw PTY stream since spawn, or
|
|
1405
|
+
* a live structured-transcript user/final event ingested. Pi's TUI renders its
|
|
1406
|
+
* input box seconds before it begins consuming the argv prompt (extension /
|
|
1407
|
+
* model loading), and during that gap the screen shows no busy marker and the
|
|
1408
|
+
* transcript has no user record — quiescence then reports a FALSE first idle
|
|
1409
|
+
* ("not started yet", not "turn complete"). Until this latches,
|
|
1410
|
+
* markPromptReady holds ready + re-arms instead of seeding working→idle.
|
|
1411
|
+
*/
|
|
1412
|
+
let spawnArgvTurnStartEvidenceSeen = false;
|
|
1413
|
+
/** Rolling ANSI-stripped PTY tail scanned for busyPattern while the argv
|
|
1414
|
+
* first-turn evidence gate is armed; dropped once evidence latches. */
|
|
1415
|
+
let spawnArgvTurnStartBusyScanTail = '';
|
|
1416
|
+
/** Fail-open deadline for the evidence gate: past this instant the gate stops
|
|
1417
|
+
* holding, so an argv prompt the CLI never consumed (e.g. dropped on a
|
|
1418
|
+
* restart) cannot park the card at 工作中 forever. */
|
|
1419
|
+
let spawnArgvTurnStartEvidenceDeadlineMs = 0;
|
|
1420
|
+
let spawnArgvTurnStartFailOpenTimer = null;
|
|
1392
1421
|
let idleDetector = null;
|
|
1393
1422
|
let isTmuxMode = false;
|
|
1394
1423
|
/** True once a crash diagnostic tmux shell (bmx-diag-<sid>) is live. */
|
|
@@ -1439,6 +1468,10 @@ const authedClients = new WeakSet();
|
|
|
1439
1468
|
const clientPtys = new Map();
|
|
1440
1469
|
/** Managed-Herdr viewers survive an in-worker /restart while backend changes. */
|
|
1441
1470
|
const herdrWebBindings = new Map();
|
|
1471
|
+
const readOnlyRemoteScrollLimiter = new ReadOnlyRemoteScrollLimiter({
|
|
1472
|
+
budget: READ_ONLY_REMOTE_SCROLL_SESSION_BUDGET,
|
|
1473
|
+
windowMs: READ_ONLY_REMOTE_SCROLL_WINDOW_MS,
|
|
1474
|
+
});
|
|
1442
1475
|
// Standalone/test fallback. Production replaces this after init with a stable
|
|
1443
1476
|
// per-session HMAC derived from the host-only dashboard secret, so an
|
|
1444
1477
|
// already-issued 「操作链接」/write link survives a worker restart (a silent
|
|
@@ -1511,7 +1544,7 @@ let closeRequested = false;
|
|
|
1511
1544
|
let capturedSpawnCommand = null;
|
|
1512
1545
|
let deferredTopicOutputTail = '';
|
|
1513
1546
|
const reportedDeferredTopicRoots = new Set();
|
|
1514
|
-
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', 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' };
|
|
1547
|
+
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' };
|
|
1515
1548
|
function cliName() {
|
|
1516
1549
|
return (lastInitConfig?.cliRuntime?.source === 'configured'
|
|
1517
1550
|
? (lastInitConfig.cliRuntime.displayName?.trim() || lastInitConfig.cliRuntime.id)
|
|
@@ -1677,17 +1710,14 @@ function releaseReadyGate(reason, opts) {
|
|
|
1677
1710
|
* the next command, capped so a slow/redrawing command can't stall the queue. */
|
|
1678
1711
|
const STARTUP_CMD_QUIET_MS = 500;
|
|
1679
1712
|
const STARTUP_CMD_CAP_MS = 4_000;
|
|
1680
|
-
/** Inter-keystroke spacing when typing a slash command into CoCo char-by-char.
|
|
1681
|
-
* CoCo (Trae CLI, Ink TUI) treats "several bytes delivered in one PTY read" as a
|
|
1682
|
-
* paste, which skips command mode + the slash picker — so each char must land as
|
|
1683
|
-
* its own keystroke. 40ms is comfortably above CoCo's coalescing window (verified
|
|
1684
|
-
* against Trae CLI 0.120.45) while keeping a short command sub-second. */
|
|
1685
|
-
const COCO_SLASH_TYPE_THROTTLE_MS = 40;
|
|
1686
1713
|
/** Type one literal input LINE into the CLI exactly like a passthrough slash
|
|
1687
|
-
* command
|
|
1688
|
-
*
|
|
1689
|
-
*
|
|
1690
|
-
* so
|
|
1714
|
+
* command. Delivery is adapter-derived: adapters that declare a paste-line
|
|
1715
|
+
* input mode (e.g. OpenCode) paste the line up front, then wait out the
|
|
1716
|
+
* adapter's settle window before pressing Enter; generic backends keep the
|
|
1717
|
+
* classic raw text → a 200ms beat (so the TUI's slash-command picker
|
|
1718
|
+
* registers the match before submit) → a separate Enter, or fall back to a
|
|
1719
|
+
* single write + CR. Shared by the `raw_input` IPC handler and
|
|
1720
|
+
* runStartupCommands so both stay in lockstep. */
|
|
1691
1721
|
async function sendRawCommandLine(be, content) {
|
|
1692
1722
|
// PR #597 extracted the CoCo/pty keystroke choreography into the shared
|
|
1693
1723
|
// writeRawCommandLine helper (unit-tested in raw-command-writer.ts). It
|
|
@@ -1695,11 +1725,7 @@ async function sendRawCommandLine(be, content) {
|
|
|
1695
1725
|
// recovery-fence transaction detects a failed submission by a thrown error, so
|
|
1696
1726
|
// bridge the two: turn an explicit `false` into the same throw the inline
|
|
1697
1727
|
// implementation used, letting runAmbiguousSubmissionTransaction cancel the WAL.
|
|
1698
|
-
const accepted = await writeRawCommandLine(be, content,
|
|
1699
|
-
coco: lastInitConfig?.cliId === 'coco',
|
|
1700
|
-
cocoThrottleMs: COCO_SLASH_TYPE_THROTTLE_MS,
|
|
1701
|
-
submitBeatMs: 200,
|
|
1702
|
-
});
|
|
1728
|
+
const accepted = await writeRawCommandLine(be, content, rawCommandWriteOptionsFor(cliAdapter ?? undefined, lastInitConfig?.cliId));
|
|
1703
1729
|
if (accepted === false) {
|
|
1704
1730
|
throw new Error('backend rejected command text or submit key input');
|
|
1705
1731
|
}
|
|
@@ -2090,6 +2116,7 @@ let currentBotmuxDispatchAttempt;
|
|
|
2090
2116
|
let currentVcMeetingImTurnOrigin;
|
|
2091
2117
|
let durableTurnInFlight = false;
|
|
2092
2118
|
function publishSandboxRelayCapability(opts = {}) {
|
|
2119
|
+
const daemonIpcPort = parseDaemonIpcPort(process.env.BOTMUX_DAEMON_IPC_PORT);
|
|
2093
2120
|
const capability = {
|
|
2094
2121
|
token: randomBytes(32).toString('hex'),
|
|
2095
2122
|
...(currentBotmuxTurnId ? { turnId: currentBotmuxTurnId } : {}),
|
|
@@ -2104,6 +2131,21 @@ function publishSandboxRelayCapability(opts = {}) {
|
|
|
2104
2131
|
body: JSON.stringify({ token: capability.token }),
|
|
2105
2132
|
}]
|
|
2106
2133
|
: []),
|
|
2134
|
+
...(readIsolationOriginChannelId
|
|
2135
|
+
? [{
|
|
2136
|
+
path: managedOriginCapabilityPath(process.env.SESSION_DATA_DIR ?? config.session.dataDir, sessionId, readIsolationOriginChannelId),
|
|
2137
|
+
body: JSON.stringify({
|
|
2138
|
+
sessionId,
|
|
2139
|
+
channelId: readIsolationOriginChannelId,
|
|
2140
|
+
capability: capability.token,
|
|
2141
|
+
...(capability.turnId ? { turnId: capability.turnId } : {}),
|
|
2142
|
+
...(capability.dispatchAttempt !== undefined
|
|
2143
|
+
? { dispatchAttempt: capability.dispatchAttempt }
|
|
2144
|
+
: {}),
|
|
2145
|
+
...(daemonIpcPort !== undefined ? { ipcPort: daemonIpcPort } : {}),
|
|
2146
|
+
}),
|
|
2147
|
+
}]
|
|
2148
|
+
: []),
|
|
2107
2149
|
];
|
|
2108
2150
|
let publishError;
|
|
2109
2151
|
for (const file of files) {
|
|
@@ -3482,7 +3524,8 @@ function failedBridgeFallbackContent(errorCode, summary, partialText) {
|
|
|
3482
3524
|
? 'worker.empty_final_failed_connection'
|
|
3483
3525
|
: 'worker.empty_final_failed';
|
|
3484
3526
|
const failure = t(key, { cliName: cliName(), reason });
|
|
3485
|
-
|
|
3527
|
+
const visiblePartialText = stripTrailingOaiMemoryCitation(partialText ?? '').trim();
|
|
3528
|
+
return visiblePartialText ? `${visiblePartialText}\n\n${failure}` : failure;
|
|
3486
3529
|
}
|
|
3487
3530
|
// ─── Bridge fallback marker (non-adopt) ────────────────────────────────────
|
|
3488
3531
|
//
|
|
@@ -4800,8 +4843,11 @@ function currentHermesBridgeDbPath() {
|
|
|
4800
4843
|
function structuredBridgeIngestPath(path, offset) {
|
|
4801
4844
|
if (structuredBridgeIsCodex())
|
|
4802
4845
|
return drainCodexRollout(path, offset);
|
|
4803
|
-
|
|
4804
|
-
|
|
4846
|
+
// adoptMode gates the drainer's bare-sentinel synthesis: adopt posts
|
|
4847
|
+
// transcript text verbatim, so a synthesised token would leak into Lark.
|
|
4848
|
+
if (structuredBridgeIsTraex()) {
|
|
4849
|
+
return drainTraexRollout(path, offset, { adoptMode: lastInitConfig?.adoptMode === true });
|
|
4850
|
+
}
|
|
4805
4851
|
if (codexBridgeIsCursor())
|
|
4806
4852
|
return drainCursorTranscript(path, offset);
|
|
4807
4853
|
if (structuredBridgeIsPi())
|
|
@@ -5599,6 +5645,9 @@ function codexBridgeIngest(opts = {}) {
|
|
|
5599
5645
|
if (result.events.length > 0)
|
|
5600
5646
|
lastStructuredBridgeActivityAtMs = Date.now();
|
|
5601
5647
|
codexBridgeQueue.ingest(result.events);
|
|
5648
|
+
// After ingest so the latch's delivery re-kick observes the started turn —
|
|
5649
|
+
// the flush's own bridge mark must queue behind it, not ahead of it.
|
|
5650
|
+
noteSpawnArgvTurnStartTranscriptEvidence(result.events);
|
|
5602
5651
|
pruneExpiredStructuredHeadsAndEmit('structured ingest');
|
|
5603
5652
|
// Transcript-driven idle: a normal `assistant_final` or no-output
|
|
5604
5653
|
// `turn_aborted` is Codex declaring end-of-turn, far more reliable than the screen-pattern heuristic
|
|
@@ -6020,13 +6069,17 @@ function emitReadyCodexTurns() {
|
|
|
6020
6069
|
isLocal: turn.isLocal,
|
|
6021
6070
|
finalText: turn.finalText,
|
|
6022
6071
|
terminalStatus: turn.terminalStatus,
|
|
6072
|
+
terminalErrorCode: turn.terminalErrorCode,
|
|
6023
6073
|
};
|
|
6024
|
-
|
|
6025
|
-
|
|
6074
|
+
// The rate-limit skip is narrowed to CLIs with a dedicated structured
|
|
6075
|
+
// rate-limit chain (Codex): TRAE 429 has no such chain, so skipping the
|
|
6076
|
+
// generic failed fallback would post nothing at all.
|
|
6077
|
+
const fallbackKind = structuredFallbackKind(gateInput, nextBoundaryMs, markers, adoptMode, structuredBridgeIsCodex());
|
|
6078
|
+
const content = fallbackKind === 'failed'
|
|
6026
6079
|
? failedBridgeFallbackContent(turn.terminalErrorCode, turn.terminalErrorSummary, turn.finalText)
|
|
6027
|
-
:
|
|
6028
|
-
? turn.finalText
|
|
6029
|
-
:
|
|
6080
|
+
: fallbackKind === 'final'
|
|
6081
|
+
? turn.finalText ?? ''
|
|
6082
|
+
: fallbackKind === 'empty_completed'
|
|
6030
6083
|
? emptyCompletedBridgeFallbackContent()
|
|
6031
6084
|
: '';
|
|
6032
6085
|
if (!content)
|
|
@@ -6295,6 +6348,10 @@ function restoreHerdrWebBindings() {
|
|
|
6295
6348
|
applyHerdrWebBindingResult(ws, binding.restore());
|
|
6296
6349
|
}
|
|
6297
6350
|
}
|
|
6351
|
+
function canHandleReadOnlyRemoteScroll() {
|
|
6352
|
+
return cliAdapter?.readOnlyRemoteScroll === true
|
|
6353
|
+
&& !(lastInitConfig?.adoptMode && lastInitConfig.adoptZellijPaneId);
|
|
6354
|
+
}
|
|
6298
6355
|
function wireHerdrWebTerminalRelays(be) {
|
|
6299
6356
|
be.onSnapshot(relayHerdrWebSnapshot);
|
|
6300
6357
|
be.onWebTerminalCursor(relayHerdrWebCursor);
|
|
@@ -7548,11 +7605,11 @@ function handleVisibleStartupInteraction(data) {
|
|
|
7548
7605
|
}
|
|
7549
7606
|
return true;
|
|
7550
7607
|
}
|
|
7551
|
-
// Mira/Mir
|
|
7608
|
+
// Mira/Mir/dsh send terminal OSC control messages. Codex App deliberately
|
|
7552
7609
|
// does not (PR #597): its signed Unix-socket channel is independent of the
|
|
7553
7610
|
// terminal/backend rendering (including Herdr and Zellij), so it is no longer
|
|
7554
7611
|
// in the terminal-OSC decode set.
|
|
7555
|
-
const APP_RUNNER_OSC_CLI_IDS = new Set(['mira', 'mir']);
|
|
7612
|
+
const APP_RUNNER_OSC_CLI_IDS = new Set(['mira', 'mir', 'dsh']);
|
|
7556
7613
|
const appRunnerControlDecoder = new RunnerControlDecoder();
|
|
7557
7614
|
let kiroSessionIdCaptureArmed = false;
|
|
7558
7615
|
let kiroSessionIdCaptureBuffer = '';
|
|
@@ -7908,12 +7965,12 @@ async function handleTrustedCodexAppMarker(kind, payload, control) {
|
|
|
7908
7965
|
log(`${cliName()} native turn ${nativeTurnId.substring(0, 12)} mapped to botmux turn ${turnId.substring(0, 12)}`);
|
|
7909
7966
|
}
|
|
7910
7967
|
let suppressDelivery = false;
|
|
7911
|
-
// What actually reaches Lark: strip
|
|
7912
|
-
//
|
|
7913
|
-
// validator asserts finalContent==='' and
|
|
7914
|
-
// If nothing remains
|
|
7915
|
-
//
|
|
7916
|
-
const deliverableContent =
|
|
7968
|
+
// What actually reaches Lark: strip internal memory attribution and a
|
|
7969
|
+
// trailing sentinel line. `finalContent` stays RAW above (the
|
|
7970
|
+
// steer_superseded validator asserts finalContent==='' and the fallback gate
|
|
7971
|
+
// must see the unstripped payload). If nothing remains, this was metadata or
|
|
7972
|
+
// a pure-silence final → persist the FIFO advance without delivering.
|
|
7973
|
+
const deliverableContent = bridgePostText(finalContent, false);
|
|
7917
7974
|
if (deliverableContent.trim().length === 0 && finalContent.trim().length > 0) {
|
|
7918
7975
|
suppressDelivery = true;
|
|
7919
7976
|
}
|
|
@@ -8327,6 +8384,20 @@ function onPtyData(data) {
|
|
|
8327
8384
|
maybeReportDeferredTopicMaterialization(data);
|
|
8328
8385
|
maybeCaptureKiroSessionId(data);
|
|
8329
8386
|
captureWorkflowTranscript(data);
|
|
8387
|
+
// Argv first-turn start evidence (Pi): latch the busy marker from the raw
|
|
8388
|
+
// PTY stream. The viewport-based defer cannot see a marker that has not
|
|
8389
|
+
// rendered yet, and a fast turn can paint and clear `Working...` between
|
|
8390
|
+
// screen samples — the stream sees every byte exactly once.
|
|
8391
|
+
if (spawnArgvNeedsWorkingSeed
|
|
8392
|
+
&& !spawnArgvTurnStartEvidenceSeen
|
|
8393
|
+
&& structuredBridgeIsPi()
|
|
8394
|
+
&& cliAdapter?.busyPattern) {
|
|
8395
|
+
spawnArgvTurnStartBusyScanTail = tailChars(spawnArgvTurnStartBusyScanTail + stripAnsiForLog(data), 500);
|
|
8396
|
+
if (cliAdapter.busyPattern.test(spawnArgvTurnStartBusyScanTail)) {
|
|
8397
|
+
spawnArgvTurnStartEvidenceSeen = true;
|
|
8398
|
+
spawnArgvTurnStartBusyScanTail = '';
|
|
8399
|
+
}
|
|
8400
|
+
}
|
|
8330
8401
|
renderer?.write(data);
|
|
8331
8402
|
// In tmux-attach mode, each web client has its own tmux attach PTY —
|
|
8332
8403
|
// no relay needed. In non-tmux mode AND in pipe mode (adopt-bridge),
|
|
@@ -8569,6 +8640,125 @@ function scheduleStructuredStartGraceRecheck(remainingMs) {
|
|
|
8569
8640
|
}, Math.max(1, remainingMs + 1));
|
|
8570
8641
|
structuredStartGraceRecheckTimer.unref?.();
|
|
8571
8642
|
}
|
|
8643
|
+
/** How long the argv first-turn evidence gate may hold ready with no start
|
|
8644
|
+
* evidence at all. Pi's startup gap (extension/model loading before it
|
|
8645
|
+
* consumes the argv prompt) is typically 3–9s; 90s covers slow hosts while
|
|
8646
|
+
* keeping a never-consumed argv prompt from parking the card at 工作中
|
|
8647
|
+
* forever. */
|
|
8648
|
+
const SPAWN_ARGV_TURN_START_EVIDENCE_GRACE_MS = 90_000;
|
|
8649
|
+
/** True while markPromptReady must hold the argv-baked first prompt's ready:
|
|
8650
|
+
* no "turn started" evidence yet and the bounded grace has not expired.
|
|
8651
|
+
* Scoped to Pi — the only quiescence-argv adapter with BOTH evidence channels
|
|
8652
|
+
* (busyPattern + always-on structured transcript); Gemini/OpenCode/MTR keep
|
|
8653
|
+
* their historical first-idle behavior. Never applies to the Grok-class busy
|
|
8654
|
+
* arm, whose first ready is handled by spawnArgvInitialPromptBusy. */
|
|
8655
|
+
function spawnArgvTurnStartGateHolds() {
|
|
8656
|
+
if (!spawnArgvNeedsWorkingSeed || spawnArgvInitialPromptBusy)
|
|
8657
|
+
return false;
|
|
8658
|
+
if (!structuredBridgeIsPi())
|
|
8659
|
+
return false;
|
|
8660
|
+
if (spawnArgvTurnStartEvidenceSeen)
|
|
8661
|
+
return false;
|
|
8662
|
+
return Date.now() < spawnArgvTurnStartEvidenceDeadlineMs;
|
|
8663
|
+
}
|
|
8664
|
+
/** Latch "the argv first turn actually began" from live structured-transcript
|
|
8665
|
+
* events. The latch must happen at INGEST time, not lazily at gate time: a
|
|
8666
|
+
* fast first turn can be started AND drained/emitted before markPromptReady
|
|
8667
|
+
* runs, leaving no started turn to observe in the queue. A user record is the
|
|
8668
|
+
* authoritative start signal; a final implies start too (covers a drain that
|
|
8669
|
+
* sees both lines at once).
|
|
8670
|
+
*
|
|
8671
|
+
* The latch is also a DELIVERY DRIVER, not just a boolean: when evidence
|
|
8672
|
+
* lands later than the 15s first-prompt timeout, every markPromptReady-based
|
|
8673
|
+
* driver has already fired rejected and stopped (probe one-shot, timeout
|
|
8674
|
+
* short-circuit) and the 90s fail-open stands down on evidenceSeen — with a
|
|
8675
|
+
* silent PTY nothing else would ever deliver a startup-queued message. The
|
|
8676
|
+
* user record hitting disk is itself the proof the TUI accepts input, so
|
|
8677
|
+
* re-kick immediately. (The PTY busyPattern latch needs no re-kick: busy
|
|
8678
|
+
* output implies a later quiescence edge that re-drives markPromptReady.) */
|
|
8679
|
+
function noteSpawnArgvTurnStartTranscriptEvidence(events) {
|
|
8680
|
+
// The whole evidence mechanism is Pi-scoped (matching the onPtyData busy
|
|
8681
|
+
// scan and spawnArgvTurnStartGateHolds); this generic ingest path also
|
|
8682
|
+
// serves Grok — another argv-baked + structured-bridge + type-ahead CLI —
|
|
8683
|
+
// so without this guard the flush side effect below would add a new
|
|
8684
|
+
// startup-window write for Grok (whose first ready is owned by the
|
|
8685
|
+
// SessionStart busy arm, not by this gate).
|
|
8686
|
+
if (!structuredBridgeIsPi())
|
|
8687
|
+
return;
|
|
8688
|
+
if (spawnArgvTurnStartEvidenceSeen || !spawnArgvNeedsWorkingSeed)
|
|
8689
|
+
return;
|
|
8690
|
+
if (!events.some(ev => ev.kind === 'user' || ev.kind === 'assistant_final'))
|
|
8691
|
+
return;
|
|
8692
|
+
spawnArgvTurnStartEvidenceSeen = true;
|
|
8693
|
+
spawnArgvTurnStartBusyScanTail = '';
|
|
8694
|
+
if (!isPromptReady && pendingMessages.length > 0) {
|
|
8695
|
+
log('Argv first-turn start evidence landed via transcript — releasing startup-queued input');
|
|
8696
|
+
flushQueuedInputAfterTurnStartEvidence();
|
|
8697
|
+
}
|
|
8698
|
+
}
|
|
8699
|
+
function stopSpawnArgvTurnStartFailOpen() {
|
|
8700
|
+
if (!spawnArgvTurnStartFailOpenTimer)
|
|
8701
|
+
return;
|
|
8702
|
+
clearTimeout(spawnArgvTurnStartFailOpenTimer);
|
|
8703
|
+
spawnArgvTurnStartFailOpenTimer = null;
|
|
8704
|
+
}
|
|
8705
|
+
/** Turn-start evidence in hand ⇒ startup-queued input becomes deliverable.
|
|
8706
|
+
*
|
|
8707
|
+
* Messages that arrive during startup (awaitingFirstPrompt) skip
|
|
8708
|
+
* handleMessage's type-ahead write (shouldWriteNow holds them, "no input box
|
|
8709
|
+
* yet") and historically relied on markPromptReady()'s tail flush for
|
|
8710
|
+
* delivery. When a Pi argv first turn never lands its terminal record
|
|
8711
|
+
* (terminate:true gap), every ready driver dies rejected: the structured
|
|
8712
|
+
* lifecycle block refuses quiescence idles, the queued-message busy probe
|
|
8713
|
+
* and the 15s first-prompt timeout both short-circuit through the same
|
|
8714
|
+
* rejected markPromptReady(), and the 90s fail-open stands down once
|
|
8715
|
+
* evidence is latched. Without an explicit re-kick the message sits in
|
|
8716
|
+
* pendingMessages forever (card pinned at 工作中, the next transcript user
|
|
8717
|
+
* event never appears, HOL-drop never fires).
|
|
8718
|
+
*
|
|
8719
|
+
* Two call sites, one safety argument — input may only be re-kicked once the
|
|
8720
|
+
* TUI provably booted far enough to accept it:
|
|
8721
|
+
* 1. markPromptReady()'s lifecycle-block rejection (a started turn /
|
|
8722
|
+
* confirmed submit exists);
|
|
8723
|
+
* 2. the transcript-evidence latch in codexBridgeIngest (the user record
|
|
8724
|
+
* just hit disk — the ONLY driver left when evidence lands later than
|
|
8725
|
+
* every probe/timeout and the PTY stays silent).
|
|
8726
|
+
* The argv turn-start evidence gate must NOT re-kick — before any evidence
|
|
8727
|
+
* the TUI may still be loading extensions/models and a type-ahead paste
|
|
8728
|
+
* could be dropped or land in a half-drawn screen (flushPending() has no
|
|
8729
|
+
* awaitingFirstPrompt gate of its own; shouldWriteNow()'s hold is the only
|
|
8730
|
+
* startup input protection).
|
|
8731
|
+
*
|
|
8732
|
+
* flushPending() still re-checks its own admission gates (type-ahead
|
|
8733
|
+
* allowance, durable HOL, injection serialization, restart fences,
|
|
8734
|
+
* ready-gate settle), so a non-type-ahead adapter keeps holding until a real
|
|
8735
|
+
* ready edge; this is a re-kick, never a bypass of those gates. */
|
|
8736
|
+
function flushQueuedInputAfterTurnStartEvidence() {
|
|
8737
|
+
if (pendingMessages.length === 0)
|
|
8738
|
+
return;
|
|
8739
|
+
flushPending();
|
|
8740
|
+
}
|
|
8741
|
+
/** Re-drive the held first idle if no start evidence ever appears. Without
|
|
8742
|
+
* this, a swallowed quiescence idle followed by a fully silent PTY (argv
|
|
8743
|
+
* prompt never consumed) would leave no later signal to re-check the gate.
|
|
8744
|
+
* The re-driven idle still passes deferPromptReadyWhileBusy, so a turn whose
|
|
8745
|
+
* evidence was merely missed keeps deferring on a busy viewport. */
|
|
8746
|
+
function scheduleSpawnArgvTurnStartFailOpen() {
|
|
8747
|
+
if (spawnArgvTurnStartFailOpenTimer)
|
|
8748
|
+
return;
|
|
8749
|
+
const backendAtSchedule = backend;
|
|
8750
|
+
const cliGenerationAtSchedule = cliSpawnGeneration;
|
|
8751
|
+
spawnArgvTurnStartFailOpenTimer = setTimeout(() => {
|
|
8752
|
+
spawnArgvTurnStartFailOpenTimer = null;
|
|
8753
|
+
if (backend !== backendAtSchedule || cliSpawnGeneration !== cliGenerationAtSchedule)
|
|
8754
|
+
return;
|
|
8755
|
+
if (isPromptReady || !spawnArgvNeedsWorkingSeed || spawnArgvTurnStartEvidenceSeen)
|
|
8756
|
+
return;
|
|
8757
|
+
log('Argv-baked first prompt showed no start evidence within grace — failing open to quiescence idle');
|
|
8758
|
+
idleDetector?.fireIdle();
|
|
8759
|
+
}, Math.max(1, spawnArgvTurnStartEvidenceDeadlineMs - Date.now()));
|
|
8760
|
+
spawnArgvTurnStartFailOpenTimer.unref?.();
|
|
8761
|
+
}
|
|
8572
8762
|
function markPromptReady() {
|
|
8573
8763
|
if (bareShellLaunchBlocked) {
|
|
8574
8764
|
log('Ignoring non-PTY prompt-ready while bare-shell launch block is active');
|
|
@@ -8644,6 +8834,29 @@ function markPromptReady() {
|
|
|
8644
8834
|
}
|
|
8645
8835
|
if (freshnessAction === 'ignore')
|
|
8646
8836
|
return;
|
|
8837
|
+
// Argv-baked first prompt start gate (Pi): the TUI paints its input box
|
|
8838
|
+
// seconds before the CLI begins consuming the argv prompt, and that startup
|
|
8839
|
+
// window has no busyPattern on screen and no transcript user record —
|
|
8840
|
+
// quiescence then produces a FALSE first idle ("not started yet", not "turn
|
|
8841
|
+
// complete"). Letting it through would set isPromptReady for the whole turn
|
|
8842
|
+
// and seed working→idle mid-turn (premature ✅ DONE + card flip to 等待输入).
|
|
8843
|
+
// Hold ready and re-arm until the turn visibly starts; bounded fail-open via
|
|
8844
|
+
// SPAWN_ARGV_TURN_START_EVIDENCE_GRACE_MS restores the historical quiescence
|
|
8845
|
+
// behavior if evidence never appears.
|
|
8846
|
+
// Deliberately NO flush re-kick here (unlike the lifecycle block below):
|
|
8847
|
+
// with zero turn-start evidence the TUI may still be booting, and a
|
|
8848
|
+
// type-ahead paste could be dropped or land in a half-drawn screen —
|
|
8849
|
+
// shouldWriteNow()'s awaitingFirstPrompt hold is the only startup input
|
|
8850
|
+
// protection and flushPending() does not re-check it. Queued messages are
|
|
8851
|
+
// delivered the moment evidence appears (the transcript latch re-kicks, a
|
|
8852
|
+
// busy-marker latch implies a later quiescence edge) or by the 90s
|
|
8853
|
+
// fail-open.
|
|
8854
|
+
if (spawnArgvTurnStartGateHolds()) {
|
|
8855
|
+
log('Argv-baked first prompt has not visibly started (no busy marker or transcript user event since spawn) — re-arming idle detector');
|
|
8856
|
+
idleDetector?.reset();
|
|
8857
|
+
scheduleSpawnArgvTurnStartFailOpen();
|
|
8858
|
+
return;
|
|
8859
|
+
}
|
|
8647
8860
|
// Screen prompt/quiescence is only a UI heuristic. Structured transcript
|
|
8648
8861
|
// bridges have the stronger lifecycle signal: a transcript-started turn
|
|
8649
8862
|
// without assistant_final is still running even if the TUI redraw exposes a
|
|
@@ -8660,6 +8873,7 @@ function markPromptReady() {
|
|
|
8660
8873
|
idleDetector?.reset();
|
|
8661
8874
|
if (remainingMs !== undefined)
|
|
8662
8875
|
scheduleStructuredStartGraceRecheck(remainingMs);
|
|
8876
|
+
flushQueuedInputAfterTurnStartEvidence();
|
|
8663
8877
|
return;
|
|
8664
8878
|
}
|
|
8665
8879
|
structuredRejectedReadyEvidenceGeneration = undefined;
|
|
@@ -8706,7 +8920,17 @@ function markPromptReady() {
|
|
|
8706
8920
|
// make the CLI busy, so the idle state is transient and shouldn't appear
|
|
8707
8921
|
// in the card. This avoids a false "就绪" flash on daemon restart
|
|
8708
8922
|
// (where the initial prompt is queued before the CLI becomes idle).
|
|
8709
|
-
|
|
8923
|
+
//
|
|
8924
|
+
// ALSO skip when the Grok-class busy arm is pending (spawnArgvInitialPromptBusy):
|
|
8925
|
+
// for these adapters the FIRST ready is a pre-execution SessionStart edge, not a
|
|
8926
|
+
// turn boundary — the argv-baked first prompt is still running. isPromptReady was
|
|
8927
|
+
// just set true above, so this generic snapshot would project 'idle' and reach the
|
|
8928
|
+
// daemon BEFORE the busy arm below re-publishes 'working'. Combined with the
|
|
8929
|
+
// first-turn working already sent by startScreenUpdates, the daemon would then see
|
|
8930
|
+
// working→idle and fire finishTurnReactions() — a premature ✅ DONE mid-turn (and a
|
|
8931
|
+
// 「工作中→等待输入→工作中」 flicker on the open card). The busy arm below owns the
|
|
8932
|
+
// correct 'working' publish for this path, so this idle must not escape first.
|
|
8933
|
+
if (renderer && !spawnArgvInitialPromptBusy && pendingMessages.length === 0 && pendingAdoptMessages.length === 0 && pendingRawInputs.length === 0 && pendingSessionRename === null && !isFlushing) {
|
|
8710
8934
|
const { content } = renderer.snapshot();
|
|
8711
8935
|
send({
|
|
8712
8936
|
type: 'screen_update',
|
|
@@ -9079,7 +9303,10 @@ async function detectBareShellLaunch() {
|
|
|
9079
9303
|
// rcfile may still finish and exec the CLI after this bounded check.
|
|
9080
9304
|
const launchShell = (lastInitConfig?.launchShell || process.env.SHELL || '').trim();
|
|
9081
9305
|
const expectedShell = launchShell ? basename(launchShell) : '';
|
|
9082
|
-
|
|
9306
|
+
if (!comm)
|
|
9307
|
+
return false;
|
|
9308
|
+
const shellComm = comm;
|
|
9309
|
+
const trampolined = bareShellLaunchKind(shellComm, expectedShell) === 'trampoline';
|
|
9083
9310
|
bareShellLaunchBlocked = true;
|
|
9084
9311
|
// Injection-first flushes can enter while isPromptReady is still true. Make
|
|
9085
9312
|
// this a real busy/blocked transition so a later PTY-ready event re-enters
|
|
@@ -9094,17 +9321,18 @@ async function detectBareShellLaunch() {
|
|
|
9094
9321
|
const cli = cliName();
|
|
9095
9322
|
let message;
|
|
9096
9323
|
if (trampolined) {
|
|
9324
|
+
const guidance = bareShellLaunchGuidance(shellComm, expectedShell);
|
|
9097
9325
|
message =
|
|
9098
|
-
`⚠️ 会话没能启动:pane 里现在是裸 \`${
|
|
9099
|
-
`最可能原因:botmux 用 \`${expectedShell}\` 启动 CLI,但 pane 落到了 \`${
|
|
9326
|
+
`⚠️ 会话没能启动:pane 里现在是裸 \`${shellComm}\`,${cli} 没真正跑起来——所以我没把你的消息打进去(否则会被当 shell 命令执行,报 \`parse error\`)。\n\n` +
|
|
9327
|
+
`最可能原因:botmux 用 \`${expectedShell}\` 启动 CLI,但 pane 落到了 \`${shellComm}\`。通常是 rc 文件(如 \`${guidance.rcFileHint}\`)里有 \`exec ${shellComm}\` 这类跳转——\`${expectedShell} -i\` 会 source rc,于是 shell 被顶替,CLI 的启动命令没机会跑。\n\n` +
|
|
9100
9328
|
`两种修法(任选其一,改完重启 daemon 再发一条消息):\n` +
|
|
9101
|
-
`①
|
|
9102
|
-
`② 给这个 bot 配 \`launchShell: ${
|
|
9329
|
+
`① 给那行加守卫,只在手动开终端时切:\`${guidance.manualTerminalGuard}\`(注意 PATH/nvm 等导出放在它之前)\n` +
|
|
9330
|
+
`② 给这个 bot 配 \`launchShell: ${shellComm}\`(dashboard 机器人配置,或 \`/config launchShell ${shellComm}\`),直接用 \`${shellComm}\` 启动绕开 \`${expectedShell}\` 的 rc——但要确保 PATH/nvm 在 \`${shellComm}\` 的 rc 里。`;
|
|
9103
9331
|
}
|
|
9104
9332
|
else {
|
|
9105
9333
|
message =
|
|
9106
|
-
`⚠️ ${cli} 启动时间较长:pane 里暂时仍是 \`${
|
|
9107
|
-
`仅凭进程仍是 \`${
|
|
9334
|
+
`⚠️ ${cli} 启动时间较长:pane 里暂时仍是 \`${shellComm}\`。我没有把消息写进 shell,消息还在队列里;检测到真实输入框后会自动继续投递,无需重发。\n\n` +
|
|
9335
|
+
`仅凭进程仍是 \`${shellComm}\` 无法判断具体原因,可能只是 rc 文件或机器负载让启动变慢。若长时间没有恢复,请打开 Web 终端查看当前提示,处理后等待自动继续,或使用 \`/restart\` 重启会话。`;
|
|
9108
9336
|
}
|
|
9109
9337
|
const pendingTurn = pendingMessages[0];
|
|
9110
9338
|
send({
|
|
@@ -9985,8 +10213,48 @@ function startScreenUpdates() {
|
|
|
9985
10213
|
// watermark — there we must capture every tick (see shouldCaptureScreen).
|
|
9986
10214
|
let lastSnapshotPtyActivity = -1;
|
|
9987
10215
|
screenUpdateTimer = setInterval(() => {
|
|
9988
|
-
if (awaitingFirstPrompt)
|
|
10216
|
+
if (awaitingFirstPrompt) {
|
|
10217
|
+
// First-turn 「工作中」 publisher. The async sampler below is fully gated
|
|
10218
|
+
// until the first turn ends (markPromptReady flips awaitingFirstPrompt) or
|
|
10219
|
+
// the 15s soft timeout, so an argv-baked first prompt (Pi/Grok/…, delivered
|
|
10220
|
+
// via the launch command — it never goes through flushPending) would emit
|
|
10221
|
+
// NO screen_update for the whole turn: the daemon card sits at 'starting'
|
|
10222
|
+
// and jumps straight to 「等待输入」 on the first idle, skipping 「工作中」.
|
|
10223
|
+
//
|
|
10224
|
+
// The status projection already reads 'working' during turn one (isPromptReady
|
|
10225
|
+
// is still false — same rule that makes every post-submit turn working), so we
|
|
10226
|
+
// simply PUBLISH that projection once instead of scraping the screen for a busy
|
|
10227
|
+
// marker. This is the general "sent to the CLI ⇒ working" model: it fires for
|
|
10228
|
+
// every argv-baked first prompt (spawnArgvNeedsWorkingSeed), not just CLIs that
|
|
10229
|
+
// happen to render a detectable busyPattern.
|
|
10230
|
+
//
|
|
10231
|
+
// Gate on spawnArgvNeedsWorkingSeed so it stays a no-op for a NON-argv first
|
|
10232
|
+
// turn (Claude/Codex/type-ahead): there the prompt is still queued and the CLI
|
|
10233
|
+
// is genuinely booting — not working — until flushPending() submits it after
|
|
10234
|
+
// the ready edge. Empty-prompt adopt sessions also don't arm the flag, so an
|
|
10235
|
+
// attach never falsely claims working.
|
|
10236
|
+
//
|
|
10237
|
+
// Pure publisher: it only send()s a status and updates the local lastSentStatus
|
|
10238
|
+
// dedup — it never touches isPromptReady, the idle detector, or the argv seed
|
|
10239
|
+
// flags (spawnArgvInitialPromptBusy / -NeedsWorkingSeed), so the first-prompt
|
|
10240
|
+
// evidence machinery and the end-of-turn seed are unchanged.
|
|
10241
|
+
if (spawnArgvNeedsWorkingSeed && lastSentStatus !== 'working' && renderer) {
|
|
10242
|
+
const projected = projectedRuntimeScreenStatus();
|
|
10243
|
+
if (projected === 'working') {
|
|
10244
|
+
const { content } = renderer.snapshot();
|
|
10245
|
+
lastSentStatus = 'working';
|
|
10246
|
+
send({
|
|
10247
|
+
type: 'screen_update',
|
|
10248
|
+
content,
|
|
10249
|
+
status: 'working',
|
|
10250
|
+
turnId: currentBotmuxTurnId,
|
|
10251
|
+
dispatchAttempt: currentBotmuxDispatchAttempt,
|
|
10252
|
+
});
|
|
10253
|
+
log('Argv-baked first prompt in flight — publishing projected working before first prompt');
|
|
10254
|
+
}
|
|
10255
|
+
}
|
|
9989
10256
|
return;
|
|
10257
|
+
}
|
|
9990
10258
|
void (async () => {
|
|
9991
10259
|
const { snapshot, status } = await snapshotWithLatestRuntimeStatus(async () => {
|
|
9992
10260
|
let content = lastContent;
|
|
@@ -10708,6 +10976,8 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
10708
10976
|
// Feishu. Thread the flag so the reconstructed config keeps the boundary.
|
|
10709
10977
|
if (cfg.apiOnly)
|
|
10710
10978
|
sessionEnv.BOTMUX_API_ONLY = '1';
|
|
10979
|
+
if (cfg.feedback)
|
|
10980
|
+
sessionEnv.BOTMUX_FEEDBACK_POLICY = JSON.stringify(cfg.feedback);
|
|
10711
10981
|
// Session scope for `botmux send` inside the sandbox. Thread sessions
|
|
10712
10982
|
// anchor on a real om_ message (reply_in_thread); chat-scope sessions use
|
|
10713
10983
|
// the chat id as anchor (sessionAnchorId), which is NOT a message id —
|
|
@@ -10744,6 +11014,12 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
10744
11014
|
// Per-bot env (bots.json `env`) takes precedence over session context;
|
|
10745
11015
|
// explicit riff config.env takes precedence over both.
|
|
10746
11016
|
const mergedEnv = { ...sessionEnv, ...sanitizePerBotEnv(cfg.env), ...cfg.backendConfig.env };
|
|
11017
|
+
// The effective policy is a host-resolved snapshot, not a user-overridable
|
|
11018
|
+
// backend env knob. Re-freeze it after config.env/per-bot env merge.
|
|
11019
|
+
if (cfg.feedback)
|
|
11020
|
+
mergedEnv.BOTMUX_FEEDBACK_POLICY = JSON.stringify(cfg.feedback);
|
|
11021
|
+
else
|
|
11022
|
+
delete mergedEnv.BOTMUX_FEEDBACK_POLICY;
|
|
10747
11023
|
// Re-freeze the no-transport capability keys AFTER the merge: a stale or
|
|
10748
11024
|
// attacker-shaped backendConfig.env / per-bot env merges LAST and would
|
|
10749
11025
|
// otherwise override the frozen values, restoring send capability for a
|
|
@@ -10896,6 +11172,19 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
10896
11172
|
resolvedBin: canonicalPolicyPath(cliAdapter.resolvedBin),
|
|
10897
11173
|
})
|
|
10898
11174
|
: undefined;
|
|
11175
|
+
const managedOriginChannelRequired = willReadIsolate
|
|
11176
|
+
|| credentialOnlySeatbelt
|
|
11177
|
+
|| credentialOnlyBwrap;
|
|
11178
|
+
const managedOriginChannelPolicyDigest = (willReadIsolate || willWriteSandbox)
|
|
11179
|
+
? darwinIsolationPolicyDigest
|
|
11180
|
+
: managedOriginChannelRequired
|
|
11181
|
+
? createHash('sha256').update(JSON.stringify({
|
|
11182
|
+
domain: 'botmux.credential-origin-channel.v1',
|
|
11183
|
+
platform: process.platform,
|
|
11184
|
+
configuredBotmuxHome: canonicalPolicyPath(configuredBotmuxHome),
|
|
11185
|
+
defaultBotmuxHome: canonicalPolicyPath(defaultBotmuxHome),
|
|
11186
|
+
})).digest('hex')
|
|
11187
|
+
: undefined;
|
|
10899
11188
|
let mcpRuntimeManifest = readSessionMcpRuntimeManifest(cfg.sessionId, config.session.dataDir);
|
|
10900
11189
|
const hasMcpRuntimeEntries = !!cliAdapter.mcpGateway && !!mcpRuntimeManifest?.entries.length;
|
|
10901
11190
|
const reuseRecordedHerdrTarget = !sandboxRequested && !hasMcpRuntimeEntries;
|
|
@@ -11052,8 +11341,7 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
11052
11341
|
if (paneLive) {
|
|
11053
11342
|
const markerPath = join(isolationRuntimeDataDir, 'read-isolation', `${cfg.sessionId}.boot`);
|
|
11054
11343
|
const marker = readManagedOriginAuthorityFile(markerPath);
|
|
11055
|
-
const
|
|
11056
|
-
&& (willReadIsolate || willWriteSandbox);
|
|
11344
|
+
const originChannelPolicyExpected = !!managedOriginChannelPolicyDigest;
|
|
11057
11345
|
// A stamped pane must match even when the new policy is OFF. Otherwise a
|
|
11058
11346
|
// disable followed by restart could reattach the still-confined process
|
|
11059
11347
|
// without rebuilding its authority/profile. An unsafe planted marker
|
|
@@ -11062,16 +11350,16 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
11062
11350
|
? isolatedPaneReattachSafe(marker, {
|
|
11063
11351
|
requiredCapabilities: appliedIsolationCapabilities,
|
|
11064
11352
|
exactCapabilities: true,
|
|
11065
|
-
...(
|
|
11353
|
+
...(originChannelPolicyExpected ? {
|
|
11066
11354
|
readIsolation: willReadIsolate,
|
|
11067
11355
|
writeSandbox: willWriteSandbox,
|
|
11068
11356
|
requireOriginChannel: true,
|
|
11069
|
-
policyDigest:
|
|
11357
|
+
policyDigest: managedOriginChannelPolicyDigest,
|
|
11070
11358
|
} : {}),
|
|
11071
11359
|
})
|
|
11072
11360
|
: marker === null && !hostEntryExistsNoFollow(markerPath);
|
|
11073
11361
|
if (policyMatches) {
|
|
11074
|
-
if (
|
|
11362
|
+
if (originChannelPolicyExpected) {
|
|
11075
11363
|
persistentPaneOriginChannelId = isolatedPaneOriginChannel(marker);
|
|
11076
11364
|
}
|
|
11077
11365
|
// Pane was spawned under the current isolation policy → still confined
|
|
@@ -11132,7 +11420,7 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
11132
11420
|
}
|
|
11133
11421
|
}
|
|
11134
11422
|
}
|
|
11135
|
-
readIsolationOriginChannelId =
|
|
11423
|
+
readIsolationOriginChannelId = managedOriginChannelRequired
|
|
11136
11424
|
? (persistentPaneOriginChannelId ?? randomBytes(32).toString('hex'))
|
|
11137
11425
|
: null;
|
|
11138
11426
|
if (readIsolationOriginChannelId) {
|
|
@@ -11463,6 +11751,14 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
11463
11751
|
injectsReadyHook: cliAdapter.injectsReadyHook === true,
|
|
11464
11752
|
reliableTurnTerminal: cliAdapter.reliableTurnTerminal === true,
|
|
11465
11753
|
});
|
|
11754
|
+
// Fresh evidence window for the argv first-turn start gate (Pi): the new
|
|
11755
|
+
// generation must not inherit a previous spawn's latch or fail-open timer.
|
|
11756
|
+
stopSpawnArgvTurnStartFailOpen();
|
|
11757
|
+
spawnArgvTurnStartEvidenceSeen = false;
|
|
11758
|
+
spawnArgvTurnStartBusyScanTail = '';
|
|
11759
|
+
spawnArgvTurnStartEvidenceDeadlineMs = spawnArgvNeedsWorkingSeed
|
|
11760
|
+
? Date.now() + SPAWN_ARGV_TURN_START_EVIDENCE_GRACE_MS
|
|
11761
|
+
: 0;
|
|
11466
11762
|
if (deferInitialPrompt && preparedDeferredInput) {
|
|
11467
11763
|
piInitialPromptAdditionalArgs = [...(preparedDeferredInput.additionalArgs ?? [])];
|
|
11468
11764
|
piInitialPromptEnv = { ...(preparedDeferredInput.env ?? {}) };
|
|
@@ -11479,7 +11775,7 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
11479
11775
|
try {
|
|
11480
11776
|
const credPath = sendCredFilePath(process.env.SESSION_DATA_DIR, cfg.larkAppId);
|
|
11481
11777
|
mkdirSync(dirname(credPath), { recursive: true });
|
|
11482
|
-
writeFileSync(credPath, JSON.stringify({ larkAppId: cfg.larkAppId, larkAppSecret: cfg.larkAppSecret, brand: cfg.brand, apiOnly: cfg.apiOnly }), { mode: 0o600 });
|
|
11778
|
+
writeFileSync(credPath, JSON.stringify({ larkAppId: cfg.larkAppId, larkAppSecret: cfg.larkAppSecret, brand: cfg.brand, apiOnly: cfg.apiOnly, feedback: cfg.feedback }), { mode: 0o600 });
|
|
11483
11779
|
}
|
|
11484
11780
|
catch (e) {
|
|
11485
11781
|
log(`[sandbox] WARN could not write send-cred file: ${e.message}`);
|
|
@@ -11516,6 +11812,8 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
11516
11812
|
larkAppId: cfg.larkAppId,
|
|
11517
11813
|
locale: cfg.locale,
|
|
11518
11814
|
model: ttadkGateway ? undefined : cfg.model,
|
|
11815
|
+
// dsh runner only; other adapters ignore the field.
|
|
11816
|
+
turnTimeoutMs: cfg.turnTimeoutMs,
|
|
11519
11817
|
reasoningEffort: cfg.reasoningEffort,
|
|
11520
11818
|
disableCliBypass: cfg.disableCliBypass === true,
|
|
11521
11819
|
// Codex-family hook-trust bypass: global toggle (default ON) so a headless
|
|
@@ -11772,8 +12070,19 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
11772
12070
|
throw new Error('[read-isolation] origin channel is unavailable');
|
|
11773
12071
|
}
|
|
11774
12072
|
childEnv.BOTMUX_READ_ISOLATED = '1';
|
|
12073
|
+
}
|
|
12074
|
+
if (readIsolationOriginChannelId) {
|
|
11775
12075
|
childEnv.BOTMUX_ORIGIN_CHANNEL_ID = readIsolationOriginChannelId;
|
|
11776
12076
|
}
|
|
12077
|
+
// Credential-only children have no writable relay outbox. Publish the same
|
|
12078
|
+
// rotating managed-origin claim through a host-owned per-channel directory
|
|
12079
|
+
// before the Seatbelt/bwrap wrapper is compiled, then expose only that
|
|
12080
|
+
// directory read-only below.
|
|
12081
|
+
if (readIsolationOriginChannelId && !sandboxRequested) {
|
|
12082
|
+
readIsolationOriginCapabilityFile = managedOriginCapabilityPath(isolationRuntimeDataDir, cfg.sessionId, readIsolationOriginChannelId);
|
|
12083
|
+
ensureManagedOriginCapabilityLeafSafe(readIsolationOriginCapabilityFile);
|
|
12084
|
+
publishSandboxRelayCapability({ failClosed: true });
|
|
12085
|
+
}
|
|
11777
12086
|
// Per-bot env (bots.json `env`): extra vars for THIS bot's CLI only — e.g.
|
|
11778
12087
|
// ANTHROPIC_BASE_URL/ANTHROPIC_AUTH_TOKEN to run a bot on GLM/a 3rd-party
|
|
11779
12088
|
// provider, an HTTPS_PROXY, or a CLI feature flag. Passed as injectEnv (NOT
|
|
@@ -11948,6 +12257,11 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
11948
12257
|
writeFileSync(tsFile, '');
|
|
11949
12258
|
}
|
|
11950
12259
|
catch { /* */ }
|
|
12260
|
+
// UserPromptSubmit sidecar 目录(#794):daemon 逐 turn 写入,沙盒内 hook 只读。
|
|
12261
|
+
try {
|
|
12262
|
+
mkdirSync(join(dataDir, 'prompt-ctx', cfg.sessionId), { recursive: true, mode: 0o700 });
|
|
12263
|
+
}
|
|
12264
|
+
catch { /* */ }
|
|
11951
12265
|
try {
|
|
11952
12266
|
mkdirSync(join(dataDir, 'attachments', cfg.larkAppId), { recursive: true });
|
|
11953
12267
|
}
|
|
@@ -12002,7 +12316,7 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
12002
12316
|
if (readIsolationOriginCapabilityFile) {
|
|
12003
12317
|
ensureManagedOriginCapabilityLeafSafe(readIsolationOriginCapabilityFile);
|
|
12004
12318
|
publishSandboxRelayCapability({ failClosed: true });
|
|
12005
|
-
mandatoryReadOnlyPaths.push(
|
|
12319
|
+
mandatoryReadOnlyPaths.push(managedOriginCapabilityDirectory(dataDir, cfg.sessionId, readIsolationOriginChannelId));
|
|
12006
12320
|
mandatoryReadOnlyPaths.push(managedOriginAttestationDirectory(dataDir, cfg.sessionId, readIsolationOriginChannelId));
|
|
12007
12321
|
mandatoryReadOnlyPaths.push(managedOriginRootLocatorPath(userInfo().homedir, cfg.sessionId));
|
|
12008
12322
|
}
|
|
@@ -12288,12 +12602,12 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
12288
12602
|
try {
|
|
12289
12603
|
const markerDir = join(isolationRuntimeDataDir, 'read-isolation');
|
|
12290
12604
|
mkdirSync(markerDir, { recursive: true });
|
|
12291
|
-
replaceManagedOriginCapabilityFile(join(markerDir, `${cfg.sessionId}.boot`), isolationPaneMarkerContent(cfg.daemonBootId ?? '', appliedIsolationCapabilities,
|
|
12605
|
+
replaceManagedOriginCapabilityFile(join(markerDir, `${cfg.sessionId}.boot`), isolationPaneMarkerContent(cfg.daemonBootId ?? '', appliedIsolationCapabilities, managedOriginChannelPolicyDigest
|
|
12292
12606
|
? {
|
|
12293
12607
|
originChannelId: persistentPaneOriginChannelId,
|
|
12294
12608
|
readIsolation: willReadIsolate,
|
|
12295
12609
|
writeSandbox: willWriteSandbox,
|
|
12296
|
-
policyDigest:
|
|
12610
|
+
policyDigest: managedOriginChannelPolicyDigest,
|
|
12297
12611
|
}
|
|
12298
12612
|
: undefined));
|
|
12299
12613
|
}
|
|
@@ -12337,11 +12651,13 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
12337
12651
|
// the input box). cjadk's own botmux integration (`cjadk feishu`, see its
|
|
12338
12652
|
// botmux-wrapper-writer) sets CJADK_INTERACTIVE=0 to disable all of that.
|
|
12339
12653
|
// We mirror it here so a `cjadk <agent>` wrapperCli is driven the way
|
|
12340
|
-
// cjadk intends — no selector, clean soft-newline input.
|
|
12341
|
-
//
|
|
12342
|
-
|
|
12343
|
-
|
|
12344
|
-
|
|
12654
|
+
// cjadk intends — no selector, clean soft-newline input. The env set
|
|
12655
|
+
// comes from wrapperLaunchEnv — the shared single source of truth also
|
|
12656
|
+
// used by one-shot children (session-group AI titling).
|
|
12657
|
+
const wrapperEnv = wrapperLaunchEnv(cfg.wrapperCli);
|
|
12658
|
+
if (wrapperEnv) {
|
|
12659
|
+
Object.assign(childEnv, wrapperEnv);
|
|
12660
|
+
log(`wrapper launcher env applied: ${Object.keys(wrapperEnv).join(', ')} (mirrors the wrapper's own non-interactive integration)`);
|
|
12345
12661
|
}
|
|
12346
12662
|
}
|
|
12347
12663
|
}
|
|
@@ -12379,8 +12695,9 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
12379
12695
|
});
|
|
12380
12696
|
const profileDir = join(isolationRuntimeDataDir, 'read-isolation');
|
|
12381
12697
|
mkdirSync(profileDir, { recursive: true });
|
|
12698
|
+
const originDirectory = managedOriginCapabilityDirectory(isolationRuntimeDataDir, cfg.sessionId, readIsolationOriginChannelId);
|
|
12382
12699
|
const profilePath = join(profileDir, `${cfg.sessionId}.sb`);
|
|
12383
|
-
replaceManagedOriginCapabilityFile(profilePath, buildSeatbeltProfile(rules.denyPaths.map(canonical), [], [], [], rules.denyRegexes, undefined, {
|
|
12700
|
+
replaceManagedOriginCapabilityFile(profilePath, buildSeatbeltProfile([...rules.denyPaths.map(canonical), canonical(profileDir)], [canonical(originDirectory)], [], [canonical(profileDir)], rules.denyRegexes, undefined, {
|
|
12384
12701
|
denyWritePaths: [...new Set([
|
|
12385
12702
|
...rules.denyWritePaths.map(canonical),
|
|
12386
12703
|
canonical(profileDir),
|
|
@@ -12482,7 +12799,10 @@ async function spawnCli(cfg, opts = {}) {
|
|
|
12482
12799
|
const credentialSandbox = prepareCredentialOnlySandbox({
|
|
12483
12800
|
hideDirectories: [...hideDirectories],
|
|
12484
12801
|
hideFiles: [...hideFiles],
|
|
12485
|
-
|
|
12802
|
+
privateReadonlyDirectories: [{
|
|
12803
|
+
parent: realpathSync(panePolicyDir),
|
|
12804
|
+
directory: realpathSync(managedOriginCapabilityDirectory(isolationRuntimeDataDir, cfg.sessionId, readIsolationOriginChannelId)),
|
|
12805
|
+
}],
|
|
12486
12806
|
workingDir: spawnCwd,
|
|
12487
12807
|
cliBin: credentialCliBin,
|
|
12488
12808
|
cliArgs: spawnArgs,
|
|
@@ -13319,6 +13639,8 @@ function killCli(opts = {}) {
|
|
|
13319
13639
|
stopReattachIdleProbe();
|
|
13320
13640
|
stopBusyPatternIdleProbe();
|
|
13321
13641
|
stopStructuredStartGraceRecheck();
|
|
13642
|
+
stopSpawnArgvTurnStartFailOpen();
|
|
13643
|
+
spawnArgvTurnStartBusyScanTail = '';
|
|
13322
13644
|
structuredRejectedReadyEvidenceGeneration = undefined;
|
|
13323
13645
|
ptyOutputGeneration.reset();
|
|
13324
13646
|
// Cancel any pending ready-gate fallback / settle timers; spawnCli re-arms on respawn.
|
|
@@ -13590,6 +13912,7 @@ function startWebServer(host, preferredPort) {
|
|
|
13590
13912
|
// normal buffer until resize causes a redraw. Preserve that mode so
|
|
13591
13913
|
// scroll gestures continue to target the CLI's own transcript.
|
|
13592
13914
|
const forceRemoteScroll = effectiveBackendType === 'herdr' && cliAdapter?.altScreen === true;
|
|
13915
|
+
const allowReadOnlyRemoteScroll = canHandleReadOnlyRemoteScroll();
|
|
13593
13916
|
// The wheel burst cap throttles backends where a forwarded wheel is
|
|
13594
13917
|
// EXPENSIVE or has no local terminal to drive:
|
|
13595
13918
|
// • Herdr — each forwarded wheel → pane send-text + snapshot re-render.
|
|
@@ -13607,7 +13930,7 @@ function startWebServer(host, preferredPort) {
|
|
|
13607
13930
|
|| effectiveBackendType === 'tmux'
|
|
13608
13931
|
|| effectiveBackendType === 'zellij';
|
|
13609
13932
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
13610
|
-
res.end(getTerminalHtml(hasWrite, platformReadonly, loginUrl, forceRemoteScroll, localTerminalBackend));
|
|
13933
|
+
res.end(getTerminalHtml(hasWrite, platformReadonly, loginUrl, forceRemoteScroll, localTerminalBackend, allowReadOnlyRemoteScroll));
|
|
13611
13934
|
});
|
|
13612
13935
|
wss = new WebSocketServer({
|
|
13613
13936
|
server: httpServer,
|
|
@@ -13639,6 +13962,7 @@ function startWebServer(host, preferredPort) {
|
|
|
13639
13962
|
return;
|
|
13640
13963
|
}
|
|
13641
13964
|
const { hasWrite } = resolveTerminalAccessForReq(req, url);
|
|
13965
|
+
const allowReadOnlyRemoteScroll = canHandleReadOnlyRemoteScroll();
|
|
13642
13966
|
if (hasWrite)
|
|
13643
13967
|
authedClients.add(ws);
|
|
13644
13968
|
log(`WS client connected (total: ${wsClients.size}, write: ${hasWrite})`);
|
|
@@ -13864,8 +14188,17 @@ function startWebServer(host, preferredPort) {
|
|
|
13864
14188
|
scrollback,
|
|
13865
14189
|
onError: log,
|
|
13866
14190
|
});
|
|
13867
|
-
|
|
13868
|
-
|
|
14191
|
+
// A capture-pane seed carries screen cells but no DECSET state: a fresh
|
|
14192
|
+
// client xterm never learns the CLI enabled mouse tracking (grok build:
|
|
14193
|
+
// 1003+1006), so clicks/double-clicks are silently swallowed instead of
|
|
14194
|
+
// reported. Re-assert the pane's live input modes after the seed —
|
|
14195
|
+
// write clients only: a read-only viewer forwards no input anyway, and
|
|
14196
|
+
// mouse-mode xterm would break its plain select-to-copy. Raw-scrollback
|
|
14197
|
+
// seeds already contain the original DECSET bytes; backends that can't
|
|
14198
|
+
// be queried (herdr/zmx/pty) simply don't implement the hook.
|
|
14199
|
+
const modeSeed = hasWrite ? (backend?.capturePaneInputModes?.() ?? '') : '';
|
|
14200
|
+
if (seed.length > 0 || modeSeed.length > 0) {
|
|
14201
|
+
ws.send(seed + modeSeed + herdrWebCursorSequence());
|
|
13869
14202
|
}
|
|
13870
14203
|
ws.on('message', (raw) => {
|
|
13871
14204
|
try {
|
|
@@ -13890,6 +14223,18 @@ function startWebServer(host, preferredPort) {
|
|
|
13890
14223
|
}
|
|
13891
14224
|
backend?.write(msg.data);
|
|
13892
14225
|
}
|
|
14226
|
+
else if (msg.type === 'scroll' && typeof msg.data === 'string') {
|
|
14227
|
+
if (!allowReadOnlyRemoteScroll || authedClients.has(ws))
|
|
14228
|
+
return;
|
|
14229
|
+
const parsed = parseReadOnlyRemoteScrollPayload(msg.data);
|
|
14230
|
+
if (!parsed)
|
|
14231
|
+
return;
|
|
14232
|
+
if (!readOnlyRemoteScrollLimiter.tryConsume(parsed.eventCount))
|
|
14233
|
+
return;
|
|
14234
|
+
if (usesHerdrSnapshotWebHistory())
|
|
14235
|
+
herdrWebScrollDirection = parsed.direction;
|
|
14236
|
+
backend?.write(msg.data);
|
|
14237
|
+
}
|
|
13893
14238
|
}
|
|
13894
14239
|
catch { /* ignore non-JSON or bad messages */ }
|
|
13895
14240
|
});
|
|
@@ -13912,7 +14257,7 @@ function startWebServer(host, preferredPort) {
|
|
|
13912
14257
|
.then(resolve, reject);
|
|
13913
14258
|
});
|
|
13914
14259
|
}
|
|
13915
|
-
function getTerminalHtml(hasWrite, platformReadonly = false, loginUrl = '', forceRemoteScroll = false, localTerminalBackend = false) {
|
|
14260
|
+
function getTerminalHtml(hasWrite, platformReadonly = false, loginUrl = '', forceRemoteScroll = false, localTerminalBackend = false, allowReadOnlyRemoteScroll = false) {
|
|
13916
14261
|
const label = sessionId.substring(0, 8);
|
|
13917
14262
|
return `<!DOCTYPE html>
|
|
13918
14263
|
<html>
|
|
@@ -14050,6 +14395,7 @@ var hasToken=${hasWrite};
|
|
|
14050
14395
|
var platformReadonly=${platformReadonly};
|
|
14051
14396
|
var remoteScroll=${forceRemoteScroll};
|
|
14052
14397
|
var localTerminalBackend=${localTerminalBackend};
|
|
14398
|
+
var readOnlyRemoteScroll=${allowReadOnlyRemoteScroll};
|
|
14053
14399
|
if(!hasToken){
|
|
14054
14400
|
if(platformReadonly){var _lb=document.getElementById('login-banner');_lb.classList.add('show');}
|
|
14055
14401
|
else{var _rb=document.getElementById('readonly-banner');_rb.classList.add('show');_rb.addEventListener('click',function(){_rb.classList.remove('show')});}
|
|
@@ -14253,13 +14599,34 @@ if(hasToken && !/[?&]imefix=0\\b/.test(location.search)){(function(){
|
|
|
14253
14599
|
|
|
14254
14600
|
// ── WebSocket ──
|
|
14255
14601
|
var ws_=null,el=document.getElementById('status');
|
|
14602
|
+
function _sendInput(d){if(ws_&&ws_.readyState===1)ws_.send(JSON.stringify({type:'input',data:d}))}
|
|
14603
|
+
// Pure pointer-motion reports (SGR button code 35 + shift/alt/ctrl modifier
|
|
14604
|
+
// bits). Emitted by xterm once the seed re-asserts DECSET 1003 (grok build) —
|
|
14605
|
+
// one per cell crossed. Each forwarded report costs a synchronous tmux
|
|
14606
|
+
// send-keys exec in the worker, so an unthrottled sweep across a 120-col pane
|
|
14607
|
+
// would stall the relay for every viewer. Clicks/drags/wheel stay immediate.
|
|
14608
|
+
var _MOTION_RE=/^(?:\\x1b\\[<(?:35|39|43|47|51|55|59|63);\\d+;\\d+[Mm])+$/;
|
|
14609
|
+
var _motionPend=null,_motionT=0;
|
|
14256
14610
|
term.onData(function(d){
|
|
14257
14611
|
if(!hasToken){
|
|
14258
14612
|
// Mouse escape sequences are input too: a TUI can bind clicks or wheel
|
|
14259
14613
|
// events to actions. View links never forward terminal bytes.
|
|
14260
14614
|
_showReadonlyToast();return;
|
|
14261
14615
|
}
|
|
14262
|
-
if(
|
|
14616
|
+
if(_MOTION_RE.test(d)){
|
|
14617
|
+
// Trailing throttle: keep only the LATEST motion, flush every 90ms. Hover
|
|
14618
|
+
// feedback survives; the send-keys exec rate is bounded (~11/s).
|
|
14619
|
+
_motionPend=d;
|
|
14620
|
+
if(!_motionT)_motionT=setTimeout(function(){
|
|
14621
|
+
_motionT=0;if(_motionPend){_sendInput(_motionPend);_motionPend=null}
|
|
14622
|
+
},90);
|
|
14623
|
+
return;
|
|
14624
|
+
}
|
|
14625
|
+
// A press/release/drag/key supersedes any pending motion (it carries its own
|
|
14626
|
+
// coordinates); dropping it preserves event ordering for the TUI.
|
|
14627
|
+
if(_motionT){clearTimeout(_motionT);_motionT=0}
|
|
14628
|
+
_motionPend=null;
|
|
14629
|
+
_sendInput(d);
|
|
14263
14630
|
});
|
|
14264
14631
|
var fixedSize=false,_lastC=0,_lastR=0,_rzT=0;
|
|
14265
14632
|
function sendResize(){
|
|
@@ -14346,8 +14713,9 @@ window.addEventListener('resize',onViewportResize);
|
|
|
14346
14713
|
// Alt-screen + mouse-mode CLIs (e.g. Claude Code) keep NO scrollback in xterm OR
|
|
14347
14714
|
// tmux — their whole transcript is redrawn by the app inside the fixed alt-screen
|
|
14348
14715
|
// grid, so term.scrollLines() reveals nothing. In the alternate buffer we forward
|
|
14349
|
-
// scrolling as SGR mouse-wheel events so the CLI scrolls its own transcript
|
|
14350
|
-
//
|
|
14716
|
+
// scrolling as SGR mouse-wheel events so the CLI scrolls its own transcript.
|
|
14717
|
+
// Read-only viewers get this remote path only for adapters that explicitly opt
|
|
14718
|
+
// in; the server then accepts only validated wheel events, never general input.
|
|
14351
14719
|
// Normal-buffer CLIs keep xterm's native scrollback scroll. Capture-phase +
|
|
14352
14720
|
// stopPropagation pre-empts xterm's own handler. Skipped for pure tmux/zellij
|
|
14353
14721
|
// ATTACH (gate), where the attach client owns scrolling via copy-mode.
|
|
@@ -14407,7 +14775,7 @@ function _cellAt(clientX,clientY){
|
|
|
14407
14775
|
return col+';'+row;
|
|
14408
14776
|
}
|
|
14409
14777
|
function _fwdScroll(px,coord){
|
|
14410
|
-
if(!hasToken||!ws_||ws_.readyState!==1||!px)return;
|
|
14778
|
+
if((!hasToken&&!readOnlyRemoteScroll)||!ws_||ws_.readyState!==1||!px)return;
|
|
14411
14779
|
coord=coord||(((term.cols>>1)+1)+';'+((term.rows>>1)+1)); // never (1,1)
|
|
14412
14780
|
var dir=px<0?-1:1;
|
|
14413
14781
|
if(_scrollBurstDir&&dir!==_scrollBurstDir){_scrollAccum=0;_scrollBurstTicks=0;}
|
|
@@ -14421,7 +14789,7 @@ function _fwdScroll(px,coord){
|
|
|
14421
14789
|
_scrollAccum+=up?_SCROLL_STEP:-_SCROLL_STEP;n++;_scrollBurstTicks++;
|
|
14422
14790
|
}
|
|
14423
14791
|
if(_scrollBurstTicks>=_SCROLL_BURST_MAX)_scrollAccum=0;
|
|
14424
|
-
if(data)ws_.send(JSON.stringify({type:'input',data:data}));
|
|
14792
|
+
if(data)ws_.send(JSON.stringify({type:hasToken?'input':'scroll',data:data}));
|
|
14425
14793
|
}
|
|
14426
14794
|
if(!${isTmuxMode && !isPipeMode}){
|
|
14427
14795
|
document.getElementById('terminal').addEventListener('wheel',function(e){
|
|
@@ -14434,7 +14802,9 @@ if(!${isTmuxMode && !isPipeMode}){
|
|
|
14434
14802
|
return;
|
|
14435
14803
|
}
|
|
14436
14804
|
if(!hasToken){
|
|
14437
|
-
e.preventDefault();e.stopPropagation();
|
|
14805
|
+
e.preventDefault();e.stopPropagation();
|
|
14806
|
+
if(readOnlyRemoteScroll)_fwdScroll(px,_cellAt(e.clientX,e.clientY));
|
|
14807
|
+
else term.scrollLines(e.deltaY>0?3:-3);return;
|
|
14438
14808
|
}
|
|
14439
14809
|
e.preventDefault();e.stopPropagation();
|
|
14440
14810
|
_fwdScroll(px,_cellAt(e.clientX,e.clientY)); // report the cell under the pointer
|
|
@@ -14755,8 +15125,7 @@ if(!${isTmuxMode && !isPipeMode}){
|
|
|
14755
15125
|
if(e.touches.length===1)_tLastY=e.touches[0].clientY;
|
|
14756
15126
|
},{capture:true,passive:true});
|
|
14757
15127
|
_tTerm.addEventListener('touchmove',function(e){
|
|
14758
|
-
|
|
14759
|
-
if(!hasToken||_tLastY===null||e.touches.length!==1)return;
|
|
15128
|
+
if((!hasToken&&!readOnlyRemoteScroll)||_tLastY===null||e.touches.length!==1)return;
|
|
14760
15129
|
e.preventDefault();e.stopPropagation();
|
|
14761
15130
|
var y=e.touches[0].clientY;
|
|
14762
15131
|
var px=_tLastY-y;
|
|
@@ -15934,7 +16303,17 @@ process.on('message', async (raw) => {
|
|
|
15934
16303
|
break;
|
|
15935
16304
|
}
|
|
15936
16305
|
closeRequested = true;
|
|
15937
|
-
|
|
16306
|
+
// This ACK is what resolves the daemon's close fence (worker-pool
|
|
16307
|
+
// resolveCloseFence) so `/close` can return. It MUST be flushed: a plain
|
|
16308
|
+
// send() only queues on process.send's async IPC buffer, and the fully
|
|
16309
|
+
// synchronous teardown + process.exit(0) below never yield the event loop
|
|
16310
|
+
// to drain it. Worse, process.exit(0) can wedge in node-pty's native
|
|
16311
|
+
// reader-thread join whenever a web-terminal client PTY was attached — so
|
|
16312
|
+
// the queued ACK would be lost entirely and the fence would only resolve
|
|
16313
|
+
// on the daemon's 7s SIGKILL backstop (the ~7s dashboard/card close stall).
|
|
16314
|
+
// closeRequested already fences bridge-marker reads, so ACKing before
|
|
16315
|
+
// teardown stays safe; sendAndFlush yields so the ACK truly departs.
|
|
16316
|
+
await sendAndFlush({ type: 'session_close_ready', sessionId });
|
|
15938
16317
|
stopScreenshotLoop();
|
|
15939
16318
|
stopBridgeWatcher();
|
|
15940
16319
|
stopCodexBridge();
|
|
@@ -16115,7 +16494,11 @@ process.on('message', async (raw) => {
|
|
|
16115
16494
|
lastAbortedCloseRequestId = null;
|
|
16116
16495
|
backend?.commitDestroySession?.();
|
|
16117
16496
|
closeRequested = true;
|
|
16118
|
-
|
|
16497
|
+
// Flush the fence-resolving ACK before the synchronous teardown +
|
|
16498
|
+
// process.exit(0); see the local-close case for the node-pty exit-wedge
|
|
16499
|
+
// rationale (a queued send() would be dropped and strand `/close` behind
|
|
16500
|
+
// the daemon's 7s SIGKILL backstop).
|
|
16501
|
+
await sendAndFlush({ type: 'session_close_ready', sessionId });
|
|
16119
16502
|
stopScreenshotLoop();
|
|
16120
16503
|
stopBridgeWatcher();
|
|
16121
16504
|
stopCodexBridge();
|