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
|
@@ -3,10 +3,11 @@ import { config } from '../../config.js';
|
|
|
3
3
|
import { getBot, getAllBots, getOwnerOpenId } from '../../bot-registry.js';
|
|
4
4
|
import { canOperate, canTalk } from './event-dispatcher.js';
|
|
5
5
|
import { updateMessage, deleteMessage, replyMessage, sendMessage, sendUserMessage, sendEphemeralCard, getMessageDetail, isHumanOpenId, resolveUserUnionId as defaultResolveUserUnionId } from './client.js';
|
|
6
|
-
import { buildSessionCard, buildStreamingCard, buildTuiPromptCard, buildTuiPromptProcessingCard, buildGrantResultCard, getCliDisplayName, truncateContent, buildConfigCard, buildConfigTextCard, CONFIG_UNSET, buildRepoSelectCard } from './card-builder.js';
|
|
6
|
+
import { buildSessionCard, buildStreamingCard, buildTuiPromptCard, buildTuiPromptProcessingCard, buildGrantResultCard, getCliDisplayName, truncateContent, buildConfigCard, buildConfigQuotaCard, buildConfigTextCard, CONFIG_UNSET, buildRepoSelectCard } from './card-builder.js';
|
|
7
7
|
import { codexServiceTierBadge } from '../../services/codex-service-tier.js';
|
|
8
8
|
import { findConfigField, applyConfigField, coerceConfigValue, getConfigCardData, } from '../../services/bot-config-store.js';
|
|
9
9
|
import { updateBotGrantPrefs } from '../../services/grant-prefs-store.js';
|
|
10
|
+
import { MAX_GRANT_QUOTA } from '../../services/grant-policy.js';
|
|
10
11
|
import { writeTeamRoleFile, deleteTeamRoleFile } from '../../core/role-resolver.js';
|
|
11
12
|
import { addChatGrant, addGlobalGrant } from '../../services/grant-store.js';
|
|
12
13
|
import { checkNonce, clearPending, getPendingGrantLimits, getPendingMessage, markDenied, updatePendingGrantLimits, } from './grant-pending.js';
|
|
@@ -29,22 +30,25 @@ import { ttadkConfigModelChoices } from '../../setup/cli-selection.js';
|
|
|
29
30
|
import { logger } from '../../utils/logger.js';
|
|
30
31
|
import * as sessionStore from '../../services/session-store.js';
|
|
31
32
|
import { loadFrozenCards, saveFrozenCards } from '../../services/frozen-card-store.js';
|
|
32
|
-
import { forkWorker, sendWorkerInput, sendWorkerSessionInput,
|
|
33
|
+
import { forkWorker, sendWorkerInput, sendWorkerSessionInput, closeSession as closeWorkerPoolSession, teardownAuthoritativePersistentBackingBeforeClose, scheduleCardPatch, parkStreamCard, clearUsageLimitState, cardUsageLimit, writableTerminalLinkFor, workerHasInitialized, sessionSupportsWebTerminal, readableTerminalUrlFor, resolvePrivateCardAudience, deliverWriteLinkCard, deliverEphemeralOrReply, CARD_POSTING_SENTINEL, requestSessionRestart, isSessionTransferring, getDaemonStreamingCardUsageSnapshot, withActiveSessionKeyLock } from '../../core/worker-pool.js';
|
|
33
34
|
import { getSessionWorkingDir, buildNewTopicCliInput, getAvailableBots, persistStreamCardState, resumeSession, rememberLastCliInput, ensureSessionWhiteboard } from '../../core/session-manager.js';
|
|
34
35
|
import { markInitialUserTurnPending } from '../../core/initial-user-turn.js';
|
|
35
|
-
import { publishAttentionPatch,
|
|
36
|
+
import { publishAttentionPatch, announcePendingRepoSession } from '../../core/session-activity.js';
|
|
36
37
|
import { fallbackTurnId } from '../../core/reply-target.js';
|
|
37
38
|
import { sendWorkerIpc } from '../../core/worker-ipc.js';
|
|
38
39
|
import { validateWorkingDir } from '../../core/working-dir.js';
|
|
39
|
-
import { sessionKey, sessionAnchorId, frozenDisplayMode, markRepoCardConsumed, isActiveRepoCard
|
|
40
|
+
import { activeSessionKey, sessionKey, sessionAnchorId, frozenDisplayMode, markRepoCardConsumed, isActiveRepoCard } from '../../core/types.js';
|
|
40
41
|
import { buildTerminalUrl } from '../../core/terminal-url.js';
|
|
41
42
|
import { createRepoWorktree, removeRepoWorktree, dirSuffixForBranch, pushWorktreeBranch } from '../../services/git-worktree.js';
|
|
42
43
|
import { withCodexAppContext } from '../../utils/codex-app-context.js';
|
|
43
|
-
import { resolvePairedSpawnBackendType } from '../../core/persistent-backend.js';
|
|
44
|
+
import { isRiffBackendSession, resolvePairedSpawnBackendType } from '../../core/persistent-backend.js';
|
|
44
45
|
import { sessionConfiguredRuntimeDisplayName } from '../../core/cli-runtime-display.js';
|
|
45
46
|
import { worktreeSlugFromContextAI } from '../../services/worktree-slug-ai.js';
|
|
46
47
|
import { t, localeForBot, isLocale } from '../../i18n/index.js';
|
|
47
48
|
import { isLocalCliOpenCapable, isLocalCliOpenConfigured, isLocalCliOpenReady, localCliOpenMode, openLocalCliInIterm, preflightLocalCliOpen, } from '../../services/local-cli-opener.js';
|
|
49
|
+
import { hasProtectedSessionMutationOwnership } from '../../core/session-mutation-guard.js';
|
|
50
|
+
import { persistPendingRepoCardMessageId } from '../../core/pending-repo-journal.js';
|
|
51
|
+
import { runDetachedBotTurnAdmission, withBotTurnMutation } from '../../core/bot-turn-mutation-gate.js';
|
|
48
52
|
/**
|
|
49
53
|
* Resolve the verified `union_id` of the operator who clicked a card button.
|
|
50
54
|
*
|
|
@@ -235,193 +239,210 @@ export async function commitRepoSelection(ctx, dirPath, dirLabel,
|
|
|
235
239
|
// The worktree flow already posted a precise "worktree 已创建:path 分支 …"
|
|
236
240
|
// line before funnelling in here — suppress the redundant "已选择/已切换"
|
|
237
241
|
// confirmation so the user sees a single message, not two.
|
|
238
|
-
// pinWorkingDir=false: "直接开始"/skip keeps the default cwd for this launch
|
|
239
|
-
// without persisting it (esp. $HOME) onto session.workingDir — sibling bots
|
|
240
|
-
// must still get their own repo card instead of inheriting HOME.
|
|
241
|
-
// confirmReplyText: optional confirmation to send under the same claim
|
|
242
|
-
// (used by skip_repo so the post-fork reply window cannot race a second click).
|
|
243
242
|
opts) {
|
|
244
243
|
const { ds, rootId, cardMessageId, larkAppId, operatorOpenId, activeSessions, sessionReply } = ctx;
|
|
245
244
|
const locTarget = localeForBot(ds.larkAppId);
|
|
246
245
|
// `/close` deletes the active-map entry without touching sessionId or
|
|
247
246
|
// pendingRepo — identity against the map is the only tell that the session
|
|
248
247
|
// this flow captured is gone. Checked alongside the generation snapshots.
|
|
249
|
-
const repoSessionKey =
|
|
248
|
+
const repoSessionKey = activeSessionKey(ds);
|
|
250
249
|
const sessionStillActive = () => activeSessions.get(repoSessionKey) === ds;
|
|
251
250
|
const commitGenSessionId = ds.session.sessionId;
|
|
252
251
|
const pinWorkingDir = opts?.pinWorkingDir !== false;
|
|
252
|
+
// Card callbacks are replayable. Only the currently published picker may
|
|
253
|
+
// mutate this session; a withdrawn/replaced card must stay stale even after
|
|
254
|
+
// the activation FIFO eventually settles. Internal auto-worktree commits do
|
|
255
|
+
// not originate from a card and deliberately omit cardMessageId.
|
|
256
|
+
if (cardMessageId !== undefined
|
|
257
|
+
&& (!ds.repoCardMessageId || cardMessageId !== ds.repoCardMessageId)) {
|
|
258
|
+
logger.warn(`[${tag(ds)}] Ignoring stale repo-card callback ${cardMessageId} `
|
|
259
|
+
+ `(current=${ds.repoCardMessageId ?? 'none'})`);
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
// A live Riff generation cannot use the generic close-and-refork branch.
|
|
263
|
+
// Riff teardown is a remote prepare/commit protocol; a failed cancellation
|
|
264
|
+
// followed by forkWorker would reach the double-fork kill and orphan the
|
|
265
|
+
// still-live remote task. Require an explicit /close before any card,
|
|
266
|
+
// worktree, or manual-directory selection can replace this generation.
|
|
267
|
+
if (!ds.pendingRepo && isRiffBackendSession(ds)) {
|
|
268
|
+
await sessionReply(rootId, t('cmd.cd.riff_unsupported', undefined, locTarget));
|
|
269
|
+
logger.warn(`[${tag(ds)}] Repo switch refused: Riff session requires explicit close before replacement`);
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
if (!ds.pendingRepo
|
|
273
|
+
&& hasProtectedSessionMutationOwnership(ds)) {
|
|
274
|
+
await sessionReply(rootId, '当前会话仍有待提交消息,暂不能切换仓库;请等待提交完成或关闭会话。');
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
253
277
|
if (ds.pendingRepo) {
|
|
254
|
-
|
|
255
|
-
// mirrors this transaction through the same in-memory claim. Only one may
|
|
256
|
-
// own the pending -> worker transition, including prompt preparation.
|
|
257
|
-
if (ds.pendingRepoCommitInFlight) {
|
|
258
|
-
logger.info(`[${tag(ds)}] Pending repo commit already in flight — ignoring duplicate selection`);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
278
|
+
const targetSessionId = ds.session.sessionId;
|
|
261
279
|
ds.pendingRepoCommitInFlight = true;
|
|
262
|
-
let committed = false;
|
|
263
280
|
try {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const selfBot = getBot(ds.larkAppId);
|
|
275
|
-
const botCfg = selfBot.config;
|
|
276
|
-
const effectiveCliId = sessionCliId(ds);
|
|
277
|
-
// Keep pendingRepo=true across this await. New topic messages therefore
|
|
278
|
-
// remain buffered on this same session instead of racing a worker-null
|
|
279
|
-
// safety-net fork. Snapshot every pending field only after it settles.
|
|
280
|
-
const needsPromptContext = !ds.pendingRawInput ||
|
|
281
|
-
(ds.pendingPrompt?.trim().length ?? 0) > 0 ||
|
|
282
|
-
(ds.pendingAttachments?.length ?? 0) > 0 ||
|
|
283
|
-
(ds.pendingFollowUps?.length ?? 0) > 0 ||
|
|
284
|
-
ds.pendingChatContext !== undefined;
|
|
285
|
-
const availableBots = needsPromptContext
|
|
286
|
-
? await getAvailableBots(ds.larkAppId, ds.chatId)
|
|
287
|
-
: [];
|
|
288
|
-
if (!sessionStillActive() || ds.session.sessionId !== commitGenSessionId ||
|
|
289
|
-
!ds.pendingRepo || (ds.worker && !ds.worker.killed)) {
|
|
290
|
-
logger.warn(`[${tag(ds)}] Session changed while preparing the pending-CLI prompt (${commitGenSessionId} → ${ds.session.sessionId}, active=${sessionStillActive()}, pending=${!!ds.pendingRepo}, worker=${!!ds.worker}) — aborting this fork`);
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
const pendingPrompt = ds.pendingPrompt ?? '';
|
|
294
|
-
const pendingRawInput = ds.pendingRawInput;
|
|
295
|
-
const hasBufferedInput = pendingPrompt.trim().length > 0 ||
|
|
296
|
-
(ds.pendingAttachments?.length ?? 0) > 0 ||
|
|
297
|
-
(ds.pendingFollowUps?.length ?? 0) > 0 ||
|
|
298
|
-
ds.pendingChatContext !== undefined;
|
|
299
|
-
// Nothing to submit at all (session created by a bare `/repo`, i.e. the
|
|
300
|
-
// message IS the command). Boot the CLI idle instead of burning an empty
|
|
301
|
-
// `<user_message>` opening on it, and mark the session so the user's NEXT
|
|
302
|
-
// real message becomes the new-topic first turn. Mirrors the text
|
|
303
|
-
// `/repo` path in command-handler's forkPendingCli.
|
|
304
|
-
const emptyStart = !pendingRawInput && !hasBufferedInput;
|
|
305
|
-
if (!pendingRawInput || hasBufferedInput)
|
|
306
|
-
ensureSessionWhiteboard(ds);
|
|
307
|
-
const wrappedInput = hasBufferedInput
|
|
308
|
-
? buildNewTopicCliInput(pendingPrompt, ds.session.sessionId, effectiveCliId, botCfg.cliPathOverride, ds.pendingAttachments, ds.pendingMentions, availableBots, ds.pendingFollowUps, { name: selfBot.botName, openId: selfBot.botOpenId }, locTarget, ds.pendingSender, {
|
|
309
|
-
larkAppId: ds.larkAppId,
|
|
310
|
-
chatId: ds.chatId,
|
|
311
|
-
whiteboardId: ds.session.whiteboardId,
|
|
312
|
-
substituteTrigger: ds.pendingSubstituteTrigger,
|
|
313
|
-
codexAppText: ds.pendingCodexAppText,
|
|
314
|
-
codexAppApplicationContext: ds.pendingCodexAppApplicationContext,
|
|
315
|
-
codexAppMessageContext: ds.pendingCodexAppMessageContext,
|
|
316
|
-
codexAppFollowUps: ds.pendingCodexAppFollowUps,
|
|
317
|
-
codexAppFollowUpContexts: ds.pendingCodexAppFollowUpContexts,
|
|
318
|
-
chatContext: ds.pendingChatContext,
|
|
319
|
-
})
|
|
320
|
-
: { content: '' };
|
|
321
|
-
const prompt = pendingRawInput ? '' : wrappedInput;
|
|
322
|
-
const pendingTurnId = ds.pendingTurnId;
|
|
323
|
-
const previousPendingFollowUpInput = ds.pendingFollowUpInput;
|
|
324
|
-
if (pendingRawInput && hasBufferedInput) {
|
|
325
|
-
ds.pendingFollowUpInput = {
|
|
326
|
-
userPrompt: ds.pendingCodexAppText !== undefined || ds.pendingCodexAppFollowUps
|
|
327
|
-
? [ds.pendingCodexAppText ?? '', ...(ds.pendingCodexAppFollowUps ?? [])].filter(Boolean).join('\n\n')
|
|
328
|
-
: pendingPrompt || ds.pendingFollowUps?.join('\n\n') || '',
|
|
329
|
-
cliInput: wrappedInput.content,
|
|
330
|
-
...(ds.pendingFollowUpTurnId ? { turnId: ds.pendingFollowUpTurnId } : {}),
|
|
331
|
-
...(effectiveCliId === 'codex-app' && botCfg.codexAppCleanInput === true && wrappedInput.codexAppInput
|
|
332
|
-
? { codexAppInput: wrappedInput.codexAppInput }
|
|
333
|
-
: {}),
|
|
334
|
-
codexAppInputGateFrozen: true,
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
// From here through forkWorker there is no await: publish the state
|
|
338
|
-
// transition atomically from the router's point of view.
|
|
339
|
-
ds.pendingRepo = false;
|
|
340
|
-
publishAttentionPatch(ds);
|
|
341
|
-
try {
|
|
342
|
-
if (pendingRawInput) {
|
|
343
|
-
// pendingRawTurnId is applied at the literal PTY write boundary.
|
|
344
|
-
forkWorker(ds, '', false);
|
|
281
|
+
const started = await withBotTurnMutation(ds.larkAppId, async () => {
|
|
282
|
+
const current = [...activeSessions.values()].find(candidate => candidate.session.sessionId === targetSessionId
|
|
283
|
+
&& candidate.session.status === 'active');
|
|
284
|
+
if (!current || current !== ds || !current.pendingRepo)
|
|
285
|
+
return false;
|
|
286
|
+
// "Start directly" launches in the resolved default cwd without pinning
|
|
287
|
+
// HOME onto the session for sibling-bot inheritance.
|
|
288
|
+
if (pinWorkingDir) {
|
|
289
|
+
ds.workingDir = dirPath;
|
|
290
|
+
ds.session.workingDir = dirPath;
|
|
345
291
|
}
|
|
346
|
-
|
|
347
|
-
|
|
292
|
+
// riff 多仓 stamp:只有多仓 worktree 流显式传入(保留用户选择顺序,首仓=primary);
|
|
293
|
+
// 其它选仓路径一律清除旧 stamp——workingDir 变了,旧的多仓组合不再成立。
|
|
294
|
+
ds.session.riffRepoDirs = opts?.riffRepoDirs;
|
|
295
|
+
sessionStore.updateSession(ds.session);
|
|
296
|
+
const selfBot = getBot(ds.larkAppId);
|
|
297
|
+
const botCfg = selfBot.config;
|
|
298
|
+
const effectiveCliId = sessionCliId(ds);
|
|
299
|
+
// Keep pendingRepo=true across this await. New topic messages therefore
|
|
300
|
+
// remain buffered on this same session instead of racing a worker-null
|
|
301
|
+
// safety-net fork. Snapshot every pending field only after it settles.
|
|
302
|
+
const needsPromptContext = !ds.pendingRawInput ||
|
|
303
|
+
(ds.pendingPrompt?.trim().length ?? 0) > 0 ||
|
|
304
|
+
(ds.pendingAttachments?.length ?? 0) > 0 ||
|
|
305
|
+
(ds.pendingFollowUps?.length ?? 0) > 0 ||
|
|
306
|
+
ds.pendingChatContext !== undefined;
|
|
307
|
+
const availableBots = needsPromptContext
|
|
308
|
+
? await getAvailableBots(ds.larkAppId, ds.chatId)
|
|
309
|
+
: [];
|
|
310
|
+
if (!sessionStillActive() || ds.session.sessionId !== commitGenSessionId ||
|
|
311
|
+
!ds.pendingRepo || (ds.worker && !ds.worker.killed)) {
|
|
312
|
+
logger.warn(`[${tag(ds)}] Session changed while preparing the pending-CLI prompt (${commitGenSessionId} → ${ds.session.sessionId}, active=${sessionStillActive()}, pending=${!!ds.pendingRepo}, worker=${!!ds.worker}) — aborting this fork`);
|
|
313
|
+
return;
|
|
348
314
|
}
|
|
349
|
-
|
|
350
|
-
|
|
315
|
+
const pendingPrompt = ds.pendingPrompt ?? '';
|
|
316
|
+
const pendingRawInput = ds.pendingRawInput;
|
|
317
|
+
// Raw-input cold start still wraps any input buffered while the repo card
|
|
318
|
+
// was pending — see the skip_repo branch for the rationale.
|
|
319
|
+
const hasBufferedInput = pendingPrompt.trim().length > 0 ||
|
|
320
|
+
ds.pendingCodexAppText !== undefined ||
|
|
321
|
+
(ds.pendingAttachments?.length ?? 0) > 0 ||
|
|
322
|
+
(ds.pendingFollowUps?.length ?? 0) > 0 ||
|
|
323
|
+
ds.pendingChatContext !== undefined;
|
|
324
|
+
// Nothing to submit at all (session created by a bare `/repo`, i.e. the
|
|
325
|
+
// message IS the command). Boot the CLI idle instead of burning an empty
|
|
326
|
+
// `<user_message>` opening on it, and mark the session so the user's NEXT
|
|
327
|
+
// real message becomes the new-topic first turn. Mirrors the text
|
|
328
|
+
// `/repo` path in command-handler's forkPendingCli.
|
|
329
|
+
const emptyStart = !pendingRawInput && !hasBufferedInput;
|
|
330
|
+
if (!pendingRawInput || hasBufferedInput)
|
|
331
|
+
ensureSessionWhiteboard(ds);
|
|
332
|
+
const wrappedInput = hasBufferedInput
|
|
333
|
+
? buildNewTopicCliInput(pendingPrompt, ds.session.sessionId, effectiveCliId, botCfg.cliPathOverride, ds.pendingAttachments, ds.pendingMentions, availableBots, ds.pendingFollowUps, { name: selfBot.botName, openId: selfBot.botOpenId }, locTarget, ds.pendingSender, {
|
|
334
|
+
larkAppId: ds.larkAppId,
|
|
335
|
+
chatId: ds.chatId,
|
|
336
|
+
whiteboardId: ds.session.whiteboardId,
|
|
337
|
+
substituteTrigger: ds.pendingSubstituteTrigger,
|
|
338
|
+
codexAppText: ds.pendingCodexAppText,
|
|
339
|
+
codexAppApplicationContext: ds.pendingCodexAppApplicationContext,
|
|
340
|
+
codexAppMessageContext: ds.pendingCodexAppMessageContext,
|
|
341
|
+
codexAppFollowUps: ds.pendingCodexAppFollowUps,
|
|
342
|
+
codexAppFollowUpContexts: ds.pendingCodexAppFollowUpContexts,
|
|
343
|
+
chatContext: ds.pendingChatContext,
|
|
344
|
+
})
|
|
345
|
+
: undefined;
|
|
346
|
+
const prompt = pendingRawInput ? '' : (wrappedInput ?? '');
|
|
347
|
+
// Last-line defence: prompt prep awaited above — if anything replaced
|
|
348
|
+
// OR closed the session in that window, forking now would clobber it
|
|
349
|
+
// (or resurrect a /close'd session).
|
|
350
|
+
if (!sessionStillActive() || ds.session.sessionId !== commitGenSessionId) {
|
|
351
|
+
logger.warn(`[${tag(ds)}] Session replaced or closed while preparing the pending-CLI prompt (${commitGenSessionId} → ${ds.session.sessionId}, active=${sessionStillActive()}) — aborting this fork`);
|
|
352
|
+
return false;
|
|
351
353
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
354
|
+
if (pendingRawInput && hasBufferedInput && wrappedInput) {
|
|
355
|
+
ds.pendingFollowUpInput = {
|
|
356
|
+
userPrompt: ds.pendingCodexAppText !== undefined || ds.pendingCodexAppFollowUps
|
|
357
|
+
? [ds.pendingCodexAppText ?? '', ...(ds.pendingCodexAppFollowUps ?? [])].filter(Boolean).join('\n\n')
|
|
358
|
+
: pendingPrompt || ds.pendingFollowUps?.join('\n\n') || '',
|
|
359
|
+
cliInput: wrappedInput.content,
|
|
360
|
+
...((ds.pendingFollowUpTurnIds?.at(-1) ?? ds.pendingFollowUpTurnId)
|
|
361
|
+
? { turnId: ds.pendingFollowUpTurnIds?.at(-1) ?? ds.pendingFollowUpTurnId }
|
|
362
|
+
: {}),
|
|
363
|
+
...(effectiveCliId === 'codex-app' && botCfg.codexAppCleanInput === true && wrappedInput.codexAppInput
|
|
364
|
+
? { codexAppInput: wrappedInput.codexAppInput }
|
|
365
|
+
: {}),
|
|
366
|
+
codexAppInputGateFrozen: true,
|
|
367
|
+
};
|
|
355
368
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
369
|
+
if (pendingRawInput)
|
|
370
|
+
rememberLastCliInput(ds, pendingRawInput, pendingRawInput);
|
|
371
|
+
else if (hasBufferedInput && wrappedInput)
|
|
372
|
+
rememberLastCliInput(ds, pendingPrompt, wrappedInput);
|
|
373
|
+
// Keep the reservation and every buffered opening field intact through
|
|
374
|
+
// forkWorker's synchronous pre-accept/write-ahead phase. If it throws,
|
|
375
|
+
// the user can retry this exact selection without losing the first turn.
|
|
376
|
+
const pendingTurnId = ds.pendingTurnId ?? ds.session.pendingRepoSetup?.turnId;
|
|
377
|
+
forkWorker(ds, prompt, !pendingRawInput && pendingTurnId ? { turnId: pendingTurnId } : false);
|
|
378
|
+
ds.pendingRepo = false;
|
|
379
|
+
// A queued activation owns the route through its adapter-level ACK. Every
|
|
380
|
+
// buffer below was synchronously folded into prompt N and is safe to clear;
|
|
381
|
+
// later inbounds observe this gate and enter the separate exact staged FIFO.
|
|
382
|
+
ds.initialStartPending = ds.session.queuedActivationPending === true;
|
|
360
383
|
publishAttentionPatch(ds);
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
//
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
// the card locally invalid).
|
|
384
|
+
// Durable, one-shot: the CLI is up but has never received a real user turn.
|
|
385
|
+
// Bare `/repo` idle-booted above (empty prompt, no turn); mark the session
|
|
386
|
+
// so the user's NEXT real message becomes the new-topic first turn.
|
|
387
|
+
if (emptyStart)
|
|
388
|
+
markInitialUserTurnPending(ds);
|
|
389
|
+
ds.pendingPrompt = undefined;
|
|
390
|
+
ds.pendingCodexAppText = undefined;
|
|
391
|
+
ds.pendingCodexAppApplicationContext = undefined;
|
|
392
|
+
ds.pendingCodexAppMessageContext = undefined;
|
|
393
|
+
ds.pendingChatContext = undefined;
|
|
394
|
+
ds.pendingAttachments = undefined;
|
|
395
|
+
ds.pendingMentions = undefined;
|
|
396
|
+
ds.pendingSubstituteTrigger = undefined;
|
|
397
|
+
ds.pendingSender = undefined;
|
|
398
|
+
ds.pendingFollowUps = undefined;
|
|
399
|
+
ds.pendingFollowUpTurnId = undefined;
|
|
400
|
+
ds.pendingFollowUpTurnIds = undefined;
|
|
401
|
+
ds.pendingCodexAppFollowUps = undefined;
|
|
402
|
+
ds.pendingCodexAppFollowUpContexts = undefined;
|
|
403
|
+
ds.pendingCodexAppFollowUpGateAccepted = undefined;
|
|
404
|
+
ds.pendingTurnId = undefined;
|
|
405
|
+
return true;
|
|
406
|
+
});
|
|
407
|
+
if (!started)
|
|
408
|
+
return false;
|
|
409
|
+
// Invalidate synchronously at the successful commit boundary. Card
|
|
410
|
+
// withdrawal and confirmation are best effort and may await/fail;
|
|
411
|
+
// neither may leave a replayable mutation capability behind.
|
|
390
412
|
const cardToWithdraw = cardMessageId ?? ds.repoCardMessageId;
|
|
391
413
|
markRepoCardConsumed(ds, cardToWithdraw);
|
|
392
414
|
ds.repoCardMessageId = undefined;
|
|
393
|
-
//
|
|
394
|
-
//
|
|
395
|
-
//
|
|
415
|
+
// Keep the pending-selection claim until the confirmation attempt
|
|
416
|
+
// settles. This prevents a second picker action from reinterpreting the
|
|
417
|
+
// freshly-started session as a mid-session repository switch.
|
|
396
418
|
try {
|
|
397
419
|
if (!opts?.suppressConfirmReply) {
|
|
398
|
-
// A card click has no turn of its own — anchor the confirmation to the
|
|
399
|
-
// session's current reply-target turn so a shared fold-back topic keeps
|
|
400
|
-
// it in-thread (same leak as the /repo command path).
|
|
401
420
|
await sessionReply(rootId, t('cmd.repo.selected_in_pending', { name: dirLabel }, locTarget), undefined, fallbackTurnId(ds, undefined));
|
|
402
421
|
}
|
|
403
422
|
else if (opts.confirmReplyText) {
|
|
404
423
|
await sessionReply(rootId, opts.confirmReplyText, undefined, fallbackTurnId(ds, undefined));
|
|
405
424
|
}
|
|
406
425
|
}
|
|
407
|
-
catch (
|
|
408
|
-
logger.warn(`[${tag(ds)}] Confirm reply after pending repo commit failed:
|
|
426
|
+
catch (err) {
|
|
427
|
+
logger.warn(`[${tag(ds)}] Confirm reply after pending repo commit failed: `
|
|
428
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
409
429
|
}
|
|
430
|
+
// Withdrawal is deliberately detached so the card callback can ACK
|
|
431
|
+
// before Lark observes that the source message disappeared.
|
|
410
432
|
deferRepoCardWithdraw(larkAppId, cardToWithdraw);
|
|
433
|
+
logger.info(`[${tag(ds)}] Repo selected: ${dirPath}, spawning CLI`);
|
|
434
|
+
return true;
|
|
411
435
|
}
|
|
412
436
|
finally {
|
|
413
437
|
ds.pendingRepoCommitInFlight = false;
|
|
414
438
|
}
|
|
415
|
-
if (!committed)
|
|
416
|
-
return;
|
|
417
|
-
logger.info(`[${tag(ds)}] Repo selected: ${dirPath}, spawning CLI`);
|
|
418
|
-
return;
|
|
419
439
|
}
|
|
420
440
|
else {
|
|
421
441
|
// Mid-session repo switch — close old session, start fresh.
|
|
422
442
|
// ZMX close is identity/generation verified and may refuse. Prove teardown
|
|
423
|
-
// before
|
|
424
|
-
//
|
|
443
|
+
// before mutating any old-session state; on refusal the current session and
|
|
444
|
+
// card remain fully retryable. (The in-lock closeWorkerPoolSession below
|
|
445
|
+
// repeats teardown close-wins; this pre-guard fail-closes on transfer.)
|
|
425
446
|
try {
|
|
426
447
|
teardownAuthoritativePersistentBackingBeforeClose(ds);
|
|
427
448
|
}
|
|
@@ -434,17 +455,7 @@ opts) {
|
|
|
434
455
|
catch (replyErr) {
|
|
435
456
|
logger.warn(`[${tag(ds)}] Repo-switch teardown failure reply failed: ${replyErr instanceof Error ? replyErr.message : replyErr}`);
|
|
436
457
|
}
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
// Claim the current card BEFORE killWorker / any await. A concurrent click
|
|
440
|
-
// on the same Feishu card must not pass a second kill+fork while the first
|
|
441
|
-
// switch is still awaiting confirm replies. Correctness does not depend on
|
|
442
|
-
// deleteMessage succeeding.
|
|
443
|
-
const claimedCard = claimCurrentRepoCard(ds, cardMessageId);
|
|
444
|
-
if (cardMessageId && !claimedCard) {
|
|
445
|
-
// Stale / already-claimed card (entry check races can still reach here).
|
|
446
|
-
logger.info(`[${tag(ds)}] Ignoring stale mid-session repo card ${cardMessageId}`);
|
|
447
|
-
return;
|
|
458
|
+
return false;
|
|
448
459
|
}
|
|
449
460
|
// Safety net (mirrors the `/repo` text-command path): build the same
|
|
450
461
|
// "session closed" card `/close` emits BEFORE displacing the old session
|
|
@@ -457,69 +468,74 @@ opts) {
|
|
|
457
468
|
// the displaced session's stored workingDir (and the closed card), so
|
|
458
469
|
// `claude --resume` later would reopen the old context in the new repo's
|
|
459
470
|
// cwd. The new repo is pinned onto the fresh session below instead.
|
|
460
|
-
const
|
|
461
|
-
const
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
471
|
+
const targetSessionId = ds.session.sessionId;
|
|
472
|
+
const switched = await withBotTurnMutation(ds.larkAppId, async () => {
|
|
473
|
+
const candidate = [...activeSessions.values()].find(candidate => candidate.session.sessionId === targetSessionId);
|
|
474
|
+
if (!candidate || candidate !== ds || candidate.session.status !== 'active') {
|
|
475
|
+
return { ok: false, error: 'session_replaced' };
|
|
476
|
+
}
|
|
477
|
+
const key = activeSessionKey(candidate);
|
|
478
|
+
return withActiveSessionKeyLock(activeSessions, key, async () => {
|
|
479
|
+
const current = [...activeSessions.values()].find(owner => owner.session.sessionId === targetSessionId);
|
|
480
|
+
if (!current || current !== candidate
|
|
481
|
+
|| activeSessions.get(key) !== current
|
|
482
|
+
|| current.session.status !== 'active') {
|
|
483
|
+
return { ok: false, error: 'session_replaced' };
|
|
484
|
+
}
|
|
485
|
+
if (hasProtectedSessionMutationOwnership(current)) {
|
|
486
|
+
return { ok: false, error: 'dispatch_pending' };
|
|
487
|
+
}
|
|
488
|
+
const closedCard = buildClosedSessionCard(current, locTarget);
|
|
489
|
+
// Preserve the old card in memory before durable close clears its old
|
|
490
|
+
// frozen-card file; it is re-keyed under the replacement session below.
|
|
491
|
+
parkStreamCard(current);
|
|
492
|
+
const oldSession = current.session;
|
|
493
|
+
await closeWorkerPoolSession(targetSessionId);
|
|
494
|
+
if (activeSessions.get(key) === current)
|
|
495
|
+
activeSessions.delete(key);
|
|
496
|
+
if (activeSessions.has(key)) {
|
|
497
|
+
return { ok: false, error: 'session_replaced' };
|
|
498
|
+
}
|
|
499
|
+
const cardToWithdraw = cardMessageId ?? current.repoCardMessageId;
|
|
500
|
+
markRepoCardConsumed(current, cardToWithdraw);
|
|
501
|
+
current.repoCardMessageId = undefined;
|
|
502
|
+
const session = sessionStore.createSession(current.chatId, current.scope === 'chat' ? oldSession.rootMessageId : rootId, dirLabel, current.chatType, current.scope);
|
|
503
|
+
current.session = session;
|
|
504
|
+
current.lastUserPrompt = undefined;
|
|
505
|
+
current.lastCliInput = undefined;
|
|
506
|
+
current.workingDir = dirPath;
|
|
507
|
+
session.workingDir = dirPath;
|
|
508
|
+
session.larkAppId = current.larkAppId;
|
|
509
|
+
session.chatDisplayName = oldSession.chatDisplayName;
|
|
510
|
+
session.ownerOpenId = oldSession.ownerOpenId;
|
|
511
|
+
session.creatorOpenId = oldSession.creatorOpenId;
|
|
512
|
+
session.lastCallerOpenId = oldSession.lastCallerOpenId;
|
|
513
|
+
session.riffRepoDirs = opts?.riffRepoDirs;
|
|
514
|
+
sessionStore.updateSession(session);
|
|
515
|
+
current.hasHistory = false;
|
|
516
|
+
if (current.frozenCards && current.frozenCards.size > 0) {
|
|
517
|
+
saveFrozenCards(session.sessionId, current.frozenCards);
|
|
518
|
+
}
|
|
519
|
+
current.streamCardId = undefined;
|
|
520
|
+
current.streamCardNonce = undefined;
|
|
521
|
+
current.streamCardPending = undefined;
|
|
522
|
+
current.lastScreenContent = undefined;
|
|
523
|
+
current.lastScreenStatus = undefined;
|
|
524
|
+
activeSessions.set(key, current);
|
|
525
|
+
forkWorker(current, '', false);
|
|
526
|
+
// Brand-new CLI in a brand-new session record: the next real business
|
|
527
|
+
// message is its new-topic first turn (same invariant as the pending path).
|
|
528
|
+
markInitialUserTurnPending(current);
|
|
529
|
+
return { ok: true, current, closedCard, cardToWithdraw };
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
if (!switched.ok) {
|
|
533
|
+
if (switched.error === 'dispatch_pending') {
|
|
534
|
+
await sessionReply(rootId, '当前 Codex App 仍有未结算消息,暂不能切换仓库;请等待本轮完成或关闭会话。');
|
|
535
|
+
}
|
|
536
|
+
return false;
|
|
479
537
|
}
|
|
480
|
-
|
|
481
|
-
// `oc_...` chat id, not the traceable `om_...` message root stored on
|
|
482
|
-
// Session. Preserve the old identity and explicitly persist scope so card
|
|
483
|
-
// switches cannot recreate the session as a legacy scope-less thread.
|
|
484
|
-
const session = sessionStore.createSession(ds.chatId, ds.scope === 'chat' ? oldSession.rootMessageId : rootId, dirLabel, ds.chatType, ds.scope);
|
|
485
|
-
ds.session = session;
|
|
486
|
-
ds.lastUserPrompt = undefined;
|
|
487
|
-
ds.lastCliInput = undefined;
|
|
488
|
-
// Pin workingDir + larkAppId onto the new session before forkWorker.
|
|
489
|
-
// Without this, a daemon restart restores the session with an empty
|
|
490
|
-
// workingDir and the worker spawns in the bot's default cwd, so
|
|
491
|
-
// `claude --resume` looks in the wrong .claude/projects/<hash>/ dir and
|
|
492
|
-
// exits code 0 immediately, crash-looping until the rate-limiter trips.
|
|
493
|
-
ds.workingDir = dirPath;
|
|
494
|
-
ds.session.workingDir = dirPath;
|
|
495
|
-
ds.session.larkAppId = ds.larkAppId;
|
|
496
|
-
ds.session.chatDisplayName = oldSession.chatDisplayName;
|
|
497
|
-
ds.session.ownerOpenId = oldSession.ownerOpenId;
|
|
498
|
-
ds.session.creatorOpenId = oldSession.creatorOpenId;
|
|
499
|
-
ds.session.lastCallerOpenId = oldSession.lastCallerOpenId;
|
|
500
|
-
// Stamp the newly-created session, not the displaced session that was just
|
|
501
|
-
// closed. Plain/single-repo switches pass undefined and clear stale state.
|
|
502
|
-
ds.session.riffRepoDirs = opts?.riffRepoDirs;
|
|
503
|
-
sessionStore.updateSession(ds.session);
|
|
504
|
-
ds.hasHistory = false;
|
|
505
|
-
// Re-persist the parked card under the NEW sessionId so a daemon crash
|
|
506
|
-
// before the next POST doesn't strand it. closeSession() above wiped
|
|
507
|
-
// the on-disk file under the OLD sessionId; without this re-save, the
|
|
508
|
-
// in-memory Map only survives in process memory.
|
|
509
|
-
if (ds.frozenCards && ds.frozenCards.size > 0) {
|
|
510
|
-
saveFrozenCards(ds.session.sessionId, ds.frozenCards);
|
|
511
|
-
}
|
|
512
|
-
// Drop the old turn's streaming-card reference so worker_ready POSTs a
|
|
513
|
-
// fresh card for the new session instead of PATCHing the previous one.
|
|
514
|
-
ds.streamCardId = undefined;
|
|
515
|
-
ds.streamCardNonce = undefined;
|
|
516
|
-
ds.streamCardPending = undefined;
|
|
517
|
-
ds.lastScreenContent = undefined;
|
|
518
|
-
ds.lastScreenStatus = undefined;
|
|
519
|
-
forkWorker(ds, '', false);
|
|
520
|
-
// Brand-new CLI in a brand-new session record: the next real business
|
|
521
|
-
// message is its new-topic first turn (same invariant as the pending path).
|
|
522
|
-
markInitialUserTurnPending(ds);
|
|
538
|
+
await deliverEphemeralOrReply(switched.current, operatorOpenId, switched.closedCard, 'interactive', () => sessionReply(rootId, switched.closedCard, 'interactive'));
|
|
523
539
|
if (!opts?.suppressConfirmReply) {
|
|
524
540
|
try {
|
|
525
541
|
await sessionReply(rootId, t('cmd.repo.switched_to', { name: dirLabel }, locTarget));
|
|
@@ -528,11 +544,13 @@ opts) {
|
|
|
528
544
|
logger.warn(`[${tag(ds)}] Confirm reply after mid-session repo switch failed: ${e instanceof Error ? e.message : e}`);
|
|
529
545
|
}
|
|
530
546
|
}
|
|
531
|
-
|
|
532
|
-
//
|
|
533
|
-
//
|
|
534
|
-
deferRepoCardWithdraw(larkAppId,
|
|
547
|
+
// Return the callback ACK before recalling the card. Lark can keep the
|
|
548
|
+
// delete request pending; awaiting it here stalls the card action and lets
|
|
549
|
+
// the client retry an otherwise successful repository switch.
|
|
550
|
+
deferRepoCardWithdraw(larkAppId, switched.cardToWithdraw);
|
|
551
|
+
logger.info(`[${tag(switched.current)}] Repo switched to ${dirPath}, new session created`);
|
|
535
552
|
}
|
|
553
|
+
return true;
|
|
536
554
|
}
|
|
537
555
|
/**
|
|
538
556
|
* 仅默认目录 + auto-worktree 的**异步**提交:`ds` 必须已注册进 activeSessions 且处于
|
|
@@ -578,11 +596,15 @@ export async function runAutoWorktreeCommit(deps) {
|
|
|
578
596
|
// pendingRepo session, folds any messages buffered during creation (pendingPrompt
|
|
579
597
|
// + pendingFollowUps) into the first turn. suppressConfirmReply: the worktree
|
|
580
598
|
// helper already posted the '已创建/回退' line, so skip the '已选择' confirmation.
|
|
581
|
-
|
|
599
|
+
// The worktree build is intentionally detached from its caller's inbound
|
|
600
|
+
// admission. Re-enter with a fresh lease at the delayed commit/fork edge;
|
|
601
|
+
// the outer lease may have ended minutes ago and must not authorize this
|
|
602
|
+
// descendant across a bot-wide config mutation.
|
|
603
|
+
await runDetachedBotTurnAdmission(larkAppId, () => commitRepoSelection({
|
|
582
604
|
ds, rootId: anchor, larkAppId, operatorOpenId, activeSessions,
|
|
583
605
|
// Never reached under suppressConfirmReply for a pendingRepo session.
|
|
584
606
|
sessionReply: async () => '',
|
|
585
|
-
}, wt.dir, pathBasename(wt.dir), { suppressConfirmReply: true });
|
|
607
|
+
}, wt.dir, pathBasename(wt.dir), { suppressConfirmReply: true }));
|
|
586
608
|
}
|
|
587
609
|
catch (e) {
|
|
588
610
|
// No recovery fork here: forking with an empty prompt would DROP the buffered
|
|
@@ -1129,8 +1151,11 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1129
1151
|
// in place rather than appending a new message.
|
|
1130
1152
|
return JSON.parse(cardJson);
|
|
1131
1153
|
}
|
|
1132
|
-
// ─── /botconfig 交互卡片:切换布尔开关 / 选择 cli·model·lang /
|
|
1133
|
-
const CONFIG_CARD_ACTIONS = [
|
|
1154
|
+
// ─── /botconfig 交互卡片:切换布尔开关 / 选择 cli·model·lang / 编辑自由输入项 ──
|
|
1155
|
+
const CONFIG_CARD_ACTIONS = [
|
|
1156
|
+
'config_toggle', 'config_set', 'config_quota', 'config_quota_open',
|
|
1157
|
+
'config_quota_save', 'config_text_open', 'config_text_save',
|
|
1158
|
+
];
|
|
1134
1159
|
if (value?.action && larkAppId && CONFIG_CARD_ACTIONS.includes(value.action)) {
|
|
1135
1160
|
// 卡片携带的渲染语言(`/botconfig en` 的覆盖)优先;缺省回落 bot 默认。
|
|
1136
1161
|
const vLoc = value?.loc;
|
|
@@ -1216,11 +1241,57 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1216
1241
|
logger.info(`[config:${larkAppId}] text field ${spec.key} saved via card`);
|
|
1217
1242
|
return { toast: { type: 'success', content: `✓ ${spec.key} = ${r.newText}` } };
|
|
1218
1243
|
}
|
|
1219
|
-
//
|
|
1244
|
+
// 消息额度使用独立私信子卡自由输入,与 Dashboard 的 1–1000 语义一致。
|
|
1245
|
+
if (value.action === 'config_quota_open') {
|
|
1246
|
+
const d = getConfigCardData(larkAppId, modelChoices);
|
|
1247
|
+
if (!d)
|
|
1248
|
+
return { toast: { type: 'error', content: t('cmd.config.no_bot', undefined, loc) } };
|
|
1249
|
+
try {
|
|
1250
|
+
await sendUserMessage(larkAppId, operatorOpenId, buildConfigQuotaCard(d, loc), 'interactive');
|
|
1251
|
+
return { toast: { type: 'success', content: t('card.config.quota_sent', undefined, loc) } };
|
|
1252
|
+
}
|
|
1253
|
+
catch {
|
|
1254
|
+
return { toast: { type: 'error', content: t('card.config.quota_send_fail', undefined, loc) } };
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
if (value.action === 'config_quota_save') {
|
|
1258
|
+
const fv = action?.form_value ?? {};
|
|
1259
|
+
const raw = String(fv.messageQuota ?? '').trim();
|
|
1260
|
+
let limit = null;
|
|
1261
|
+
if (raw) {
|
|
1262
|
+
const n = Number(raw);
|
|
1263
|
+
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(n) || n < 1 || n > MAX_GRANT_QUOTA) {
|
|
1264
|
+
return { toast: { type: 'error', content: t('card.config.quota_invalid', undefined, loc) } };
|
|
1265
|
+
}
|
|
1266
|
+
limit = n;
|
|
1267
|
+
}
|
|
1268
|
+
const qr = await updateBotGrantPrefs(larkAppId, { messageQuotaDefaultLimit: limit });
|
|
1269
|
+
if (!qr.ok)
|
|
1270
|
+
return { toast: { type: 'error', content: t('cmd.config.write_failed', { reason: qr.reason }, loc) } };
|
|
1271
|
+
return {
|
|
1272
|
+
toast: {
|
|
1273
|
+
type: 'success',
|
|
1274
|
+
content: limit == null
|
|
1275
|
+
? t('card.config.quota_saved_default', undefined, loc)
|
|
1276
|
+
: t('card.config.quota_saved', { quota: limit }, loc),
|
|
1277
|
+
},
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
// 兼容已发出的旧版下拉卡:'off' = 恢复内置策略,正整数 = 设定。
|
|
1220
1281
|
if (value.action === 'config_quota') {
|
|
1221
1282
|
const raw = action?.option ?? '';
|
|
1222
|
-
|
|
1223
|
-
|
|
1283
|
+
// legacy 是只读初始项,用来让历史 >1000 额度仍能生成合法下拉卡。
|
|
1284
|
+
if (raw === 'legacy') {
|
|
1285
|
+
return { toast: { type: 'info', content: t('card.config.quota_legacy_unchanged', undefined, loc) }, ...reRender() };
|
|
1286
|
+
}
|
|
1287
|
+
let limit = null;
|
|
1288
|
+
if (raw !== 'off') {
|
|
1289
|
+
const n = Number(raw);
|
|
1290
|
+
if (!/^\d+$/.test(raw) || !Number.isSafeInteger(n) || n < 1 || n > MAX_GRANT_QUOTA) {
|
|
1291
|
+
return { toast: { type: 'error', content: t('card.config.quota_invalid', undefined, loc) } };
|
|
1292
|
+
}
|
|
1293
|
+
limit = n;
|
|
1294
|
+
}
|
|
1224
1295
|
const qr = await updateBotGrantPrefs(larkAppId, { messageQuotaDefaultLimit: limit });
|
|
1225
1296
|
if (!qr.ok)
|
|
1226
1297
|
return { toast: { type: 'error', content: t('cmd.config.write_failed', { reason: qr.reason }, loc) } };
|
|
@@ -1823,20 +1894,38 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1823
1894
|
logger.info(`[${tag(ds)}] voice_summary blocked because worker is busy: ${ds.lastScreenStatus ?? 'unknown'}`);
|
|
1824
1895
|
return { toast: { type: 'warning', content: t('card.voice.toast_worker_busy', undefined, locDs) } };
|
|
1825
1896
|
}
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
voicedCardIds.add(dedupeKey);
|
|
1897
|
+
if ((!ds.worker || ds.worker.killed) && hasProtectedSessionMutationOwnership(ds)) {
|
|
1898
|
+
logger.info(`[${tag(ds)}] voice_summary deferred behind durable opening ownership`);
|
|
1899
|
+
return { toast: { type: 'warning', content: t('card.voice.toast_worker_busy', undefined, locDs) } };
|
|
1830
1900
|
}
|
|
1831
1901
|
const instruction = voiceSummaryInstruction(locDs);
|
|
1832
1902
|
const voiceInput = {
|
|
1833
1903
|
content: instruction,
|
|
1834
1904
|
codexAppInput: withCodexAppContext({ text: t('card.voice.user_message', undefined, locDs) }, 'botmux_voice_summary_instruction', instruction, 'application'),
|
|
1835
1905
|
};
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1906
|
+
let accepted = false;
|
|
1907
|
+
try {
|
|
1908
|
+
if (ds.worker && !ds.worker.killed)
|
|
1909
|
+
accepted = sendWorkerInput(ds, voiceInput);
|
|
1910
|
+
else {
|
|
1911
|
+
forkWorker(ds, voiceInput, ds.hasHistory);
|
|
1912
|
+
accepted = true;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
catch (err) {
|
|
1916
|
+
logger.warn(`[${tag(ds)}] voice_summary failed before acceptance: `
|
|
1917
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
1918
|
+
}
|
|
1919
|
+
if (!accepted) {
|
|
1920
|
+
return { toast: { type: 'warning', content: t('card.voice.toast_worker_busy', undefined, locDs) } };
|
|
1921
|
+
}
|
|
1922
|
+
// Burn the replay key only after live IPC or the durable activation tail
|
|
1923
|
+
// accepted the exact instruction.
|
|
1924
|
+
voicedCardIds.add(dedupeKey);
|
|
1925
|
+
if (voicedCardIds.size > 5000) {
|
|
1926
|
+
voicedCardIds.clear();
|
|
1927
|
+
voicedCardIds.add(dedupeKey);
|
|
1928
|
+
}
|
|
1840
1929
|
logger.info(`[${tag(ds)}] voice_summary triggered by ${operatorOpenId ?? '?'}`);
|
|
1841
1930
|
return { toast: { type: 'success', content: t('card.voice.toast_wait', undefined, locDs) } };
|
|
1842
1931
|
}
|
|
@@ -1847,11 +1936,27 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1847
1936
|
// already omits the restart button when adoptMode=true, but a stale
|
|
1848
1937
|
// pre-fix card or a malformed action payload could still arrive.
|
|
1849
1938
|
const locDs = localeForBot(ds.larkAppId);
|
|
1850
|
-
if (ds.adoptedFrom) {
|
|
1939
|
+
if (ds.adoptedFrom || ds.initConfig?.adoptMode) {
|
|
1851
1940
|
logger.warn(`[${tag(ds)}] Rejected restart on adopt session — would kill user's pane`);
|
|
1852
1941
|
await sessionReply(rootId, t('card.action.adopt_no_restart', undefined, locDs));
|
|
1853
1942
|
return;
|
|
1854
1943
|
}
|
|
1944
|
+
// New Riff cards omit this button, but old/stale cards remain clickable.
|
|
1945
|
+
// Surface the same explicit close-and-recreate guidance as /restart
|
|
1946
|
+
// instead of forwarding an IPC the Riff worker must silently refuse.
|
|
1947
|
+
if (isRiffBackendSession(ds)) {
|
|
1948
|
+
logger.warn(`[${tag(ds)}] Rejected restart on Riff backend session`);
|
|
1949
|
+
const unsupported = t('cmd.restart.riff_unsupported', undefined, locDs);
|
|
1950
|
+
await deliverEphemeralOrReply(ds, operatorOpenId, unsupported, 'text', () => sessionReply(rootId, unsupported));
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
// Codex App: an accepted-but-unsettled dispatch still owns the turn route.
|
|
1954
|
+
// requestSessionRestart does not itself gate on dispatch ownership, so
|
|
1955
|
+
// reject here before the restart coordinator tears the worker down.
|
|
1956
|
+
if (hasProtectedSessionMutationOwnership(ds)) {
|
|
1957
|
+
await sessionReply(rootId, '当前 Codex App 仍有未结算消息,暂不能重启;请等待本轮完成或关闭会话。');
|
|
1958
|
+
return;
|
|
1959
|
+
}
|
|
1855
1960
|
if (isSessionTransferring(ds)) {
|
|
1856
1961
|
return {
|
|
1857
1962
|
toast: {
|
|
@@ -1877,27 +1982,40 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1877
1982
|
// 会话」却静默无反应会让人以为按钮坏了,给一条失败 toast(成功路径不弹,已关卡即反馈)。
|
|
1878
1983
|
return { toast: { type: 'warning', content: t('card.action.session_gone', undefined, localeForBot(larkAppId)) } };
|
|
1879
1984
|
}
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1985
|
+
const targetSessionId = ds.session.sessionId;
|
|
1986
|
+
const closed = await withBotTurnMutation(ds.larkAppId, async () => {
|
|
1987
|
+
// Card payload roots survive transfers. Re-resolve by immutable session
|
|
1988
|
+
// id after draining admissions, then let the pool remove the target's
|
|
1989
|
+
// CURRENT activeSessionKey. Never delete the stale payload root, which
|
|
1990
|
+
// may now belong to an unrelated pending FIFO owner.
|
|
1991
|
+
const current = [...activeSessions.values()].find(candidate => candidate.session.sessionId === targetSessionId);
|
|
1992
|
+
if (!current)
|
|
1993
|
+
return undefined;
|
|
1994
|
+
const botCfg = getBot(current.larkAppId).config;
|
|
1995
|
+
// Build the closed card BEFORE closeWorkerPoolSession — it reads the
|
|
1996
|
+
// live session's identity off `current`.
|
|
1997
|
+
const card = buildClosedSessionCard(current, localeForBot(current.larkAppId));
|
|
1998
|
+
try {
|
|
1999
|
+
await closeWorkerPoolSession(targetSessionId);
|
|
2000
|
+
}
|
|
2001
|
+
catch (err) {
|
|
2002
|
+
logger.error(`[${tag(current)}] Refused close because backing teardown was not verified: ${err}`);
|
|
2003
|
+
return { status: 'teardown_failed', err };
|
|
2004
|
+
}
|
|
2005
|
+
return { status: 'closed', current, botCfg, card };
|
|
2006
|
+
});
|
|
2007
|
+
if (!closed) {
|
|
2008
|
+
return { toast: { type: 'warning', content: t('card.action.session_gone', undefined, localeForBot(larkAppId)) } };
|
|
1886
2009
|
}
|
|
1887
|
-
|
|
1888
|
-
logger.error(`[${tag(ds)}] Refused close because backing teardown was not verified: ${err}`);
|
|
2010
|
+
if (closed.status === 'teardown_failed') {
|
|
1889
2011
|
return {
|
|
1890
2012
|
toast: {
|
|
1891
2013
|
type: 'warning',
|
|
1892
|
-
content: `会话关闭失败:${err instanceof Error ? err.message : String(err)}`,
|
|
2014
|
+
content: `会话关闭失败:${closed.err instanceof Error ? closed.err.message : String(closed.err)}`,
|
|
1893
2015
|
},
|
|
1894
2016
|
};
|
|
1895
2017
|
}
|
|
1896
|
-
|
|
1897
|
-
// cleanup. A new session may claim the same route during that await; only
|
|
1898
|
-
// delete here if this handler still owns the exact object it closed.
|
|
1899
|
-
if (activeSessions.get(sKey) === ds)
|
|
1900
|
-
activeSessions.delete(sKey);
|
|
2018
|
+
const { current, botCfg, card } = closed;
|
|
1901
2019
|
// The closed card carries session title / CLI name / workingDir / resume
|
|
1902
2020
|
// command. In private-card mode those must not leak to the group — send the
|
|
1903
2021
|
// closed card ephemeral to the same owner audience instead. No group
|
|
@@ -1906,15 +2024,15 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1906
2024
|
// clicked, so a card built in private mode stays ephemeral even if the
|
|
1907
2025
|
// bot's `privateCard` config was turned off in the meantime.
|
|
1908
2026
|
if (value?.visibility === 'private' || botCfg.privateCard) {
|
|
1909
|
-
const audience = resolvePrivateCardAudience(
|
|
2027
|
+
const audience = resolvePrivateCardAudience(current);
|
|
1910
2028
|
for (const openId of audience) {
|
|
1911
|
-
await sendEphemeralCard(
|
|
2029
|
+
await sendEphemeralCard(current.larkAppId, current.chatId, openId, card).catch(err => logger.warn(`[${tag(current)}] private close card ephemeral send to ${openId.substring(0, 8)}… failed: ${err}`));
|
|
1912
2030
|
}
|
|
1913
|
-
logger.info(`[${tag(
|
|
2031
|
+
logger.info(`[${tag(current)}] Closed via card button (private close card → ${audience.length} owner(s))`);
|
|
1914
2032
|
}
|
|
1915
2033
|
else {
|
|
1916
|
-
await deliverEphemeralOrReply(
|
|
1917
|
-
logger.info(`[${tag(
|
|
2034
|
+
await deliverEphemeralOrReply(current, operatorOpenId, card, 'interactive', () => sessionReply(rootId, card, 'interactive'));
|
|
2035
|
+
logger.info(`[${tag(current)}] Closed via card button`);
|
|
1918
2036
|
}
|
|
1919
2037
|
}
|
|
1920
2038
|
if (actionType === 'resume') {
|
|
@@ -1955,10 +2073,17 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1955
2073
|
}
|
|
1956
2074
|
}
|
|
1957
2075
|
if (actionType === 'disconnect' && ds) {
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
2076
|
+
const targetSessionId = ds.session.sessionId;
|
|
2077
|
+
const disconnected = await withBotTurnMutation(ds.larkAppId, async () => {
|
|
2078
|
+
const current = [...activeSessions.values()].find(candidate => candidate.session.sessionId === targetSessionId);
|
|
2079
|
+
if (!current)
|
|
2080
|
+
return undefined;
|
|
2081
|
+
await closeWorkerPoolSession(targetSessionId);
|
|
2082
|
+
return current;
|
|
2083
|
+
});
|
|
2084
|
+
if (!disconnected) {
|
|
2085
|
+
return { toast: { type: 'warning', content: t('card.action.session_gone', undefined, localeForBot(larkAppId)) } };
|
|
2086
|
+
}
|
|
1962
2087
|
await sessionReply(rootId, t('card.action.disconnected', undefined, localeForBot(ds.larkAppId)));
|
|
1963
2088
|
logger.info(`[${tag(ds)}] Disconnected (adopt) via card button`);
|
|
1964
2089
|
}
|
|
@@ -1981,6 +2106,36 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1981
2106
|
await sessionReply(rootId, t('card.action.retry_last_task_not_ready', { retryLabel: ds.usageLimit.retryLabel }, locDs));
|
|
1982
2107
|
return;
|
|
1983
2108
|
}
|
|
2109
|
+
const retryCodexAppInput = ds.lastCodexAppInput
|
|
2110
|
+
? (({ clientUserMessageId: _priorMessageId, ...input }) => input)(ds.lastCodexAppInput)
|
|
2111
|
+
: undefined;
|
|
2112
|
+
const retryInput = {
|
|
2113
|
+
content: cliInput,
|
|
2114
|
+
...(retryCodexAppInput ? { codexAppInput: retryCodexAppInput } : {}),
|
|
2115
|
+
};
|
|
2116
|
+
if ((!ds.worker || ds.worker.killed) && hasProtectedSessionMutationOwnership(ds)) {
|
|
2117
|
+
await sessionReply(rootId, t('card.action.retry_last_task_unavailable', undefined, locDs));
|
|
2118
|
+
return;
|
|
2119
|
+
}
|
|
2120
|
+
let accepted = false;
|
|
2121
|
+
try {
|
|
2122
|
+
if (ds.worker && !ds.worker.killed)
|
|
2123
|
+
accepted = sendWorkerInput(ds, retryInput);
|
|
2124
|
+
else {
|
|
2125
|
+
forkWorker(ds, retryInput, ds.hasHistory);
|
|
2126
|
+
accepted = true;
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
catch (err) {
|
|
2130
|
+
logger.warn(`[${tag(ds)}] retry_last_task failed before acceptance: `
|
|
2131
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
2132
|
+
}
|
|
2133
|
+
if (!accepted) {
|
|
2134
|
+
await sessionReply(rootId, t('card.action.retry_last_task_unavailable', undefined, locDs));
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
// Only now consume the one-shot retry state and advertise working: live
|
|
2138
|
+
// IPC or the durable activation tail already owns the exact retry input.
|
|
1984
2139
|
clearUsageLimitState(ds);
|
|
1985
2140
|
ds.lastScreenStatus = 'working';
|
|
1986
2141
|
ds.streamCardPending = true;
|
|
@@ -1993,17 +2148,6 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1993
2148
|
cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, ds.currentTurnTitle, '', 'working', sessionCliId(ds), ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, locDs, undefined, writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: sessionCliId(ds) }), getDaemonStreamingCardUsageSnapshot(ds, sessionCliId(ds)), sessionRuntimeDisplayName(ds), codexServiceTierBadge(sessionCliId(ds), ds.codexServiceTier));
|
|
1994
2149
|
scheduleCardPatch(ds, cardJson);
|
|
1995
2150
|
}
|
|
1996
|
-
const retryCodexAppInput = ds.lastCodexAppInput
|
|
1997
|
-
? (({ clientUserMessageId: _priorMessageId, ...input }) => input)(ds.lastCodexAppInput)
|
|
1998
|
-
: undefined;
|
|
1999
|
-
const retryInput = {
|
|
2000
|
-
content: cliInput,
|
|
2001
|
-
...(retryCodexAppInput ? { codexAppInput: retryCodexAppInput } : {}),
|
|
2002
|
-
};
|
|
2003
|
-
if (ds.worker && !ds.worker.killed)
|
|
2004
|
-
sendWorkerInput(ds, retryInput);
|
|
2005
|
-
else
|
|
2006
|
-
forkWorker(ds, retryInput, ds.hasHistory);
|
|
2007
2151
|
logger.info(`[${tag(ds)}] Retrying last task after usage limit`);
|
|
2008
2152
|
if (cardJson) {
|
|
2009
2153
|
try {
|
|
@@ -2527,21 +2671,24 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2527
2671
|
}
|
|
2528
2672
|
if (ds.pendingRepo) {
|
|
2529
2673
|
if (ds.worktreeCreating || ds.pendingRepoCommitInFlight) {
|
|
2530
|
-
return {
|
|
2674
|
+
return {
|
|
2675
|
+
toast: {
|
|
2676
|
+
type: 'info',
|
|
2677
|
+
content: t('cmd.repo.worktree_in_progress', undefined, locDs),
|
|
2678
|
+
},
|
|
2679
|
+
};
|
|
2531
2680
|
}
|
|
2532
2681
|
const cwd = getSessionWorkingDir(ds);
|
|
2533
|
-
//
|
|
2534
|
-
//
|
|
2535
|
-
//
|
|
2536
|
-
//
|
|
2537
|
-
|
|
2538
|
-
// + card withdraw run under the claim inside commitRepoSelection.
|
|
2539
|
-
await commitRepoSelection({ ds, rootId, cardMessageId, larkAppId, operatorOpenId, activeSessions, sessionReply }, cwd, pathBasename(cwd) || cwd, {
|
|
2682
|
+
// "Start directly" is the same pending-start commit as choosing a
|
|
2683
|
+
// directory, just pinned to the current/default cwd. Reusing the shared
|
|
2684
|
+
// helper gives this card path the bot mutation, exact-owner recheck,
|
|
2685
|
+
// buffered-sidecar handling, and fork-before-release ordering.
|
|
2686
|
+
const started = await commitRepoSelection({ ds, rootId, cardMessageId, larkAppId: larkAppId ?? ds.larkAppId, operatorOpenId, activeSessions, sessionReply }, cwd, cwd, {
|
|
2540
2687
|
suppressConfirmReply: true,
|
|
2541
2688
|
confirmReplyText: t('cmd.skip.opened', { cwd }, locDs),
|
|
2542
2689
|
pinWorkingDir: false,
|
|
2543
2690
|
});
|
|
2544
|
-
if (
|
|
2691
|
+
if (started) {
|
|
2545
2692
|
logger.info(`[${tag(ds)}] Skip repo, spawning CLI in ${cwd}`);
|
|
2546
2693
|
}
|
|
2547
2694
|
}
|
|
@@ -2583,10 +2730,10 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2583
2730
|
// re-send a fresh repo card in the new mode — a form can't ride an
|
|
2584
2731
|
// in-place patch, so the old card is withdrawn and a new one posted.
|
|
2585
2732
|
const locDs = localeForBot(ds.larkAppId);
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
return { toast: { type: '
|
|
2733
|
+
if (!cardMessageId || !ds.repoCardMessageId || cardMessageId !== ds.repoCardMessageId) {
|
|
2734
|
+
logger.warn(`[${tag(ds)}] Ignoring stale worktree-toggle picker ${cardMessageId ?? 'none'} `
|
|
2735
|
+
+ `(current=${ds.repoCardMessageId ?? 'none'})`);
|
|
2736
|
+
return { toast: { type: 'warning', content: t('card.repo.toast_stale_picker', undefined, locDs) } };
|
|
2590
2737
|
}
|
|
2591
2738
|
const spec = findConfigField('worktreeMultiPicker');
|
|
2592
2739
|
if (!spec)
|
|
@@ -2596,22 +2743,44 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2596
2743
|
if (!r.ok)
|
|
2597
2744
|
return { toast: { type: 'error', content: t('cmd.config.write_failed', { reason: r.reason }, locDs) } };
|
|
2598
2745
|
const projects = lastRepoScan.get(ds.chatId) ?? [];
|
|
2599
|
-
// await so a rejected delete is caught here (not an unhandled rejection);
|
|
2600
|
-
// a missing/already-gone card is fine — we post the fresh one regardless.
|
|
2601
|
-
if (ds.repoCardMessageId && ds.larkAppId) {
|
|
2602
|
-
try {
|
|
2603
|
-
await deleteMessage(ds.larkAppId, ds.repoCardMessageId);
|
|
2604
|
-
}
|
|
2605
|
-
catch { /* card already gone */ }
|
|
2606
|
-
}
|
|
2607
2746
|
const newCard = buildRepoSelectCard(projects, getSessionWorkingDir(ds), rootId, locDs, next);
|
|
2608
|
-
ds.repoCardMessageId
|
|
2747
|
+
const oldCardMessageId = ds.repoCardMessageId;
|
|
2748
|
+
let newCardMessageId;
|
|
2749
|
+
try {
|
|
2750
|
+
newCardMessageId = await sessionReply(rootId, newCard, 'interactive');
|
|
2751
|
+
}
|
|
2752
|
+
catch (err) {
|
|
2753
|
+
logger.warn(`[${tag(ds)}] Failed to publish replacement repo picker; old card remains authoritative: `
|
|
2754
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
2755
|
+
return { toast: { type: 'error', content: t('cmd.config.write_failed', { reason: err instanceof Error ? err.message : String(err) }, locDs) } };
|
|
2756
|
+
}
|
|
2757
|
+
try {
|
|
2758
|
+
// Switch durable authority before changing runtime identity or
|
|
2759
|
+
// withdrawing the old card. A failed write leaves the old picker fully
|
|
2760
|
+
// usable and makes the newly-published card stale by exact-ID guard.
|
|
2761
|
+
persistPendingRepoCardMessageId(ds, newCardMessageId);
|
|
2762
|
+
}
|
|
2763
|
+
catch (err) {
|
|
2764
|
+
logger.error(`[${tag(ds)}] Failed to persist replacement repo picker ${newCardMessageId}; `
|
|
2765
|
+
+ `old picker ${oldCardMessageId} retained: `
|
|
2766
|
+
+ `${err instanceof Error ? err.message : String(err)}`);
|
|
2767
|
+
return { toast: { type: 'error', content: t('cmd.config.write_failed', { reason: err instanceof Error ? err.message : String(err) }, locDs) } };
|
|
2768
|
+
}
|
|
2769
|
+
ds.repoCardMessageId = newCardMessageId;
|
|
2770
|
+
// The fresh ID is now durable. Withdrawal is best-effort and cannot
|
|
2771
|
+
// invalidate the new authority if Lark reports the old card missing.
|
|
2772
|
+
try {
|
|
2773
|
+
await deleteMessage(ds.larkAppId, oldCardMessageId);
|
|
2774
|
+
}
|
|
2775
|
+
catch { /* card already gone */ }
|
|
2609
2776
|
return { toast: { type: 'info', content: t(next ? 'card.repo.toast_worktree_mode_switched' : 'card.repo.toast_worktree_mode_switched_back', undefined, locDs) } };
|
|
2610
2777
|
}
|
|
2611
2778
|
if (actionType === 'repo_worktree_submit' && ds) {
|
|
2612
2779
|
const locDs = localeForBot(ds.larkAppId);
|
|
2613
|
-
if (cardMessageId
|
|
2614
|
-
|
|
2780
|
+
if (!cardMessageId || !ds.repoCardMessageId || cardMessageId !== ds.repoCardMessageId) {
|
|
2781
|
+
logger.warn(`[${tag(ds)}] Ignoring stale worktree-submit picker ${cardMessageId ?? 'none'} `
|
|
2782
|
+
+ `(current=${ds.repoCardMessageId ?? 'none'})`);
|
|
2783
|
+
return { toast: { type: 'warning', content: t('card.repo.toast_stale_picker', undefined, locDs) } };
|
|
2615
2784
|
}
|
|
2616
2785
|
const selectedPaths = stringListFromLarkMultiSelect(action?.form_value?.repo_worktree_paths);
|
|
2617
2786
|
if (selectedPaths.length === 0) {
|
|
@@ -2741,11 +2910,20 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2741
2910
|
logger.warn(`Card action: no active session found for root ${rootId}`);
|
|
2742
2911
|
return;
|
|
2743
2912
|
}
|
|
2744
|
-
//
|
|
2745
|
-
//
|
|
2746
|
-
//
|
|
2747
|
-
|
|
2748
|
-
|
|
2913
|
+
// The picker message id is the capability for every repo dropdown. Check it
|
|
2914
|
+
// before slug generation, worktreeCreating, git creation/push, or commit.
|
|
2915
|
+
// In particular the direct single-select `repo_worktree` callback does not
|
|
2916
|
+
// pass through the form-submit branch above, so relying on the later commit
|
|
2917
|
+
// check would allow a stale card to create an orphan worktree first.
|
|
2918
|
+
if (!cardMessageId || !targetDs.repoCardMessageId || cardMessageId !== targetDs.repoCardMessageId) {
|
|
2919
|
+
logger.warn(`[${tag(targetDs)}] Ignoring stale ${repoKey} picker ${cardMessageId ?? 'none'} `
|
|
2920
|
+
+ `(current=${targetDs.repoCardMessageId ?? 'none'})`);
|
|
2921
|
+
return {
|
|
2922
|
+
toast: {
|
|
2923
|
+
type: 'warning',
|
|
2924
|
+
content: t('card.repo.toast_stale_picker', undefined, localeForBot(targetDs.larkAppId)),
|
|
2925
|
+
},
|
|
2926
|
+
};
|
|
2749
2927
|
}
|
|
2750
2928
|
// 权限边界:pendingRepo(首次选 repo 才能 spawn)放行「会话发起人 或 canOperate」,
|
|
2751
2929
|
// 让本群授权用户能完成自己的首次使用;非 pending 的 mid-session 切换是管理动作,要 canOperate。
|
|
@@ -2757,6 +2935,14 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2757
2935
|
logger.info(`Repo card action blocked for ${operatorOpenId} (pending=${targetDs.pendingRepo})`);
|
|
2758
2936
|
return { toast: { type: 'error', content: t('card.grant.toast_no_repo_perm', undefined, localeForBot(targetDs.larkAppId)) } };
|
|
2759
2937
|
}
|
|
2938
|
+
// Reject a live Riff repo/worktree replacement before slug generation or
|
|
2939
|
+
// any local/remote Git side effect. First-spawn pendingRepo selections stay
|
|
2940
|
+
// recoverable when a synchronous fork failure has already stamped Riff.
|
|
2941
|
+
if (!targetDs.pendingRepo && isRiffBackendSession(targetDs)) {
|
|
2942
|
+
await sessionReply(rootId, t('cmd.cd.riff_unsupported', undefined, localeForBot(targetDs.larkAppId)));
|
|
2943
|
+
logger.warn(`[${tag(targetDs)}] Repo switch refused before Git work: Riff session requires explicit close before replacement`);
|
|
2944
|
+
return;
|
|
2945
|
+
}
|
|
2760
2946
|
// Resolve the project name from cached scan
|
|
2761
2947
|
const cached = lastRepoScan.get(targetDs.chatId);
|
|
2762
2948
|
const project = cached?.find(p => p.path === selectedPath);
|