botmux 3.10.0 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.runtime-build-id +1 -1
- package/dist/adapters/backend/herdr-backend.d.ts +4 -3
- package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
- package/dist/adapters/backend/herdr-backend.js +11 -8
- package/dist/adapters/backend/herdr-backend.js.map +1 -1
- package/dist/adapters/backend/pty-backend.d.ts +1 -1
- package/dist/adapters/backend/pty-backend.d.ts.map +1 -1
- package/dist/adapters/backend/pty-backend.js +4 -1
- package/dist/adapters/backend/pty-backend.js.map +1 -1
- package/dist/adapters/backend/riff-backend.d.ts +29 -5
- package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
- package/dist/adapters/backend/riff-backend.js +302 -31
- package/dist/adapters/backend/riff-backend.js.map +1 -1
- package/dist/adapters/backend/sandbox.d.ts +6 -0
- package/dist/adapters/backend/sandbox.d.ts.map +1 -1
- package/dist/adapters/backend/sandbox.js +7 -0
- package/dist/adapters/backend/sandbox.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +4 -1
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +37 -3
- 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 +3 -3
- package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zellij-backend.js +9 -3
- package/dist/adapters/backend/zellij-backend.js.map +1 -1
- package/dist/adapters/backend/zellij-observe-backend.d.ts +3 -3
- package/dist/adapters/backend/zellij-observe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zellij-observe-backend.js +12 -7
- package/dist/adapters/backend/zellij-observe-backend.js.map +1 -1
- package/dist/adapters/cli/codex-app.d.ts.map +1 -1
- package/dist/adapters/cli/codex-app.js +11 -2
- package/dist/adapters/cli/codex-app.js.map +1 -1
- package/dist/adapters/cli/read-isolation.d.ts +35 -2
- package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
- package/dist/adapters/cli/read-isolation.js +88 -7
- package/dist/adapters/cli/read-isolation.js.map +1 -1
- package/dist/adapters/cli/runner-input.d.ts +15 -14
- package/dist/adapters/cli/runner-input.d.ts.map +1 -1
- package/dist/adapters/cli/runner-input.js +64 -26
- package/dist/adapters/cli/runner-input.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +20 -5
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +24 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +110 -5
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/fleet-shutdown.d.ts +47 -0
- package/dist/cli/fleet-shutdown.d.ts.map +1 -0
- package/dist/cli/fleet-shutdown.js +387 -0
- package/dist/cli/fleet-shutdown.js.map +1 -0
- package/dist/cli/pm2-descriptor-guard.d.ts +19 -0
- package/dist/cli/pm2-descriptor-guard.d.ts.map +1 -0
- package/dist/cli/pm2-descriptor-guard.js +122 -0
- package/dist/cli/pm2-descriptor-guard.js.map +1 -0
- package/dist/cli/pm2-exact-start.d.ts +16 -0
- package/dist/cli/pm2-exact-start.d.ts.map +1 -0
- package/dist/cli/pm2-exact-start.js +63 -0
- package/dist/cli/pm2-exact-start.js.map +1 -0
- package/dist/cli/pm2-god-admission.d.ts +8 -0
- package/dist/cli/pm2-god-admission.d.ts.map +1 -0
- package/dist/cli/pm2-god-admission.js +25 -0
- package/dist/cli/pm2-god-admission.js.map +1 -0
- package/dist/cli/pm2-jlist.d.ts +13 -0
- package/dist/cli/pm2-jlist.d.ts.map +1 -0
- package/dist/cli/pm2-jlist.js +108 -0
- package/dist/cli/pm2-jlist.js.map +1 -0
- package/dist/cli/pm2-preflight.d.ts +10 -0
- package/dist/cli/pm2-preflight.d.ts.map +1 -0
- package/dist/cli/pm2-preflight.js +23 -0
- package/dist/cli/pm2-preflight.js.map +1 -0
- package/dist/cli/pm2-shutdown-capability.d.ts +27 -0
- package/dist/cli/pm2-shutdown-capability.d.ts.map +1 -0
- package/dist/cli/pm2-shutdown-capability.js +161 -0
- package/dist/cli/pm2-shutdown-capability.js.map +1 -0
- package/dist/cli/pm2-start-transaction.d.ts +55 -0
- package/dist/cli/pm2-start-transaction.d.ts.map +1 -0
- package/dist/cli/pm2-start-transaction.js +220 -0
- package/dist/cli/pm2-start-transaction.js.map +1 -0
- package/dist/cli/send-dispatch.d.ts +12 -0
- package/dist/cli/send-dispatch.d.ts.map +1 -1
- package/dist/cli/send-dispatch.js +31 -4
- package/dist/cli/send-dispatch.js.map +1 -1
- package/dist/cli/session-list-liveness.d.ts +1 -0
- package/dist/cli/session-list-liveness.d.ts.map +1 -1
- package/dist/cli/session-list-liveness.js +6 -0
- package/dist/cli/session-list-liveness.js.map +1 -1
- package/dist/cli/session-picker-layout.d.ts +26 -0
- package/dist/cli/session-picker-layout.d.ts.map +1 -0
- package/dist/cli/session-picker-layout.js +123 -0
- package/dist/cli/session-picker-layout.js.map +1 -0
- package/dist/cli/session-picker-viewport.d.ts +49 -0
- package/dist/cli/session-picker-viewport.d.ts.map +1 -0
- package/dist/cli/session-picker-viewport.js +46 -0
- package/dist/cli/session-picker-viewport.js.map +1 -0
- package/dist/cli/supervisor-shutdown-client.d.ts +31 -0
- package/dist/cli/supervisor-shutdown-client.d.ts.map +1 -0
- package/dist/cli/supervisor-shutdown-client.js +146 -0
- package/dist/cli/supervisor-shutdown-client.js.map +1 -0
- package/dist/cli/terminal-width.d.ts +48 -0
- package/dist/cli/terminal-width.d.ts.map +1 -0
- package/dist/cli/terminal-width.js +119 -0
- package/dist/cli/terminal-width.js.map +1 -0
- package/dist/cli/vc-agent.d.ts.map +1 -1
- package/dist/cli/vc-agent.js +1 -1
- package/dist/cli/vc-agent.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2269 -716
- package/dist/cli.js.map +1 -1
- package/dist/codex-app-runner.js +1630 -125
- package/dist/codex-app-runner.js.map +1 -1
- package/dist/core/ask-api.d.ts +8 -1
- package/dist/core/ask-api.d.ts.map +1 -1
- package/dist/core/ask-api.js +18 -0
- package/dist/core/ask-api.js.map +1 -1
- package/dist/core/ask-broker.d.ts +20 -0
- package/dist/core/ask-broker.d.ts.map +1 -1
- package/dist/core/ask-broker.js +467 -47
- package/dist/core/ask-broker.js.map +1 -1
- package/dist/core/ask-persist-store.d.ts +126 -0
- package/dist/core/ask-persist-store.d.ts.map +1 -0
- package/dist/core/ask-persist-store.js +188 -0
- package/dist/core/ask-persist-store.js.map +1 -0
- package/dist/core/ask-types.d.ts +80 -0
- package/dist/core/ask-types.d.ts.map +1 -1
- package/dist/core/ask-types.js +62 -0
- package/dist/core/ask-types.js.map +1 -1
- package/dist/core/cli-session-id-publisher.d.ts +20 -0
- package/dist/core/cli-session-id-publisher.d.ts.map +1 -0
- package/dist/core/cli-session-id-publisher.js +21 -0
- package/dist/core/cli-session-id-publisher.js.map +1 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +427 -255
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/cost-calculator.d.ts +13 -0
- package/dist/core/cost-calculator.d.ts.map +1 -1
- package/dist/core/cost-calculator.js +385 -26
- package/dist/core/cost-calculator.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +9 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +842 -288
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/explicit-session-backing-cleanup.d.ts +49 -0
- package/dist/core/explicit-session-backing-cleanup.d.ts.map +1 -0
- package/dist/core/explicit-session-backing-cleanup.js +76 -0
- package/dist/core/explicit-session-backing-cleanup.js.map +1 -0
- package/dist/core/idle-worker-sweeper.d.ts +20 -0
- package/dist/core/idle-worker-sweeper.d.ts.map +1 -1
- package/dist/core/idle-worker-sweeper.js +18 -0
- package/dist/core/idle-worker-sweeper.js.map +1 -1
- package/dist/core/inflight-input-tracker.d.ts +10 -0
- package/dist/core/inflight-input-tracker.d.ts.map +1 -1
- package/dist/core/inflight-input-tracker.js +7 -0
- package/dist/core/inflight-input-tracker.js.map +1 -1
- package/dist/core/managed-origin-attestation.d.ts +51 -0
- package/dist/core/managed-origin-attestation.d.ts.map +1 -0
- package/dist/core/managed-origin-attestation.js +208 -0
- package/dist/core/managed-origin-attestation.js.map +1 -0
- package/dist/core/managed-origin-capability.d.ts +62 -3
- package/dist/core/managed-origin-capability.d.ts.map +1 -1
- package/dist/core/managed-origin-capability.js +419 -9
- package/dist/core/managed-origin-capability.js.map +1 -1
- package/dist/core/pending-repo-journal.d.ts +6 -0
- package/dist/core/pending-repo-journal.d.ts.map +1 -0
- package/dist/core/pending-repo-journal.js +85 -0
- package/dist/core/pending-repo-journal.js.map +1 -0
- package/dist/core/persistent-backend.d.ts +6 -1
- package/dist/core/persistent-backend.d.ts.map +1 -1
- package/dist/core/persistent-backend.js +12 -6
- package/dist/core/persistent-backend.js.map +1 -1
- package/dist/core/process-start-identity.d.ts +3 -0
- package/dist/core/process-start-identity.d.ts.map +1 -0
- package/dist/core/process-start-identity.js +62 -0
- package/dist/core/process-start-identity.js.map +1 -0
- package/dist/core/raw-command-writer.d.ts +34 -0
- package/dist/core/raw-command-writer.d.ts.map +1 -0
- package/dist/core/raw-command-writer.js +48 -0
- package/dist/core/raw-command-writer.js.map +1 -0
- package/dist/core/reply-target.d.ts +85 -22
- package/dist/core/reply-target.d.ts.map +1 -1
- package/dist/core/reply-target.js +267 -32
- package/dist/core/reply-target.js.map +1 -1
- package/dist/core/resource-monitor/runtime.d.ts.map +1 -1
- package/dist/core/resource-monitor/runtime.js +4 -1
- package/dist/core/resource-monitor/runtime.js.map +1 -1
- package/dist/core/restart-report.d.ts +6 -1
- package/dist/core/restart-report.d.ts.map +1 -1
- package/dist/core/restart-report.js +22 -4
- package/dist/core/restart-report.js.map +1 -1
- package/dist/core/riff-shutdown-detach.d.ts +164 -0
- package/dist/core/riff-shutdown-detach.d.ts.map +1 -0
- package/dist/core/riff-shutdown-detach.js +787 -0
- package/dist/core/riff-shutdown-detach.js.map +1 -0
- package/dist/core/riff-worker-shutdown-readiness.d.ts +17 -0
- package/dist/core/riff-worker-shutdown-readiness.d.ts.map +1 -0
- package/dist/core/riff-worker-shutdown-readiness.js +21 -0
- package/dist/core/riff-worker-shutdown-readiness.js.map +1 -0
- package/dist/core/session-manager.d.ts +6 -6
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +1227 -594
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-marker.d.ts +1 -1
- package/dist/core/session-marker.d.ts.map +1 -1
- package/dist/core/session-marker.js +2 -2
- package/dist/core/session-marker.js.map +1 -1
- package/dist/core/session-mutation-guard.d.ts +17 -0
- package/dist/core/session-mutation-guard.d.ts.map +1 -0
- package/dist/core/session-mutation-guard.js +33 -0
- package/dist/core/session-mutation-guard.js.map +1 -0
- package/dist/core/shutdown-budgets.d.ts +27 -0
- package/dist/core/shutdown-budgets.d.ts.map +1 -0
- package/dist/core/shutdown-budgets.js +47 -0
- package/dist/core/shutdown-budgets.js.map +1 -0
- package/dist/core/supervisor-shutdown-ipc.d.ts +10 -0
- package/dist/core/supervisor-shutdown-ipc.d.ts.map +1 -0
- package/dist/core/supervisor-shutdown-ipc.js +10 -0
- package/dist/core/supervisor-shutdown-ipc.js.map +1 -0
- package/dist/core/supervisor-shutdown-protocol.d.ts +16 -0
- package/dist/core/supervisor-shutdown-protocol.d.ts.map +1 -0
- package/dist/core/supervisor-shutdown-protocol.js +16 -0
- package/dist/core/supervisor-shutdown-protocol.js.map +1 -0
- package/dist/core/trigger-final-suppression.d.ts +4 -0
- package/dist/core/trigger-final-suppression.d.ts.map +1 -1
- package/dist/core/trigger-final-suppression.js +6 -11
- package/dist/core/trigger-final-suppression.js.map +1 -1
- package/dist/core/trigger-session.d.ts +70 -0
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +923 -145
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +94 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +9 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +194 -13
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +2597 -547
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts +76 -2
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +2255 -773
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +20 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +24 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/feed-groups.d.ts +17 -0
- package/dist/dashboard/feed-groups.d.ts.map +1 -0
- package/dist/dashboard/feed-groups.js +75 -0
- package/dist/dashboard/feed-groups.js.map +1 -0
- package/dist/dashboard/session-card-model.d.ts +1 -1
- package/dist/dashboard/session-card-model.d.ts.map +1 -1
- package/dist/dashboard/session-card-model.js +8 -5
- package/dist/dashboard/session-card-model.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +4 -0
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +139 -46
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +4 -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/connectors-page.d.ts.map +1 -1
- package/dist/dashboard/web/connectors-page.js +40 -3
- package/dist/dashboard/web/connectors-page.js.map +1 -1
- package/dist/dashboard/web/groups-page.d.ts.map +1 -1
- package/dist/dashboard/web/groups-page.js +95 -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 +86 -40
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/kanban-model.js +1 -1
- package/dist/dashboard/web/kanban-model.js.map +1 -1
- package/dist/dashboard/web/overview-page.d.ts.map +1 -1
- package/dist/dashboard/web/overview-page.js +3 -2
- package/dist/dashboard/web/overview-page.js.map +1 -1
- package/dist/dashboard/web/overview.d.ts +3 -0
- package/dist/dashboard/web/overview.d.ts.map +1 -1
- package/dist/dashboard/web/overview.js +2 -0
- package/dist/dashboard/web/overview.js.map +1 -1
- package/dist/dashboard/web/sessions-kanban.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-kanban.js +2 -0
- 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 +103 -2
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +3 -2
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/ui.d.ts +8 -0
- package/dist/dashboard/web/ui.d.ts.map +1 -1
- package/dist/dashboard/web/ui.js +15 -0
- package/dist/dashboard/web/ui.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-GED4FEMS.js +10 -0
- package/dist/dashboard-web/chunks/chunk-6J5LS5AU.js +51 -0
- package/dist/dashboard-web/chunks/{chunk-ZU3UUKAR.js → chunk-HK7NIBJG.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-3UE7S77S.js → chunk-KRHWZEXU.js} +1 -1
- package/dist/dashboard-web/chunks/chunk-VBXJOJFH.js +1 -0
- package/dist/dashboard-web/chunks/connectors-page-EELM66G3.js +1 -0
- package/dist/dashboard-web/chunks/groups-page-WKUV6E42.js +8 -0
- package/dist/dashboard-web/chunks/{insights-page-T2ZX2RWL.js → insights-page-54UBPPOC.js} +1 -1
- package/dist/dashboard-web/chunks/{monitor-room-YNVBAGR6.js → monitor-room-PORHVKRH.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-LHM55YWN.js → monitoring-page-XGA7AXNL.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-ATXVP7PW.js → office-page-AO3AFG45.js} +1 -1
- package/dist/dashboard-web/chunks/overview-page-F5BUTYR6.js +1 -0
- package/dist/dashboard-web/chunks/{roles-page-56VWM6CD.js → roles-page-M5GIASJU.js} +1 -1
- package/dist/dashboard-web/chunks/{schedules-page-S5K6U4EO.js → schedules-page-O232WQRR.js} +1 -1
- package/dist/dashboard-web/chunks/sessions-page-YFRPSTIL.js +6 -0
- package/dist/dashboard-web/chunks/{settings-page-QUZDQARR.js → settings-page-OJAZLZQT.js} +1 -1
- package/dist/dashboard-web/chunks/{skills-page-IFY7AEND.js → skills-page-EFZQPWV4.js} +1 -1
- package/dist/dashboard-web/chunks/{team-federation-page-C7FJQPFZ.js → team-federation-page-ZJVAP7HW.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-2LMF3WWR.js → v3-page-GYWTRGO2.js} +1 -1
- package/dist/dashboard-web/chunks/{whiteboards-page-3XC6QRMY.js → whiteboards-page-MSDAG6P7.js} +1 -1
- package/dist/dashboard-web/style.css +255 -0
- package/dist/dashboard.js +172 -7
- package/dist/dashboard.js.map +1 -1
- package/dist/desktop/renderer/app.js +21 -0
- package/dist/desktop/renderer/app.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +35 -9
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +35 -9
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/ask-card.d.ts +23 -0
- package/dist/im/lark/ask-card.d.ts.map +1 -1
- package/dist/im/lark/ask-card.js +94 -4
- package/dist/im/lark/ask-card.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +6 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +112 -15
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +1 -1
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +496 -310
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +7 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +39 -26
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/doc-comment.d.ts +7 -4
- package/dist/im/lark/doc-comment.d.ts.map +1 -1
- package/dist/im/lark/doc-comment.js +19 -6
- package/dist/im/lark/doc-comment.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +16 -0
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +88 -23
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/forward-followup-content.d.ts.map +1 -1
- package/dist/im/lark/forward-followup-content.js +9 -1
- package/dist/im/lark/forward-followup-content.js.map +1 -1
- package/dist/im/lark/grant-command.d.ts.map +1 -1
- package/dist/im/lark/grant-command.js +7 -4
- package/dist/im/lark/grant-command.js.map +1 -1
- package/dist/im/lark/md-card.d.ts +11 -0
- package/dist/im/lark/md-card.d.ts.map +1 -1
- package/dist/im/lark/md-card.js +46 -0
- package/dist/im/lark/md-card.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts +23 -2
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +58 -3
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/overview-card.d.ts.map +1 -1
- package/dist/im/lark/overview-card.js +2 -1
- package/dist/im/lark/overview-card.js.map +1 -1
- package/dist/services/async-trigger-state.d.ts +6 -1
- package/dist/services/async-trigger-state.d.ts.map +1 -1
- package/dist/services/async-trigger-state.js +21 -0
- package/dist/services/async-trigger-state.js.map +1 -1
- package/dist/services/async-trigger-store.d.ts +23 -1
- package/dist/services/async-trigger-store.d.ts.map +1 -1
- package/dist/services/async-trigger-store.js +96 -13
- package/dist/services/async-trigger-store.js.map +1 -1
- package/dist/services/bridge-fallback-gate.d.ts +40 -0
- package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
- package/dist/services/bridge-fallback-gate.js +124 -24
- package/dist/services/bridge-fallback-gate.js.map +1 -1
- package/dist/services/codex-app-runner-protocol.d.ts +8 -0
- package/dist/services/codex-app-runner-protocol.d.ts.map +1 -1
- package/dist/services/codex-app-runner-protocol.js +9 -2
- package/dist/services/codex-app-runner-protocol.js.map +1 -1
- package/dist/services/codex-bridge-queue.d.ts +1 -0
- package/dist/services/codex-bridge-queue.d.ts.map +1 -1
- package/dist/services/codex-bridge-queue.js +1 -0
- package/dist/services/codex-bridge-queue.js.map +1 -1
- package/dist/services/codex-service-tier.d.ts +2 -0
- package/dist/services/codex-service-tier.d.ts.map +1 -1
- package/dist/services/codex-service-tier.js +1 -0
- package/dist/services/codex-service-tier.js.map +1 -1
- package/dist/services/codex-transcript.d.ts +27 -0
- package/dist/services/codex-transcript.d.ts.map +1 -1
- package/dist/services/codex-transcript.js +340 -2
- package/dist/services/codex-transcript.js.map +1 -1
- package/dist/services/grant-policy.d.ts +3 -0
- package/dist/services/grant-policy.d.ts.map +1 -1
- package/dist/services/grant-policy.js +5 -0
- package/dist/services/grant-policy.js.map +1 -1
- package/dist/services/grant-prefs-store.d.ts +6 -3
- package/dist/services/grant-prefs-store.d.ts.map +1 -1
- package/dist/services/grant-prefs-store.js +42 -12
- package/dist/services/grant-prefs-store.js.map +1 -1
- package/dist/services/hook-runner.d.ts +20 -1
- package/dist/services/hook-runner.d.ts.map +1 -1
- package/dist/services/hook-runner.js +17 -13
- package/dist/services/hook-runner.js.map +1 -1
- package/dist/services/idempotency-store.d.ts +134 -0
- package/dist/services/idempotency-store.d.ts.map +1 -0
- package/dist/services/idempotency-store.js +299 -0
- package/dist/services/idempotency-store.js.map +1 -0
- package/dist/services/jsonl-cursor.d.ts +7 -3
- package/dist/services/jsonl-cursor.d.ts.map +1 -1
- package/dist/services/jsonl-cursor.js +55 -31
- package/dist/services/jsonl-cursor.js.map +1 -1
- package/dist/services/open-platform-rename.d.ts.map +1 -1
- package/dist/services/open-platform-rename.js +1 -108
- package/dist/services/open-platform-rename.js.map +1 -1
- package/dist/services/restart-intent-store.d.ts +22 -2
- package/dist/services/restart-intent-store.d.ts.map +1 -1
- package/dist/services/restart-intent-store.js +160 -18
- package/dist/services/restart-intent-store.js.map +1 -1
- package/dist/services/sandbox-store.d.ts +22 -0
- package/dist/services/sandbox-store.d.ts.map +1 -1
- package/dist/services/sandbox-store.js +37 -8
- package/dist/services/sandbox-store.js.map +1 -1
- package/dist/services/send-policy.d.ts +41 -33
- package/dist/services/send-policy.d.ts.map +1 -1
- package/dist/services/send-policy.js +39 -40
- package/dist/services/send-policy.js.map +1 -1
- package/dist/services/session-store.d.ts +66 -0
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +403 -67
- package/dist/services/session-store.js.map +1 -1
- package/dist/services/submit-notification.d.ts +3 -0
- package/dist/services/submit-notification.d.ts.map +1 -0
- package/dist/services/submit-notification.js +15 -0
- package/dist/services/submit-notification.js.map +1 -0
- package/dist/services/traex-transcript.d.ts +25 -2
- package/dist/services/traex-transcript.d.ts.map +1 -1
- package/dist/services/traex-transcript.js +133 -1
- package/dist/services/traex-transcript.js.map +1 -1
- package/dist/services/trigger-types.d.ts +13 -1
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js +39 -0
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/services/voice/index.d.ts +3 -2
- package/dist/services/voice/index.d.ts.map +1 -1
- package/dist/services/voice/index.js +5 -5
- package/dist/services/voice/index.js.map +1 -1
- package/dist/services/voice/openai.d.ts +2 -1
- package/dist/services/voice/openai.d.ts.map +1 -1
- package/dist/services/voice/openai.js +2 -1
- package/dist/services/voice/openai.js.map +1 -1
- package/dist/services/voice/sami.d.ts +5 -2
- package/dist/services/voice/sami.d.ts.map +1 -1
- package/dist/services/voice/sami.js +19 -4
- package/dist/services/voice/sami.js.map +1 -1
- package/dist/setup/bots-store.d.ts.map +1 -1
- package/dist/setup/bots-store.js +10 -4
- package/dist/setup/bots-store.js.map +1 -1
- package/dist/setup/lark-scopes.json +2 -0
- package/dist/setup/open-platform-automation.d.ts +9 -1
- package/dist/setup/open-platform-automation.d.ts.map +1 -1
- package/dist/setup/open-platform-automation.js +44 -15
- package/dist/setup/open-platform-automation.js.map +1 -1
- package/dist/setup/open-platform-visibility.d.ts +42 -0
- package/dist/setup/open-platform-visibility.d.ts.map +1 -0
- package/dist/setup/open-platform-visibility.js +133 -0
- package/dist/setup/open-platform-visibility.js.map +1 -0
- package/dist/setup/verify-permissions.d.ts.map +1 -1
- package/dist/setup/verify-permissions.js +6 -0
- package/dist/setup/verify-permissions.js.map +1 -1
- package/dist/skills/definitions.js +2 -2
- package/dist/types.d.ts +290 -13
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/bot-routing.d.ts +1 -0
- package/dist/utils/bot-routing.d.ts.map +1 -1
- package/dist/utils/bot-routing.js +1 -1
- package/dist/utils/bot-routing.js.map +1 -1
- package/dist/utils/child-env.d.ts +1 -1
- package/dist/utils/child-env.d.ts.map +1 -1
- package/dist/utils/child-env.js +9 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/utils/codex-app-control.d.ts +438 -0
- package/dist/utils/codex-app-control.d.ts.map +1 -0
- package/dist/utils/codex-app-control.js +2336 -0
- package/dist/utils/codex-app-control.js.map +1 -0
- package/dist/utils/codex-app-dispatch-ledger.d.ts +78 -0
- package/dist/utils/codex-app-dispatch-ledger.d.ts.map +1 -0
- package/dist/utils/codex-app-dispatch-ledger.js +149 -0
- package/dist/utils/codex-app-dispatch-ledger.js.map +1 -0
- package/dist/utils/codex-app-turn-dispatch.d.ts +77 -0
- package/dist/utils/codex-app-turn-dispatch.d.ts.map +1 -0
- package/dist/utils/codex-app-turn-dispatch.js +141 -0
- package/dist/utils/codex-app-turn-dispatch.js.map +1 -0
- package/dist/utils/codex-app-turn-liveness.d.ts +125 -0
- package/dist/utils/codex-app-turn-liveness.d.ts.map +1 -0
- package/dist/utils/codex-app-turn-liveness.js +301 -0
- package/dist/utils/codex-app-turn-liveness.js.map +1 -0
- package/dist/utils/idle-detector.d.ts +12 -0
- package/dist/utils/idle-detector.d.ts.map +1 -1
- package/dist/utils/idle-detector.js +19 -0
- package/dist/utils/idle-detector.js.map +1 -1
- package/dist/utils/input-gate.d.ts +87 -0
- package/dist/utils/input-gate.d.ts.map +1 -1
- package/dist/utils/input-gate.js +86 -0
- package/dist/utils/input-gate.js.map +1 -1
- package/dist/utils/lark-upload.d.ts.map +1 -1
- package/dist/utils/lark-upload.js +52 -1
- package/dist/utils/lark-upload.js.map +1 -1
- package/dist/utils/pending-input-queue.d.ts +15 -0
- package/dist/utils/pending-input-queue.d.ts.map +1 -1
- package/dist/utils/pending-input-queue.js +5 -1
- package/dist/utils/pending-input-queue.js.map +1 -1
- package/dist/utils/user-token.d.ts.map +1 -1
- package/dist/utils/user-token.js +53 -2
- package/dist/utils/user-token.js.map +1 -1
- package/dist/worker-ipc-preload.d.ts +12 -0
- package/dist/worker-ipc-preload.d.ts.map +1 -0
- package/dist/worker-ipc-preload.js +44 -0
- package/dist/worker-ipc-preload.js.map +1 -0
- package/dist/worker.js +2604 -325
- package/dist/worker.js.map +1 -1
- package/dist/workflows/v3/session-relay-client.d.ts +1 -0
- package/dist/workflows/v3/session-relay-client.d.ts.map +1 -1
- package/dist/workflows/v3/session-relay-client.js +12 -3
- package/dist/workflows/v3/session-relay-client.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-LTVOUDIS.js +0 -10
- package/dist/dashboard-web/chunks/chunk-DCS35UMD.js +0 -1
- package/dist/dashboard-web/chunks/chunk-MFQPCVHY.js +0 -51
- package/dist/dashboard-web/chunks/connectors-page-5KWDIMKC.js +0 -1
- package/dist/dashboard-web/chunks/groups-page-FHOKYXMW.js +0 -8
- package/dist/dashboard-web/chunks/overview-page-QYI3KBDD.js +0 -1
- package/dist/dashboard-web/chunks/sessions-page-WBP4N7D2.js +0 -6
|
@@ -0,0 +1,787 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import * as sessionStore from '../services/session-store.js';
|
|
3
|
+
import { logger } from '../utils/logger.js';
|
|
4
|
+
import { RIFF_ADMISSION_RESTORE_TIMEOUT_MS, RIFF_SHUTDOWN_BATCH_PERSIST_TIMEOUT_MS, RIFF_SHUTDOWN_DRAIN_TIMEOUT_MS, RIFF_SHUTDOWN_INITIAL_SNAPSHOT_TIMEOUT_MS, } from './shutdown-budgets.js';
|
|
5
|
+
/** The active registry can retain multiple aliases to one exact runtime object
|
|
6
|
+
* after transfer/restore. Process that object once, but refuse two distinct
|
|
7
|
+
* objects claiming the same durable session id: there is no unique generation
|
|
8
|
+
* that shutdown can safely fence or retire. */
|
|
9
|
+
export function collectUniqueDaemonShutdownSessions(candidates) {
|
|
10
|
+
const seenObjects = new Set();
|
|
11
|
+
const bySessionId = new Map();
|
|
12
|
+
const sessions = [];
|
|
13
|
+
for (const ds of candidates) {
|
|
14
|
+
if (seenObjects.has(ds))
|
|
15
|
+
continue;
|
|
16
|
+
seenObjects.add(ds);
|
|
17
|
+
const sessionId = ds.session.sessionId;
|
|
18
|
+
const existing = bySessionId.get(sessionId);
|
|
19
|
+
if (existing && existing !== ds) {
|
|
20
|
+
return {
|
|
21
|
+
ok: false,
|
|
22
|
+
sessionId,
|
|
23
|
+
error: `distinct daemon session generations share session id ${sessionId}`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
bySessionId.set(sessionId, ds);
|
|
27
|
+
sessions.push(ds);
|
|
28
|
+
}
|
|
29
|
+
return { ok: true, sessions };
|
|
30
|
+
}
|
|
31
|
+
function label(ds) {
|
|
32
|
+
return ds.session.sessionId.slice(0, 8);
|
|
33
|
+
}
|
|
34
|
+
function workerHasExited(worker) {
|
|
35
|
+
return (worker.exitCode !== null && worker.exitCode !== undefined)
|
|
36
|
+
|| (worker.signalCode !== null && worker.signalCode !== undefined);
|
|
37
|
+
}
|
|
38
|
+
function send(worker, message) {
|
|
39
|
+
try {
|
|
40
|
+
worker.send(message);
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Attach the exact-worker response/exit listeners before publishing a phase
|
|
48
|
+
* request. Results from another generation/session/request are inert. */
|
|
49
|
+
function requestPhase(ds, worker, requestId, phase, timeoutMs) {
|
|
50
|
+
return new Promise(resolve => {
|
|
51
|
+
let settled = false;
|
|
52
|
+
const finish = (result) => {
|
|
53
|
+
if (settled)
|
|
54
|
+
return;
|
|
55
|
+
settled = true;
|
|
56
|
+
clearTimeout(timer);
|
|
57
|
+
worker.removeListener('message', onMessage);
|
|
58
|
+
worker.removeListener('exit', onExit);
|
|
59
|
+
resolve(result);
|
|
60
|
+
};
|
|
61
|
+
const onMessage = (raw) => {
|
|
62
|
+
const msg = raw;
|
|
63
|
+
if (msg?.type !== 'riff_shutdown_result'
|
|
64
|
+
|| msg.requestId !== requestId
|
|
65
|
+
|| msg.phase !== phase)
|
|
66
|
+
return;
|
|
67
|
+
if (ds.worker !== worker) {
|
|
68
|
+
finish({ ok: false, taskId: msg.taskId, error: 'stale_worker_generation' });
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
finish({
|
|
72
|
+
ok: msg.ok,
|
|
73
|
+
taskId: msg.taskId,
|
|
74
|
+
...(msg.error ? { error: msg.error } : {}),
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
const onExit = () => finish({
|
|
78
|
+
ok: false,
|
|
79
|
+
taskId: null,
|
|
80
|
+
error: `worker_exited_during_shutdown_${phase}`,
|
|
81
|
+
});
|
|
82
|
+
const timer = setTimeout(() => finish({
|
|
83
|
+
ok: false,
|
|
84
|
+
taskId: null,
|
|
85
|
+
error: `riff_shutdown_${phase}_timeout`,
|
|
86
|
+
}), timeoutMs);
|
|
87
|
+
timer.unref?.();
|
|
88
|
+
worker.on('message', onMessage);
|
|
89
|
+
worker.once('exit', onExit);
|
|
90
|
+
const message = phase === 'prepare'
|
|
91
|
+
? { type: 'riff_shutdown_prepare', requestId }
|
|
92
|
+
: { type: 'riff_shutdown_abort', requestId };
|
|
93
|
+
if (!send(worker, message)) {
|
|
94
|
+
finish({ ok: false, taskId: null, error: `riff_shutdown_${phase}_send_failed` });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function clearWorkerOwnership(ds, worker) {
|
|
99
|
+
if (ds.worker !== worker)
|
|
100
|
+
return;
|
|
101
|
+
ds.worker = null;
|
|
102
|
+
ds.workerPort = null;
|
|
103
|
+
ds.workerToken = null;
|
|
104
|
+
ds.workerViewToken = null;
|
|
105
|
+
ds.managedTurnOrigin = undefined;
|
|
106
|
+
ds.riffShutdownState = undefined;
|
|
107
|
+
}
|
|
108
|
+
function clearWorkerlessShutdownState(ds, requestId) {
|
|
109
|
+
if (ds.riffShutdownState?.requestId !== requestId)
|
|
110
|
+
return;
|
|
111
|
+
if (ds.worker)
|
|
112
|
+
return;
|
|
113
|
+
ds.riffShutdownState = undefined;
|
|
114
|
+
}
|
|
115
|
+
/** Daemon-owned accepted input has not crossed worker IPC yet. The bot-wide
|
|
116
|
+
* mutation lease blocks new admissions, but these older buffers must also be
|
|
117
|
+
* empty before a worker can be detached. */
|
|
118
|
+
function daemonInputBlocker(ds) {
|
|
119
|
+
const parts = [];
|
|
120
|
+
if (ds.pendingRepoCommitInFlight || ds.worktreeCreating)
|
|
121
|
+
parts.push('initial_start=1');
|
|
122
|
+
if (ds.pendingPrompt)
|
|
123
|
+
parts.push('prompt=1');
|
|
124
|
+
if (ds.pendingRawInput)
|
|
125
|
+
parts.push('raw=1');
|
|
126
|
+
if (ds.pendingFollowUpInput)
|
|
127
|
+
parts.push('raw_followup=1');
|
|
128
|
+
if ((ds.pendingFollowUps?.length ?? 0) > 0) {
|
|
129
|
+
parts.push(`followups=${ds.pendingFollowUps.length}`);
|
|
130
|
+
}
|
|
131
|
+
if (ds.session.queued)
|
|
132
|
+
parts.push('queued=1');
|
|
133
|
+
return parts.length > 0 ? parts.join(',') : null;
|
|
134
|
+
}
|
|
135
|
+
async function abortWorkerPreparation(ds, worker, requestId, timeoutMs, lineageVerified = false, exactTask) {
|
|
136
|
+
if (workerHasExited(worker)) {
|
|
137
|
+
if (ds.worker && ds.worker !== worker) {
|
|
138
|
+
return {
|
|
139
|
+
ok: false,
|
|
140
|
+
taskId: ds.riffShutdownState?.taskId ?? ds.session.riffParentTaskId ?? null,
|
|
141
|
+
error: 'new_worker_generation',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (lineageVerified) {
|
|
145
|
+
if (ds.worker === worker)
|
|
146
|
+
clearWorkerOwnership(ds, worker);
|
|
147
|
+
else
|
|
148
|
+
clearWorkerlessShutdownState(ds, requestId);
|
|
149
|
+
return { ok: true, taskId: ds.session.riffParentTaskId ?? null };
|
|
150
|
+
}
|
|
151
|
+
// No backend remains to ACK admission restoration and the drained lineage
|
|
152
|
+
// was not durably recovered. Retain a daemon-side fence instead of claiming
|
|
153
|
+
// rollback success and permitting a stale-lineage replacement.
|
|
154
|
+
if (!ds.riffShutdownState || ds.riffShutdownState.requestId === requestId) {
|
|
155
|
+
ds.riffShutdownState = {
|
|
156
|
+
phase: 'prepared',
|
|
157
|
+
requestId,
|
|
158
|
+
taskId: ds.session.riffParentTaskId ?? null,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
ok: false,
|
|
163
|
+
taskId: ds.session.riffParentTaskId ?? null,
|
|
164
|
+
error: 'worker_exited_before_admission_restore',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (ds.worker !== worker) {
|
|
168
|
+
return {
|
|
169
|
+
ok: false,
|
|
170
|
+
taskId: ds.riffShutdownState?.taskId ?? ds.session.riffParentTaskId ?? null,
|
|
171
|
+
error: 'new_worker_generation',
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const result = await requestPhase(ds, worker, requestId, 'abort', timeoutMs);
|
|
175
|
+
if (result.ok && exactTask && result.taskId !== exactTask.taskId) {
|
|
176
|
+
return {
|
|
177
|
+
ok: false,
|
|
178
|
+
taskId: result.taskId,
|
|
179
|
+
error: `abort_task_lineage_mismatch:expected=${exactTask.taskId ?? 'none'},`
|
|
180
|
+
+ `actual=${result.taskId ?? 'none'}`,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (result.ok && ds.riffShutdownState?.requestId === requestId) {
|
|
184
|
+
ds.riffShutdownState = undefined;
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
}
|
|
188
|
+
function unfencedRefusal(ds, error, requestId) {
|
|
189
|
+
return {
|
|
190
|
+
ok: false,
|
|
191
|
+
fence: 'none',
|
|
192
|
+
...(requestId ? { requestId } : {}),
|
|
193
|
+
taskId: ds.session.riffParentTaskId ?? null,
|
|
194
|
+
error,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** Phase 1: fence one exact Riff generation and drain task-id materialization.
|
|
198
|
+
* Nothing exits or restores admission here. A failure after the prepare send
|
|
199
|
+
* returns an exact possibly-fenced handle so all peers can be restored in one
|
|
200
|
+
* concurrent fleet wave rather than serial drain+abort chains. */
|
|
201
|
+
export async function prepareRiffSessionForShutdown(ds, options = {}) {
|
|
202
|
+
const frozenBackend = ds.initConfig?.backendType ?? ds.session.backendType;
|
|
203
|
+
if (frozenBackend !== 'riff') {
|
|
204
|
+
return unfencedRefusal(ds, 'not_riff_backend');
|
|
205
|
+
}
|
|
206
|
+
if (ds.riffCloseState || ds.riffShutdownState) {
|
|
207
|
+
return unfencedRefusal(ds, ds.riffCloseState ? 'explicit_close_in_progress' : 'shutdown_detach_in_progress');
|
|
208
|
+
}
|
|
209
|
+
const daemonBlockerBeforePrepare = daemonInputBlocker(ds);
|
|
210
|
+
if (daemonBlockerBeforePrepare) {
|
|
211
|
+
return unfencedRefusal(ds, `daemon_inputs_not_drained:${daemonBlockerBeforePrepare}`);
|
|
212
|
+
}
|
|
213
|
+
const runtimeTaskIdAtPrepare = ds.session.riffParentTaskId ?? null;
|
|
214
|
+
let durableSnapshot = options.durableSnapshot;
|
|
215
|
+
if (!durableSnapshot) {
|
|
216
|
+
try {
|
|
217
|
+
[durableSnapshot] = sessionStore.getActiveRiffShutdownSnapshotsBatch([
|
|
218
|
+
ds.session.sessionId,
|
|
219
|
+
]);
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
return unfencedRefusal(ds, `durable_session_read_failed:${err instanceof Error ? err.message : String(err)}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (!durableSnapshot || durableSnapshot.sessionId !== ds.session.sessionId) {
|
|
226
|
+
return unfencedRefusal(ds, 'durable_session_snapshot_mismatch');
|
|
227
|
+
}
|
|
228
|
+
const durableTaskIdAtPrepare = durableSnapshot.taskId;
|
|
229
|
+
const runtimeOwner = {
|
|
230
|
+
pid: ds.session.pid ?? null,
|
|
231
|
+
larkAppId: ds.session.larkAppId ?? null,
|
|
232
|
+
backendType: ds.session.backendType ?? null,
|
|
233
|
+
};
|
|
234
|
+
const durableOwnerAtPrepare = durableSnapshot.owner;
|
|
235
|
+
if (durableOwnerAtPrepare.pid !== runtimeOwner.pid
|
|
236
|
+
|| durableOwnerAtPrepare.larkAppId !== runtimeOwner.larkAppId
|
|
237
|
+
|| durableOwnerAtPrepare.backendType !== runtimeOwner.backendType) {
|
|
238
|
+
return unfencedRefusal(ds, `durable_session_owner_mismatch:current=${JSON.stringify(durableOwnerAtPrepare)},`
|
|
239
|
+
+ `runtime=${JSON.stringify(runtimeOwner)}`);
|
|
240
|
+
}
|
|
241
|
+
const now = options.now ?? Date.now;
|
|
242
|
+
const abortReserveMs = options.abortTimeoutMs ?? RIFF_ADMISSION_RESTORE_TIMEOUT_MS;
|
|
243
|
+
let drainTimeoutMs = options.drainTimeoutMs ?? RIFF_SHUTDOWN_DRAIN_TIMEOUT_MS;
|
|
244
|
+
if (options.deadlineMs !== undefined) {
|
|
245
|
+
const availableDrainMs = options.deadlineMs
|
|
246
|
+
- now()
|
|
247
|
+
- RIFF_SHUTDOWN_BATCH_PERSIST_TIMEOUT_MS
|
|
248
|
+
- abortReserveMs;
|
|
249
|
+
if (availableDrainMs <= 0) {
|
|
250
|
+
return unfencedRefusal(ds, 'insufficient_abort_budget_before_fence');
|
|
251
|
+
}
|
|
252
|
+
drainTimeoutMs = Math.min(drainTimeoutMs, availableDrainMs);
|
|
253
|
+
}
|
|
254
|
+
const requestId = randomUUID();
|
|
255
|
+
const worker = ds.worker;
|
|
256
|
+
if (!worker || workerHasExited(worker)) {
|
|
257
|
+
// Retire a previously observed dead handle before installing the
|
|
258
|
+
// workerless fence. Any later non-null ds.worker is then unambiguously a
|
|
259
|
+
// new generation and cannot be blessed by this transaction.
|
|
260
|
+
if (worker)
|
|
261
|
+
clearWorkerOwnership(ds, worker);
|
|
262
|
+
// Workerless rows can still carry a newer runtime-only lineage after a
|
|
263
|
+
// failed ordinary riff_task_id save. Fence daemon admission now; phase 2
|
|
264
|
+
// performs an exact owner/lineage CAS and fresh disk verification.
|
|
265
|
+
ds.riffShutdownState = {
|
|
266
|
+
phase: 'prepared',
|
|
267
|
+
requestId,
|
|
268
|
+
taskId: ds.session.riffParentTaskId ?? null,
|
|
269
|
+
};
|
|
270
|
+
return {
|
|
271
|
+
ok: true,
|
|
272
|
+
fence: 'prepared',
|
|
273
|
+
requestId,
|
|
274
|
+
taskId: ds.session.riffParentTaskId ?? null,
|
|
275
|
+
runtimeTaskIdAtPrepare,
|
|
276
|
+
durableTaskIdAtPrepare,
|
|
277
|
+
durableOwnerAtPrepare,
|
|
278
|
+
lineageVerified: false,
|
|
279
|
+
worker: null,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
ds.riffShutdownState = { phase: 'preparing', requestId };
|
|
283
|
+
const prepared = await requestPhase(ds, worker, requestId, 'prepare', drainTimeoutMs);
|
|
284
|
+
if (!prepared.ok) {
|
|
285
|
+
// This exact response proves the worker refused before installing a backend
|
|
286
|
+
// fence. Every other failure has ambiguous fence state and requires a
|
|
287
|
+
// positive admission-restored ACK.
|
|
288
|
+
const unfencedWorkerRefusal = prepared.error === 'explicit_close_in_progress'
|
|
289
|
+
|| prepared.error === 'not_riff_backend'
|
|
290
|
+
|| prepared.error === 'riff_shutdown_prepare_send_failed'
|
|
291
|
+
|| prepared.error?.startsWith('worker_inputs_not_drained:') === true;
|
|
292
|
+
if (unfencedWorkerRefusal && ds.riffShutdownState?.requestId === requestId) {
|
|
293
|
+
ds.riffShutdownState = undefined;
|
|
294
|
+
}
|
|
295
|
+
if (unfencedWorkerRefusal) {
|
|
296
|
+
return {
|
|
297
|
+
ok: false,
|
|
298
|
+
fence: 'none',
|
|
299
|
+
requestId,
|
|
300
|
+
taskId: prepared.taskId,
|
|
301
|
+
error: prepared.error ?? 'riff_shutdown_prepare_failed',
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
ok: false,
|
|
306
|
+
fence: 'possible',
|
|
307
|
+
requestId,
|
|
308
|
+
taskId: prepared.taskId,
|
|
309
|
+
error: prepared.error ?? 'riff_shutdown_prepare_failed',
|
|
310
|
+
worker,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
if (ds.worker !== worker) {
|
|
314
|
+
return {
|
|
315
|
+
ok: false,
|
|
316
|
+
fence: 'possible',
|
|
317
|
+
requestId,
|
|
318
|
+
taskId: prepared.taskId,
|
|
319
|
+
error: 'stale_worker_generation',
|
|
320
|
+
worker,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
ds.riffShutdownState = { phase: 'prepared', requestId, taskId: prepared.taskId };
|
|
324
|
+
// Worker events can release a queued-activation journal/tail independently
|
|
325
|
+
// of bot-turn admission. Re-sample after drain so commit cannot strand input.
|
|
326
|
+
const daemonBlockerAfterPrepare = daemonInputBlocker(ds);
|
|
327
|
+
if (daemonBlockerAfterPrepare) {
|
|
328
|
+
return {
|
|
329
|
+
ok: false,
|
|
330
|
+
fence: 'possible',
|
|
331
|
+
requestId,
|
|
332
|
+
taskId: prepared.taskId,
|
|
333
|
+
error: `daemon_inputs_not_drained:${daemonBlockerAfterPrepare}`,
|
|
334
|
+
worker,
|
|
335
|
+
expectedAbortTaskId: prepared.taskId,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
ok: true,
|
|
340
|
+
fence: 'prepared',
|
|
341
|
+
requestId,
|
|
342
|
+
taskId: prepared.taskId,
|
|
343
|
+
runtimeTaskIdAtPrepare,
|
|
344
|
+
durableTaskIdAtPrepare,
|
|
345
|
+
durableOwnerAtPrepare,
|
|
346
|
+
lineageVerified: false,
|
|
347
|
+
worker,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
/** Take one fresh projection for the complete candidate set, then (and only
|
|
351
|
+
* then) publish prepare requests concurrently. */
|
|
352
|
+
export async function prepareRiffFleetForShutdown(candidates, options = {}) {
|
|
353
|
+
if (candidates.length === 0)
|
|
354
|
+
return [];
|
|
355
|
+
const now = options.now ?? Date.now;
|
|
356
|
+
const configuredSnapshotTimeout = options.snapshotTimeoutMs
|
|
357
|
+
?? RIFF_SHUTDOWN_INITIAL_SNAPSHOT_TIMEOUT_MS;
|
|
358
|
+
const remaining = options.deadlineMs === undefined
|
|
359
|
+
? configuredSnapshotTimeout
|
|
360
|
+
: Math.max(0, options.deadlineMs - now());
|
|
361
|
+
if (remaining <= 0) {
|
|
362
|
+
return candidates.map(ds => ({
|
|
363
|
+
ds,
|
|
364
|
+
result: unfencedRefusal(ds, 'shutdown_deadline_elapsed_before_initial_snapshot'),
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
367
|
+
let snapshots;
|
|
368
|
+
try {
|
|
369
|
+
snapshots = sessionStore.getActiveRiffShutdownSnapshotsBatch(candidates.map(ds => ds.session.sessionId), { maxWaitMs: Math.min(configuredSnapshotTimeout, remaining) });
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
const message = `initial_riff_snapshot_failed:${error instanceof Error
|
|
373
|
+
? error.message
|
|
374
|
+
: String(error)}`;
|
|
375
|
+
return candidates.map(ds => ({ ds, result: unfencedRefusal(ds, message) }));
|
|
376
|
+
}
|
|
377
|
+
const snapshotsBySession = new Map(snapshots.map(snapshot => [snapshot.sessionId, snapshot]));
|
|
378
|
+
return Promise.all(candidates.map(async (ds) => ({
|
|
379
|
+
ds,
|
|
380
|
+
result: await prepareRiffSessionForShutdown(ds, {
|
|
381
|
+
...options,
|
|
382
|
+
durableSnapshot: snapshotsBySession.get(ds.session.sessionId),
|
|
383
|
+
}),
|
|
384
|
+
})));
|
|
385
|
+
}
|
|
386
|
+
function validatePreparedRiffShutdownForPersistence(ds, prepared) {
|
|
387
|
+
if (ds.riffShutdownState?.requestId !== prepared.requestId
|
|
388
|
+
|| ds.riffShutdownState.phase !== 'prepared') {
|
|
389
|
+
return {
|
|
390
|
+
ok: false,
|
|
391
|
+
requestId: prepared.requestId,
|
|
392
|
+
taskId: prepared.taskId,
|
|
393
|
+
error: 'shutdown_prepare_ownership_lost',
|
|
394
|
+
rollbackDisposition: 'retain_fence',
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
const daemonBlocker = daemonInputBlocker(ds);
|
|
398
|
+
if (daemonBlocker) {
|
|
399
|
+
return {
|
|
400
|
+
ok: false,
|
|
401
|
+
requestId: prepared.requestId,
|
|
402
|
+
taskId: prepared.taskId,
|
|
403
|
+
error: `daemon_inputs_not_drained:${daemonBlocker}`,
|
|
404
|
+
rollbackDisposition: 'abort_safe',
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
if (prepared.worker) {
|
|
408
|
+
if (ds.worker !== prepared.worker || workerHasExited(prepared.worker)) {
|
|
409
|
+
return {
|
|
410
|
+
ok: false,
|
|
411
|
+
requestId: prepared.requestId,
|
|
412
|
+
taskId: prepared.taskId,
|
|
413
|
+
error: 'stale_worker_generation',
|
|
414
|
+
rollbackDisposition: 'retain_fence',
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
else if (ds.worker) {
|
|
419
|
+
return {
|
|
420
|
+
ok: false,
|
|
421
|
+
requestId: prepared.requestId,
|
|
422
|
+
taskId: prepared.taskId,
|
|
423
|
+
error: 'new_worker_generation',
|
|
424
|
+
rollbackDisposition: 'retain_fence',
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const runtimeOwner = {
|
|
428
|
+
pid: ds.session.pid ?? null,
|
|
429
|
+
larkAppId: ds.session.larkAppId ?? null,
|
|
430
|
+
backendType: ds.session.backendType ?? null,
|
|
431
|
+
};
|
|
432
|
+
if (runtimeOwner.pid !== prepared.durableOwnerAtPrepare.pid
|
|
433
|
+
|| runtimeOwner.larkAppId !== prepared.durableOwnerAtPrepare.larkAppId
|
|
434
|
+
|| runtimeOwner.backendType !== prepared.durableOwnerAtPrepare.backendType) {
|
|
435
|
+
return {
|
|
436
|
+
ok: false,
|
|
437
|
+
requestId: prepared.requestId,
|
|
438
|
+
taskId: prepared.taskId,
|
|
439
|
+
error: `runtime_owner_changed:${JSON.stringify(runtimeOwner)}`,
|
|
440
|
+
rollbackDisposition: 'retain_fence',
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
const runtimeTaskId = ds.session.riffParentTaskId ?? null;
|
|
444
|
+
const runtimeLineageExpected = prepared.worker
|
|
445
|
+
? runtimeTaskId === prepared.runtimeTaskIdAtPrepare || runtimeTaskId === prepared.taskId
|
|
446
|
+
: runtimeTaskId === prepared.runtimeTaskIdAtPrepare;
|
|
447
|
+
if (!runtimeLineageExpected) {
|
|
448
|
+
return {
|
|
449
|
+
ok: false,
|
|
450
|
+
requestId: prepared.requestId,
|
|
451
|
+
taskId: prepared.taskId,
|
|
452
|
+
error: `runtime_lineage_changed:${runtimeTaskId ?? 'none'}`,
|
|
453
|
+
rollbackDisposition: 'retain_fence',
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
return { ok: true };
|
|
457
|
+
}
|
|
458
|
+
/** Phase 2 fleet transaction: validate every runtime generation, then compare
|
|
459
|
+
* and publish all durable lineage rows with one lock and one rename. */
|
|
460
|
+
export function persistPreparedRiffShutdownFleet(entries, options = {}) {
|
|
461
|
+
if (entries.length === 0)
|
|
462
|
+
return { ok: true };
|
|
463
|
+
const now = options.now ?? Date.now;
|
|
464
|
+
const validationFailures = entries
|
|
465
|
+
.map(({ ds, result }) => ({
|
|
466
|
+
sessionId: ds.session.sessionId,
|
|
467
|
+
failure: validatePreparedRiffShutdownForPersistence(ds, result),
|
|
468
|
+
}))
|
|
469
|
+
.filter((entry) => !entry.failure.ok);
|
|
470
|
+
if (validationFailures.length > 0) {
|
|
471
|
+
const retainFencedSessionIds = validationFailures
|
|
472
|
+
.filter(({ failure }) => failure.rollbackDisposition === 'retain_fence')
|
|
473
|
+
.map(({ sessionId }) => sessionId);
|
|
474
|
+
return {
|
|
475
|
+
ok: false,
|
|
476
|
+
taskId: null,
|
|
477
|
+
error: `Riff fleet persistence preflight failed: ${validationFailures
|
|
478
|
+
.map(({ sessionId, failure }) => `${sessionId}:${failure.error}`)
|
|
479
|
+
.join(';')}`,
|
|
480
|
+
rollbackDisposition: retainFencedSessionIds.length > 0 ? 'retain_fence' : 'abort_safe',
|
|
481
|
+
sessionIds: validationFailures.map(({ sessionId }) => sessionId),
|
|
482
|
+
retainFencedSessionIds,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
const configuredPersistTimeout = options.persistTimeoutMs
|
|
486
|
+
?? RIFF_SHUTDOWN_BATCH_PERSIST_TIMEOUT_MS;
|
|
487
|
+
const remaining = options.deadlineMs === undefined
|
|
488
|
+
? configuredPersistTimeout
|
|
489
|
+
: Math.max(0, options.deadlineMs - now());
|
|
490
|
+
if (remaining <= 0) {
|
|
491
|
+
return {
|
|
492
|
+
ok: false,
|
|
493
|
+
taskId: null,
|
|
494
|
+
error: 'Riff fleet lineage batch failed (prewrite_io): shutdown deadline elapsed',
|
|
495
|
+
rollbackDisposition: 'abort_safe',
|
|
496
|
+
sessionIds: entries.map(({ ds }) => ds.session.sessionId),
|
|
497
|
+
retainFencedSessionIds: [],
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
sessionStore.persistActiveRiffLineagesExactBatch(entries.map(({ ds, result }) => ({
|
|
502
|
+
sessionId: ds.session.sessionId,
|
|
503
|
+
taskId: result.durableTaskIdAtPrepare,
|
|
504
|
+
owner: result.durableOwnerAtPrepare,
|
|
505
|
+
targetTaskId: result.taskId,
|
|
506
|
+
expectedCurrentTaskIds: [result.durableTaskIdAtPrepare, result.taskId],
|
|
507
|
+
})), {
|
|
508
|
+
maxWaitMs: Math.min(configuredPersistTimeout, remaining),
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
const batchError = error instanceof sessionStore.RiffLineageBatchError ? error : undefined;
|
|
513
|
+
const stage = batchError?.stage ?? 'prewrite_io';
|
|
514
|
+
const retainFencedSessionIds = stage === 'postrename_ambiguity'
|
|
515
|
+
? entries.map(({ ds }) => ds.session.sessionId)
|
|
516
|
+
: stage === 'prewrite_ownership'
|
|
517
|
+
? [...(batchError?.sessionIds ?? [])]
|
|
518
|
+
: [];
|
|
519
|
+
return {
|
|
520
|
+
ok: false,
|
|
521
|
+
taskId: null,
|
|
522
|
+
error: `Riff fleet lineage batch failed (${stage}): ${error instanceof Error
|
|
523
|
+
? error.message
|
|
524
|
+
: String(error)}`,
|
|
525
|
+
rollbackDisposition: retainFencedSessionIds.length > 0 ? 'retain_fence' : 'abort_safe',
|
|
526
|
+
sessionIds: batchError?.sessionIds ?? entries.map(({ ds }) => ds.session.sessionId),
|
|
527
|
+
retainFencedSessionIds,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
for (const { ds, result } of entries) {
|
|
531
|
+
ds.session.riffParentTaskId = result.taskId ?? undefined;
|
|
532
|
+
result.lineageVerified = true;
|
|
533
|
+
}
|
|
534
|
+
if (options.deadlineMs !== undefined && now() >= options.deadlineMs) {
|
|
535
|
+
const sessionIds = entries.map(({ ds }) => ds.session.sessionId);
|
|
536
|
+
return {
|
|
537
|
+
ok: false,
|
|
538
|
+
taskId: null,
|
|
539
|
+
error: 'shutdown_deadline_elapsed_after_batch_persist',
|
|
540
|
+
rollbackDisposition: 'retain_fence',
|
|
541
|
+
sessionIds,
|
|
542
|
+
retainFencedSessionIds: sessionIds,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
const lost = entries
|
|
546
|
+
.filter(({ ds, result }) => !isPreparedRiffSessionCurrent(ds, result))
|
|
547
|
+
.map(({ ds }) => ds.session.sessionId);
|
|
548
|
+
if (lost.length > 0) {
|
|
549
|
+
return {
|
|
550
|
+
ok: false,
|
|
551
|
+
taskId: null,
|
|
552
|
+
error: `shutdown_prepare_ownership_lost_after_batch_persist:${lost.join(',')}`,
|
|
553
|
+
rollbackDisposition: 'retain_fence',
|
|
554
|
+
sessionIds: lost,
|
|
555
|
+
retainFencedSessionIds: lost,
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
return { ok: true };
|
|
559
|
+
}
|
|
560
|
+
/** Single-session compatibility path. Fleet shutdown uses the batch function
|
|
561
|
+
* above; this API remains for explicit focused operations and older callers. */
|
|
562
|
+
export function persistPreparedRiffShutdown(ds, prepared) {
|
|
563
|
+
const validation = validatePreparedRiffShutdownForPersistence(ds, prepared);
|
|
564
|
+
if (!validation.ok)
|
|
565
|
+
return validation;
|
|
566
|
+
try {
|
|
567
|
+
sessionStore.persistActiveRiffLineageExact(ds.session.sessionId, prepared.taskId, {
|
|
568
|
+
expectedCurrentTaskIds: [prepared.durableTaskIdAtPrepare, prepared.taskId],
|
|
569
|
+
expectedOwner: prepared.durableOwnerAtPrepare,
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
catch (err) {
|
|
573
|
+
return {
|
|
574
|
+
ok: false,
|
|
575
|
+
requestId: prepared.requestId,
|
|
576
|
+
taskId: prepared.taskId,
|
|
577
|
+
error: `lineage_persist_failed:${err instanceof Error ? err.message : String(err)}`,
|
|
578
|
+
rollbackDisposition: err instanceof sessionStore.RiffLineageOwnershipError
|
|
579
|
+
? 'retain_fence'
|
|
580
|
+
: 'abort_safe',
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
let fresh;
|
|
584
|
+
try {
|
|
585
|
+
fresh = sessionStore.getSessionFresh(ds.session.sessionId);
|
|
586
|
+
}
|
|
587
|
+
catch (err) {
|
|
588
|
+
return {
|
|
589
|
+
ok: false,
|
|
590
|
+
requestId: prepared.requestId,
|
|
591
|
+
taskId: prepared.taskId,
|
|
592
|
+
error: `fresh_lineage_verification_failed:${err instanceof Error ? err.message : String(err)}`,
|
|
593
|
+
rollbackDisposition: 'retain_fence',
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
const freshTaskId = fresh?.riffParentTaskId ?? null;
|
|
597
|
+
const freshOwner = fresh
|
|
598
|
+
? {
|
|
599
|
+
pid: fresh.pid ?? null,
|
|
600
|
+
larkAppId: fresh.larkAppId ?? null,
|
|
601
|
+
backendType: fresh.backendType ?? null,
|
|
602
|
+
}
|
|
603
|
+
: null;
|
|
604
|
+
const freshOwnerMatches = freshOwner !== null
|
|
605
|
+
&& freshOwner.pid === prepared.durableOwnerAtPrepare.pid
|
|
606
|
+
&& freshOwner.larkAppId === prepared.durableOwnerAtPrepare.larkAppId
|
|
607
|
+
&& freshOwner.backendType === prepared.durableOwnerAtPrepare.backendType;
|
|
608
|
+
if (!fresh
|
|
609
|
+
|| fresh.status !== 'active'
|
|
610
|
+
|| freshTaskId !== prepared.taskId
|
|
611
|
+
|| !freshOwnerMatches) {
|
|
612
|
+
return {
|
|
613
|
+
ok: false,
|
|
614
|
+
requestId: prepared.requestId,
|
|
615
|
+
taskId: prepared.taskId,
|
|
616
|
+
error: `fresh_lineage_verification_failed:status=${fresh?.status ?? 'missing'},`
|
|
617
|
+
+ `task=${freshTaskId ?? 'none'},expected=${prepared.taskId ?? 'none'},`
|
|
618
|
+
+ `owner=${JSON.stringify(freshOwner)},`
|
|
619
|
+
+ `expectedOwner=${JSON.stringify(prepared.durableOwnerAtPrepare)}`,
|
|
620
|
+
rollbackDisposition: 'retain_fence',
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
ds.session.riffParentTaskId = prepared.taskId ?? undefined;
|
|
624
|
+
prepared.lineageVerified = true;
|
|
625
|
+
if (!isPreparedRiffSessionCurrent(ds, prepared)) {
|
|
626
|
+
return {
|
|
627
|
+
ok: false,
|
|
628
|
+
requestId: prepared.requestId,
|
|
629
|
+
taskId: prepared.taskId,
|
|
630
|
+
error: 'shutdown_prepare_ownership_lost_after_persist',
|
|
631
|
+
rollbackDisposition: 'retain_fence',
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
return { ok: true };
|
|
635
|
+
}
|
|
636
|
+
export function isPreparedRiffSessionCurrent(ds, prepared) {
|
|
637
|
+
if (ds.riffShutdownState?.requestId !== prepared.requestId
|
|
638
|
+
|| ds.riffShutdownState.phase !== 'prepared')
|
|
639
|
+
return false;
|
|
640
|
+
if (prepared.worker) {
|
|
641
|
+
return ds.worker === prepared.worker && !workerHasExited(prepared.worker);
|
|
642
|
+
}
|
|
643
|
+
return ds.worker === null;
|
|
644
|
+
}
|
|
645
|
+
/** After verified lineage persistence, an exact prepared worker that exited
|
|
646
|
+
* and was cleared by worker-pool can be safely rolled back locally. A new
|
|
647
|
+
* worker generation or a different fence remains ownership ambiguity. */
|
|
648
|
+
export function canAbortVerifiedExitedRiffPreparation(ds, prepared) {
|
|
649
|
+
return prepared.lineageVerified
|
|
650
|
+
&& !!prepared.worker
|
|
651
|
+
&& workerHasExited(prepared.worker)
|
|
652
|
+
&& ds.worker === null
|
|
653
|
+
&& ds.riffShutdownState?.requestId === prepared.requestId;
|
|
654
|
+
}
|
|
655
|
+
/** Roll back one prepared participant. State clears only after the exact worker
|
|
656
|
+
* ACKs admission restoration; timeout/late ACK remains deliberately fail-closed. */
|
|
657
|
+
export async function abortPreparedRiffShutdown(ds, prepared, options = {}) {
|
|
658
|
+
if (ds.riffShutdownState?.requestId !== prepared.requestId) {
|
|
659
|
+
return { ok: false, taskId: prepared.taskId, error: 'shutdown_prepare_ownership_lost' };
|
|
660
|
+
}
|
|
661
|
+
if (!prepared.worker) {
|
|
662
|
+
if (ds.worker) {
|
|
663
|
+
return { ok: false, taskId: prepared.taskId, error: 'new_worker_generation' };
|
|
664
|
+
}
|
|
665
|
+
clearWorkerlessShutdownState(ds, prepared.requestId);
|
|
666
|
+
return { ok: true, taskId: prepared.taskId };
|
|
667
|
+
}
|
|
668
|
+
if (workerHasExited(prepared.worker)) {
|
|
669
|
+
return abortWorkerPreparation(ds, prepared.worker, prepared.requestId, options.abortTimeoutMs ?? RIFF_ADMISSION_RESTORE_TIMEOUT_MS, prepared.lineageVerified, { taskId: prepared.taskId });
|
|
670
|
+
}
|
|
671
|
+
return abortWorkerPreparation(ds, prepared.worker, prepared.requestId, options.abortTimeoutMs ?? RIFF_ADMISSION_RESTORE_TIMEOUT_MS, prepared.lineageVerified, { taskId: prepared.taskId });
|
|
672
|
+
}
|
|
673
|
+
async function abortPossiblyFencedRiffShutdown(ds, participant, timeoutMs) {
|
|
674
|
+
if (ds.riffShutdownState?.requestId !== participant.requestId) {
|
|
675
|
+
return {
|
|
676
|
+
ok: false,
|
|
677
|
+
taskId: participant.taskId,
|
|
678
|
+
error: 'shutdown_prepare_ownership_lost',
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
return abortWorkerPreparation(ds, participant.worker, participant.requestId, timeoutMs, false, Object.prototype.hasOwnProperty.call(participant, 'expectedAbortTaskId')
|
|
682
|
+
? { taskId: participant.expectedAbortTaskId ?? null }
|
|
683
|
+
: undefined);
|
|
684
|
+
}
|
|
685
|
+
/** Restore every exact prepared/possibly-fenced generation concurrently. No
|
|
686
|
+
* participant can consume another's timeout budget. */
|
|
687
|
+
export async function abortRiffShutdownFleet(entries, options = {}) {
|
|
688
|
+
const now = options.now ?? Date.now;
|
|
689
|
+
const configuredTimeout = options.abortTimeoutMs ?? RIFF_ADMISSION_RESTORE_TIMEOUT_MS;
|
|
690
|
+
const remaining = options.deadlineMs === undefined
|
|
691
|
+
? configuredTimeout
|
|
692
|
+
: Math.max(0, options.deadlineMs - now());
|
|
693
|
+
const timeoutMs = Math.min(configuredTimeout, remaining);
|
|
694
|
+
return Promise.all(entries.map(async ({ ds, result: participant }) => {
|
|
695
|
+
if (timeoutMs <= 0) {
|
|
696
|
+
return {
|
|
697
|
+
ds,
|
|
698
|
+
participant,
|
|
699
|
+
result: {
|
|
700
|
+
ok: false,
|
|
701
|
+
taskId: participant.taskId,
|
|
702
|
+
error: 'shutdown_deadline_elapsed_before_abort',
|
|
703
|
+
},
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
const result = participant.ok
|
|
707
|
+
? await abortPreparedRiffShutdown(ds, participant, { abortTimeoutMs: timeoutMs })
|
|
708
|
+
: await abortPossiblyFencedRiffShutdown(ds, participant, timeoutMs);
|
|
709
|
+
return { ds, participant, result };
|
|
710
|
+
}));
|
|
711
|
+
}
|
|
712
|
+
/** Phase 3: synchronous, infallible-after-validation commit. The coordinator
|
|
713
|
+
* validates every participant first, then calls this without an intervening
|
|
714
|
+
* await, so one session can never refuse after a peer was detached. */
|
|
715
|
+
export function commitPreparedRiffShutdown(ds, prepared) {
|
|
716
|
+
if (!isPreparedRiffSessionCurrent(ds, prepared))
|
|
717
|
+
return false;
|
|
718
|
+
if (!prepared.worker) {
|
|
719
|
+
clearWorkerlessShutdownState(ds, prepared.requestId);
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
if (!send(prepared.worker, { type: 'riff_shutdown_commit', requestId: prepared.requestId })) {
|
|
723
|
+
// Lineage is already durably fresh-verified. Direct retirement is safe even
|
|
724
|
+
// if the final IPC channel closed between validation and send.
|
|
725
|
+
try {
|
|
726
|
+
prepared.worker.kill('SIGTERM');
|
|
727
|
+
}
|
|
728
|
+
catch { /* already exited */ }
|
|
729
|
+
}
|
|
730
|
+
clearWorkerOwnership(ds, prepared.worker);
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
/** Single-session compatibility wrapper. Daemon shutdown intentionally uses
|
|
734
|
+
* the explicit three-phase API above so a multi-Riff fleet cannot half-commit. */
|
|
735
|
+
export async function detachRiffWorkerForShutdown(ds, options = {}) {
|
|
736
|
+
const prepared = await prepareRiffSessionForShutdown(ds, options);
|
|
737
|
+
if (!prepared.ok) {
|
|
738
|
+
if (prepared.fence === 'none')
|
|
739
|
+
return prepared;
|
|
740
|
+
const [aborted] = await abortRiffShutdownFleet([{ ds, result: prepared }], options);
|
|
741
|
+
const abortSuffix = aborted.result.ok
|
|
742
|
+
? ''
|
|
743
|
+
: `;admission_restore_failed:${aborted.result.error ?? 'unknown'}`;
|
|
744
|
+
logger.error(`[${label(ds)}] Riff shutdown drain failed (${prepared.error}${abortSuffix}); `
|
|
745
|
+
+ (aborted.result.ok
|
|
746
|
+
? 'worker retained with admission restored'
|
|
747
|
+
: 'worker retained and admission fence kept fail-closed'));
|
|
748
|
+
return {
|
|
749
|
+
ok: false,
|
|
750
|
+
requestId: prepared.requestId,
|
|
751
|
+
taskId: prepared.taskId,
|
|
752
|
+
error: prepared.error + abortSuffix,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
const persisted = persistPreparedRiffShutdown(ds, prepared);
|
|
756
|
+
if (!persisted.ok) {
|
|
757
|
+
if (persisted.rollbackDisposition === 'retain_fence')
|
|
758
|
+
return persisted;
|
|
759
|
+
const aborted = await abortPreparedRiffShutdown(ds, prepared, options);
|
|
760
|
+
return {
|
|
761
|
+
ok: false,
|
|
762
|
+
requestId: prepared.requestId,
|
|
763
|
+
taskId: prepared.taskId,
|
|
764
|
+
error: persisted.error
|
|
765
|
+
+ (aborted.ok ? '' : `;admission_restore_failed:${aborted.error ?? 'unknown'}`),
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
if (!commitPreparedRiffShutdown(ds, prepared)) {
|
|
769
|
+
return {
|
|
770
|
+
ok: false,
|
|
771
|
+
requestId: prepared.requestId,
|
|
772
|
+
taskId: prepared.taskId,
|
|
773
|
+
error: 'shutdown_prepare_ownership_lost_before_commit',
|
|
774
|
+
rollbackDisposition: 'retain_fence',
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
logger.info(`[${label(ds)}] Riff shutdown detach committed after durable lineage `
|
|
778
|
+
+ `${prepared.taskId ?? 'none'}`);
|
|
779
|
+
return {
|
|
780
|
+
ok: true,
|
|
781
|
+
requestId: prepared.requestId,
|
|
782
|
+
taskId: prepared.taskId,
|
|
783
|
+
disposition: 'lineage_persisted',
|
|
784
|
+
...(prepared.worker ? { worker: prepared.worker } : {}),
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
//# sourceMappingURL=riff-shutdown-detach.js.map
|