botmux 3.7.1 → 3.8.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/capabilities.d.ts +17 -0
- package/dist/adapters/backend/capabilities.d.ts.map +1 -0
- package/dist/adapters/backend/capabilities.js +22 -0
- package/dist/adapters/backend/capabilities.js.map +1 -0
- package/dist/adapters/backend/critical-control-key.d.ts +19 -0
- package/dist/adapters/backend/critical-control-key.d.ts.map +1 -0
- package/dist/adapters/backend/critical-control-key.js +34 -0
- package/dist/adapters/backend/critical-control-key.js.map +1 -0
- package/dist/adapters/backend/herdr-backend.d.ts +9 -0
- package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
- package/dist/adapters/backend/herdr-backend.js +23 -4
- package/dist/adapters/backend/herdr-backend.js.map +1 -1
- package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
- package/dist/adapters/backend/riff-backend.js +3 -2
- package/dist/adapters/backend/riff-backend.js.map +1 -1
- package/dist/adapters/backend/session-backend-selector.d.ts +61 -6
- package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -1
- package/dist/adapters/backend/session-backend-selector.js +173 -9
- package/dist/adapters/backend/session-backend-selector.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +10 -3
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +25 -8
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +3 -2
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +45 -4
- package/dist/adapters/backend/types.d.ts.map +1 -1
- package/dist/adapters/backend/types.js +11 -0
- package/dist/adapters/backend/types.js.map +1 -1
- package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zellij-backend.js +3 -2
- package/dist/adapters/backend/zellij-backend.js.map +1 -1
- package/dist/adapters/backend/zmx-backend.d.ts +286 -0
- package/dist/adapters/backend/zmx-backend.d.ts.map +1 -0
- package/dist/adapters/backend/zmx-backend.js +2396 -0
- package/dist/adapters/backend/zmx-backend.js.map +1 -0
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +3 -0
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/adapters/cli/codex-app.d.ts.map +1 -1
- package/dist/adapters/cli/codex-app.js +5 -1
- package/dist/adapters/cli/codex-app.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +26 -1
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/fs-policy.d.ts +79 -1
- package/dist/adapters/cli/fs-policy.d.ts.map +1 -1
- package/dist/adapters/cli/fs-policy.js +290 -13
- package/dist/adapters/cli/fs-policy.js.map +1 -1
- package/dist/adapters/cli/grok.d.ts.map +1 -1
- package/dist/adapters/cli/grok.js +4 -1
- package/dist/adapters/cli/grok.js.map +1 -1
- package/dist/adapters/cli/oh-my-pi.d.ts.map +1 -1
- package/dist/adapters/cli/oh-my-pi.js +5 -3
- package/dist/adapters/cli/oh-my-pi.js.map +1 -1
- package/dist/adapters/cli/shared-hints.d.ts +7 -4
- package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
- package/dist/adapters/cli/shared-hints.js +52 -35
- package/dist/adapters/cli/shared-hints.js.map +1 -1
- package/dist/adapters/cli/strict-input-handle.d.ts +13 -0
- package/dist/adapters/cli/strict-input-handle.d.ts.map +1 -0
- package/dist/adapters/cli/strict-input-handle.js +39 -0
- package/dist/adapters/cli/strict-input-handle.js.map +1 -0
- package/dist/adapters/cli/traex.d.ts.map +1 -1
- package/dist/adapters/cli/traex.js +6 -4
- package/dist/adapters/cli/traex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +16 -0
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +137 -8
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +298 -12
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/session-list-liveness.d.ts +1 -1
- package/dist/cli/session-list-liveness.js +1 -1
- package/dist/cli/zmx-managed-attach.d.ts +41 -0
- package/dist/cli/zmx-managed-attach.d.ts.map +1 -0
- package/dist/cli/zmx-managed-attach.js +72 -0
- package/dist/cli/zmx-managed-attach.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +780 -131
- package/dist/cli.js.map +1 -1
- package/dist/codex-app-runner.js +90 -2
- package/dist/codex-app-runner.js.map +1 -1
- package/dist/codex-rpc-engine.d.ts.map +1 -1
- package/dist/codex-rpc-engine.js +22 -6
- package/dist/codex-rpc-engine.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +29 -1
- package/dist/config.js.map +1 -1
- package/dist/core/ask-broker.d.ts +22 -1
- package/dist/core/ask-broker.d.ts.map +1 -1
- package/dist/core/ask-broker.js +12 -5
- package/dist/core/ask-broker.js.map +1 -1
- package/dist/core/botmux-wrapper.d.ts +18 -0
- package/dist/core/botmux-wrapper.d.ts.map +1 -1
- package/dist/core/botmux-wrapper.js +25 -1
- package/dist/core/botmux-wrapper.js.map +1 -1
- package/dist/core/chat-rename.d.ts +27 -0
- package/dist/core/chat-rename.d.ts.map +1 -0
- package/dist/core/chat-rename.js +53 -0
- package/dist/core/chat-rename.js.map +1 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +160 -12
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/cost-calculator.d.ts +25 -1
- package/dist/core/cost-calculator.d.ts.map +1 -1
- package/dist/core/cost-calculator.js +102 -4
- package/dist/core/cost-calculator.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +16 -2
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +611 -62
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dashboard-rows.d.ts +22 -1
- package/dist/core/dashboard-rows.d.ts.map +1 -1
- package/dist/core/dashboard-rows.js +61 -0
- package/dist/core/dashboard-rows.js.map +1 -1
- package/dist/core/dashboard-url.d.ts +6 -0
- package/dist/core/dashboard-url.d.ts.map +1 -1
- package/dist/core/dashboard-url.js +11 -1
- package/dist/core/dashboard-url.js.map +1 -1
- package/dist/core/device-isolation-daemon.d.ts +28 -6
- package/dist/core/device-isolation-daemon.d.ts.map +1 -1
- package/dist/core/device-isolation-daemon.js +96 -13
- package/dist/core/device-isolation-daemon.js.map +1 -1
- package/dist/core/host-overload-alert.d.ts +75 -0
- package/dist/core/host-overload-alert.d.ts.map +1 -1
- package/dist/core/host-overload-alert.js +103 -0
- package/dist/core/host-overload-alert.js.map +1 -1
- package/dist/core/idle-worker-sweeper.js +1 -1
- package/dist/core/idle-worker-sweeper.js.map +1 -1
- package/dist/core/inflight-input-tracker.d.ts +3 -0
- package/dist/core/inflight-input-tracker.d.ts.map +1 -1
- package/dist/core/inflight-input-tracker.js +9 -0
- package/dist/core/inflight-input-tracker.js.map +1 -1
- package/dist/core/passthrough-commands.d.ts.map +1 -1
- package/dist/core/passthrough-commands.js +10 -0
- package/dist/core/passthrough-commands.js.map +1 -1
- package/dist/core/persistent-backend.d.ts +32 -4
- package/dist/core/persistent-backend.d.ts.map +1 -1
- package/dist/core/persistent-backend.js +75 -6
- package/dist/core/persistent-backend.js.map +1 -1
- package/dist/core/plugins/mcp/gateway.js +2 -2
- package/dist/core/plugins/mcp/gateway.js.map +1 -1
- package/dist/core/plugins/runtime.d.ts +3 -0
- package/dist/core/plugins/runtime.d.ts.map +1 -1
- package/dist/core/plugins/runtime.js.map +1 -1
- package/dist/core/plugins/service-manager.d.ts +4 -1
- package/dist/core/plugins/service-manager.d.ts.map +1 -1
- package/dist/core/plugins/service-manager.js +11 -6
- package/dist/core/plugins/service-manager.js.map +1 -1
- package/dist/core/role-library.d.ts +48 -1
- package/dist/core/role-library.d.ts.map +1 -1
- package/dist/core/role-library.js +111 -3
- package/dist/core/role-library.js.map +1 -1
- package/dist/core/role-resolver.d.ts +14 -2
- package/dist/core/role-resolver.d.ts.map +1 -1
- package/dist/core/role-resolver.js +47 -4
- package/dist/core/role-resolver.js.map +1 -1
- package/dist/core/session-activity.d.ts +14 -0
- package/dist/core/session-activity.d.ts.map +1 -1
- package/dist/core/session-activity.js +46 -1
- package/dist/core/session-activity.js.map +1 -1
- package/dist/core/session-board.d.ts +5 -2
- package/dist/core/session-board.d.ts.map +1 -1
- package/dist/core/session-board.js +5 -2
- package/dist/core/session-board.js.map +1 -1
- package/dist/core/session-liveness.d.ts +6 -5
- package/dist/core/session-liveness.d.ts.map +1 -1
- package/dist/core/session-liveness.js +34 -13
- package/dist/core/session-liveness.js.map +1 -1
- package/dist/core/session-manager.d.ts +3 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +397 -103
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-marker.d.ts +11 -2
- package/dist/core/session-marker.d.ts.map +1 -1
- package/dist/core/session-marker.js +50 -11
- package/dist/core/session-marker.js.map +1 -1
- package/dist/core/session-message-preview.d.ts +20 -0
- package/dist/core/session-message-preview.d.ts.map +1 -0
- package/dist/core/session-message-preview.js +188 -0
- package/dist/core/session-message-preview.js.map +1 -0
- package/dist/core/session-title.d.ts +5 -1
- package/dist/core/session-title.d.ts.map +1 -1
- package/dist/core/session-title.js +20 -3
- package/dist/core/session-title.js.map +1 -1
- package/dist/core/startup-commands.d.ts +1 -1
- package/dist/core/startup-commands.js +1 -1
- package/dist/core/terminal-url.d.ts.map +1 -1
- package/dist/core/terminal-url.js +3 -2
- package/dist/core/terminal-url.js.map +1 -1
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +75 -12
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +35 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +23 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-ipc.d.ts +11 -0
- package/dist/core/worker-ipc.d.ts.map +1 -0
- package/dist/core/worker-ipc.js +26 -0
- package/dist/core/worker-ipc.js.map +1 -0
- package/dist/core/worker-pool.d.ts +179 -29
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +2118 -331
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts +132 -3
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +1327 -449
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/auth.d.ts +2 -0
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +9 -0
- package/dist/dashboard/auth.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +4 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +5 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/federation-api.d.ts.map +1 -1
- package/dist/dashboard/federation-api.js +4 -0
- package/dist/dashboard/federation-api.js.map +1 -1
- package/dist/dashboard/federation-spoke-api.d.ts.map +1 -1
- package/dist/dashboard/federation-spoke-api.js +32 -5
- package/dist/dashboard/federation-spoke-api.js.map +1 -1
- package/dist/dashboard/public-redact.d.ts +8 -2
- package/dist/dashboard/public-redact.d.ts.map +1 -1
- package/dist/dashboard/public-redact.js +34 -7
- package/dist/dashboard/public-redact.js.map +1 -1
- package/dist/dashboard/session-card-model.d.ts.map +1 -1
- package/dist/dashboard/session-card-model.js +10 -2
- package/dist/dashboard/session-card-model.js.map +1 -1
- package/dist/dashboard/settings-write-applier.d.ts +35 -1
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +88 -1
- package/dist/dashboard/settings-write-applier.js.map +1 -1
- package/dist/dashboard/web/app.d.ts.map +1 -1
- package/dist/dashboard/web/app.js +0 -1
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.d.ts +15 -0
- package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults-page.js +212 -58
- package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +3 -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/bot-onboarding.d.ts +2 -1
- package/dist/dashboard/web/bot-onboarding.d.ts.map +1 -1
- package/dist/dashboard/web/bot-onboarding.js.map +1 -1
- package/dist/dashboard/web/clipboard.d.ts +2 -0
- package/dist/dashboard/web/clipboard.d.ts.map +1 -0
- package/dist/dashboard/web/clipboard.js +42 -0
- package/dist/dashboard/web/clipboard.js.map +1 -0
- package/dist/dashboard/web/connectors-page.d.ts.map +1 -1
- package/dist/dashboard/web/connectors-page.js +12 -8
- package/dist/dashboard/web/connectors-page.js.map +1 -1
- package/dist/dashboard/web/dashboard-components.d.ts +17 -16
- package/dist/dashboard/web/dashboard-components.d.ts.map +1 -1
- package/dist/dashboard/web/dashboard-components.js.map +1 -1
- package/dist/dashboard/web/groups-page.d.ts.map +1 -1
- package/dist/dashboard/web/groups-page.js +7 -3
- 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 +205 -12
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/icon-tooltip.d.ts.map +1 -1
- package/dist/dashboard/web/icon-tooltip.js +16 -1
- package/dist/dashboard/web/icon-tooltip.js.map +1 -1
- package/dist/dashboard/web/listener-filters.d.ts +23 -0
- package/dist/dashboard/web/listener-filters.d.ts.map +1 -0
- package/dist/dashboard/web/listener-filters.js +36 -0
- package/dist/dashboard/web/listener-filters.js.map +1 -0
- package/dist/dashboard/web/overview-page.d.ts.map +1 -1
- package/dist/dashboard/web/overview-page.js.map +1 -1
- package/dist/dashboard/web/plugin-page.d.ts.map +1 -1
- package/dist/dashboard/web/plugin-page.js.map +1 -1
- package/dist/dashboard/web/preview-markdown.d.ts +5 -0
- package/dist/dashboard/web/preview-markdown.d.ts.map +1 -0
- package/dist/dashboard/web/preview-markdown.js +52 -0
- package/dist/dashboard/web/preview-markdown.js.map +1 -0
- package/dist/dashboard/web/roles-page.d.ts.map +1 -1
- package/dist/dashboard/web/roles-page.js +535 -10
- package/dist/dashboard/web/roles-page.js.map +1 -1
- package/dist/dashboard/web/roles.d.ts +77 -0
- package/dist/dashboard/web/roles.d.ts.map +1 -1
- package/dist/dashboard/web/roles.js +98 -0
- package/dist/dashboard/web/roles.js.map +1 -1
- package/dist/dashboard/web/session-terminal.d.ts.map +1 -1
- package/dist/dashboard/web/session-terminal.js +6 -4
- package/dist/dashboard/web/session-terminal.js.map +1 -1
- package/dist/dashboard/web/sessions-kanban.d.ts +4 -1
- package/dist/dashboard/web/sessions-kanban.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-kanban.js +1 -1
- package/dist/dashboard/web/sessions-kanban.js.map +1 -1
- package/dist/dashboard/web/sessions-page.d.ts +3 -2
- package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
- package/dist/dashboard/web/sessions-page.js +163 -28
- package/dist/dashboard/web/sessions-page.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts +25 -0
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +58 -7
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/settings-page.d.ts +28 -3
- package/dist/dashboard/web/settings-page.d.ts.map +1 -1
- package/dist/dashboard/web/settings-page.js +61 -1
- package/dist/dashboard/web/settings-page.js.map +1 -1
- package/dist/dashboard/web/skills-page.d.ts +6 -5
- package/dist/dashboard/web/skills-page.d.ts.map +1 -1
- package/dist/dashboard/web/skills-page.js +2 -0
- package/dist/dashboard/web/skills-page.js.map +1 -1
- package/dist/dashboard/web/v3-components.d.ts +3 -2
- package/dist/dashboard/web/v3-components.d.ts.map +1 -1
- package/dist/dashboard/web/v3-components.js.map +1 -1
- package/dist/dashboard/web/vc-consumer-profiles-section.d.ts +12 -2
- package/dist/dashboard/web/vc-consumer-profiles-section.d.ts.map +1 -1
- package/dist/dashboard/web/vc-consumer-profiles-section.js +0 -9
- package/dist/dashboard/web/vc-consumer-profiles-section.js.map +1 -1
- package/dist/dashboard/web/webhook-logs-page.d.ts +2 -1
- package/dist/dashboard/web/webhook-logs-page.d.ts.map +1 -1
- package/dist/dashboard/web/webhook-logs-page.js +4 -1
- package/dist/dashboard/web/webhook-logs-page.js.map +1 -1
- package/dist/dashboard-web/app.js +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-FAPIMQV3.js +10 -0
- package/dist/dashboard-web/chunks/{chunk-QFMH6ITB.js → chunk-5CJMHTT4.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-KQB42ML5.js → chunk-DLZK52JU.js} +14 -12
- package/dist/dashboard-web/chunks/chunk-FDZ7WOBW.js +16 -0
- package/dist/dashboard-web/chunks/{chunk-3QAUJXAS.js → chunk-GTAJV5YC.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-FNPGOZMX.js → chunk-IHNG6KE6.js} +1 -1
- package/dist/dashboard-web/chunks/{chunk-FLGXZJTD.js → chunk-VA6QWSGQ.js} +1 -1
- package/dist/dashboard-web/chunks/chunk-XJ7BGPJH.js +1 -0
- package/dist/dashboard-web/chunks/chunk-ZJVTGU5X.js +1 -0
- package/dist/dashboard-web/chunks/connectors-page-LKWRT3GS.js +1 -0
- package/dist/dashboard-web/chunks/groups-page-V6654ICQ.js +8 -0
- package/dist/dashboard-web/chunks/insights-page-237RMFQ5.js +7 -0
- package/dist/dashboard-web/chunks/{monitor-room-7IVIS75W.js → monitor-room-OO5Y4FRQ.js} +1 -1
- package/dist/dashboard-web/chunks/{monitoring-page-5SSJRQZF.js → monitoring-page-OUEITM5P.js} +1 -1
- package/dist/dashboard-web/chunks/{office-page-EOND3L5D.js → office-page-4KNAHQ4Q.js} +1 -1
- package/dist/dashboard-web/chunks/{overview-page-NISBVBBJ.js → overview-page-QDP4YK2S.js} +1 -1
- package/dist/dashboard-web/chunks/{plugin-page-JXC5F5YV.js → plugin-page-VEGTCEAS.js} +1 -1
- package/dist/dashboard-web/chunks/roles-page-PGXNZZNP.js +1 -0
- package/dist/dashboard-web/chunks/{schedules-page-AVOOKLUH.js → schedules-page-BJNC5TID.js} +1 -1
- package/dist/dashboard-web/chunks/sessions-page-M2ZYYCMO.js +6 -0
- package/dist/dashboard-web/chunks/settings-page-IJFZKUUW.js +2 -0
- package/dist/dashboard-web/chunks/{skills-page-5MW54PCC.js → skills-page-YRAIRZOQ.js} +3 -3
- package/dist/dashboard-web/chunks/{team-federation-page-2SNIMVXI.js → team-federation-page-DEU27GRU.js} +1 -1
- package/dist/dashboard-web/chunks/{v3-page-W6SBXXQC.js → v3-page-6KHEW2YL.js} +5 -5
- package/dist/dashboard-web/chunks/{whiteboards-page-STC54KTP.js → whiteboards-page-VDECAALH.js} +1 -1
- package/dist/dashboard-web/style.css +12545 -11432
- package/dist/dashboard.js +215 -36
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +41 -2
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +47 -0
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +71 -19
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +71 -19
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/callback-button-marker.d.ts +34 -0
- package/dist/im/lark/callback-button-marker.d.ts.map +1 -0
- package/dist/im/lark/callback-button-marker.js +99 -0
- package/dist/im/lark/callback-button-marker.js.map +1 -0
- package/dist/im/lark/card-builder.d.ts +46 -10
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +299 -78
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +3 -4
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +380 -148
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +24 -1
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +101 -8
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/doc-comment.d.ts.map +1 -1
- package/dist/im/lark/doc-comment.js +6 -1
- package/dist/im/lark/doc-comment.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +115 -3
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +534 -147
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-command.d.ts +16 -12
- package/dist/im/lark/grant-command.d.ts.map +1 -1
- package/dist/im/lark/grant-command.js +48 -151
- package/dist/im/lark/grant-command.js.map +1 -1
- package/dist/im/lark/grant-pending.d.ts +12 -4
- package/dist/im/lark/grant-pending.d.ts.map +1 -1
- package/dist/im/lark/grant-pending.js +31 -8
- package/dist/im/lark/grant-pending.js.map +1 -1
- package/dist/im/lark/invite-command.d.ts +20 -0
- package/dist/im/lark/invite-command.d.ts.map +1 -0
- package/dist/im/lark/invite-command.js +239 -0
- package/dist/im/lark/invite-command.js.map +1 -0
- package/dist/im/lark/md-card.d.ts +66 -4
- package/dist/im/lark/md-card.d.ts.map +1 -1
- package/dist/im/lark/md-card.js +201 -31
- package/dist/im/lark/md-card.js.map +1 -1
- package/dist/im/lark/mention-targets.d.ts +43 -0
- package/dist/im/lark/mention-targets.d.ts.map +1 -0
- package/dist/im/lark/mention-targets.js +190 -0
- package/dist/im/lark/mention-targets.js.map +1 -0
- package/dist/im/lark/message-parser.d.ts +31 -0
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +305 -32
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/reply-card-footer-signature.d.ts +7 -0
- package/dist/im/lark/reply-card-footer-signature.d.ts.map +1 -0
- package/dist/im/lark/reply-card-footer-signature.js +7 -0
- package/dist/im/lark/reply-card-footer-signature.js.map +1 -0
- package/dist/im/lark/sessions-card.d.ts.map +1 -1
- package/dist/im/lark/sessions-card.js +4 -1
- package/dist/im/lark/sessions-card.js.map +1 -1
- package/dist/im/lark/substitute-command.d.ts.map +1 -1
- package/dist/im/lark/substitute-command.js +8 -0
- package/dist/im/lark/substitute-command.js.map +1 -1
- package/dist/im/lark/topic-root-context.d.ts +35 -0
- package/dist/im/lark/topic-root-context.d.ts.map +1 -0
- package/dist/im/lark/topic-root-context.js +37 -0
- package/dist/im/lark/topic-root-context.js.map +1 -0
- package/dist/im/lark/v3-blocked-card.d.ts.map +1 -1
- package/dist/im/lark/v3-blocked-card.js +2 -1
- package/dist/im/lark/v3-blocked-card.js.map +1 -1
- package/dist/im/lark/v3-gate-card.d.ts.map +1 -1
- package/dist/im/lark/v3-gate-card.js +2 -1
- package/dist/im/lark/v3-gate-card.js.map +1 -1
- package/dist/im/lark/v3-loop-grant-card.d.ts.map +1 -1
- package/dist/im/lark/v3-loop-grant-card.js +2 -1
- package/dist/im/lark/v3-loop-grant-card.js.map +1 -1
- package/dist/im/lark/v3-progress-card.d.ts.map +1 -1
- package/dist/im/lark/v3-progress-card.js +2 -1
- package/dist/im/lark/v3-progress-card.js.map +1 -1
- package/dist/im/lark/v3-revisit-grant-card.d.ts.map +1 -1
- package/dist/im/lark/v3-revisit-grant-card.js +2 -1
- package/dist/im/lark/v3-revisit-grant-card.js.map +1 -1
- package/dist/index-core-only.d.ts +3 -0
- package/dist/index-core-only.d.ts.map +1 -0
- package/dist/index-core-only.js +107 -0
- package/dist/index-core-only.js.map +1 -0
- package/dist/services/async-trigger-state.d.ts +8 -0
- package/dist/services/async-trigger-state.d.ts.map +1 -1
- package/dist/services/async-trigger-state.js +3 -2
- package/dist/services/async-trigger-state.js.map +1 -1
- package/dist/services/async-trigger-store.d.ts +10 -2
- package/dist/services/async-trigger-store.d.ts.map +1 -1
- package/dist/services/async-trigger-store.js +3 -2
- package/dist/services/async-trigger-store.js.map +1 -1
- package/dist/services/backend-availability.d.ts +3 -1
- package/dist/services/backend-availability.d.ts.map +1 -1
- package/dist/services/backend-availability.js +14 -1
- package/dist/services/backend-availability.js.map +1 -1
- package/dist/services/backend-type-store.d.ts.map +1 -1
- package/dist/services/backend-type-store.js +1 -1
- package/dist/services/backend-type-store.js.map +1 -1
- package/dist/services/bot-config-store.d.ts.map +1 -1
- package/dist/services/bot-config-store.js +2 -1
- package/dist/services/bot-config-store.js.map +1 -1
- package/dist/services/bridge-fallback-gate.d.ts +14 -1
- package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
- package/dist/services/bridge-fallback-gate.js +65 -1
- package/dist/services/bridge-fallback-gate.js.map +1 -1
- package/dist/services/card-prefs-store.d.ts +7 -2
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +30 -4
- package/dist/services/card-prefs-store.js.map +1 -1
- package/dist/services/codex-app-runner-protocol.d.ts +9 -0
- package/dist/services/codex-app-runner-protocol.d.ts.map +1 -1
- package/dist/services/codex-app-runner-protocol.js +17 -0
- package/dist/services/codex-app-runner-protocol.js.map +1 -1
- package/dist/services/codex-app-token-usage.d.ts +93 -0
- package/dist/services/codex-app-token-usage.d.ts.map +1 -0
- package/dist/services/codex-app-token-usage.js +190 -0
- package/dist/services/codex-app-token-usage.js.map +1 -0
- package/dist/services/codex-transcript.d.ts +6 -1
- package/dist/services/codex-transcript.d.ts.map +1 -1
- package/dist/services/codex-transcript.js +103 -32
- package/dist/services/codex-transcript.js.map +1 -1
- package/dist/services/federation-roster.d.ts +4 -0
- package/dist/services/federation-roster.d.ts.map +1 -1
- package/dist/services/federation-roster.js +3 -0
- package/dist/services/federation-roster.js.map +1 -1
- package/dist/services/federation-store.d.ts +6 -0
- package/dist/services/federation-store.d.ts.map +1 -1
- package/dist/services/federation-store.js.map +1 -1
- package/dist/services/grant-policy.d.ts +13 -0
- package/dist/services/grant-policy.d.ts.map +1 -0
- package/dist/services/grant-policy.js +40 -0
- package/dist/services/grant-policy.js.map +1 -0
- package/dist/services/grant-store.d.ts +24 -3
- package/dist/services/grant-store.d.ts.map +1 -1
- package/dist/services/grant-store.js +196 -12
- package/dist/services/grant-store.js.map +1 -1
- package/dist/services/groups-store.d.ts +36 -0
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +123 -1
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/jsonl-cursor.d.ts.map +1 -1
- package/dist/services/jsonl-cursor.js +8 -2
- package/dist/services/jsonl-cursor.js.map +1 -1
- package/dist/services/local-cli-opener.d.ts.map +1 -1
- package/dist/services/local-cli-opener.js +32 -0
- package/dist/services/local-cli-opener.js.map +1 -1
- package/dist/services/message-listener-run-preview-store.d.ts +43 -0
- package/dist/services/message-listener-run-preview-store.d.ts.map +1 -0
- package/dist/services/message-listener-run-preview-store.js +152 -0
- package/dist/services/message-listener-run-preview-store.js.map +1 -0
- package/dist/services/message-listener-store.d.ts +26 -0
- package/dist/services/message-listener-store.d.ts.map +1 -0
- package/dist/services/message-listener-store.js +138 -0
- package/dist/services/message-listener-store.js.map +1 -0
- package/dist/services/message-listener.d.ts +112 -0
- package/dist/services/message-listener.d.ts.map +1 -0
- package/dist/services/message-listener.js +391 -0
- package/dist/services/message-listener.js.map +1 -0
- package/dist/services/overload-alert-migration.d.ts +15 -0
- package/dist/services/overload-alert-migration.d.ts.map +1 -0
- package/dist/services/overload-alert-migration.js +115 -0
- package/dist/services/overload-alert-migration.js.map +1 -0
- package/dist/services/send-policy.d.ts +38 -0
- package/dist/services/send-policy.d.ts.map +1 -1
- package/dist/services/send-policy.js +34 -0
- package/dist/services/send-policy.js.map +1 -1
- package/dist/services/session-store.d.ts +12 -1
- package/dist/services/session-store.d.ts.map +1 -1
- package/dist/services/session-store.js +44 -2
- package/dist/services/session-store.js.map +1 -1
- package/dist/services/skill-registry-store.d.ts.map +1 -1
- package/dist/services/skill-registry-store.js +87 -12
- package/dist/services/skill-registry-store.js.map +1 -1
- package/dist/services/substitute-mode-normalize.d.ts +3 -0
- package/dist/services/substitute-mode-normalize.d.ts.map +1 -1
- package/dist/services/substitute-mode-normalize.js +8 -0
- package/dist/services/substitute-mode-normalize.js.map +1 -1
- package/dist/services/substitute-mode-store.d.ts.map +1 -1
- package/dist/services/substitute-mode-store.js +8 -1
- package/dist/services/substitute-mode-store.js.map +1 -1
- package/dist/services/transcript-resolver.d.ts +6 -1
- package/dist/services/transcript-resolver.d.ts.map +1 -1
- package/dist/services/transcript-resolver.js +145 -29
- package/dist/services/transcript-resolver.js.map +1 -1
- package/dist/services/trigger-types.d.ts +16 -0
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js +6 -0
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/setup/bot-config-editor.js +2 -2
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/setup/ensure-zmx.d.ts +31 -0
- package/dist/setup/ensure-zmx.d.ts.map +1 -0
- package/dist/setup/ensure-zmx.js +161 -0
- package/dist/setup/ensure-zmx.js.map +1 -0
- package/dist/setup/index.d.ts +1 -1
- package/dist/setup/index.js +1 -1
- package/dist/setup/open-platform-automation.d.ts +9 -0
- package/dist/setup/open-platform-automation.d.ts.map +1 -1
- package/dist/setup/open-platform-automation.js +15 -2
- package/dist/setup/open-platform-automation.js.map +1 -1
- package/dist/setup/setup-args.d.ts +1 -1
- package/dist/setup/setup-args.d.ts.map +1 -1
- package/dist/setup/setup-args.js +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +32 -0
- package/dist/skills/definitions.js.map +1 -1
- package/dist/skills/injection-mode.d.ts +6 -2
- package/dist/skills/injection-mode.d.ts.map +1 -1
- package/dist/skills/injection-mode.js +10 -5
- package/dist/skills/injection-mode.js.map +1 -1
- package/dist/types.d.ts +79 -3
- package/dist/types.d.ts.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 +4 -0
- package/dist/utils/child-env.js.map +1 -1
- package/dist/utils/effort-confirm-dialog.d.ts +40 -0
- package/dist/utils/effort-confirm-dialog.d.ts.map +1 -0
- package/dist/utils/effort-confirm-dialog.js +74 -0
- package/dist/utils/effort-confirm-dialog.js.map +1 -0
- package/dist/utils/pending-input-queue.d.ts +56 -0
- package/dist/utils/pending-input-queue.d.ts.map +1 -1
- package/dist/utils/pending-input-queue.js +80 -0
- package/dist/utils/pending-input-queue.js.map +1 -1
- package/dist/utils/render-dimensions.d.ts +8 -0
- package/dist/utils/render-dimensions.d.ts.map +1 -1
- package/dist/utils/render-dimensions.js +10 -0
- package/dist/utils/render-dimensions.js.map +1 -1
- package/dist/utils/stuck-key-guard.d.ts +3 -2
- package/dist/utils/stuck-key-guard.d.ts.map +1 -1
- package/dist/utils/stuck-key-guard.js +3 -3
- package/dist/utils/stuck-key-guard.js.map +1 -1
- package/dist/utils/terminal-renderer.d.ts +6 -0
- package/dist/utils/terminal-renderer.d.ts.map +1 -1
- package/dist/utils/terminal-renderer.js +15 -0
- package/dist/utils/terminal-renderer.js.map +1 -1
- package/dist/utils/tui-input-delivery.d.ts +32 -0
- package/dist/utils/tui-input-delivery.d.ts.map +1 -0
- package/dist/utils/tui-input-delivery.js +53 -0
- package/dist/utils/tui-input-delivery.js.map +1 -0
- package/dist/utils/xml.d.ts +19 -0
- package/dist/utils/xml.d.ts.map +1 -0
- package/dist/utils/xml.js +26 -0
- package/dist/utils/xml.js.map +1 -0
- package/dist/worker.js +1852 -374
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +8 -8
- package/dist/workflows/events/payloads.d.ts +8 -8
- package/dist/workflows/events/schema.d.ts +160 -160
- package/dist/workflows/v3/session-relay-client.js +1 -1
- package/dist/workflows/v3/session-relay-client.js.map +1 -1
- package/dist/workflows/v3/worker-fence.d.ts.map +1 -1
- package/dist/workflows/v3/worker-fence.js +15 -10
- package/dist/workflows/v3/worker-fence.js.map +1 -1
- package/package.json +1 -1
- package/dist/dashboard-web/chunks/bot-defaults-page-UKA7K4JV.js +0 -10
- package/dist/dashboard-web/chunks/chunk-DFLNXQNT.js +0 -1
- package/dist/dashboard-web/chunks/connectors-page-A77YAB5Y.js +0 -1
- package/dist/dashboard-web/chunks/groups-page-RUZNVXQB.js +0 -8
- package/dist/dashboard-web/chunks/insights-page-RF7U7RTA.js +0 -22
- package/dist/dashboard-web/chunks/roles-page-BUMKD7Y4.js +0 -1
- package/dist/dashboard-web/chunks/sessions-page-6HLSEAB7.js +0 -2
- package/dist/dashboard-web/chunks/settings-page-MJDZHQ4X.js +0 -2
|
@@ -3,12 +3,13 @@ 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,
|
|
7
|
-
import { findConfigField, applyConfigField, coerceConfigValue, getConfigCardData } from '../../services/bot-config-store.js';
|
|
6
|
+
import { buildSessionCard, buildStreamingCard, buildTuiPromptCard, buildTuiPromptProcessingCard, buildGrantResultCard, getCliDisplayName, truncateContent, buildConfigCard, buildConfigTextCard, CONFIG_UNSET, buildRepoSelectCard } from './card-builder.js';
|
|
7
|
+
import { findConfigField, applyConfigField, coerceConfigValue, getConfigCardData, } from '../../services/bot-config-store.js';
|
|
8
8
|
import { updateBotGrantPrefs } from '../../services/grant-prefs-store.js';
|
|
9
9
|
import { writeTeamRoleFile, deleteTeamRoleFile } from '../../core/role-resolver.js';
|
|
10
10
|
import { addChatGrant, addGlobalGrant } from '../../services/grant-store.js';
|
|
11
|
-
import { checkNonce, clearPending, markDenied,
|
|
11
|
+
import { checkNonce, clearPending, getPendingGrantLimits, getPendingMessage, markDenied, updatePendingGrantLimits, } from './grant-pending.js';
|
|
12
|
+
import { normalizeGrantDurationOption, normalizeGrantQuotaOption } from '../../services/grant-policy.js';
|
|
12
13
|
import { claimOverloadNonce, releaseOverloadNonce } from './overload-nonce.js';
|
|
13
14
|
import { buildOverloadAlertCard, buildOverloadExpiredCard, OVERLOAD_ACTION_CLEAN_STOPPED, OVERLOAD_ACTION_SUSPEND_IDLE, OVERLOAD_ACTION_NOOP, } from '../../core/host-overload-alert.js';
|
|
14
15
|
import { listOnlineDaemons } from '../../utils/daemon-discovery.js';
|
|
@@ -27,11 +28,12 @@ import { ttadkConfigModelChoices } from '../../setup/cli-selection.js';
|
|
|
27
28
|
import { logger } from '../../utils/logger.js';
|
|
28
29
|
import * as sessionStore from '../../services/session-store.js';
|
|
29
30
|
import { loadFrozenCards, saveFrozenCards } from '../../services/frozen-card-store.js';
|
|
30
|
-
import { forkWorker, sendWorkerInput, killWorker, scheduleCardPatch, parkStreamCard, clearUsageLimitState, cardUsageLimit, writableTerminalLinkFor, resolvePrivateCardAudience, deliverWriteLinkCard, deliverEphemeralOrReply, CARD_POSTING_SENTINEL, requestSessionRestart } from '../../core/worker-pool.js';
|
|
31
|
+
import { forkWorker, sendWorkerInput, sendWorkerSessionInput, killWorker, closeSession, teardownAuthoritativePersistentBackingBeforeClose, scheduleCardPatch, parkStreamCard, clearUsageLimitState, cardUsageLimit, writableTerminalLinkFor, workerHasInitialized, sessionSupportsWebTerminal, readableTerminalUrlFor, resolvePrivateCardAudience, deliverWriteLinkCard, deliverEphemeralOrReply, CARD_POSTING_SENTINEL, requestSessionRestart, isSessionTransferring, getDaemonStreamingCardUsageSnapshot } from '../../core/worker-pool.js';
|
|
31
32
|
import { getSessionWorkingDir, buildNewTopicCliInput, getAvailableBots, persistStreamCardState, resumeSession, rememberLastCliInput, ensureSessionWhiteboard } from '../../core/session-manager.js';
|
|
32
33
|
import { markInitialUserTurnPending } from '../../core/initial-user-turn.js';
|
|
33
|
-
import { publishAttentionPatch, announcePendingRepoSession } from '../../core/session-activity.js';
|
|
34
|
+
import { publishAttentionPatch, publishClosedSessionPatch, announcePendingRepoSession } from '../../core/session-activity.js';
|
|
34
35
|
import { fallbackTurnId } from '../../core/reply-target.js';
|
|
36
|
+
import { sendWorkerIpc } from '../../core/worker-ipc.js';
|
|
35
37
|
import { validateWorkingDir } from '../../core/working-dir.js';
|
|
36
38
|
import { sessionKey, sessionAnchorId, frozenDisplayMode, markRepoCardConsumed, isActiveRepoCard, claimCurrentRepoCard } from '../../core/types.js';
|
|
37
39
|
import { buildTerminalUrl } from '../../core/terminal-url.js';
|
|
@@ -402,6 +404,23 @@ opts) {
|
|
|
402
404
|
}
|
|
403
405
|
else {
|
|
404
406
|
// Mid-session repo switch — close old session, start fresh.
|
|
407
|
+
// ZMX close is identity/generation verified and may refuse. Prove teardown
|
|
408
|
+
// before claiming the card or mutating any old-session state; on refusal
|
|
409
|
+
// the current session and card remain fully retryable.
|
|
410
|
+
try {
|
|
411
|
+
teardownAuthoritativePersistentBackingBeforeClose(ds);
|
|
412
|
+
}
|
|
413
|
+
catch (err) {
|
|
414
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
415
|
+
logger.warn(`[${tag(ds)}] Repo switch refused because backing teardown was not proven: ${reason}`);
|
|
416
|
+
try {
|
|
417
|
+
await sessionReply(rootId, t('cmd.repo.switch_close_failed', { error: reason }, locTarget));
|
|
418
|
+
}
|
|
419
|
+
catch (replyErr) {
|
|
420
|
+
logger.warn(`[${tag(ds)}] Repo-switch teardown failure reply failed: ${replyErr instanceof Error ? replyErr.message : replyErr}`);
|
|
421
|
+
}
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
405
424
|
// Claim the current card BEFORE killWorker / any await. A concurrent click
|
|
406
425
|
// on the same Feishu card must not pass a second kill+fork while the first
|
|
407
426
|
// switch is still awaiting confirm replies. Correctness does not depend on
|
|
@@ -423,6 +442,7 @@ opts) {
|
|
|
423
442
|
// the displaced session's stored workingDir (and the closed card), so
|
|
424
443
|
// `claude --resume` later would reopen the old context in the new repo's
|
|
425
444
|
// cwd. The new repo is pinned onto the fresh session below instead.
|
|
445
|
+
const oldSession = ds.session;
|
|
426
446
|
const closedCard = buildClosedSessionCard(ds, locTarget);
|
|
427
447
|
killWorker(ds);
|
|
428
448
|
// Park the current card in `frozenCards` so the next POST under the new
|
|
@@ -433,8 +453,15 @@ opts) {
|
|
|
433
453
|
// stays in the thread instead of vanishing prematurely.
|
|
434
454
|
parkStreamCard(ds);
|
|
435
455
|
sessionStore.closeSession(ds.session.sessionId);
|
|
456
|
+
publishClosedSessionPatch(ds.session.sessionId, ds.session.closedAt ? Date.parse(ds.session.closedAt) : undefined);
|
|
436
457
|
await deliverEphemeralOrReply(ds, operatorOpenId, closedCard, 'interactive', () => sessionReply(rootId, closedCard, 'interactive'));
|
|
437
|
-
|
|
458
|
+
// The close card delivery yields to inbound routing and dashboard IPC. If
|
|
459
|
+
// the user closed/replaced this generation during that await, do not mint a
|
|
460
|
+
// fresh active row from the stale continuation.
|
|
461
|
+
if (!sessionStillActive() || ds.session !== oldSession) {
|
|
462
|
+
logger.warn(`[${tag(ds)}] Repo switch cancelled after old-session close; routing generation changed during card delivery`);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
438
465
|
// `rootId` is the routing anchor. For chat-scope sessions it is the
|
|
439
466
|
// `oc_...` chat id, not the traceable `om_...` message root stored on
|
|
440
467
|
// Session. Preserve the old identity and explicitly persist scope so card
|
|
@@ -519,6 +546,18 @@ export async function runAutoWorktreeCommit(deps) {
|
|
|
519
546
|
const wt = await maybeCreateDefaultWorktree(larkAppId, baseDir, {
|
|
520
547
|
isBotDefaultDir: true, title, prompt, locale: localeForBot(larkAppId), notify,
|
|
521
548
|
});
|
|
549
|
+
// The pendingRepo placeholder can legitimately be consumed WHILE this
|
|
550
|
+
// up-to-30s build runs — e.g. the Codex-notifier「继续处理」callback adopts
|
|
551
|
+
// the same DM session and clears pendingRepo before we get here. If we now
|
|
552
|
+
// funneled into commitRepoSelection, its mid-session branch would kill the
|
|
553
|
+
// freshly-adopted worker and replace it with a botmux-owned worktree
|
|
554
|
+
// session. Bail on the late result instead: the takeover already owns the
|
|
555
|
+
// session. (commitRepoSelection also re-checks pendingRepo under its claim,
|
|
556
|
+
// but that check runs after an await — fence here before any mutation.)
|
|
557
|
+
if (!ds.pendingRepo) {
|
|
558
|
+
logger.info(`[${tag(ds)}] auto-worktree completion ignored — pendingRepo already consumed (session taken over)`);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
522
561
|
// Commit even on fallback (wt.dir === baseDir) — the session must still start.
|
|
523
562
|
// commitRepoSelection has its own /close + generation guards and, for a
|
|
524
563
|
// pendingRepo session, folds any messages buffered during creation (pendingPrompt
|
|
@@ -546,17 +585,19 @@ export async function runAutoWorktreeCommit(deps) {
|
|
|
546
585
|
/**
|
|
547
586
|
* Drive a host-overload降压 sweep across daemons.
|
|
548
587
|
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
551
|
-
*
|
|
552
|
-
* so ONE daemon does the whole job: it tries daemons in order until the first
|
|
553
|
-
* one succeeds (not a fixed `daemons[0]`, so a single flaky descriptor doesn't
|
|
554
|
-
* sink the action while healthy siblings sit idle). Fanning clean_stopped out
|
|
555
|
-
* would make siblings race the same zombies and double-count them.
|
|
588
|
+
* Both modes fan out to EVERY online daemon and sum the affected counts. Each
|
|
589
|
+
* daemon owns one bot-scoped session store and its own live workers, so neither
|
|
590
|
+
* stopped sessions nor idle workers can be swept through a sibling daemon.
|
|
556
591
|
*
|
|
557
|
-
*
|
|
558
|
-
* the caller rolls back the nonce
|
|
559
|
-
*
|
|
592
|
+
* Fail-closed on partial failure: if ANY discovered daemon doesn't ACK, throw —
|
|
593
|
+
* the caller then rolls back the nonce (releaseOverloadNonce) so the owner can
|
|
594
|
+
* retry, instead of burning the button on a card that reports「已清理 0」while
|
|
595
|
+
* the daemon that actually held the zombies never ran. Both sweeps are
|
|
596
|
+
* idempotent (an already-closed session leaves the stopped set; an already-
|
|
597
|
+
* suspended worker leaves the idle set), so a retry only re-hits whatever
|
|
598
|
+
* failed. Reporting a partial count as a completed action would resurrect the
|
|
599
|
+
* exact "显示 0、实际没清" symptom this fix exists to kill — just triggered by
|
|
600
|
+
* "the daemon holding the zombies failed" instead of "the first daemon had none".
|
|
560
601
|
*/
|
|
561
602
|
async function sweepHostOverload(mode) {
|
|
562
603
|
const daemons = listOnlineDaemons();
|
|
@@ -580,35 +621,20 @@ async function sweepHostOverload(mode) {
|
|
|
580
621
|
return null;
|
|
581
622
|
}
|
|
582
623
|
};
|
|
583
|
-
if
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
for (const d of daemons) {
|
|
587
|
-
const n = await postSweep(d);
|
|
588
|
-
if (n !== null)
|
|
589
|
-
return n;
|
|
590
|
-
}
|
|
591
|
-
throw new Error(`sweep clean_stopped reached no daemon (${daemons.length} failed)`);
|
|
592
|
-
}
|
|
593
|
-
// suspend_idle: fan out to all daemons and sum. Throw only if not one acked.
|
|
594
|
-
let affected = 0;
|
|
595
|
-
let ok = 0;
|
|
624
|
+
// Fan out to all daemons and sum. Fail the whole action if ANY didn't ACK, so
|
|
625
|
+
// the caller keeps the button retriable rather than reporting a partial sweep
|
|
626
|
+
// as complete.
|
|
596
627
|
const results = await Promise.all(daemons.map(postSweep));
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
ok++;
|
|
601
|
-
}
|
|
628
|
+
const failed = results.filter(n => n === null).length;
|
|
629
|
+
if (failed > 0) {
|
|
630
|
+
throw new Error(`sweep ${mode}: ${failed}/${daemons.length} daemon(s) did not ack — retriable`);
|
|
602
631
|
}
|
|
603
|
-
|
|
604
|
-
throw new Error(`sweep suspend_idle reached no daemon (${daemons.length} failed)`);
|
|
605
|
-
return affected;
|
|
632
|
+
return results.reduce((sum, n) => sum + (n ?? 0), 0);
|
|
606
633
|
}
|
|
607
634
|
/**
|
|
608
|
-
* Machine-wide counts for the overload alert preview.
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
* Best-effort: an unreachable daemon just contributes 0.
|
|
635
|
+
* Machine-wide counts for the overload alert preview. Each daemon reports its
|
|
636
|
+
* bot-scoped stopped sessions and its own live workers, so both fields must be
|
|
637
|
+
* summed. Best-effort: an unreachable daemon just contributes 0.
|
|
612
638
|
*/
|
|
613
639
|
export async function countHostOverload() {
|
|
614
640
|
const daemons = listOnlineDaemons();
|
|
@@ -620,7 +646,7 @@ export async function countHostOverload() {
|
|
|
620
646
|
const body = await res.json().catch(() => ({}));
|
|
621
647
|
if (res.ok && body?.ok) {
|
|
622
648
|
if (typeof body.stopped === 'number')
|
|
623
|
-
stopped
|
|
649
|
+
stopped += body.stopped;
|
|
624
650
|
if (typeof body.idle === 'number')
|
|
625
651
|
idle += body.idle;
|
|
626
652
|
}
|
|
@@ -710,9 +736,13 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
710
736
|
// Rebuild the SAME card: clicked button → ✓done+disabled, other → still live.
|
|
711
737
|
return JSON.parse(buildOverloadAlertCard(st));
|
|
712
738
|
}
|
|
713
|
-
// ───
|
|
739
|
+
// ─── 群内授权卡片动作(限制提交 + grant/revoke,talk-only)──────────────────
|
|
714
740
|
// 不绑定 session,必须在 session 解析之前处理。owner 强闸门 + nonce 校验。
|
|
715
|
-
if (value?.action && (value.action === 'grant_chat'
|
|
741
|
+
if (value?.action && (value.action === 'grant_chat'
|
|
742
|
+
|| value.action === 'grant_global'
|
|
743
|
+
|| value.action === 'grant_deny'
|
|
744
|
+
|| value.action === 'grant_set_duration'
|
|
745
|
+
|| value.action === 'grant_set_quota') && larkAppId) {
|
|
716
746
|
const loc = localeForBot(larkAppId);
|
|
717
747
|
const owner = getOwnerOpenId(larkAppId);
|
|
718
748
|
// owner 强闸门:必须是当前 app 的 owner 本人(比 canOperate 更严)
|
|
@@ -731,6 +761,23 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
731
761
|
if (!targets.length || !grantChatId || !nonce || !targets.every(tt => checkNonce(larkAppId, grantChatId, tt, nonce))) {
|
|
732
762
|
return { toast: { type: 'error', content: t('card.grant.toast_expired', undefined, loc) } };
|
|
733
763
|
}
|
|
764
|
+
// 兼容已发出的旧卡:旧卡用下拉 callback 暂存限制,新卡在授权按钮的 form_submit
|
|
765
|
+
// 中一次提交有效期和额度。两条路径都必须走服务端校验,不能信任卡片输入。
|
|
766
|
+
if (value.action === 'grant_set_duration' || value.action === 'grant_set_quota') {
|
|
767
|
+
const selected = action?.option;
|
|
768
|
+
const parsed = value.action === 'grant_set_duration'
|
|
769
|
+
? normalizeGrantDurationOption(selected)
|
|
770
|
+
: normalizeGrantQuotaOption(selected);
|
|
771
|
+
if (parsed === null) {
|
|
772
|
+
return { toast: { type: 'error', content: t('card.grant.toast_bad_limit', undefined, loc) } };
|
|
773
|
+
}
|
|
774
|
+
const updated = updatePendingGrantLimits(larkAppId, grantChatId, targets, nonce, value.action === 'grant_set_duration'
|
|
775
|
+
? { durationMs: parsed ?? null }
|
|
776
|
+
: { quota: parsed ?? null });
|
|
777
|
+
return updated
|
|
778
|
+
? { toast: { type: 'success', content: t('card.grant.toast_limit_staged', undefined, loc) } }
|
|
779
|
+
: { toast: { type: 'error', content: t('card.grant.toast_expired', undefined, loc) } };
|
|
780
|
+
}
|
|
734
781
|
// 拒绝:只把卡更新成「已拒绝」+ 全部目标进 deny 冷却,绝不触碰 grant-store。
|
|
735
782
|
// 返回原始卡 body,由 dispatcher 包成 in-place patch(不再走 updateMessage 双写)。
|
|
736
783
|
if (value.action === 'grant_deny') {
|
|
@@ -738,14 +785,36 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
738
785
|
markDenied(larkAppId, grantChatId, tt);
|
|
739
786
|
return JSON.parse(buildGrantResultCard('deny', loc));
|
|
740
787
|
}
|
|
788
|
+
const formValue = action?.form_value;
|
|
789
|
+
if (formValue) {
|
|
790
|
+
const duration = normalizeGrantDurationOption(formValue.grant_duration);
|
|
791
|
+
const quota = normalizeGrantQuotaOption(typeof formValue.grant_quota === 'string' ? formValue.grant_quota.trim() : formValue.grant_quota);
|
|
792
|
+
if (quota === null) {
|
|
793
|
+
return { toast: { type: 'error', content: t('card.grant.toast_bad_quota', undefined, loc) } };
|
|
794
|
+
}
|
|
795
|
+
if (duration === null) {
|
|
796
|
+
return { toast: { type: 'error', content: t('card.grant.toast_bad_limit', undefined, loc) } };
|
|
797
|
+
}
|
|
798
|
+
if (!updatePendingGrantLimits(larkAppId, grantChatId, targets, nonce, {
|
|
799
|
+
durationMs: duration ?? null,
|
|
800
|
+
quota: quota ?? null,
|
|
801
|
+
})) {
|
|
802
|
+
return { toast: { type: 'error', content: t('card.grant.toast_expired', undefined, loc) } };
|
|
803
|
+
}
|
|
804
|
+
}
|
|
741
805
|
// 授权(talk-only):grant_chat 写本群 chatGrants,grant_global 写全局 globalGrants,
|
|
742
806
|
// 两者都绝不碰 allowedUsers(operate 只由 bots.json 配)。逐个落库,统计成功/失败。
|
|
743
807
|
const kind = value.action === 'grant_global' ? 'global' : 'chat';
|
|
744
808
|
const names = Array.isArray(value.target_names) ? value.target_names : [];
|
|
745
809
|
const idToName = new Map();
|
|
746
810
|
targets.forEach((tt, i) => idToName.set(tt, names[i] ?? ''));
|
|
747
|
-
//
|
|
748
|
-
const
|
|
811
|
+
// 限制挂在 pending 上;确认时生成绝对 expiresAt,多目标共享同一组限制。
|
|
812
|
+
const limits = getPendingGrantLimits(larkAppId, grantChatId, targets[0]);
|
|
813
|
+
if (!limits) {
|
|
814
|
+
return { toast: { type: 'error', content: t('card.grant.toast_expired', undefined, loc) } };
|
|
815
|
+
}
|
|
816
|
+
const quota = limits.quota;
|
|
817
|
+
const expiresAt = limits.durationMs === undefined ? undefined : Date.now() + limits.durationMs;
|
|
749
818
|
// 触发本次授权申请的原始消息事件:授权成功后重放,用户无需再 @ 一遍。
|
|
750
819
|
// 自助申请卡每次只对应一个 target,取第一个即可。
|
|
751
820
|
const pendingMessage = getPendingMessage(larkAppId, grantChatId, targets[0]);
|
|
@@ -753,8 +822,8 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
753
822
|
const failed = [];
|
|
754
823
|
for (const tt of targets) {
|
|
755
824
|
const res = kind === 'global'
|
|
756
|
-
? await addGlobalGrant(larkAppId, tt, quota)
|
|
757
|
-
: await addChatGrant(larkAppId, grantChatId, tt, quota);
|
|
825
|
+
? await addGlobalGrant(larkAppId, tt, quota, expiresAt)
|
|
826
|
+
: await addChatGrant(larkAppId, grantChatId, tt, quota, expiresAt);
|
|
758
827
|
if (res.ok) {
|
|
759
828
|
clearPending(larkAppId, grantChatId, tt);
|
|
760
829
|
granted.push(tt);
|
|
@@ -801,12 +870,12 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
801
870
|
}
|
|
802
871
|
// 通知卡的 grantee 渲染参数:bot 只用纯文本名字(不 <at>,否则唤醒对方 bot 误拉空会话),真人 @ 点名。
|
|
803
872
|
const notifyTargets = granted.map((id, i) => ({ openId: id, name: idToName.get(id) || undefined, isBot: !humanFlags[i] }));
|
|
804
|
-
//
|
|
805
|
-
//
|
|
806
|
-
//
|
|
807
|
-
//
|
|
808
|
-
const resultCardBody = JSON.parse(buildGrantResultCard(kind, loc));
|
|
809
|
-
if (cardMessageId) {
|
|
873
|
+
// 授权成功后:**就地 patch 原卡**为终态(正文直接 @ 被授权人 + 额度/有效期),这一张卡既是
|
|
874
|
+
// 「已授权」结果态、又 ping 到被授权人——无需再单独发通知卡、也无需撤回原卡(申晗 2026-07-31
|
|
875
|
+
// 反馈:直接在原卡更新即可)。同步返回该 body 即完成 in-place patch,避免 deleteMessage 与
|
|
876
|
+
// callback 响应竞态导致客户端 300000。仅「部分失败」仍走后台补一条文字告知。
|
|
877
|
+
const resultCardBody = JSON.parse(buildGrantResultCard(kind, loc, quota, expiresAt, notifyTargets));
|
|
878
|
+
if (cardMessageId && failed.length > 0) {
|
|
810
879
|
let replyInThread = true;
|
|
811
880
|
try {
|
|
812
881
|
const detail = await getMessageDetail(larkAppId, cardMessageId);
|
|
@@ -816,31 +885,17 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
816
885
|
replyInThread = Boolean(item.thread_id);
|
|
817
886
|
}
|
|
818
887
|
catch (err) {
|
|
819
|
-
logger.debug(`grant
|
|
888
|
+
logger.debug(`grant partial-failure thread-mode probe failed, defaulting to thread reply: ${err}`);
|
|
820
889
|
}
|
|
821
|
-
// fire-and-forget:
|
|
890
|
+
// fire-and-forget: 仅在有部分目标失败时补一条文字告知(不阻塞 callback)。
|
|
822
891
|
Promise.resolve()
|
|
823
892
|
.then(async () => {
|
|
893
|
+
const failNames = failed.map(f => idToName.get(f.openId) || f.openId).join('、');
|
|
824
894
|
try {
|
|
825
|
-
await replyMessage(larkAppId, cardMessageId,
|
|
895
|
+
await replyMessage(larkAppId, cardMessageId, t('card.grant.partial_failed', { names: failNames }, loc), 'text', replyInThread);
|
|
826
896
|
}
|
|
827
897
|
catch (err) {
|
|
828
|
-
logger.warn(`grant
|
|
829
|
-
}
|
|
830
|
-
if (failed.length > 0) {
|
|
831
|
-
const failNames = failed.map(f => idToName.get(f.openId) || f.openId).join('、');
|
|
832
|
-
try {
|
|
833
|
-
await replyMessage(larkAppId, cardMessageId, t('card.grant.partial_failed', { names: failNames }, loc), 'text', replyInThread);
|
|
834
|
-
}
|
|
835
|
-
catch (err) {
|
|
836
|
-
logger.warn(`grant partial-failure notice failed: ${err}`);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
try {
|
|
840
|
-
await deleteMessage(larkAppId, cardMessageId);
|
|
841
|
-
}
|
|
842
|
-
catch (err) {
|
|
843
|
-
logger.debug(`grant card withdraw (post-callback) failed: ${err}`);
|
|
898
|
+
logger.warn(`grant partial-failure notice failed: ${err}`);
|
|
844
899
|
}
|
|
845
900
|
})
|
|
846
901
|
.catch(err => logger.error(`grant post-callback background tasks failed: ${err}`));
|
|
@@ -966,6 +1021,7 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
966
1021
|
// session's chatType for DM targets. Default 'group' covers legacy cards.
|
|
967
1022
|
const targetScope = value.target_scope ?? 'chat';
|
|
968
1023
|
const targetChatType = value.target_chat_type ?? 'group';
|
|
1024
|
+
const cardVisibility = value.visibility ?? 'public';
|
|
969
1025
|
const invokerOpenId = value.invoker_open_id;
|
|
970
1026
|
if (!targetChatId || !targetRootId || !operatorOpenId) {
|
|
971
1027
|
return { toast: { type: 'error', content: t('card.relay.toast_failed', { error: 'missing_value' }, loc) } };
|
|
@@ -1018,7 +1074,32 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1018
1074
|
selectedSessionId: nextSelected,
|
|
1019
1075
|
searchQuery: nextSearch,
|
|
1020
1076
|
page: nextPage,
|
|
1021
|
-
}, targetScope, targetChatType);
|
|
1077
|
+
}, targetScope, targetChatType, cardVisibility);
|
|
1078
|
+
// ── Private (ephemeral) picker: delete-then-resend ────────────────────
|
|
1079
|
+
// Ephemeral cards CANNOT be PATCH-updated (Feishu legacy interface), so we
|
|
1080
|
+
// can't return a body for the dispatcher to patch in place. Instead delete
|
|
1081
|
+
// the clicked card and resend a fresh ephemeral one carrying the new state
|
|
1082
|
+
// — the same send→interact→delete→resend pattern Feishu documents for
|
|
1083
|
+
// ephemeral flows. Resend FIRST, then delete the old one, so a resend
|
|
1084
|
+
// failure doesn't leave the user with no card at all. The toast keeps the
|
|
1085
|
+
// callback response non-empty (returning {} would make Lark show nothing).
|
|
1086
|
+
if (cardVisibility === 'private') {
|
|
1087
|
+
const { sendEphemeralCard, deleteEphemeralCard } = await import('./client.js');
|
|
1088
|
+
try {
|
|
1089
|
+
await sendEphemeralCard(larkAppId, targetChatId, invokerOpenId ?? operatorOpenId, cardJson);
|
|
1090
|
+
if (cardMessageId) {
|
|
1091
|
+
await deleteEphemeralCard(larkAppId, cardMessageId).catch(() => { });
|
|
1092
|
+
}
|
|
1093
|
+
// No card body returned — the replacement is already sent. An empty
|
|
1094
|
+
// response is fine here (the new ephemeral card is the user-visible
|
|
1095
|
+
// result); a toast would double up with the freshly-rendered card.
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
catch (err) {
|
|
1099
|
+
logger.warn(`[card-action] relay private picker re-render failed (${err instanceof Error ? err.message : err}); leaving old card in place`);
|
|
1100
|
+
return { toast: { type: 'error', content: t('card.relay.toast_failed', { error: 'ephemeral_refresh_failed' }, loc) } };
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1022
1103
|
// Return an updated card body — event-dispatcher wraps this as
|
|
1023
1104
|
// { card: { type: 'raw', data: <body> } } so Lark patches the picker
|
|
1024
1105
|
// in place rather than appending a new message.
|
|
@@ -1167,6 +1248,7 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1167
1248
|
// field and default to 'group' (their pickers never offered DM targets).
|
|
1168
1249
|
const targetScope = value.target_scope ?? 'chat';
|
|
1169
1250
|
const targetChatType = value.target_chat_type ?? 'group';
|
|
1251
|
+
const cardVisibility = value.visibility ?? 'public';
|
|
1170
1252
|
const targetAnchor = targetScope === 'chat' ? targetChatId : targetRootId;
|
|
1171
1253
|
const invokerOpenId = value.invoker_open_id;
|
|
1172
1254
|
if (!sourceSessionId || !targetChatId || !targetRootId) {
|
|
@@ -1208,22 +1290,20 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1208
1290
|
// collectRelayPickerEntries already filters scratches at render time,
|
|
1209
1291
|
// but a stale picker (rendered before a scratch was created) could
|
|
1210
1292
|
// still produce a confirm click; this is the depth defense.
|
|
1211
|
-
{
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
return { toast: { type: 'error', content: t('card.relay.toast_not_started_yet', undefined, loc) } };
|
|
1215
|
-
}
|
|
1293
|
+
const { isDisposableCommandScratch, isRelayableRealSession } = await import('../../core/worker-pool.js');
|
|
1294
|
+
if (!isRelayableRealSession(sourceDs)) {
|
|
1295
|
+
return { toast: { type: 'error', content: t('card.relay.toast_not_started_yet', undefined, loc) } };
|
|
1216
1296
|
}
|
|
1217
1297
|
// Pre-flight target-chat conflict check — done BEFORE sendMessage M1 so
|
|
1218
1298
|
// a refusal doesn't leave a misleading "已接力" announcement in the
|
|
1219
1299
|
// target chat (王皓 caught this in testing). Mirror the same predicate
|
|
1220
|
-
// transferSession uses
|
|
1221
|
-
//
|
|
1222
|
-
//
|
|
1300
|
+
// transferSession uses. Only a narrowly classified daemon-command scratch
|
|
1301
|
+
// may be ignored; worker-less queued/adopt/deferred/real sessions still own
|
|
1302
|
+
// the anchor and must block before a misleading M1 is sent.
|
|
1223
1303
|
const targetConflict = [...activeSessions.values()].find(c => c !== sourceDs
|
|
1224
1304
|
&& c.larkAppId === larkAppId
|
|
1225
1305
|
&& sessionAnchorId(c) === targetAnchor
|
|
1226
|
-
&&
|
|
1306
|
+
&& !isDisposableCommandScratch(c));
|
|
1227
1307
|
if (targetConflict) {
|
|
1228
1308
|
const conflictTitle = targetConflict.session.title || targetConflict.session.sessionId.substring(0, 8);
|
|
1229
1309
|
// Send as a regular text message in the target chat instead of a
|
|
@@ -1291,8 +1371,16 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1291
1371
|
return { toast: { type: 'error', content: t('card.relay.toast_failed', { error: r.error }, loc) } };
|
|
1292
1372
|
}
|
|
1293
1373
|
// Best-effort: remove the picker card now that the selection resolved.
|
|
1374
|
+
// Ephemeral (private) pickers need the ephemeral-delete endpoint —
|
|
1375
|
+
// deleteMessage (im/v1/messages) doesn't apply to ephemeral message ids.
|
|
1294
1376
|
if (cardMessageId && larkAppId) {
|
|
1295
|
-
|
|
1377
|
+
if (cardVisibility === 'private') {
|
|
1378
|
+
const { deleteEphemeralCard } = await import('./client.js');
|
|
1379
|
+
deleteEphemeralCard(larkAppId, cardMessageId).catch(() => { });
|
|
1380
|
+
}
|
|
1381
|
+
else {
|
|
1382
|
+
deleteMessage(larkAppId, cardMessageId).catch(() => { });
|
|
1383
|
+
}
|
|
1296
1384
|
}
|
|
1297
1385
|
return { toast: { type: 'success', content: t('card.relay.toast_success', undefined, loc) } };
|
|
1298
1386
|
}
|
|
@@ -1562,6 +1650,14 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1562
1650
|
await sessionReply(rootId, t('card.action.adopt_no_restart', undefined, locDs));
|
|
1563
1651
|
return;
|
|
1564
1652
|
}
|
|
1653
|
+
if (isSessionTransferring(ds)) {
|
|
1654
|
+
return {
|
|
1655
|
+
toast: {
|
|
1656
|
+
type: 'warning',
|
|
1657
|
+
content: t('cmd.session.transfer_in_progress', undefined, locDs),
|
|
1658
|
+
},
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1565
1661
|
const effectiveCliId = sessionCliId(ds);
|
|
1566
1662
|
const cliName = getCliDisplayName(effectiveCliId);
|
|
1567
1663
|
logger.info(`[${tag(ds)}] Correlated restart via card button`);
|
|
@@ -1583,9 +1679,23 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1583
1679
|
// Build the closed card BEFORE killWorker/closeSession — it reads the
|
|
1584
1680
|
// live session's identity off `ds`.
|
|
1585
1681
|
const card = buildClosedSessionCard(ds, localeForBot(ds.larkAppId));
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1682
|
+
try {
|
|
1683
|
+
await closeSession(ds.session.sessionId);
|
|
1684
|
+
}
|
|
1685
|
+
catch (err) {
|
|
1686
|
+
logger.error(`[${tag(ds)}] Refused close because backing teardown was not verified: ${err}`);
|
|
1687
|
+
return {
|
|
1688
|
+
toast: {
|
|
1689
|
+
type: 'warning',
|
|
1690
|
+
content: `会话关闭失败:${err instanceof Error ? err.message : String(err)}`,
|
|
1691
|
+
},
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
// closeSession removes the captured ds before awaiting best-effort remote
|
|
1695
|
+
// cleanup. A new session may claim the same route during that await; only
|
|
1696
|
+
// delete here if this handler still owns the exact object it closed.
|
|
1697
|
+
if (activeSessions.get(sKey) === ds)
|
|
1698
|
+
activeSessions.delete(sKey);
|
|
1589
1699
|
// The closed card carries session title / CLI name / workingDir / resume
|
|
1590
1700
|
// command. In private-card mode those must not leak to the group — send the
|
|
1591
1701
|
// closed card ephemeral to the same owner audience instead. No group
|
|
@@ -1637,11 +1747,15 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1637
1747
|
else if (result.error === 'deferred_unmaterialized') {
|
|
1638
1748
|
await sessionReply(rootId, t('card.action.resume_deferred_unmaterialized', undefined, locDsResume));
|
|
1639
1749
|
}
|
|
1750
|
+
else if (result.error === 'resume_cancelled') {
|
|
1751
|
+
await sessionReply(rootId, t('card.action.resume_cancelled', undefined, locDsResume));
|
|
1752
|
+
}
|
|
1640
1753
|
}
|
|
1641
1754
|
}
|
|
1642
1755
|
if (actionType === 'disconnect' && ds) {
|
|
1643
1756
|
killWorker(ds);
|
|
1644
1757
|
sessionStore.closeSession(ds.session.sessionId);
|
|
1758
|
+
publishClosedSessionPatch(ds.session.sessionId, ds.session.closedAt ? Date.parse(ds.session.closedAt) : undefined);
|
|
1645
1759
|
activeSessions.delete(sKey);
|
|
1646
1760
|
await sessionReply(rootId, t('card.action.disconnected', undefined, localeForBot(ds.larkAppId)));
|
|
1647
1761
|
logger.info(`[${tag(ds)}] Disconnected (adopt) via card button`);
|
|
@@ -1672,9 +1786,9 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1672
1786
|
ds.currentImageKey = undefined;
|
|
1673
1787
|
persistStreamCardState(ds);
|
|
1674
1788
|
let cardJson;
|
|
1675
|
-
if (ds.streamCardId && ds.streamCardId !== CARD_POSTING_SENTINEL && ds
|
|
1676
|
-
const readUrl =
|
|
1677
|
-
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) }));
|
|
1789
|
+
if (ds.streamCardId && ds.streamCardId !== CARD_POSTING_SENTINEL && workerHasInitialized(ds)) {
|
|
1790
|
+
const readUrl = readableTerminalUrlFor(ds);
|
|
1791
|
+
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)));
|
|
1678
1792
|
scheduleCardPatch(ds, cardJson);
|
|
1679
1793
|
}
|
|
1680
1794
|
const retryCodexAppInput = ds.lastCodexAppInput
|
|
@@ -1698,6 +1812,14 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1698
1812
|
return;
|
|
1699
1813
|
}
|
|
1700
1814
|
if (actionType === 'tui_keys' && ds) {
|
|
1815
|
+
if (isSessionTransferring(ds)) {
|
|
1816
|
+
return {
|
|
1817
|
+
toast: {
|
|
1818
|
+
type: 'warning',
|
|
1819
|
+
content: t('cmd.session.transfer_in_progress', undefined, localeForBot(ds.larkAppId)),
|
|
1820
|
+
},
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1701
1823
|
// Fail-closed: only act on a currently-active card (either the
|
|
1702
1824
|
// ScreenAnalyzer TUI prompt card or our stuck-warning card). A stale
|
|
1703
1825
|
// click from a resolved/replaced card must NOT send any IPC to the
|
|
@@ -1718,6 +1840,10 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1718
1840
|
const optionType = value?.option_type ?? 'select';
|
|
1719
1841
|
const selectedIndex = Number(value?.selected_index ?? 0);
|
|
1720
1842
|
const selectedText = value?.selected_text ?? `Option ${selectedIndex + 1}`;
|
|
1843
|
+
if (isActiveTuiCard && ds.tuiPromptProcessing) {
|
|
1844
|
+
logger.info(`[${tag(ds)}] Duplicate TUI prompt card click — dropped (processing already in flight)`);
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1721
1847
|
if (optionType === 'toggle') {
|
|
1722
1848
|
// Only a ScreenAnalyzer TUI card may own toggle state. A stuck-warning
|
|
1723
1849
|
// card must never read or mutate the other card's global selections.
|
|
@@ -1755,6 +1881,7 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1755
1881
|
if (ds.worker) {
|
|
1756
1882
|
let allKeys = [];
|
|
1757
1883
|
let isFinalStuck = false;
|
|
1884
|
+
let dispatchedKeys = false;
|
|
1758
1885
|
if (isActiveTuiCard && ds.tuiToggledIndices?.length && ds.tuiPromptOptions) {
|
|
1759
1886
|
// Send each toggled option's keys in sequence
|
|
1760
1887
|
for (const ti of ds.tuiToggledIndices.sort((a, b) => a - b)) {
|
|
@@ -1805,6 +1932,7 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1805
1932
|
allKeys.push(...keys);
|
|
1806
1933
|
}
|
|
1807
1934
|
if (allKeys.length > 0) {
|
|
1935
|
+
const effectiveFinal = isFinal || isFinalStuck;
|
|
1808
1936
|
// Atomic processing claim: if a previous click is already in flight
|
|
1809
1937
|
// (waiting for tui_keys_delivered / stuck_warning_expired ACK), drop
|
|
1810
1938
|
// this duplicate. Without this, two rapid clicks could both pass the
|
|
@@ -1816,6 +1944,9 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1816
1944
|
}
|
|
1817
1945
|
ds.stuckWarningProcessing = true;
|
|
1818
1946
|
}
|
|
1947
|
+
if (isActiveTuiCard && effectiveFinal) {
|
|
1948
|
+
ds.tuiPromptProcessing = true;
|
|
1949
|
+
}
|
|
1819
1950
|
// Only the stuck-warning card's Enter action re-arms the detector
|
|
1820
1951
|
// (Enter advances from the hook list to a per-hook review). Match by
|
|
1821
1952
|
// the actual keys sent — NOT by optionType, since t is typed 'confirm'
|
|
@@ -1831,11 +1962,39 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1831
1962
|
const stuckNonce = isActiveStuckCard ? ds.stuckWarningNonce : undefined;
|
|
1832
1963
|
const stuckCliLifetime = isActiveStuckCard ? ds.stuckWarningCliLifetime : undefined;
|
|
1833
1964
|
const stuckPage = isActiveStuckCard ? ds.stuckWarningPageType : undefined;
|
|
1834
|
-
const
|
|
1835
|
-
|
|
1965
|
+
const resolveText = isActiveTuiCard && ds.tuiToggledIndices?.length
|
|
1966
|
+
? ds.tuiToggledIndices.map(i => ds.tuiPromptOptions?.[i]?.text).filter(Boolean).join(', ')
|
|
1967
|
+
: selectedText;
|
|
1968
|
+
try {
|
|
1969
|
+
await sendWorkerIpc(ds.worker, {
|
|
1970
|
+
type: 'tui_keys',
|
|
1971
|
+
keys: allKeys,
|
|
1972
|
+
isFinal: effectiveFinal,
|
|
1973
|
+
rearmStuckDetector: isStuckWarningEnter,
|
|
1974
|
+
stuckNonce,
|
|
1975
|
+
stuckCliLifetime,
|
|
1976
|
+
stuckPageType: stuckPage,
|
|
1977
|
+
cardMessageId: isActiveTuiCard ? cardMessageId : undefined,
|
|
1978
|
+
selectedText: resolveText || selectedText,
|
|
1979
|
+
});
|
|
1980
|
+
dispatchedKeys = true;
|
|
1981
|
+
}
|
|
1982
|
+
catch (err) {
|
|
1983
|
+
if (isActiveStuckCard)
|
|
1984
|
+
ds.stuckWarningProcessing = false;
|
|
1985
|
+
if (isActiveTuiCard && effectiveFinal)
|
|
1986
|
+
ds.tuiPromptProcessing = false;
|
|
1987
|
+
logger.warn(`[${tag(ds)}] TUI key IPC delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1988
|
+
return {
|
|
1989
|
+
toast: {
|
|
1990
|
+
type: 'warning',
|
|
1991
|
+
content: t('card.action.tui_ipc_failed', undefined, localeForBot(ds.larkAppId)),
|
|
1992
|
+
},
|
|
1993
|
+
};
|
|
1994
|
+
}
|
|
1836
1995
|
logger.info(`[${tag(ds)}] TUI keys: [${allKeys.join(',')}] final=${effectiveFinal} rearmStuck=${isStuckWarningEnter} stuckNonce=${stuckNonce ?? 'none'} — "${selectedText}"`);
|
|
1837
1996
|
}
|
|
1838
|
-
if (isFinal || isFinalStuck) {
|
|
1997
|
+
if ((isFinal || isFinalStuck) && dispatchedKeys) {
|
|
1839
1998
|
const resolveText = isActiveTuiCard && ds.tuiToggledIndices?.length
|
|
1840
1999
|
? ds.tuiToggledIndices.map(i => ds.tuiPromptOptions?.[i]?.text).filter(Boolean).join(', ')
|
|
1841
2000
|
: selectedText;
|
|
@@ -1844,8 +2003,9 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1844
2003
|
// For a stuck-warning card, do NOT clear authority or render success
|
|
1845
2004
|
// here. The worker may still reject the keys (stale screen). We show a
|
|
1846
2005
|
// "processing" state to block duplicate clicks, and wait for the
|
|
1847
|
-
// worker's tui_keys_delivered (success)
|
|
1848
|
-
// ("page changed, not sent") ACK before
|
|
2006
|
+
// worker's tui_keys_delivered (success), stuck_warning_expired
|
|
2007
|
+
// ("page changed, not sent"), or tui_prompt_submit_failed ACK before
|
|
2008
|
+
// resolving the card.
|
|
1849
2009
|
if (isActiveStuckCard) {
|
|
1850
2010
|
if (cardMessageId) {
|
|
1851
2011
|
const processingCard = buildTuiPromptProcessingCard('处理中…', locDs);
|
|
@@ -1857,22 +2017,9 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1857
2017
|
}
|
|
1858
2018
|
catch { /* fall through */ }
|
|
1859
2019
|
}
|
|
1860
|
-
// Normal TUI prompt
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
const resolvedCard = buildTuiPromptResolvedCard(finalText, locDs);
|
|
1864
|
-
updateMessage(ds.larkAppId, cardMessageId, resolvedCard).catch(err => logger.debug(`[${tag(ds)}] Failed to update TUI prompt card: ${err}`));
|
|
1865
|
-
}, allKeys.length * 100 + 500);
|
|
1866
|
-
}
|
|
1867
|
-
// Clear state only for the card that was actually clicked — a stuck
|
|
1868
|
-
// card click must NOT wipe the ScreenAnalyzer TUI prompt state (or
|
|
1869
|
-
// vice versa) if both coexist / race.
|
|
1870
|
-
if (cardMessageId === ds.tuiPromptCardId) {
|
|
1871
|
-
ds.tuiPromptCardId = undefined;
|
|
1872
|
-
ds.tuiPromptOptions = undefined;
|
|
1873
|
-
ds.tuiPromptMultiSelect = undefined;
|
|
1874
|
-
ds.tuiToggledIndices = undefined;
|
|
1875
|
-
}
|
|
2020
|
+
// Normal TUI prompt cards also remain in processing until the worker
|
|
2021
|
+
// confirms backend delivery via tui_prompt_resolved. A worker/backend
|
|
2022
|
+
// rejection produces tui_prompt_submit_failed instead.
|
|
1876
2023
|
publishAttentionPatch(ds);
|
|
1877
2024
|
try {
|
|
1878
2025
|
return JSON.parse(buildTuiPromptProcessingCard(finalText, locDs));
|
|
@@ -1882,6 +2029,14 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1882
2029
|
}
|
|
1883
2030
|
}
|
|
1884
2031
|
if (actionType === 'tui_text_input' && ds) {
|
|
2032
|
+
if (isSessionTransferring(ds)) {
|
|
2033
|
+
return {
|
|
2034
|
+
toast: {
|
|
2035
|
+
type: 'warning',
|
|
2036
|
+
content: t('cmd.session.transfer_in_progress', undefined, localeForBot(ds.larkAppId)),
|
|
2037
|
+
},
|
|
2038
|
+
};
|
|
2039
|
+
}
|
|
1885
2040
|
const inputTextRaw = action?.form_value?.tui_custom_input;
|
|
1886
2041
|
const inputText = typeof inputTextRaw === 'string' ? inputTextRaw : '';
|
|
1887
2042
|
let inputKeys = [];
|
|
@@ -1890,20 +2045,49 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1890
2045
|
}
|
|
1891
2046
|
catch { /* bad json */ }
|
|
1892
2047
|
const locDs = localeForBot(ds.larkAppId);
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
ds.tuiPromptCardId = undefined;
|
|
1902
|
-
ds.tuiPromptOptions = undefined;
|
|
1903
|
-
publishAttentionPatch(ds);
|
|
2048
|
+
const isActiveTuiCard = !!ds.tuiPromptCardId && cardMessageId === ds.tuiPromptCardId;
|
|
2049
|
+
if (!isActiveTuiCard) {
|
|
2050
|
+
logger.info(`[${tag(ds)}] tui_text_input from stale card ${cardMessageId} — ignored`);
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
if (ds.tuiPromptProcessing) {
|
|
2054
|
+
logger.info(`[${tag(ds)}] Duplicate TUI text input — dropped (processing already in flight)`);
|
|
2055
|
+
return;
|
|
1904
2056
|
}
|
|
2057
|
+
if (!ds.worker || !inputText || inputKeys.length === 0) {
|
|
2058
|
+
logger.info(`[${tag(ds)}] TUI text input not dispatched ` +
|
|
2059
|
+
`(worker=${!!ds.worker}, text=${!!inputText}, keys=${inputKeys.length})`);
|
|
2060
|
+
return {
|
|
2061
|
+
toast: {
|
|
2062
|
+
type: 'warning',
|
|
2063
|
+
content: t('card.action.tui_ipc_failed', undefined, locDs),
|
|
2064
|
+
},
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
// Atomic IPC — worker handles keys + text in one flow to avoid race
|
|
2068
|
+
ds.tuiPromptProcessing = true;
|
|
1905
2069
|
try {
|
|
1906
|
-
|
|
2070
|
+
await sendWorkerIpc(ds.worker, {
|
|
2071
|
+
type: 'tui_text_input',
|
|
2072
|
+
keys: inputKeys,
|
|
2073
|
+
text: inputText,
|
|
2074
|
+
cardMessageId,
|
|
2075
|
+
});
|
|
2076
|
+
}
|
|
2077
|
+
catch (err) {
|
|
2078
|
+
ds.tuiPromptProcessing = false;
|
|
2079
|
+
logger.warn(`[${tag(ds)}] TUI text IPC delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2080
|
+
return {
|
|
2081
|
+
toast: {
|
|
2082
|
+
type: 'warning',
|
|
2083
|
+
content: t('card.action.tui_ipc_failed', undefined, locDs),
|
|
2084
|
+
},
|
|
2085
|
+
};
|
|
2086
|
+
}
|
|
2087
|
+
logger.info(`[${tag(ds)}] TUI text input: "${inputText}" (keys: ${JSON.stringify(inputKeys)})`);
|
|
2088
|
+
publishAttentionPatch(ds);
|
|
2089
|
+
try {
|
|
2090
|
+
return JSON.parse(buildTuiPromptProcessingCard(inputText, locDs));
|
|
1907
2091
|
}
|
|
1908
2092
|
catch { /* fall through */ }
|
|
1909
2093
|
}
|
|
@@ -1923,10 +2107,20 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1923
2107
|
return launchLocalCli(ds, locDs);
|
|
1924
2108
|
}
|
|
1925
2109
|
if (actionType === 'get_write_link' && ds && operatorOpenId) {
|
|
1926
|
-
const botCfg = getBot(ds.larkAppId).config;
|
|
1927
2110
|
const effectiveCliId = sessionCliId(ds);
|
|
1928
2111
|
const locDs = localeForBot(ds.larkAppId);
|
|
1929
|
-
if (
|
|
2112
|
+
if (!sessionSupportsWebTerminal(ds)) {
|
|
2113
|
+
// Old cards can retain a get_write_link callback and stale port/token
|
|
2114
|
+
// fields after the session is restored onto ZMX. This is a permanent
|
|
2115
|
+
// backend capability boundary, not a transient startup delay.
|
|
2116
|
+
const unsupportedCard = JSON.stringify({
|
|
2117
|
+
config: { wide_screen_mode: true },
|
|
2118
|
+
elements: [{ tag: 'markdown', content: t('card.action.terminal_unsupported', undefined, locDs) }],
|
|
2119
|
+
});
|
|
2120
|
+
await deliverEphemeralOrReply(ds, operatorOpenId, unsupportedCard, 'interactive', () => sessionReply(rootId, unsupportedCard, 'interactive'));
|
|
2121
|
+
return;
|
|
2122
|
+
}
|
|
2123
|
+
if (sessionSupportsWebTerminal(ds) && (ds.riffAccessUrl || (ds.workerPort && ds.workerToken))) {
|
|
1930
2124
|
const writeUrl = buildTerminalUrl(ds, { write: true });
|
|
1931
2125
|
const cardJson = buildSessionCard(ds.session.sessionId, sessionAnchorId(ds), writeUrl, ds.session.title || getCliDisplayName(effectiveCliId), effectiveCliId, true, // showManageButtons — write-link card includes restart & close
|
|
1932
2126
|
!!ds.adoptedFrom, // adoptMode — disconnect, never close-the-CLI
|
|
@@ -1973,13 +2167,13 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
1973
2167
|
const next = nextMode(cur);
|
|
1974
2168
|
ds.displayMode = next;
|
|
1975
2169
|
persistStreamCardState(ds);
|
|
1976
|
-
if (ds.worker) {
|
|
1977
|
-
ds
|
|
2170
|
+
if (ds.worker || isSessionTransferring(ds)) {
|
|
2171
|
+
sendWorkerSessionInput(ds, { type: 'set_display_mode', mode: next });
|
|
1978
2172
|
}
|
|
1979
|
-
if (cardMessageId && ds
|
|
1980
|
-
const readUrl =
|
|
2173
|
+
if (cardMessageId && workerHasInitialized(ds)) {
|
|
2174
|
+
const readUrl = readableTerminalUrlFor(ds);
|
|
1981
2175
|
const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
1982
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent || '', ds.lastScreenStatus || 'working', effectiveCliId, next, ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }));
|
|
2176
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent || '', ds.lastScreenStatus || 'working', effectiveCliId, next, ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId));
|
|
1983
2177
|
updateMessage(ds.larkAppId, cardMessageId, cardJson).catch(err => logger.debug(`[${tag(ds)}] Failed to migrate unknown frozen card: ${err}`));
|
|
1984
2178
|
logger.info(`[${tag(ds)}] Migrated unknown frozen card to ${next} (legacy nonce=${clickedNonce})`);
|
|
1985
2179
|
try {
|
|
@@ -2000,13 +2194,13 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2000
2194
|
const next = nextMode(cur);
|
|
2001
2195
|
ds.displayMode = next;
|
|
2002
2196
|
persistStreamCardState(ds);
|
|
2003
|
-
if (ds.worker) {
|
|
2004
|
-
ds
|
|
2197
|
+
if (ds.worker || isSessionTransferring(ds)) {
|
|
2198
|
+
sendWorkerSessionInput(ds, { type: 'set_display_mode', mode: next });
|
|
2005
2199
|
}
|
|
2006
2200
|
const effectiveCliId = sessionCliId(ds);
|
|
2007
|
-
const readUrl =
|
|
2201
|
+
const readUrl = readableTerminalUrlFor(ds);
|
|
2008
2202
|
const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
2009
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent || '', ds.lastScreenStatus || 'working', effectiveCliId, next, ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }));
|
|
2203
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent || '', ds.lastScreenStatus || 'working', effectiveCliId, next, ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId));
|
|
2010
2204
|
updateMessage(ds.larkAppId, frozen.messageId, cardJson).catch(err => logger.debug(`[${tag(ds)}] Failed to migrate frozen card: ${err}`));
|
|
2011
2205
|
ds.frozenCards.delete(clickedNonce);
|
|
2012
2206
|
saveFrozenCards(ds.session.sessionId, ds.frozenCards);
|
|
@@ -2024,13 +2218,13 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2024
2218
|
const next = nextMode(cur);
|
|
2025
2219
|
ds.displayMode = next;
|
|
2026
2220
|
persistStreamCardState(ds);
|
|
2027
|
-
if (ds.worker) {
|
|
2028
|
-
ds
|
|
2221
|
+
if (ds.worker || isSessionTransferring(ds)) {
|
|
2222
|
+
sendWorkerSessionInput(ds, { type: 'set_display_mode', mode: next });
|
|
2029
2223
|
}
|
|
2030
|
-
if (ds.streamCardId && ds
|
|
2031
|
-
const readUrl =
|
|
2224
|
+
if (ds.streamCardId && workerHasInitialized(ds)) {
|
|
2225
|
+
const readUrl = readableTerminalUrlFor(ds);
|
|
2032
2226
|
const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
2033
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent || '', ds.lastScreenStatus || 'working', effectiveCliId, next, ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }));
|
|
2227
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent || '', ds.lastScreenStatus || 'working', effectiveCliId, next, ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId));
|
|
2034
2228
|
if (cardMessageId && cardMessageId !== ds.streamCardId) {
|
|
2035
2229
|
updateMessage(ds.larkAppId, cardMessageId, cardJson).catch(err => logger.debug(`[${tag(ds)}] Failed to migrate clicked legacy card: ${err}`));
|
|
2036
2230
|
}
|
|
@@ -2069,19 +2263,19 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2069
2263
|
}
|
|
2070
2264
|
// Manual screenshot refresh — force immediate capture bypassing 10s interval + hash dedup.
|
|
2071
2265
|
if (actionType === 'refresh_screenshot' && ds) {
|
|
2072
|
-
if (ds.worker) {
|
|
2073
|
-
ds
|
|
2266
|
+
if (ds.worker || isSessionTransferring(ds)) {
|
|
2267
|
+
sendWorkerSessionInput(ds, { type: 'refresh_screen' });
|
|
2074
2268
|
logger.info(`[${tag(ds)}] Manual screenshot refresh`);
|
|
2075
2269
|
}
|
|
2076
2270
|
// Return the current card JSON so Feishu doesn't revert the displayed
|
|
2077
2271
|
// image to the originally-POSTed initial frame while waiting for the
|
|
2078
2272
|
// fresh screenshot PATCH (~1s).
|
|
2079
|
-
if (ds.streamCardId && ds.streamCardId !== CARD_POSTING_SENTINEL && ds
|
|
2273
|
+
if (ds.streamCardId && ds.streamCardId !== CARD_POSTING_SENTINEL && workerHasInitialized(ds)) {
|
|
2080
2274
|
const botCfg = getBot(ds.larkAppId).config;
|
|
2081
2275
|
const effectiveCliId = sessionCliId(ds);
|
|
2082
|
-
const readUrl =
|
|
2276
|
+
const readUrl = readableTerminalUrlFor(ds);
|
|
2083
2277
|
const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
2084
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent || '', ds.lastScreenStatus || 'working', effectiveCliId, ds.displayMode ?? 'screenshot', ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }));
|
|
2278
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent || '', ds.lastScreenStatus || 'working', effectiveCliId, ds.displayMode ?? 'screenshot', ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId));
|
|
2085
2279
|
if (cardMessageId && cardMessageId !== ds.streamCardId) {
|
|
2086
2280
|
updateMessage(ds.larkAppId, cardMessageId, cardJson).catch(err => logger.debug(`[${tag(ds)}] Failed to migrate clicked legacy card: ${err}`));
|
|
2087
2281
|
}
|
|
@@ -2094,19 +2288,27 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2094
2288
|
}
|
|
2095
2289
|
// Quick-action keys (Esc, ^C, Tab, Space, Enter, ←↑↓→, ½ page) — forward to worker.
|
|
2096
2290
|
if (actionType === 'term_action' && ds) {
|
|
2291
|
+
if (isSessionTransferring(ds)) {
|
|
2292
|
+
return {
|
|
2293
|
+
toast: {
|
|
2294
|
+
type: 'warning',
|
|
2295
|
+
content: t('cmd.session.transfer_in_progress', undefined, localeForBot(ds.larkAppId)),
|
|
2296
|
+
},
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2097
2299
|
const key = value?.key;
|
|
2098
2300
|
if (!key)
|
|
2099
2301
|
return;
|
|
2100
2302
|
if (ds.worker) {
|
|
2101
|
-
ds
|
|
2303
|
+
sendWorkerSessionInput(ds, { type: 'term_action', key });
|
|
2102
2304
|
logger.info(`[${tag(ds)}] term_action: ${key}`);
|
|
2103
2305
|
}
|
|
2104
|
-
if (ds.streamCardId && ds.streamCardId !== CARD_POSTING_SENTINEL && ds
|
|
2306
|
+
if (ds.streamCardId && ds.streamCardId !== CARD_POSTING_SENTINEL && workerHasInitialized(ds)) {
|
|
2105
2307
|
const botCfg = getBot(ds.larkAppId).config;
|
|
2106
2308
|
const effectiveCliId = sessionCliId(ds);
|
|
2107
|
-
const readUrl =
|
|
2309
|
+
const readUrl = readableTerminalUrlFor(ds);
|
|
2108
2310
|
const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
|
|
2109
|
-
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent || '', ds.lastScreenStatus || 'working', effectiveCliId, ds.displayMode ?? 'screenshot', ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }));
|
|
2311
|
+
const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent || '', ds.lastScreenStatus || 'working', effectiveCliId, ds.displayMode ?? 'screenshot', ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds), isLocalCliOpenReady(ds, { cliId: effectiveCliId }), getDaemonStreamingCardUsageSnapshot(ds, effectiveCliId));
|
|
2110
2312
|
try {
|
|
2111
2313
|
return JSON.parse(cardJson);
|
|
2112
2314
|
}
|
|
@@ -2261,6 +2463,10 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2261
2463
|
const ds = activeSessions.get(sKey);
|
|
2262
2464
|
if (!ds)
|
|
2263
2465
|
return;
|
|
2466
|
+
const sourceSession = ds.session;
|
|
2467
|
+
if (isSessionTransferring(ds)) {
|
|
2468
|
+
return { toast: { type: 'warning', content: t('cmd.session.transfer_in_progress', undefined, localeForBot(ds.larkAppId)) } };
|
|
2469
|
+
}
|
|
2264
2470
|
if (!canOperate(ds.larkAppId, ds.chatId, operatorOpenId)) {
|
|
2265
2471
|
logger.info(`codex_app_thread_select blocked for non-operator user: ${operatorOpenId} (chat=${ds.chatId})`);
|
|
2266
2472
|
return { toast: { type: 'error', content: t('card.grant.toast_no_repo_perm', undefined, localeForBot(ds.larkAppId)) } };
|
|
@@ -2290,6 +2496,12 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2290
2496
|
await sessionReply(rootId, t('cmd.codex_app_adopt.list_failed', { error: err?.message ?? String(err) }, localeForBot(ds.larkAppId)));
|
|
2291
2497
|
return;
|
|
2292
2498
|
}
|
|
2499
|
+
if (ds.session !== sourceSession
|
|
2500
|
+
|| ds.session.status !== 'active'
|
|
2501
|
+
|| activeSessions.get(sKey) !== ds
|
|
2502
|
+
|| isSessionTransferring(ds)) {
|
|
2503
|
+
return { toast: { type: 'warning', content: t('cmd.session.transfer_in_progress', undefined, localeForBot(ds.larkAppId)) } };
|
|
2504
|
+
}
|
|
2293
2505
|
const target = threads.find(t => t.threadId === selected.threadId);
|
|
2294
2506
|
if (!target) {
|
|
2295
2507
|
await sessionReply(rootId, t('cmd.codex_app_adopt.thread_not_found', { threadId: selected.threadId }, localeForBot(ds.larkAppId)));
|
|
@@ -2311,6 +2523,10 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2311
2523
|
const ds = activeSessions.get(sKey);
|
|
2312
2524
|
if (!ds)
|
|
2313
2525
|
return;
|
|
2526
|
+
const sourceSession = ds.session;
|
|
2527
|
+
if (isSessionTransferring(ds)) {
|
|
2528
|
+
return { toast: { type: 'warning', content: t('cmd.session.transfer_in_progress', undefined, localeForBot(ds.larkAppId)) } };
|
|
2529
|
+
}
|
|
2314
2530
|
// /adopt 是管理动作:下拉入口同样要求 canOperate(命令路径已在 daemon 层 gate)。
|
|
2315
2531
|
if (!canOperate(ds.larkAppId, ds.chatId, operatorOpenId)) {
|
|
2316
2532
|
logger.info(`adopt_select blocked for non-operator user: ${operatorOpenId} (chat=${ds.chatId})`);
|
|
@@ -2375,6 +2591,12 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2375
2591
|
await new Promise(r => setTimeout(r, 150));
|
|
2376
2592
|
target = await resolveAdoptTarget();
|
|
2377
2593
|
}
|
|
2594
|
+
if (ds.session !== sourceSession
|
|
2595
|
+
|| ds.session.status !== 'active'
|
|
2596
|
+
|| activeSessions.get(sKey) !== ds
|
|
2597
|
+
|| isSessionTransferring(ds)) {
|
|
2598
|
+
return { toast: { type: 'warning', content: t('cmd.session.transfer_in_progress', undefined, localeForBot(ds.larkAppId)) } };
|
|
2599
|
+
}
|
|
2378
2600
|
if (!target) {
|
|
2379
2601
|
await sessionReply(rootId, t('cmd.adopt.target_exited', undefined, localeForBot(ds.larkAppId)));
|
|
2380
2602
|
if (cardMessageId && larkAppId)
|
|
@@ -2398,6 +2620,10 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2398
2620
|
const ds = activeSessions.get(sKey);
|
|
2399
2621
|
if (!ds)
|
|
2400
2622
|
return;
|
|
2623
|
+
const sourceSession = ds.session;
|
|
2624
|
+
if (isSessionTransferring(ds)) {
|
|
2625
|
+
return { toast: { type: 'warning', content: t('cmd.session.transfer_in_progress', undefined, localeForBot(ds.larkAppId)) } };
|
|
2626
|
+
}
|
|
2401
2627
|
if (!canOperate(ds.larkAppId, ds.chatId, operatorOpenId)) {
|
|
2402
2628
|
logger.info(`adopt_resume_select blocked for non-operator user: ${operatorOpenId} (chat=${ds.chatId})`);
|
|
2403
2629
|
return { toast: { type: 'error', content: t('card.grant.toast_no_repo_perm', undefined, localeForBot(ds.larkAppId)) } };
|
|
@@ -2416,6 +2642,12 @@ export async function handleCardAction(data, deps, larkAppId) {
|
|
|
2416
2642
|
const botCfg = getBot(ds.larkAppId).config;
|
|
2417
2643
|
const { discoverResumableSessionsForBot, startResumeImportSession } = await import('../../core/command-handler.js');
|
|
2418
2644
|
const resumable = await discoverResumableSessionsForBot(botCfg.cliId, botCfg.cliPathOverride, activeSessions);
|
|
2645
|
+
if (ds.session !== sourceSession
|
|
2646
|
+
|| ds.session.status !== 'active'
|
|
2647
|
+
|| activeSessions.get(sKey) !== ds
|
|
2648
|
+
|| isSessionTransferring(ds)) {
|
|
2649
|
+
return { toast: { type: 'warning', content: t('cmd.session.transfer_in_progress', undefined, localeForBot(ds.larkAppId)) } };
|
|
2650
|
+
}
|
|
2419
2651
|
const target = resumable.find(r => r.cliSessionId === selected.cliSessionId);
|
|
2420
2652
|
if (!target) {
|
|
2421
2653
|
await sessionReply(rootId, t('cmd.adopt.resume_not_found', { id: selected.cliSessionId }, localeForBot(ds.larkAppId)));
|