botmux 3.5.0 → 3.6.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 +56 -396
- package/README.md +55 -396
- package/dist/.runtime-build-id +1 -0
- package/dist/adapters/backend/sandbox.d.ts +99 -279
- package/dist/adapters/backend/sandbox.d.ts.map +1 -1
- package/dist/adapters/backend/sandbox.js +424 -826
- package/dist/adapters/backend/sandbox.js.map +1 -1
- package/dist/adapters/cli/claude-code.d.ts +6 -0
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +197 -11
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/coco.js +4 -4
- package/dist/adapters/cli/coco.js.map +1 -1
- package/dist/adapters/cli/codex-app.d.ts.map +1 -1
- package/dist/adapters/cli/codex-app.js +9 -6
- package/dist/adapters/cli/codex-app.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +9 -5
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/fs-policy.d.ts +244 -0
- package/dist/adapters/cli/fs-policy.d.ts.map +1 -0
- package/dist/adapters/cli/fs-policy.js +537 -0
- package/dist/adapters/cli/fs-policy.js.map +1 -0
- package/dist/adapters/cli/grok.d.ts +2 -1
- package/dist/adapters/cli/grok.d.ts.map +1 -1
- package/dist/adapters/cli/grok.js +2 -1
- package/dist/adapters/cli/grok.js.map +1 -1
- package/dist/adapters/cli/hermes.d.ts.map +1 -1
- package/dist/adapters/cli/hermes.js +8 -4
- package/dist/adapters/cli/hermes.js.map +1 -1
- package/dist/adapters/cli/mtr.d.ts.map +1 -1
- package/dist/adapters/cli/mtr.js +3 -2
- package/dist/adapters/cli/mtr.js.map +1 -1
- package/dist/adapters/cli/opencode.d.ts.map +1 -1
- package/dist/adapters/cli/opencode.js +5 -4
- package/dist/adapters/cli/opencode.js.map +1 -1
- package/dist/adapters/cli/read-isolation.d.ts +14 -200
- package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
- package/dist/adapters/cli/read-isolation.js +18 -477
- package/dist/adapters/cli/read-isolation.js.map +1 -1
- package/dist/adapters/cli/relay.js +2 -2
- package/dist/adapters/cli/relay.js.map +1 -1
- package/dist/adapters/cli/runner-input.d.ts +2 -2
- package/dist/adapters/cli/runner-input.d.ts.map +1 -1
- package/dist/adapters/cli/runner-input.js +9 -6
- package/dist/adapters/cli/runner-input.js.map +1 -1
- package/dist/adapters/cli/seed.d.ts.map +1 -1
- package/dist/adapters/cli/seed.js +2 -1
- package/dist/adapters/cli/seed.js.map +1 -1
- package/dist/adapters/cli/traex.d.ts.map +1 -1
- package/dist/adapters/cli/traex.js +3 -2
- package/dist/adapters/cli/traex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +25 -18
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +54 -23
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +91 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +297 -104
- package/dist/cli.js.map +1 -1
- package/dist/codex-app-runner.js +85 -192
- package/dist/codex-app-runner.js.map +1 -1
- package/dist/config.d.ts +0 -16
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -30
- package/dist/config.js.map +1 -1
- package/dist/core/ask-broker.d.ts +12 -0
- package/dist/core/ask-broker.d.ts.map +1 -1
- package/dist/core/ask-broker.js +41 -0
- package/dist/core/ask-broker.js.map +1 -1
- package/dist/core/bot-turn-mutation-gate.d.ts +24 -0
- package/dist/core/bot-turn-mutation-gate.d.ts.map +1 -0
- package/dist/core/bot-turn-mutation-gate.js +369 -0
- package/dist/core/bot-turn-mutation-gate.js.map +1 -0
- package/dist/core/command-handler.d.ts +5 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +53 -73
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +356 -84
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dashboard-rows.d.ts +8 -0
- package/dist/core/dashboard-rows.d.ts.map +1 -1
- package/dist/core/dashboard-rows.js.map +1 -1
- package/dist/core/host-overload-alert.d.ts +157 -0
- package/dist/core/host-overload-alert.d.ts.map +1 -0
- package/dist/core/host-overload-alert.js +251 -0
- package/dist/core/host-overload-alert.js.map +1 -0
- package/dist/core/passthrough-commands.d.ts +8 -0
- package/dist/core/passthrough-commands.d.ts.map +1 -1
- package/dist/core/passthrough-commands.js +20 -1
- package/dist/core/passthrough-commands.js.map +1 -1
- package/dist/core/plugins/mcp/gateway-installer.d.ts.map +1 -1
- package/dist/core/plugins/mcp/gateway-installer.js +14 -2
- package/dist/core/plugins/mcp/gateway-installer.js.map +1 -1
- package/dist/core/restart-coordinator.d.ts +28 -0
- package/dist/core/restart-coordinator.d.ts.map +1 -0
- package/dist/core/restart-coordinator.js +82 -0
- package/dist/core/restart-coordinator.js.map +1 -0
- package/dist/core/restart-followup-policy.d.ts +112 -0
- package/dist/core/restart-followup-policy.d.ts.map +1 -0
- package/dist/core/restart-followup-policy.js +49 -0
- package/dist/core/restart-followup-policy.js.map +1 -0
- package/dist/core/role-library.d.ts +1 -1
- package/dist/core/role-library.js +1 -1
- package/dist/core/session-cwd.d.ts.map +1 -1
- package/dist/core/session-cwd.js +8 -0
- package/dist/core/session-cwd.js.map +1 -1
- package/dist/core/session-discovery.d.ts +14 -0
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +25 -0
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-liveness.d.ts +15 -0
- package/dist/core/session-liveness.d.ts.map +1 -0
- package/dist/core/session-liveness.js +76 -0
- package/dist/core/session-liveness.js.map +1 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +90 -60
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-row-enrichment.d.ts +15 -0
- package/dist/core/session-row-enrichment.d.ts.map +1 -0
- package/dist/core/session-row-enrichment.js +93 -0
- package/dist/core/session-row-enrichment.js.map +1 -0
- package/dist/core/slash-inject.js +1 -1
- package/dist/core/slash-inject.js.map +1 -1
- package/dist/core/trigger-final-suppression.d.ts +24 -0
- package/dist/core/trigger-final-suppression.d.ts.map +1 -0
- package/dist/core/trigger-final-suppression.js +97 -0
- package/dist/core/trigger-final-suppression.js.map +1 -0
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +64 -4
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +6 -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 +44 -0
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +333 -38
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts +5 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +526 -35
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/aggregator.d.ts +1 -0
- package/dist/dashboard/aggregator.d.ts.map +1 -1
- package/dist/dashboard/aggregator.js +34 -6
- package/dist/dashboard/aggregator.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +8 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +10 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/compat.d.ts +102 -2
- package/dist/dashboard/compat.d.ts.map +1 -1
- package/dist/dashboard/compat.js +78 -14
- package/dist/dashboard/compat.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts.map +1 -1
- package/dist/dashboard/connector-api.js +1 -0
- package/dist/dashboard/connector-api.js.map +1 -1
- package/dist/dashboard/desktop-asks.d.ts +21 -0
- package/dist/dashboard/desktop-asks.d.ts.map +1 -0
- package/dist/dashboard/desktop-asks.js +49 -0
- package/dist/dashboard/desktop-asks.js.map +1 -0
- package/dist/dashboard/public-redact.d.ts +7 -0
- package/dist/dashboard/public-redact.d.ts.map +1 -1
- package/dist/dashboard/public-redact.js +35 -3
- package/dist/dashboard/public-redact.js.map +1 -1
- package/dist/dashboard/session-presentation.d.ts +16 -0
- package/dist/dashboard/session-presentation.d.ts.map +1 -0
- package/dist/dashboard/session-presentation.js +62 -0
- package/dist/dashboard/session-presentation.js.map +1 -0
- package/dist/dashboard/settings-write-applier.d.ts +2 -1
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +19 -1
- package/dist/dashboard/settings-write-applier.js.map +1 -1
- package/dist/dashboard/trigger-api.d.ts.map +1 -1
- package/dist/dashboard/trigger-api.js +19 -1
- package/dist/dashboard/trigger-api.js.map +1 -1
- package/dist/dashboard/vc-consumer-profiles-api.d.ts +7 -2
- package/dist/dashboard/vc-consumer-profiles-api.d.ts.map +1 -1
- package/dist/dashboard/vc-consumer-profiles-api.js +13 -0
- 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 +18 -5
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +16 -0
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +288 -7
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +10 -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/client-shell.d.ts +29 -0
- package/dist/dashboard/web/client-shell.d.ts.map +1 -0
- package/dist/dashboard/web/client-shell.js +89 -0
- package/dist/dashboard/web/client-shell.js.map +1 -0
- package/dist/dashboard/web/connectors-page.d.ts.map +1 -1
- package/dist/dashboard/web/connectors-page.js +5 -1
- package/dist/dashboard/web/connectors-page.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +130 -22
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/preferences.d.ts +7 -0
- package/dist/dashboard/web/preferences.d.ts.map +1 -1
- package/dist/dashboard/web/preferences.js +32 -0
- package/dist/dashboard/web/preferences.js.map +1 -1
- package/dist/dashboard/web/sessions-kanban.d.ts +1 -1
- package/dist/dashboard/web/sessions-kanban.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-kanban.js +1 -1
- package/dist/dashboard/web/sessions-kanban.js.map +1 -1
- package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-page.js +78 -67
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts +4 -0
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +14 -1
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/settings-page.d.ts +6 -0
- package/dist/dashboard/web/settings-page.d.ts.map +1 -1
- package/dist/dashboard/web/settings-page.js +55 -30
- package/dist/dashboard/web/settings-page.js.map +1 -1
- package/dist/dashboard/web/store.d.ts +1 -2
- package/dist/dashboard/web/store.d.ts.map +1 -1
- package/dist/dashboard/web/store.js +69 -20
- package/dist/dashboard/web/store.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 +128 -42
- package/dist/dashboard/web/vc-consumer-profiles-section.js.map +1 -1
- package/dist/dashboard/webhook-routes.d.ts.map +1 -1
- package/dist/dashboard/webhook-routes.js +9 -2
- package/dist/dashboard/webhook-routes.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-VQMPUJ4Y.js +10 -0
- package/dist/dashboard-web/chunks/chunk-AGCPF6MT.js +1 -0
- package/dist/dashboard-web/chunks/{chunk-QCPPKCX3.js → chunk-JMRGZQDB.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-EJDYUINZ.js → chunk-NP4JUBGS.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-WXPMNEYV.js → chunk-P327LXOT.js} +1 -1
- package/dist/dashboard-web/chunks/chunk-SNKATZWK.js +49 -0
- package/dist/dashboard-web/chunks/connectors-page-DOY4J3ZL.js +1 -0
- package/dist/dashboard-web/chunks/{groups-page-3P4TLUMF.js → groups-page-74TS2DIH.js} +1 -1
- package/dist/dashboard-web/chunks/{insights-page-TDCZV56A.js → insights-page-AYCO65JC.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-QWCWL3SX.js → monitor-room-YKYLTSAN.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-IOWFSNVC.js → monitoring-page-QBIUNRBR.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-5AUPEHZP.js → office-page-4PH2A26P.js} +1 -1
- package/dist/dashboard-web/chunks/overview-page-H73D5UT4.js +1 -0
- package/dist/dashboard-web/chunks/{roles-page-WJVB52ET.js → roles-page-U4BTOQ7F.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-RVRWHP4V.js → schedules-page-RHRHX7R7.js} +1 -1
- package/dist/dashboard-web/chunks/sessions-page-VSC6AM5G.js +2 -0
- package/dist/dashboard-web/chunks/settings-page-LF3RKM5P.js +2 -0
- package/dist/dashboard-web/chunks/{skills-page-7YT5TIX5.js → skills-page-SYOIGGXH.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-NJZKTU7A.js → team-federation-page-RMBLASTW.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-NQ73UA2Q.js → v3-page-XJXKKJRN.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-DSM3MDMN.js → whiteboards-page-K3YVSTHJ.js} +1 -1
- package/dist/dashboard-web/style.css +571 -3
- package/dist/dashboard.js +238 -9
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +11 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +27 -1
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +6 -19
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +6 -19
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +1 -14
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +1 -35
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +10 -0
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +161 -44
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +47 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +127 -18
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +13 -0
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +48 -3
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/identity-cache.d.ts +25 -1
- package/dist/im/lark/identity-cache.d.ts.map +1 -1
- package/dist/im/lark/identity-cache.js +47 -1
- package/dist/im/lark/identity-cache.js.map +1 -1
- package/dist/im/lark/overload-nonce.d.ts +19 -0
- package/dist/im/lark/overload-nonce.d.ts.map +1 -0
- package/dist/im/lark/overload-nonce.js +72 -0
- package/dist/im/lark/overload-nonce.js.map +1 -0
- package/dist/im/lark/v3-progress-card.d.ts.map +1 -1
- package/dist/im/lark/v3-progress-card.js +5 -0
- package/dist/im/lark/v3-progress-card.js.map +1 -1
- package/dist/services/async-trigger-state.d.ts +82 -0
- package/dist/services/async-trigger-state.d.ts.map +1 -0
- package/dist/services/async-trigger-state.js +95 -0
- package/dist/services/async-trigger-state.js.map +1 -0
- package/dist/services/async-trigger-store.d.ts +28 -0
- package/dist/services/async-trigger-store.d.ts.map +1 -0
- package/dist/services/async-trigger-store.js +120 -0
- package/dist/services/async-trigger-store.js.map +1 -0
- package/dist/services/bot-config-store.d.ts.map +1 -1
- package/dist/services/bot-config-store.js +34 -2
- package/dist/services/bot-config-store.js.map +1 -1
- package/dist/services/bridge-fallback-gate.d.ts +32 -0
- package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
- package/dist/services/bridge-fallback-gate.js +39 -0
- package/dist/services/bridge-fallback-gate.js.map +1 -1
- package/dist/services/bridge-turn-queue.d.ts.map +1 -1
- package/dist/services/bridge-turn-queue.js +15 -1
- package/dist/services/bridge-turn-queue.js.map +1 -1
- package/dist/services/card-prefs-store.d.ts +4 -0
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +8 -0
- package/dist/services/card-prefs-store.js.map +1 -1
- package/dist/services/claude-transcript.d.ts +23 -0
- package/dist/services/claude-transcript.d.ts.map +1 -1
- package/dist/services/claude-transcript.js +33 -0
- package/dist/services/claude-transcript.js.map +1 -1
- package/dist/services/codex-app-runner-protocol.d.ts +61 -0
- package/dist/services/codex-app-runner-protocol.d.ts.map +1 -0
- package/dist/services/codex-app-runner-protocol.js +170 -0
- package/dist/services/codex-app-runner-protocol.js.map +1 -0
- package/dist/services/codex-app-turn-controller.d.ts +72 -0
- package/dist/services/codex-app-turn-controller.d.ts.map +1 -0
- package/dist/services/codex-app-turn-controller.js +524 -0
- package/dist/services/codex-app-turn-controller.js.map +1 -0
- package/dist/services/codex-runner-freshness.d.ts +39 -0
- package/dist/services/codex-runner-freshness.d.ts.map +1 -0
- package/dist/services/codex-runner-freshness.js +78 -0
- package/dist/services/codex-runner-freshness.js.map +1 -0
- package/dist/services/connector-store.d.ts +6 -0
- package/dist/services/connector-store.d.ts.map +1 -1
- package/dist/services/connector-store.js.map +1 -1
- package/dist/services/grant-store.d.ts.map +1 -1
- package/dist/services/grant-store.js +16 -0
- package/dist/services/grant-store.js.map +1 -1
- package/dist/services/hermes-transcript.d.ts +1 -0
- package/dist/services/hermes-transcript.d.ts.map +1 -1
- package/dist/services/hermes-transcript.js +49 -0
- package/dist/services/hermes-transcript.js.map +1 -1
- package/dist/services/sandbox-migration.d.ts +14 -0
- package/dist/services/sandbox-migration.d.ts.map +1 -0
- package/dist/services/sandbox-migration.js +85 -0
- package/dist/services/sandbox-migration.js.map +1 -0
- package/dist/services/sandbox-store.d.ts +28 -0
- package/dist/services/sandbox-store.d.ts.map +1 -1
- package/dist/services/sandbox-store.js +82 -0
- package/dist/services/sandbox-store.js.map +1 -1
- package/dist/services/schedule-split-migration.d.ts +2 -0
- package/dist/services/schedule-split-migration.d.ts.map +1 -0
- package/dist/services/schedule-split-migration.js +159 -0
- package/dist/services/schedule-split-migration.js.map +1 -0
- package/dist/services/schedule-store.d.ts +28 -4
- package/dist/services/schedule-store.d.ts.map +1 -1
- package/dist/services/schedule-store.js +118 -33
- package/dist/services/schedule-store.js.map +1 -1
- package/dist/services/trigger-types.d.ts +18 -1
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js +3 -0
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/services/vc-meeting-consumer-profile-store.d.ts.map +1 -1
- package/dist/services/vc-meeting-consumer-profile-store.js +1 -0
- package/dist/services/vc-meeting-consumer-profile-store.js.map +1 -1
- package/dist/services/vc-meeting-consumer-profile-templates.d.ts +32 -0
- package/dist/services/vc-meeting-consumer-profile-templates.d.ts.map +1 -0
- package/dist/services/vc-meeting-consumer-profile-templates.js +106 -0
- package/dist/services/vc-meeting-consumer-profile-templates.js.map +1 -0
- package/dist/services/vc-meeting-delivery-feed.d.ts +1 -1
- package/dist/services/vc-meeting-delivery-feed.d.ts.map +1 -1
- package/dist/services/vc-meeting-delivery-feed.js +2 -1
- package/dist/services/vc-meeting-delivery-feed.js.map +1 -1
- package/dist/services/vc-meeting-delivery-hub-store.d.ts +3 -1
- package/dist/services/vc-meeting-delivery-hub-store.d.ts.map +1 -1
- package/dist/services/vc-meeting-delivery-hub-store.js +11 -0
- package/dist/services/vc-meeting-delivery-hub-store.js.map +1 -1
- package/dist/services/vc-meeting-delivery-protocol.d.ts +9 -2
- package/dist/services/vc-meeting-delivery-protocol.d.ts.map +1 -1
- package/dist/services/vc-meeting-delivery-protocol.js +10 -1
- package/dist/services/vc-meeting-delivery-protocol.js.map +1 -1
- package/dist/services/vc-meeting-delivery-receiver.d.ts.map +1 -1
- package/dist/services/vc-meeting-delivery-receiver.js +10 -1
- package/dist/services/vc-meeting-delivery-receiver.js.map +1 -1
- package/dist/services/vc-meeting-delivery-store.d.ts +12 -2
- package/dist/services/vc-meeting-delivery-store.d.ts.map +1 -1
- package/dist/services/vc-meeting-delivery-store.js +14 -2
- package/dist/services/vc-meeting-delivery-store.js.map +1 -1
- package/dist/services/vc-meeting-im-reply.d.ts +2 -1
- package/dist/services/vc-meeting-im-reply.d.ts.map +1 -1
- package/dist/services/vc-meeting-im-reply.js +19 -3
- package/dist/services/vc-meeting-im-reply.js.map +1 -1
- package/dist/services/vc-meeting-listener-output-protocol.d.ts +27 -0
- package/dist/services/vc-meeting-listener-output-protocol.d.ts.map +1 -0
- package/dist/services/vc-meeting-listener-output-protocol.js +58 -0
- package/dist/services/vc-meeting-listener-output-protocol.js.map +1 -0
- package/dist/services/vc-meeting-listener-topic-store.d.ts +31 -0
- package/dist/services/vc-meeting-listener-topic-store.d.ts.map +1 -0
- package/dist/services/vc-meeting-listener-topic-store.js +120 -0
- package/dist/services/vc-meeting-listener-topic-store.js.map +1 -0
- package/dist/services/vc-meeting-runtime-store.d.ts +2 -1
- package/dist/services/vc-meeting-runtime-store.d.ts.map +1 -1
- package/dist/services/vc-meeting-runtime-store.js +8 -0
- package/dist/services/vc-meeting-runtime-store.js.map +1 -1
- package/dist/services/vc-meeting-send-policy.d.ts +8 -1
- package/dist/services/vc-meeting-send-policy.d.ts.map +1 -1
- package/dist/services/vc-meeting-send-policy.js +8 -0
- package/dist/services/vc-meeting-send-policy.js.map +1 -1
- package/dist/types.d.ts +53 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/allowed-users-apply.d.ts +76 -0
- package/dist/utils/allowed-users-apply.d.ts.map +1 -0
- package/dist/utils/allowed-users-apply.js +123 -0
- package/dist/utils/allowed-users-apply.js.map +1 -0
- package/dist/utils/allowed-users-cache.d.ts +29 -0
- package/dist/utils/allowed-users-cache.d.ts.map +1 -0
- package/dist/utils/allowed-users-cache.js +82 -0
- package/dist/utils/allowed-users-cache.js.map +1 -0
- package/dist/utils/child-env.d.ts +4 -3
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +16 -1
- package/dist/utils/child-env.js.map +1 -1
- package/dist/utils/cli-usage-limit.d.ts +27 -1
- package/dist/utils/cli-usage-limit.d.ts.map +1 -1
- package/dist/utils/cli-usage-limit.js +92 -20
- package/dist/utils/cli-usage-limit.js.map +1 -1
- package/dist/utils/input-gate.d.ts +2 -0
- package/dist/utils/input-gate.d.ts.map +1 -1
- package/dist/utils/input-gate.js +2 -0
- package/dist/utils/input-gate.js.map +1 -1
- package/dist/utils/runtime-build-id.d.ts +21 -0
- package/dist/utils/runtime-build-id.d.ts.map +1 -0
- package/dist/utils/runtime-build-id.js +83 -0
- package/dist/utils/runtime-build-id.js.map +1 -0
- package/dist/utils/terminal-renderer.d.ts +3 -3
- package/dist/utils/terminal-renderer.js +3 -3
- package/dist/vc-agent/cards.d.ts.map +1 -1
- package/dist/vc-agent/cards.js +21 -1
- package/dist/vc-agent/cards.js.map +1 -1
- package/dist/worker.js +1010 -669
- package/dist/worker.js.map +1 -1
- package/dist/workflows/events/payloads.d.ts +56 -0
- package/dist/workflows/events/payloads.d.ts.map +1 -1
- package/dist/workflows/events/payloads.js +9 -0
- package/dist/workflows/events/payloads.js.map +1 -1
- package/dist/workflows/events/schema.d.ts +86 -0
- package/dist/workflows/events/schema.d.ts.map +1 -1
- package/dist/workflows/hostExecutors/botmux-schedule.d.ts.map +1 -1
- package/dist/workflows/hostExecutors/botmux-schedule.js +13 -1
- package/dist/workflows/hostExecutors/botmux-schedule.js.map +1 -1
- package/dist/workflows/migration/v2-run-archive-schema.d.ts +20 -20
- package/dist/workflows/migration/v2-to-v3.d.ts.map +1 -1
- package/dist/workflows/migration/v2-to-v3.js +5 -1
- package/dist/workflows/migration/v2-to-v3.js.map +1 -1
- package/dist/workflows/shared/sandbox-policy.d.ts +10 -1
- package/dist/workflows/shared/sandbox-policy.d.ts.map +1 -1
- package/dist/workflows/shared/sandbox-policy.js +12 -0
- package/dist/workflows/shared/sandbox-policy.js.map +1 -1
- package/dist/workflows/v3/architect.d.ts.map +1 -1
- package/dist/workflows/v3/architect.js +1 -0
- package/dist/workflows/v3/architect.js.map +1 -1
- package/dist/workflows/v3/bot-resolve.d.ts.map +1 -1
- package/dist/workflows/v3/bot-resolve.js +36 -0
- package/dist/workflows/v3/bot-resolve.js.map +1 -1
- package/dist/workflows/v3/cli-run.d.ts +9 -0
- package/dist/workflows/v3/cli-run.d.ts.map +1 -1
- package/dist/workflows/v3/cli-run.js +17 -2
- package/dist/workflows/v3/cli-run.js.map +1 -1
- package/dist/workflows/v3/contract.d.ts +8 -0
- package/dist/workflows/v3/contract.d.ts.map +1 -1
- package/dist/workflows/v3/contract.js.map +1 -1
- package/dist/workflows/v3/goal-cli.d.ts +58 -0
- package/dist/workflows/v3/goal-cli.d.ts.map +1 -0
- package/dist/workflows/v3/goal-cli.js +430 -0
- package/dist/workflows/v3/goal-cli.js.map +1 -0
- package/dist/workflows/v3/library-materialize.d.ts.map +1 -1
- package/dist/workflows/v3/library-materialize.js +5 -1
- package/dist/workflows/v3/library-materialize.js.map +1 -1
- package/dist/workflows/v3/library-schema.d.ts +21 -0
- package/dist/workflows/v3/library-schema.d.ts.map +1 -1
- package/dist/workflows/v3/library-schema.js +95 -1
- package/dist/workflows/v3/library-schema.js.map +1 -1
- package/dist/workflows/v3/progress-projection.d.ts +3 -0
- package/dist/workflows/v3/progress-projection.d.ts.map +1 -1
- package/dist/workflows/v3/progress-projection.js +23 -1
- package/dist/workflows/v3/progress-projection.js.map +1 -1
- package/dist/workflows/v3/run-envelope.d.ts +46 -1
- package/dist/workflows/v3/run-envelope.d.ts.map +1 -1
- package/dist/workflows/v3/run-envelope.js +32 -1
- package/dist/workflows/v3/run-envelope.js.map +1 -1
- package/package.json +2 -2
- package/dist/dashboard-web/chunks/bot-defaults-page-O376WSER.js +0 -5
- package/dist/dashboard-web/chunks/chunk-H7P5UV4W.js +0 -49
- package/dist/dashboard-web/chunks/chunk-SZOB6VK2.js +0 -1
- package/dist/dashboard-web/chunks/connectors-page-IR54AOAI.js +0 -1
- package/dist/dashboard-web/chunks/overview-page-QJMI6NLW.js +0 -1
- package/dist/dashboard-web/chunks/sessions-page-KWLGBX6G.js +0 -3
- package/dist/dashboard-web/chunks/settings-page-TNZEPC7V.js +0 -2
- package/dist/services/sandbox-land.d.ts +0 -45
- package/dist/services/sandbox-land.d.ts.map +0 -1
- package/dist/services/sandbox-land.js +0 -394
- package/dist/services/sandbox-land.js.map +0 -1
- package/dist/utils/screen-analyzer.d.ts +0 -75
- package/dist/utils/screen-analyzer.d.ts.map +0 -1
- package/dist/utils/screen-analyzer.js +0 -333
- package/dist/utils/screen-analyzer.js.map +0 -1
package/dist/core/worker-pool.js
CHANGED
|
@@ -16,6 +16,7 @@ import { hookCommandFor } from '../adapters/hook-command.js';
|
|
|
16
16
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
17
17
|
import { config } from '../config.js';
|
|
18
18
|
import * as sessionStore from '../services/session-store.js';
|
|
19
|
+
import * as asyncTriggerStore from '../services/async-trigger-store.js';
|
|
19
20
|
import { persistStreamCardState, rememberLastCliInput } from './session-manager.js';
|
|
20
21
|
import { fallbackTurnId, isSubstituteTurn } from './reply-target.js';
|
|
21
22
|
import { updateMessage, deleteMessage, sendEphemeralCard, sendUserMessage, addReaction, removeReaction, getMessageChatId, MessageWithdrawnError } from '../im/lark/client.js';
|
|
@@ -35,12 +36,15 @@ import { listDocSubscriptionsForSession, removeDocSubscription } from '../servic
|
|
|
35
36
|
import { TmuxBackend } from '../adapters/backend/tmux-backend.js';
|
|
36
37
|
import { HerdrBackend } from '../adapters/backend/herdr-backend.js';
|
|
37
38
|
import { sandboxEnabled } from '../adapters/backend/sandbox.js';
|
|
38
|
-
import { isSuspendableBackendType, getSessionPersistentBackendType, persistentBackendTargetForSession, killPersistentBackendTarget, managedTargetsForCliChange, resolvePairedSpawnBackendType, resolvePersistentBackendTarget } from './persistent-backend.js';
|
|
39
|
+
import { isSuspendableBackendType, getSessionPersistentBackendType, persistentBackendTargetForSession, killPersistentBackendTarget, probePersistentBackendTarget, managedTargetsForCliChange, resolvePairedSpawnBackendType, resolvePersistentBackendTarget } from './persistent-backend.js';
|
|
39
40
|
import { getBot, getAllBots, resolveBrandLabel } from '../bot-registry.js';
|
|
41
|
+
import { RestartCoordinator } from './restart-coordinator.js';
|
|
42
|
+
import { runtimeBuildIdentity } from '../utils/runtime-build-id.js';
|
|
40
43
|
/** A random id minted once per daemon process (this lifetime). Stamped onto
|
|
41
44
|
* isolated persistent panes so a suspend→resume reattach (same id) is
|
|
42
45
|
* distinguishable from a pane surviving a daemon restart (different id). */
|
|
43
46
|
const DAEMON_BOOT_ID = randomUUID();
|
|
47
|
+
const restartCoordinator = new RestartCoordinator();
|
|
44
48
|
export function getDaemonBootId() {
|
|
45
49
|
return DAEMON_BOOT_ID;
|
|
46
50
|
}
|
|
@@ -76,8 +80,11 @@ import { evaluateVcMeetingManagedSend, resolveVcMeetingImTurnOrigin, } from '../
|
|
|
76
80
|
import { finishVcMeetingImReply, prepareVcMeetingDeliveryReply, prepareVcMeetingImReply, } from '../services/vc-meeting-im-reply.js';
|
|
77
81
|
import { neutralizeLarkAtTags } from '../services/send-policy.js';
|
|
78
82
|
import { recordVcMeetingListenerMessage } from '../services/vc-meeting-listener-message-store.js';
|
|
83
|
+
import { getVcMeetingListenerTopicRoot, recordVcMeetingListenerTopicRoot, } from '../services/vc-meeting-listener-topic-store.js';
|
|
84
|
+
import { parseVcMeetingListenerOutput } from '../services/vc-meeting-listener-output-protocol.js';
|
|
79
85
|
import { isLocalCliOpenEnabled, isLocalCliOpenReady } from '../services/local-cli-opener.js';
|
|
80
86
|
import { isSilentScheduledTurn } from './silent-schedule-turns.js';
|
|
87
|
+
import { isTriggerFinalSuppressed } from './trigger-final-suppression.js';
|
|
81
88
|
import { writeDeferredTopicBinding } from './deferred-topic-binding.js';
|
|
82
89
|
import { deferWorkerSpawnDuringDeviceIsolation } from './device-isolation-activation.js';
|
|
83
90
|
import { buildBotmuxLarkNativeSessionTitle, extractBotmuxLarkNativeSessionTitlePrompt, } from './session-title.js';
|
|
@@ -94,6 +101,12 @@ function workerForkEnv(base) {
|
|
|
94
101
|
delete env[key];
|
|
95
102
|
return env;
|
|
96
103
|
}
|
|
104
|
+
/** Fresh workers always start with hidden output, so restore the daemon-owned mode. */
|
|
105
|
+
function syncWorkerDisplayMode(ds) {
|
|
106
|
+
if (!ds.worker || !ds.displayMode || ds.displayMode === 'hidden')
|
|
107
|
+
return;
|
|
108
|
+
ds.worker.send({ type: 'set_display_mode', mode: ds.displayMode });
|
|
109
|
+
}
|
|
97
110
|
let callbacks;
|
|
98
111
|
/**
|
|
99
112
|
* Initialise worker-pool callbacks. Must be called once before forkWorker().
|
|
@@ -1089,6 +1102,7 @@ export function ensureClaudeFolderTrust(workingDir, stateJsonPath = join(homedir
|
|
|
1089
1102
|
}
|
|
1090
1103
|
// ─── Kill worker ────────────────────────────────────────────────────────────
|
|
1091
1104
|
export function killWorker(ds) {
|
|
1105
|
+
restartCoordinator.cancelSession(ds.session.sessionId);
|
|
1092
1106
|
clearUsageLimitState(ds);
|
|
1093
1107
|
ds.localProcessAttestation = undefined;
|
|
1094
1108
|
// A managed-turn capability belongs to one concrete worker generation.
|
|
@@ -1126,6 +1140,111 @@ export function killWorker(ds) {
|
|
|
1126
1140
|
ds.workerToken = null;
|
|
1127
1141
|
ds.workerViewToken = null;
|
|
1128
1142
|
}
|
|
1143
|
+
/**
|
|
1144
|
+
* Whether a worker-less restart must first destroy the session's persistent
|
|
1145
|
+
* backing pane. Adopt sessions are excluded — botmux never owned the user's
|
|
1146
|
+
* pane, so killing it would violate the bridge invariant. Pure so the
|
|
1147
|
+
* adopt-skip decision is unit-testable without spawning a worker.
|
|
1148
|
+
*/
|
|
1149
|
+
export function shouldDestroyPaneBeforeRestart(ds) {
|
|
1150
|
+
return !ds.initConfig?.adoptMode && !ds.adoptedFrom;
|
|
1151
|
+
}
|
|
1152
|
+
/**
|
|
1153
|
+
* Destroy a still-alive persistent backing pane (tmux/herdr/zellij) before a
|
|
1154
|
+
* worker-less restart forks a fresh worker. Without this, a session that lost
|
|
1155
|
+
* its worker but kept its pane (the normal post-daemon-restart state) would let
|
|
1156
|
+
* spawnCli REATTACH the surviving CLI instead of relaunching it — the CLI is
|
|
1157
|
+
* never actually restarted, yet prompt-ready still fires `restart_result:
|
|
1158
|
+
* succeeded`, so the user sees "restarted" while a wedged CLI stays wedged.
|
|
1159
|
+
* Killing the pane first forces a genuine physical fresh spawn, making the
|
|
1160
|
+
* success receipt truthful.
|
|
1161
|
+
*
|
|
1162
|
+
* Fail-safe (codex 复审观察): the kill primitives swallow their own failures
|
|
1163
|
+
* (TmuxBackend.killSession `catch {}`, Herdr `runHerdr` returns false), so a
|
|
1164
|
+
* plain try/catch here can NEVER observe a failed kill — a wedged tmux server
|
|
1165
|
+
* could leave the pane alive and the fork would silently reattach. So we PROBE
|
|
1166
|
+
* after killing and retry once if the pane survives, escalating to a loud warn
|
|
1167
|
+
* when it still exists. A surviving pane still forks (refusing would strand the
|
|
1168
|
+
* session with no restart at all, strictly worse than the original bug), but
|
|
1169
|
+
* the warn makes the rare failure diagnosable instead of a silent false
|
|
1170
|
+
* success. A fully reattach-proof path (forceFresh signal into spawnCli) is a
|
|
1171
|
+
* larger, separate change — tracked as a follow-up, not blocking this fix.
|
|
1172
|
+
*
|
|
1173
|
+
* Scope: persistent panes only (getSessionPersistentBackendType excludes riff,
|
|
1174
|
+
* which never reattaches — it always builds a fresh RiffBackend — and whose
|
|
1175
|
+
* remote task must survive a restart to preserve follow-up lineage). Adopt
|
|
1176
|
+
* sessions are skipped: botmux never owned the user's pane.
|
|
1177
|
+
*/
|
|
1178
|
+
function destroyLivePaneBeforeRestart(ds) {
|
|
1179
|
+
if (!shouldDestroyPaneBeforeRestart(ds))
|
|
1180
|
+
return;
|
|
1181
|
+
const target = persistentBackendTargetForSession(ds);
|
|
1182
|
+
if (!target)
|
|
1183
|
+
return;
|
|
1184
|
+
const killOnce = () => {
|
|
1185
|
+
try {
|
|
1186
|
+
killPersistentBackendTarget(target);
|
|
1187
|
+
}
|
|
1188
|
+
catch (err) {
|
|
1189
|
+
// The primitives normally swallow their own errors; this only catches a
|
|
1190
|
+
// truly unexpected throw (e.g. target resolution). Non-fatal — the probe
|
|
1191
|
+
// below is the real signal.
|
|
1192
|
+
logger.warn(`[${tag(ds)}] restart: kill of ${target.backendType} pane threw: ${err}`);
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
killOnce();
|
|
1196
|
+
// Advance a single probe result monotonically through the real retry path so
|
|
1197
|
+
// an 'unknown' first probe is never mislabelled as a post-retry survivor:
|
|
1198
|
+
// - 'missing' → gone, fork is genuinely fresh.
|
|
1199
|
+
// - 'unknown' → probe indeterminate (e.g. tmux server hiccup); do NOT retry
|
|
1200
|
+
// and do NOT block the restart — pre-fix always forked, stranding is worse.
|
|
1201
|
+
// - 'exists' → confirmed alive: warn, kill once more, re-probe. Only this
|
|
1202
|
+
// branch performs (and can report) a retry.
|
|
1203
|
+
let probe = probePersistentBackendTarget(target);
|
|
1204
|
+
if (probe === 'exists') {
|
|
1205
|
+
logger.warn(`[${tag(ds)}] restart: ${target.backendType} pane survived first kill — retrying before refork`);
|
|
1206
|
+
killOnce();
|
|
1207
|
+
probe = probePersistentBackendTarget(target);
|
|
1208
|
+
}
|
|
1209
|
+
if (probe === 'exists') {
|
|
1210
|
+
// The kill genuinely failed (twice). Forking will likely reattach the live
|
|
1211
|
+
// pane (the very bug this guards), so the eventual `restart_result:
|
|
1212
|
+
// succeeded` may again be untruthful — but leave a loud, greppable trail.
|
|
1213
|
+
logger.error(`[${tag(ds)}] restart: ${target.backendType} pane STILL alive after retry — `
|
|
1214
|
+
+ 'the refork may reattach instead of relaunching (restart success may be untruthful)');
|
|
1215
|
+
}
|
|
1216
|
+
else if (probe === 'unknown') {
|
|
1217
|
+
// Do NOT over-promise a relaunch: an indeterminate probe could still be a
|
|
1218
|
+
// live pane the fork reattaches. Fork proceeds (stranding is worse) but the
|
|
1219
|
+
// diagnostic must not claim a fresh relaunch it can't guarantee.
|
|
1220
|
+
logger.warn(`[${tag(ds)}] restart: ${target.backendType} kill outcome indeterminate — `
|
|
1221
|
+
+ 'refork may reattach instead of relaunching');
|
|
1222
|
+
}
|
|
1223
|
+
else {
|
|
1224
|
+
logger.info(`[${tag(ds)}] restart: ${target.backendType} pane missing after kill — CLI will physically relaunch`);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
/** Join or start one correlated physical restart for a session. */
|
|
1228
|
+
export function requestSessionRestart(ds, observer) {
|
|
1229
|
+
return restartCoordinator.request(ds.session.sessionId, observer, attemptId => {
|
|
1230
|
+
if (ds.worker && !ds.worker.killed) {
|
|
1231
|
+
ds.worker.send({ type: 'restart', attemptId });
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
// No live worker but the persistent pane may still be alive (e.g. after a
|
|
1235
|
+
// daemon restart). Tear it down first so forkWorker → spawnCli spawns a
|
|
1236
|
+
// fresh CLI instead of reattaching the old one and falsely reporting a
|
|
1237
|
+
// successful restart.
|
|
1238
|
+
destroyLivePaneBeforeRestart(ds);
|
|
1239
|
+
forkWorker(ds, '', {
|
|
1240
|
+
resume: ds.hasHistory,
|
|
1241
|
+
restartAttemptId: attemptId,
|
|
1242
|
+
});
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
export function __testOnly_resetRestartCoordinator() {
|
|
1246
|
+
restartCoordinator.reset();
|
|
1247
|
+
}
|
|
1129
1248
|
/**
|
|
1130
1249
|
* Tear down a persistent backing session (tmux/herdr/zellij) directly from the
|
|
1131
1250
|
* daemon when there is no live worker to do it via the 'close' IPC. The session
|
|
@@ -1299,26 +1418,21 @@ export async function closeSession(sessionId) {
|
|
|
1299
1418
|
// crash/limited turn may never have reached an idle edge).
|
|
1300
1419
|
recordUsageForDaemonSession(ds);
|
|
1301
1420
|
killWorker(ds);
|
|
1302
|
-
//
|
|
1303
|
-
//
|
|
1304
|
-
// /
|
|
1305
|
-
|
|
1306
|
-
const anchor = sessionAnchorId(ds);
|
|
1307
|
-
const subs = listDocSubscriptionsForSession(config.session.dataDir, ds.larkAppId, anchor);
|
|
1308
|
-
for (const sub of subs) {
|
|
1309
|
-
if (sub.managedBy !== 'watch-comment') {
|
|
1310
|
-
await unsubscribeDocFile(ds.larkAppId, { fileToken: sub.fileToken, fileType: sub.fileType });
|
|
1311
|
-
}
|
|
1312
|
-
removeDocSubscription(config.session.dataDir, ds.larkAppId, sub.fileToken);
|
|
1313
|
-
}
|
|
1314
|
-
if (subs.length)
|
|
1315
|
-
logger.info(`[doc-comment] session ${sessionId.slice(0, 8)} closed → removed ${subs.length} doc binding(s)`);
|
|
1316
|
-
}
|
|
1317
|
-
catch (err) {
|
|
1318
|
-
logger.warn(`[doc-comment] cleanup on close failed for ${sessionId.slice(0, 8)}: ${err?.message ?? err}`);
|
|
1319
|
-
}
|
|
1421
|
+
// Commit the daemon-visible close barrier before any network await below.
|
|
1422
|
+
// `botmux delete` may be running inside the session being destroyed; once
|
|
1423
|
+
// its worker/backing pane exits, a new IM message must not find this stale
|
|
1424
|
+
// entry and resurrect the old logical session while doc cleanup is pending.
|
|
1320
1425
|
activeSessionsRegistry?.delete(activeSessionKey(ds));
|
|
1321
1426
|
killedLive = true;
|
|
1427
|
+
}
|
|
1428
|
+
// Persistence is part of the same no-await barrier as Map eviction. If the
|
|
1429
|
+
// daemon crashes during best-effort doc cleanup, restart recovery must not
|
|
1430
|
+
// restore a session that was already explicitly closed.
|
|
1431
|
+
const stored = sessionStore.getSession(sessionId);
|
|
1432
|
+
const wasOpen = !!stored && stored.status !== 'closed';
|
|
1433
|
+
if (wasOpen)
|
|
1434
|
+
sessionStore.closeSession(sessionId);
|
|
1435
|
+
if (ds) {
|
|
1322
1436
|
if (!ds.exitEventEmitted) {
|
|
1323
1437
|
ds.exitEventEmitted = true;
|
|
1324
1438
|
dashboardEventBus.publish({
|
|
@@ -1328,11 +1442,10 @@ export async function closeSession(sessionId) {
|
|
|
1328
1442
|
emitSessionLifecycleHook(ds, 'session.exit', { reason: 'dashboard_close' });
|
|
1329
1443
|
}
|
|
1330
1444
|
}
|
|
1331
|
-
//
|
|
1332
|
-
|
|
1333
|
-
|
|
1445
|
+
// Preserve the existing externally-visible event order: exit first, then
|
|
1446
|
+
// the final persisted-row update. Persistence itself was already committed
|
|
1447
|
+
// above as part of the close barrier.
|
|
1334
1448
|
if (wasOpen) {
|
|
1335
|
-
sessionStore.closeSession(sessionId);
|
|
1336
1449
|
const after = sessionStore.getSession(sessionId);
|
|
1337
1450
|
dashboardEventBus.publish({
|
|
1338
1451
|
type: 'session.update',
|
|
@@ -1346,6 +1459,27 @@ export async function closeSession(sessionId) {
|
|
|
1346
1459
|
},
|
|
1347
1460
|
});
|
|
1348
1461
|
}
|
|
1462
|
+
if (ds) {
|
|
1463
|
+
// 文档入口清理:会话关闭即删除其绑定。只有旧
|
|
1464
|
+
// /subscribe-lark-doc 记录需要调飞书逐文件退订 API;
|
|
1465
|
+
// /watch-comment 仅依赖应用级评论事件,删本地监听表即可。此段允许 await,
|
|
1466
|
+
// 因为上面的内存 + 持久化关闭屏障已经提交。
|
|
1467
|
+
try {
|
|
1468
|
+
const anchor = sessionAnchorId(ds);
|
|
1469
|
+
const subs = listDocSubscriptionsForSession(config.session.dataDir, ds.larkAppId, anchor);
|
|
1470
|
+
for (const sub of subs) {
|
|
1471
|
+
if (sub.managedBy !== 'watch-comment') {
|
|
1472
|
+
await unsubscribeDocFile(ds.larkAppId, { fileToken: sub.fileToken, fileType: sub.fileType });
|
|
1473
|
+
}
|
|
1474
|
+
removeDocSubscription(config.session.dataDir, ds.larkAppId, sub.fileToken);
|
|
1475
|
+
}
|
|
1476
|
+
if (subs.length)
|
|
1477
|
+
logger.info(`[doc-comment] session ${sessionId.slice(0, 8)} closed → removed ${subs.length} doc binding(s)`);
|
|
1478
|
+
}
|
|
1479
|
+
catch (err) {
|
|
1480
|
+
logger.warn(`[doc-comment] cleanup on close failed for ${sessionId.slice(0, 8)}: ${err?.message ?? err}`);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1349
1483
|
// alreadyClosed = nothing happened on either path.
|
|
1350
1484
|
const alreadyClosed = !killedLive && !wasOpen;
|
|
1351
1485
|
return { ok: true, alreadyClosed };
|
|
@@ -1707,6 +1841,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1707
1841
|
let resume = false;
|
|
1708
1842
|
let initTurnId;
|
|
1709
1843
|
let initDispatchAttempt;
|
|
1844
|
+
let restartAttemptId;
|
|
1710
1845
|
if (typeof resumeOrTurnId === 'string') {
|
|
1711
1846
|
initTurnId = resumeOrTurnId;
|
|
1712
1847
|
}
|
|
@@ -1714,6 +1849,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1714
1849
|
resume = resumeOrTurnId.resume === true;
|
|
1715
1850
|
initTurnId = resumeOrTurnId.turnId;
|
|
1716
1851
|
initDispatchAttempt = resumeOrTurnId.dispatchAttempt;
|
|
1852
|
+
restartAttemptId = resumeOrTurnId.restartAttemptId;
|
|
1717
1853
|
}
|
|
1718
1854
|
else {
|
|
1719
1855
|
resume = resumeOrTurnId;
|
|
@@ -1766,6 +1902,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1766
1902
|
if (ds.session.sandbox === undefined) {
|
|
1767
1903
|
if (!resume) {
|
|
1768
1904
|
ds.session.sandbox = botCfg.sandbox === true;
|
|
1905
|
+
ds.session.sandboxPaths = botCfg.sandboxPaths;
|
|
1769
1906
|
ds.session.sandboxHidePaths = botCfg.sandboxHidePaths ?? [];
|
|
1770
1907
|
ds.session.sandboxReadonlyPaths = botCfg.sandboxReadonlyPaths ?? [];
|
|
1771
1908
|
ds.session.sandboxNetwork = botCfg.sandboxNetwork !== false;
|
|
@@ -1926,6 +2063,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1926
2063
|
});
|
|
1927
2064
|
// Send init config — use per-bot settings
|
|
1928
2065
|
const promptCodexAppInput = codexAppInputForSession(ds, promptPayload.codexAppInput, initAttributionTurnId);
|
|
2066
|
+
const runtimeIdentity = runtimeBuildIdentity();
|
|
1929
2067
|
const initMsg = {
|
|
1930
2068
|
type: 'init',
|
|
1931
2069
|
sessionId: ds.session.sessionId,
|
|
@@ -1951,6 +2089,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1951
2089
|
// Use the decision recorded on the session (above), NOT the live bot flag, so
|
|
1952
2090
|
// historical sessions never get retroactively sandboxed on restart.
|
|
1953
2091
|
sandbox: ds.session.sandbox === true,
|
|
2092
|
+
sandboxPaths: ds.session.sandboxPaths ?? botCfg.sandboxPaths,
|
|
1954
2093
|
sandboxHidePaths: ds.session.sandboxHidePaths ?? [],
|
|
1955
2094
|
sandboxReadonlyPaths: ds.session.sandboxReadonlyPaths ?? [],
|
|
1956
2095
|
sandboxNetwork: ds.session.sandboxNetwork !== false,
|
|
@@ -1996,6 +2135,11 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
|
|
|
1996
2135
|
vcMeetingImTurnOrigin: resolveVcMeetingImTurnOrigin(ds.session, initAttributionTurnId),
|
|
1997
2136
|
pluginBindings: botCfg.plugins,
|
|
1998
2137
|
skillPolicy: botCfg.skills,
|
|
2138
|
+
...(runtimeIdentity.status === 'known'
|
|
2139
|
+
? { runnerBuildId: runtimeIdentity.id }
|
|
2140
|
+
: {}),
|
|
2141
|
+
...(ds.session.runnerBuildId ? { persistedRunnerBuildId: ds.session.runnerBuildId } : {}),
|
|
2142
|
+
...(restartAttemptId ? { restartAttemptId } : {}),
|
|
1999
2143
|
};
|
|
2000
2144
|
worker.send(initMsg);
|
|
2001
2145
|
ds.initConfig = initMsg;
|
|
@@ -2137,6 +2281,8 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2137
2281
|
const managedFinalOutputSuppressed = (turnId, dispatchAttempt) => {
|
|
2138
2282
|
if (isSilentScheduledTurn(ds, turnId))
|
|
2139
2283
|
return true;
|
|
2284
|
+
if (isTriggerFinalSuppressed(ds, turnId))
|
|
2285
|
+
return true;
|
|
2140
2286
|
if (!ds.session.vcMeetingReceiver) {
|
|
2141
2287
|
return ordinaryManagedSuppression(turnId, dispatchAttempt);
|
|
2142
2288
|
}
|
|
@@ -2307,6 +2453,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2307
2453
|
// (if any) is left untouched. The next real user turn clears this flag
|
|
2308
2454
|
// (rememberLastCliInput) and the normal card flow resumes.
|
|
2309
2455
|
if (ds.suppressRecoveryCard) {
|
|
2456
|
+
syncWorkerDisplayMode(ds);
|
|
2310
2457
|
logger.info(`[${t}] Restored session — suppressing recovery streaming card (silent restart)`);
|
|
2311
2458
|
break;
|
|
2312
2459
|
}
|
|
@@ -2341,9 +2488,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2341
2488
|
}
|
|
2342
2489
|
persistStreamCardState(ds);
|
|
2343
2490
|
// Re-sync worker's display mode (it starts fresh in 'hidden')
|
|
2344
|
-
|
|
2345
|
-
ds.worker.send({ type: 'set_display_mode', mode: ds.displayMode });
|
|
2346
|
-
}
|
|
2491
|
+
syncWorkerDisplayMode(ds);
|
|
2347
2492
|
// The restored card is now the active one — withdraw any cards
|
|
2348
2493
|
// frozen before the daemon went down so they don't pile up in the
|
|
2349
2494
|
// thread on each restart.
|
|
@@ -2393,9 +2538,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2393
2538
|
ds.streamCardPending = false;
|
|
2394
2539
|
persistStreamCardState(ds);
|
|
2395
2540
|
// Re-sync worker's display mode (it starts fresh in 'hidden')
|
|
2396
|
-
|
|
2397
|
-
ds.worker.send({ type: 'set_display_mode', mode: ds.displayMode });
|
|
2398
|
-
}
|
|
2541
|
+
syncWorkerDisplayMode(ds);
|
|
2399
2542
|
// New card is live — recall any cards frozen by previous turns.
|
|
2400
2543
|
// Done after `streamCardId` is committed so we never delete the old
|
|
2401
2544
|
// card without a successor visible to the user.
|
|
@@ -2492,6 +2635,28 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
2492
2635
|
invalidateStuckWarning(ds, 'prompt_ready');
|
|
2493
2636
|
break;
|
|
2494
2637
|
}
|
|
2638
|
+
case 'runner_build_ready': {
|
|
2639
|
+
const identity = runtimeBuildIdentity();
|
|
2640
|
+
if (ds.worker === worker
|
|
2641
|
+
&& effectiveCliId === 'codex-app'
|
|
2642
|
+
&& identity.status === 'known'
|
|
2643
|
+
&& msg.runnerBuildId === identity.id) {
|
|
2644
|
+
ds.session.runnerBuildId = msg.runnerBuildId;
|
|
2645
|
+
sessionStore.updateSession(ds.session);
|
|
2646
|
+
}
|
|
2647
|
+
else {
|
|
2648
|
+
logger.warn(`[${t}] Ignored invalid or stale runner_build_ready`);
|
|
2649
|
+
}
|
|
2650
|
+
break;
|
|
2651
|
+
}
|
|
2652
|
+
case 'restart_result': {
|
|
2653
|
+
if (ds.worker !== worker) {
|
|
2654
|
+
logger.warn(`[${t}] Ignored restart_result from stale worker generation`);
|
|
2655
|
+
break;
|
|
2656
|
+
}
|
|
2657
|
+
restartCoordinator.resolve(ds.session.sessionId, msg.attemptId, msg.status);
|
|
2658
|
+
break;
|
|
2659
|
+
}
|
|
2495
2660
|
case 'cli_session_id': {
|
|
2496
2661
|
const wasLocalCliOpenReady = isLocalCliOpenReady(ds, { cliId: effectiveCliId });
|
|
2497
2662
|
ds.session.cliSessionId = msg.cliSessionId;
|
|
@@ -3111,6 +3276,25 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
3111
3276
|
}
|
|
3112
3277
|
break;
|
|
3113
3278
|
}
|
|
3279
|
+
case 'steer_accepted': {
|
|
3280
|
+
if (ds.worker !== worker) {
|
|
3281
|
+
logger.warn(`[${t}] Ignored steer_accepted from stale worker generation`);
|
|
3282
|
+
break;
|
|
3283
|
+
}
|
|
3284
|
+
logger.info(`[${t}] Codex App steer accepted `
|
|
3285
|
+
+ `appTurn=${msg.appTurnId.slice(0, 12)} replyTurn=${msg.turnId.slice(0, 12)}`);
|
|
3286
|
+
if (managedAuxUiSuppressed(msg.turnId, undefined))
|
|
3287
|
+
break;
|
|
3288
|
+
try {
|
|
3289
|
+
await scopedReply(tr('worker.steer_accepted', undefined, loc), 'text', msg.turnId);
|
|
3290
|
+
}
|
|
3291
|
+
catch {
|
|
3292
|
+
logger.error(`[${t}] Failed to deliver steer acknowledgement `
|
|
3293
|
+
+ `appTurn=${msg.appTurnId.slice(0, 12)} replyTurn=${msg.turnId.slice(0, 12)} `
|
|
3294
|
+
+ 'category=delivery');
|
|
3295
|
+
}
|
|
3296
|
+
break;
|
|
3297
|
+
}
|
|
3114
3298
|
case 'turn_terminal': {
|
|
3115
3299
|
if (ds.worker !== worker) {
|
|
3116
3300
|
logger.warn(`[${t}] Ignored turn_terminal from stale worker generation`);
|
|
@@ -3241,6 +3425,17 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
3241
3425
|
// Worker pops the turn off its queue right after emit, so it will
|
|
3242
3426
|
// NOT re-send this payload on its own. Daemon owns retry on
|
|
3243
3427
|
// transient Lark failures.
|
|
3428
|
+
// A real harvested answer is a definitive self-heal signal: if the
|
|
3429
|
+
// session was parked in `limited` by a structured rate-limit emit
|
|
3430
|
+
// (Claude adopt/bridge sessions where the user recovers in their own
|
|
3431
|
+
// terminal — no Lark turn, retry button, or kill to clear it), the
|
|
3432
|
+
// model is plainly working again. Clear the stale limit so the card /
|
|
3433
|
+
// Dashboard「需要你」 don't stay pinned with a dead retry countdown.
|
|
3434
|
+
if (ds.usageLimit) {
|
|
3435
|
+
clearUsageLimitState(ds);
|
|
3436
|
+
if (ds.lastScreenStatus === 'limited')
|
|
3437
|
+
ds.lastScreenStatus = 'idle';
|
|
3438
|
+
}
|
|
3244
3439
|
deliverFinalOutput(ds, msg, t, 0);
|
|
3245
3440
|
break;
|
|
3246
3441
|
}
|
|
@@ -3297,6 +3492,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
|
|
|
3297
3492
|
// A stale takeover worker never clears the replacement — during takeover the
|
|
3298
3493
|
// old worker's exit fires AFTER the new worker has been assigned.
|
|
3299
3494
|
if (ds.worker === worker) {
|
|
3495
|
+
restartCoordinator.failSession(ds.session.sessionId);
|
|
3300
3496
|
ds.worker = null;
|
|
3301
3497
|
ds.workerPort = null;
|
|
3302
3498
|
ds.managedTurnOrigin = undefined;
|
|
@@ -3443,9 +3639,14 @@ function deliverFinalOutput(ds, msg, t, attempt) {
|
|
|
3443
3639
|
}
|
|
3444
3640
|
const asyncResult = managedReceiver ? undefined : ds.asyncTriggerResults?.get(msg.turnId);
|
|
3445
3641
|
if (asyncResult) {
|
|
3642
|
+
const completedAt = Date.now();
|
|
3446
3643
|
asyncResult.status = 'completed';
|
|
3447
3644
|
asyncResult.content = msg.content;
|
|
3448
|
-
asyncResult.completedAt =
|
|
3645
|
+
asyncResult.completedAt = completedAt;
|
|
3646
|
+
// Durably persist the outcome so trigger-result can rebuild `completed`
|
|
3647
|
+
// (with content) after a daemon restart drops the in-memory Map. Stamp the
|
|
3648
|
+
// owning bot for cross-bot isolation.
|
|
3649
|
+
asyncTriggerStore.recordCompleted(ds.session.sessionId, msg.turnId, msg.content, completedAt, ds.larkAppId);
|
|
3449
3650
|
ds.lastBridgeEmittedUuid = finalOutputDedupeKey(ds, msg);
|
|
3450
3651
|
logger.info(`[${t}] Captured final_output for Async HTTP request (turn ${msg.turnId.substring(0, 8)})`);
|
|
3451
3652
|
return;
|
|
@@ -3543,12 +3744,48 @@ function deliverFinalOutput(ds, msg, t, attempt) {
|
|
|
3543
3744
|
&& managedDecision.kind === 'listener_thread'
|
|
3544
3745
|
? managedDecision.meetingOwner
|
|
3545
3746
|
: undefined;
|
|
3747
|
+
const listenerOutputPlacement = managedDecision?.ok
|
|
3748
|
+
&& managedDecision.kind === 'listener_thread'
|
|
3749
|
+
? managedDecision.outputPlacement
|
|
3750
|
+
: 'auto';
|
|
3751
|
+
const listenerOutputProtocol = managedDecision?.ok
|
|
3752
|
+
&& managedDecision.kind === 'listener_thread'
|
|
3753
|
+
? managedDecision.listenerOutputProtocol
|
|
3754
|
+
: 'plain';
|
|
3755
|
+
const meetingTopicKey = !imOrigin
|
|
3756
|
+
&& listenerOutputOwner
|
|
3757
|
+
&& listenerOutputPlacement === 'topic'
|
|
3758
|
+
? {
|
|
3759
|
+
...listenerOutputOwner,
|
|
3760
|
+
targetChatId: ds.chatId,
|
|
3761
|
+
}
|
|
3762
|
+
: undefined;
|
|
3763
|
+
let visibleAssistantText = msg.content;
|
|
3764
|
+
if (!imOrigin
|
|
3765
|
+
&& listenerOutputOwner
|
|
3766
|
+
&& msg.dispatchAttempt !== undefined
|
|
3767
|
+
&& listenerOutputProtocol === 'decision_v1') {
|
|
3768
|
+
const controlledOutput = parseVcMeetingListenerOutput(msg.content);
|
|
3769
|
+
if (!controlledOutput.ok) {
|
|
3770
|
+
ds.lastBridgeEmittedUuid = finalOutputDedupeKey(ds, msg);
|
|
3771
|
+
logger.error(`[${t}] VC listener output suppressed: invalid control envelope `
|
|
3772
|
+
+ `(${controlledOutput.reason}) turn=${msg.turnId.substring(0, 8)}`);
|
|
3773
|
+
return;
|
|
3774
|
+
}
|
|
3775
|
+
if (controlledOutput.decision === 'skip') {
|
|
3776
|
+
ds.lastBridgeEmittedUuid = finalOutputDedupeKey(ds, msg);
|
|
3777
|
+
logger.info(`[${t}] VC listener output skipped by agent decision `
|
|
3778
|
+
+ `(turn ${msg.turnId.substring(0, 8)})`);
|
|
3779
|
+
return;
|
|
3780
|
+
}
|
|
3781
|
+
visibleAssistantText = controlledOutput.content;
|
|
3782
|
+
}
|
|
3546
3783
|
// Meeting-derived text is untrusted card markdown. A model-authored
|
|
3547
3784
|
// native <at> tag and the ordinary owner footer would both create a
|
|
3548
3785
|
// second addressing side effect outside the action ledger.
|
|
3549
3786
|
const safeAssistantText = managedReceiver
|
|
3550
|
-
? neutralizeLarkAtTags(
|
|
3551
|
-
:
|
|
3787
|
+
? neutralizeLarkAtTags(visibleAssistantText)
|
|
3788
|
+
: visibleAssistantText;
|
|
3552
3789
|
const safeUserText = managedReceiver && msg.userText !== undefined
|
|
3553
3790
|
? neutralizeLarkAtTags(msg.userText)
|
|
3554
3791
|
: msg.userText;
|
|
@@ -3575,6 +3812,9 @@ function deliverFinalOutput(ds, msg, t, attempt) {
|
|
|
3575
3812
|
const proposedOutput = {
|
|
3576
3813
|
targetChatId: ds.chatId,
|
|
3577
3814
|
...(imOrigin ? { quoteTargetId: imOrigin.larkMessageId } : {}),
|
|
3815
|
+
...(!imOrigin && listenerOutputOwner
|
|
3816
|
+
? { placement: listenerOutputPlacement }
|
|
3817
|
+
: {}),
|
|
3578
3818
|
msgType: 'interactive',
|
|
3579
3819
|
content: cardJson,
|
|
3580
3820
|
};
|
|
@@ -3605,6 +3845,13 @@ function deliverFinalOutput(ds, msg, t, attempt) {
|
|
|
3605
3845
|
const recordPrimaryOutput = (messageId) => {
|
|
3606
3846
|
if (!listenerOutputOwner)
|
|
3607
3847
|
return;
|
|
3848
|
+
if (meetingTopicKey
|
|
3849
|
+
&& !getVcMeetingListenerTopicRoot(config.session.dataDir, meetingTopicKey)) {
|
|
3850
|
+
const topic = recordVcMeetingListenerTopicRoot(config.session.dataDir, meetingTopicKey, messageId);
|
|
3851
|
+
if (!topic.ok) {
|
|
3852
|
+
logger.error(`[${t}] VC listener topic anchor rejected message=${messageId} reason=${topic.reason}`);
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3608
3855
|
try {
|
|
3609
3856
|
const recorded = recordVcMeetingListenerMessage(config.session.dataDir, {
|
|
3610
3857
|
...listenerOutputOwner,
|
|
@@ -3642,6 +3889,12 @@ function deliverFinalOutput(ds, msg, t, attempt) {
|
|
|
3642
3889
|
// provider call). Never fan meeting content out to user hooks,
|
|
3643
3890
|
// including the first attempt and crash reconciliation replay.
|
|
3644
3891
|
suppressHook: true,
|
|
3892
|
+
...(!imOrigin && listenerOutputOwner
|
|
3893
|
+
? {
|
|
3894
|
+
placement: canonicalOutput.placement ?? 'auto',
|
|
3895
|
+
...(meetingTopicKey ? { meetingTopicKey } : {}),
|
|
3896
|
+
}
|
|
3897
|
+
: {}),
|
|
3645
3898
|
}
|
|
3646
3899
|
: undefined);
|
|
3647
3900
|
recordPrimaryOutput(messageId);
|
|
@@ -3701,6 +3954,30 @@ function reserveWorkerGeneration(ds) {
|
|
|
3701
3954
|
}
|
|
3702
3955
|
return workerGeneration;
|
|
3703
3956
|
}
|
|
3957
|
+
/**
|
|
3958
|
+
* Is the file sandbox active such that /adopt must be refused? A sandbox can
|
|
3959
|
+
* only be established at spawn time (bwrap wrap / Seatbelt profile); adopt
|
|
3960
|
+
* attaches to an ALREADY-running host process, so it could only ever run
|
|
3961
|
+
* UNsandboxed. From-strict UNION of every source that can require isolation:
|
|
3962
|
+
* - live per-bot `sandbox` / legacy `readIsolation`,
|
|
3963
|
+
* - the global `BOTMUX_SANDBOX=1` (sandboxEnabled()),
|
|
3964
|
+
* - the session's FROZEN sandbox decision (`session.sandbox`, recorded at
|
|
3965
|
+
* creation). forkWorker treats the frozen decision as authoritative, so a
|
|
3966
|
+
* session created sandboxed must stay un-adoptable even if the admin later
|
|
3967
|
+
* toggles the bot's global flag OFF — otherwise its `/adopt` would attach to
|
|
3968
|
+
* an unsandboxed live process. The reverse (bot=true / session=false) is
|
|
3969
|
+
* also caught by the union, which is the safe direction.
|
|
3970
|
+
* Callers reject at the ENTRY point (before persisting `adoptedFrom` / replying
|
|
3971
|
+
* "adopted") — see command-handler's `/adopt`, the adopt_select card, and the
|
|
3972
|
+
* session-manager restore branch. Resume/cold-start is unaffected: those spawn
|
|
3973
|
+
* a fresh CLI, which the sandbox wraps normally.
|
|
3974
|
+
*/
|
|
3975
|
+
export function adoptSandboxBlocked(botCfg, session) {
|
|
3976
|
+
return botCfg.sandbox === true
|
|
3977
|
+
|| botCfg.readIsolation === true
|
|
3978
|
+
|| session?.sandbox === true
|
|
3979
|
+
|| sandboxEnabled();
|
|
3980
|
+
}
|
|
3704
3981
|
export function forkAdoptWorker(ds, opts) {
|
|
3705
3982
|
const cb = requireCallbacks();
|
|
3706
3983
|
const workerPath = join(__dirname, '..', 'worker.js');
|
|
@@ -3710,12 +3987,30 @@ export function forkAdoptWorker(ds, opts) {
|
|
|
3710
3987
|
throw new Error('forkAdoptWorker called without adoptedFrom');
|
|
3711
3988
|
const bot = getBot(ds.larkAppId);
|
|
3712
3989
|
const botCfg = bot.config;
|
|
3713
|
-
//
|
|
3714
|
-
// to an existing pane
|
|
3715
|
-
//
|
|
3716
|
-
//
|
|
3717
|
-
|
|
3718
|
-
|
|
3990
|
+
// A file sandbox cannot be applied to an already-running CLI: adopt ATTACHES
|
|
3991
|
+
// to an existing host pane/process, and confinement (bwrap wrap on Linux /
|
|
3992
|
+
// Seatbelt profile on macOS) can only be established at spawn time — there is
|
|
3993
|
+
// no way to retro-fit a sandbox around a live process. Refuse to adopt when
|
|
3994
|
+
// the sandbox is active (live bot flag OR the session's FROZEN decision)
|
|
3995
|
+
// rather than run it UNsandboxed. Real adopt entry points (`/adopt`, the
|
|
3996
|
+
// adopt_select card) reject BEFORE persisting `adoptedFrom`, and the
|
|
3997
|
+
// session-manager restore branch converts a sandbox adopt to a cold-start
|
|
3998
|
+
// before it ever reaches here; this is the last-line fail-closed backstop,
|
|
3999
|
+
// which also strips any stale adopt metadata it is handed.
|
|
4000
|
+
if (adoptSandboxBlocked(botCfg, ds.session)) {
|
|
4001
|
+
logger.warn(`[${t}] sandbox-enabled session: refusing to adopt existing CLI (would run unsandboxed)`);
|
|
4002
|
+
// Strip stale adopt state so the session doesn't linger as a worker=null
|
|
4003
|
+
// pseudo-adopt whose NEXT message
|
|
4004
|
+
// would still be routed as a bridge/adopt session. It cold-starts sandboxed
|
|
4005
|
+
// via the normal resume path instead.
|
|
4006
|
+
ds.adoptedFrom = undefined;
|
|
4007
|
+
if (ds.session.adoptedFrom) {
|
|
4008
|
+
ds.session.adoptedFrom = undefined;
|
|
4009
|
+
try {
|
|
4010
|
+
sessionStore.updateSession(ds.session);
|
|
4011
|
+
}
|
|
4012
|
+
catch { /* best-effort */ }
|
|
4013
|
+
}
|
|
3719
4014
|
return;
|
|
3720
4015
|
}
|
|
3721
4016
|
// Reserve before replacing an existing bridge worker for the same reason as
|