agent-tower 0.5.4 → 0.6.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/core/event-bus.d.ts +4 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/executors/__tests__/codex.executor.test.js +27 -21
- package/dist/executors/__tests__/codex.executor.test.js.map +1 -1
- package/dist/executors/__tests__/providers-availability.test.js +41 -1
- package/dist/executors/__tests__/providers-availability.test.js.map +1 -1
- package/dist/executors/__tests__/providers.test.js +34 -1
- package/dist/executors/__tests__/providers.test.js.map +1 -1
- package/dist/executors/codex.executor.d.ts.map +1 -1
- package/dist/executors/codex.executor.js +4 -0
- package/dist/executors/codex.executor.js.map +1 -1
- package/dist/executors/default-providers.json +106 -0
- package/dist/executors/index.d.ts +1 -1
- package/dist/executors/index.d.ts.map +1 -1
- package/dist/executors/index.js +33 -14
- package/dist/executors/index.js.map +1 -1
- package/dist/executors/providers.d.ts +12 -3
- package/dist/executors/providers.d.ts.map +1 -1
- package/dist/executors/providers.js +45 -18
- package/dist/executors/providers.js.map +1 -1
- package/dist/output/types.d.ts +32 -2
- package/dist/output/types.d.ts.map +1 -1
- package/dist/output/types.js +10 -0
- package/dist/output/types.js.map +1 -1
- package/dist/routes/__tests__/providers.test.js +197 -1
- package/dist/routes/__tests__/providers.test.js.map +1 -1
- package/dist/routes/__tests__/sessions-runtime.test.d.ts +2 -0
- package/dist/routes/__tests__/sessions-runtime.test.d.ts.map +1 -0
- package/dist/routes/__tests__/sessions-runtime.test.js +107 -0
- package/dist/routes/__tests__/sessions-runtime.test.js.map +1 -0
- package/dist/routes/providers.d.ts.map +1 -1
- package/dist/routes/providers.js +14 -5
- package/dist/routes/providers.js.map +1 -1
- package/dist/routes/sessions.d.ts.map +1 -1
- package/dist/routes/sessions.js +50 -3
- package/dist/routes/sessions.js.map +1 -1
- package/dist/routes/system.d.ts.map +1 -1
- package/dist/routes/system.js +8 -6
- package/dist/routes/system.js.map +1 -1
- package/dist/runtime/__tests__/acp-agent-definitions.test.d.ts +2 -0
- package/dist/runtime/__tests__/acp-agent-definitions.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/acp-agent-definitions.test.js +367 -0
- package/dist/runtime/__tests__/acp-agent-definitions.test.js.map +1 -0
- package/dist/runtime/__tests__/acp-driver-cleanup.test.d.ts +2 -0
- package/dist/runtime/__tests__/acp-driver-cleanup.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/acp-driver-cleanup.test.js +41 -0
- package/dist/runtime/__tests__/acp-driver-cleanup.test.js.map +1 -0
- package/dist/runtime/__tests__/acp-driver-lifecycle.test.d.ts +2 -0
- package/dist/runtime/__tests__/acp-driver-lifecycle.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/acp-driver-lifecycle.test.js +241 -0
- package/dist/runtime/__tests__/acp-driver-lifecycle.test.js.map +1 -0
- package/dist/runtime/__tests__/acp-driver.test.d.ts +2 -0
- package/dist/runtime/__tests__/acp-driver.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/acp-driver.test.js +122 -0
- package/dist/runtime/__tests__/acp-driver.test.js.map +1 -0
- package/dist/runtime/__tests__/acp-history-reconciler.test.d.ts +2 -0
- package/dist/runtime/__tests__/acp-history-reconciler.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/acp-history-reconciler.test.js +69 -0
- package/dist/runtime/__tests__/acp-history-reconciler.test.js.map +1 -0
- package/dist/runtime/__tests__/acp-process-manager.test.d.ts +2 -0
- package/dist/runtime/__tests__/acp-process-manager.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/acp-process-manager.test.js +51 -0
- package/dist/runtime/__tests__/acp-process-manager.test.js.map +1 -0
- package/dist/runtime/__tests__/acp-projector.test.d.ts +2 -0
- package/dist/runtime/__tests__/acp-projector.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/acp-projector.test.js +195 -0
- package/dist/runtime/__tests__/acp-projector.test.js.map +1 -0
- package/dist/runtime/__tests__/runtime-coordinator.test.d.ts +2 -0
- package/dist/runtime/__tests__/runtime-coordinator.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/runtime-coordinator.test.js +175 -0
- package/dist/runtime/__tests__/runtime-coordinator.test.js.map +1 -0
- package/dist/runtime/acp/acp-driver.d.ts +7 -0
- package/dist/runtime/acp/acp-driver.d.ts.map +1 -0
- package/dist/runtime/acp/acp-driver.js +441 -0
- package/dist/runtime/acp/acp-driver.js.map +1 -0
- package/dist/runtime/acp/agents/claude-code.d.ts +3 -0
- package/dist/runtime/acp/agents/claude-code.d.ts.map +1 -0
- package/dist/runtime/acp/agents/claude-code.js +147 -0
- package/dist/runtime/acp/agents/claude-code.js.map +1 -0
- package/dist/runtime/acp/agents/codex.d.ts +3 -0
- package/dist/runtime/acp/agents/codex.d.ts.map +1 -0
- package/dist/runtime/acp/agents/codex.js +70 -0
- package/dist/runtime/acp/agents/codex.js.map +1 -0
- package/dist/runtime/acp/agents/cursor-agent.d.ts +2 -0
- package/dist/runtime/acp/agents/cursor-agent.d.ts.map +1 -0
- package/dist/runtime/acp/agents/cursor-agent.js +12 -0
- package/dist/runtime/acp/agents/cursor-agent.js.map +1 -0
- package/dist/runtime/acp/agents/executable-resolution.d.ts +6 -0
- package/dist/runtime/acp/agents/executable-resolution.d.ts.map +1 -0
- package/dist/runtime/acp/agents/executable-resolution.js +72 -0
- package/dist/runtime/acp/agents/executable-resolution.js.map +1 -0
- package/dist/runtime/acp/agents/gemini-cli.d.ts +2 -0
- package/dist/runtime/acp/agents/gemini-cli.d.ts.map +1 -0
- package/dist/runtime/acp/agents/gemini-cli.js +13 -0
- package/dist/runtime/acp/agents/gemini-cli.js.map +1 -0
- package/dist/runtime/acp/agents/grok-build.d.ts +2 -0
- package/dist/runtime/acp/agents/grok-build.d.ts.map +1 -0
- package/dist/runtime/acp/agents/grok-build.js +26 -0
- package/dist/runtime/acp/agents/grok-build.js.map +1 -0
- package/dist/runtime/acp/agents/kiro-cli.d.ts +2 -0
- package/dist/runtime/acp/agents/kiro-cli.d.ts.map +1 -0
- package/dist/runtime/acp/agents/kiro-cli.js +18 -0
- package/dist/runtime/acp/agents/kiro-cli.js.map +1 -0
- package/dist/runtime/acp/agents/managed-directory.d.ts +6 -0
- package/dist/runtime/acp/agents/managed-directory.d.ts.map +1 -0
- package/dist/runtime/acp/agents/managed-directory.js +31 -0
- package/dist/runtime/acp/agents/managed-directory.js.map +1 -0
- package/dist/runtime/acp/agents/minion-acp-sitecustomize.d.ts +2 -0
- package/dist/runtime/acp/agents/minion-acp-sitecustomize.d.ts.map +1 -0
- package/dist/runtime/acp/agents/minion-acp-sitecustomize.js +118 -0
- package/dist/runtime/acp/agents/minion-acp-sitecustomize.js.map +1 -0
- package/dist/runtime/acp/agents/minion-code.d.ts +2 -0
- package/dist/runtime/acp/agents/minion-code.d.ts.map +1 -0
- package/dist/runtime/acp/agents/minion-code.js +53 -0
- package/dist/runtime/acp/agents/minion-code.js.map +1 -0
- package/dist/runtime/acp/agents/native-agent.d.ts +23 -0
- package/dist/runtime/acp/agents/native-agent.d.ts.map +1 -0
- package/dist/runtime/acp/agents/native-agent.js +158 -0
- package/dist/runtime/acp/agents/native-agent.js.map +1 -0
- package/dist/runtime/acp/agents/opencode.d.ts +2 -0
- package/dist/runtime/acp/agents/opencode.d.ts.map +1 -0
- package/dist/runtime/acp/agents/opencode.js +35 -0
- package/dist/runtime/acp/agents/opencode.js.map +1 -0
- package/dist/runtime/acp/agents/pi-coding-agent.d.ts +3 -0
- package/dist/runtime/acp/agents/pi-coding-agent.d.ts.map +1 -0
- package/dist/runtime/acp/agents/pi-coding-agent.js +180 -0
- package/dist/runtime/acp/agents/pi-coding-agent.js.map +1 -0
- package/dist/runtime/acp/agents/qwen-code.d.ts +3 -0
- package/dist/runtime/acp/agents/qwen-code.d.ts.map +1 -0
- package/dist/runtime/acp/agents/qwen-code.js +67 -0
- package/dist/runtime/acp/agents/qwen-code.js.map +1 -0
- package/dist/runtime/acp/agents/registry.d.ts +5 -0
- package/dist/runtime/acp/agents/registry.d.ts.map +1 -0
- package/dist/runtime/acp/agents/registry.js +34 -0
- package/dist/runtime/acp/agents/registry.js.map +1 -0
- package/dist/runtime/acp/agents/types.d.ts +35 -0
- package/dist/runtime/acp/agents/types.d.ts.map +1 -0
- package/dist/runtime/acp/agents/types.js +2 -0
- package/dist/runtime/acp/agents/types.js.map +1 -0
- package/dist/runtime/acp/codex-provider-config.d.ts +9 -0
- package/dist/runtime/acp/codex-provider-config.d.ts.map +1 -0
- package/dist/runtime/acp/codex-provider-config.js +107 -0
- package/dist/runtime/acp/codex-provider-config.js.map +1 -0
- package/dist/runtime/acp/history-reconciler.d.ts +8 -0
- package/dist/runtime/acp/history-reconciler.d.ts.map +1 -0
- package/dist/runtime/acp/history-reconciler.js +82 -0
- package/dist/runtime/acp/history-reconciler.js.map +1 -0
- package/dist/runtime/acp/process-manager.d.ts +33 -0
- package/dist/runtime/acp/process-manager.d.ts.map +1 -0
- package/dist/runtime/acp/process-manager.js +204 -0
- package/dist/runtime/acp/process-manager.js.map +1 -0
- package/dist/runtime/acp/projector.d.ts +23 -0
- package/dist/runtime/acp/projector.d.ts.map +1 -0
- package/dist/runtime/acp/projector.js +354 -0
- package/dist/runtime/acp/projector.js.map +1 -0
- package/dist/runtime/cli-driver.d.ts +7 -0
- package/dist/runtime/cli-driver.d.ts.map +1 -0
- package/dist/runtime/cli-driver.js +282 -0
- package/dist/runtime/cli-driver.js.map +1 -0
- package/dist/runtime/cli-parser.d.ts +5 -0
- package/dist/runtime/cli-parser.d.ts.map +1 -0
- package/dist/runtime/cli-parser.js +15 -0
- package/dist/runtime/cli-parser.js.map +1 -0
- package/dist/runtime/contracts.d.ts +113 -0
- package/dist/runtime/contracts.d.ts.map +1 -0
- package/dist/runtime/contracts.js +2 -0
- package/dist/runtime/contracts.js.map +1 -0
- package/dist/runtime/errors.d.ts +9 -0
- package/dist/runtime/errors.d.ts.map +1 -0
- package/dist/runtime/errors.js +29 -0
- package/dist/runtime/errors.js.map +1 -0
- package/dist/runtime/index.d.ts +9 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +9 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/runtime-coordinator.d.ts +33 -0
- package/dist/runtime/runtime-coordinator.d.ts.map +1 -0
- package/dist/runtime/runtime-coordinator.js +347 -0
- package/dist/runtime/runtime-coordinator.js.map +1 -0
- package/dist/runtime/runtime-registry.d.ts +8 -0
- package/dist/runtime/runtime-registry.d.ts.map +1 -0
- package/dist/runtime/runtime-registry.js +17 -0
- package/dist/runtime/runtime-registry.js.map +1 -0
- package/dist/runtime/runtime-state-view.d.ts +5 -0
- package/dist/runtime/runtime-state-view.d.ts.map +1 -0
- package/dist/runtime/runtime-state-view.js +15 -0
- package/dist/runtime/runtime-state-view.js.map +1 -0
- package/dist/services/__tests__/provider-config.service.test.js +31 -2
- package/dist/services/__tests__/provider-config.service.test.js.map +1 -1
- package/dist/services/__tests__/session-manager.lifecycle.test.js +107 -2
- package/dist/services/__tests__/session-manager.lifecycle.test.js.map +1 -1
- package/dist/services/__tests__/session-manager.team-run.test.js +22 -14
- package/dist/services/__tests__/session-manager.team-run.test.js.map +1 -1
- package/dist/services/conversation.service.d.ts.map +1 -1
- package/dist/services/conversation.service.js +3 -0
- package/dist/services/conversation.service.js.map +1 -1
- package/dist/services/mcp-config.integration.test.d.ts +2 -0
- package/dist/services/mcp-config.integration.test.d.ts.map +1 -0
- package/dist/services/mcp-config.integration.test.js +75 -0
- package/dist/services/mcp-config.integration.test.js.map +1 -0
- package/dist/services/mcp-config.service.d.ts +10 -3
- package/dist/services/mcp-config.service.d.ts.map +1 -1
- package/dist/services/mcp-config.service.js +70 -18
- package/dist/services/mcp-config.service.js.map +1 -1
- package/dist/services/mcp-config.service.test.js +44 -13
- package/dist/services/mcp-config.service.test.js.map +1 -1
- package/dist/services/provider-config.service.d.ts.map +1 -1
- package/dist/services/provider-config.service.js +34 -5
- package/dist/services/provider-config.service.js.map +1 -1
- package/dist/services/provider-effective-connection.service.d.ts +7 -1
- package/dist/services/provider-effective-connection.service.d.ts.map +1 -1
- package/dist/services/provider-effective-connection.service.js +56 -1
- package/dist/services/provider-effective-connection.service.js.map +1 -1
- package/dist/services/session-manager.d.ts +32 -24
- package/dist/services/session-manager.d.ts.map +1 -1
- package/dist/services/session-manager.js +280 -445
- package/dist/services/session-manager.js.map +1 -1
- package/dist/services/team-reconciler.service.d.ts +3 -0
- package/dist/services/team-reconciler.service.d.ts.map +1 -1
- package/dist/services/team-reconciler.service.js +14 -3
- package/dist/services/team-reconciler.service.js.map +1 -1
- package/dist/services/team-run.service.d.ts.map +1 -1
- package/dist/services/team-run.service.js +5 -1
- package/dist/services/team-run.service.js.map +1 -1
- package/dist/services/workspace.service.d.ts +2 -0
- package/dist/services/workspace.service.d.ts.map +1 -1
- package/dist/socket/__tests__/socket-gateway.test.js +42 -0
- package/dist/socket/__tests__/socket-gateway.test.js.map +1 -1
- package/dist/socket/events.d.ts +1 -1
- package/dist/socket/events.d.ts.map +1 -1
- package/dist/socket/events.js.map +1 -1
- package/dist/socket/index.js +1 -1
- package/dist/socket/index.js.map +1 -1
- package/dist/socket/socket-gateway.d.ts.map +1 -1
- package/dist/socket/socket-gateway.js +13 -1
- package/dist/socket/socket-gateway.js.map +1 -1
- package/dist/types/index.d.ts +7 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -1
- package/dist/web/agent-icons/codex.svg +4 -1
- package/dist/web/agent-icons/grok-build.svg +5 -0
- package/dist/web/agent-icons/kiro-cli.svg +6 -0
- package/dist/web/agent-icons/opencode.svg +5 -0
- package/dist/web/agent-icons/pi-coding-agent.svg +5 -0
- package/dist/web/agent-icons/qwen-code.svg +13 -0
- package/dist/web/assets/{AgentDemoPage-C3SHOCDR.js → AgentDemoPage-XrttOpQW.js} +1 -1
- package/dist/web/assets/{AgentEnvironmentSettingsPage-D-BWLcC0.js → AgentEnvironmentSettingsPage-CAo0bCpZ.js} +1 -1
- package/dist/web/assets/AgentLogo-BCwbooTN.js +1 -0
- package/dist/web/assets/ConversationPage-kyHuICil.js +3 -0
- package/dist/web/assets/CreateTaskInput-B67vzMJD.js +1 -0
- package/dist/web/assets/{DemoPage-Cd1ggnZY.js → DemoPage-DHMh9-zU.js} +1 -1
- package/dist/web/assets/{GeneralSettingsPage-B9D9wA_9.js → GeneralSettingsPage-DvWaqR9O.js} +1 -1
- package/dist/web/assets/{Icons-CM6Yg2lw.js → Icons-DADUmnB1.js} +1 -1
- package/dist/web/assets/{LoadingPreviewPage-BJ_Se_T_.js → LoadingPreviewPage-Dk91Ua1W.js} +1 -1
- package/dist/web/assets/{McpSettingsPage-B5GtqNme.js → McpSettingsPage-CH8jFyxp.js} +1 -1
- package/dist/web/assets/{MemberAvatar-yC8O30hv.js → MemberAvatar-l-WCdlLA.js} +1 -1
- package/dist/web/assets/{NotificationSettingsPage-BaJpYHLD.js → NotificationSettingsPage-Byu8b9X3.js} +1 -1
- package/dist/web/assets/{ProfileSettingsPage-DYXew66J.js → ProfileSettingsPage-BdylqWUj.js} +1 -1
- package/dist/web/assets/{ProjectKanbanPage-DbgIwiUC.js → ProjectKanbanPage--cx6wNDR.js} +37 -36
- package/dist/web/assets/{ProjectSettingsPage-B2K8Kw_S.js → ProjectSettingsPage-6psG0Vfc.js} +1 -1
- package/dist/web/assets/ProviderSettingsPage-BPEEkhtA.js +93 -0
- package/dist/web/assets/{SettingsMasterDetail-ByKe_zcK.js → SettingsMasterDetail-DTGIIGdI.js} +1 -1
- package/dist/web/assets/{TeamSettingsPage-4sHo9Wpb.js → TeamSettingsPage-BXuG4Bou.js} +1 -1
- package/dist/web/assets/agent-tower-lockup-DPAcheCt.png +0 -0
- package/dist/web/assets/{arc-g6Y4qqcK.js → arc-DrcjdWTE.js} +1 -1
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-ILRcqNPe.js → architectureDiagram-3BPJPVTR-CAoBhXSL.js} +1 -1
- package/dist/web/assets/{arrow-left-D0MkHwKU.js → arrow-left-CCWDjOmp.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-ZTMQ6CTT.js → blockDiagram-GPEHLZMM-CcTSBbUq.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-CAW8PcM8.js → c4Diagram-AAUBKEIU-BuUKdgUg.js} +1 -1
- package/dist/web/assets/channel-Bh_PCZNP.js +1 -0
- package/dist/web/assets/check-BN_PVKzE.js +1 -0
- package/dist/web/assets/{chevron-down-CmGXPki1.js → chevron-down-BQcW9sFf.js} +1 -1
- package/dist/web/assets/{chevron-right-jrkLz9qG.js → chevron-right-pH923eYp.js} +1 -1
- package/dist/web/assets/{chevron-up-UwBybVMK.js → chevron-up-DyQW115F.js} +1 -1
- package/dist/web/assets/{chunk-2J33WTMH-BvuLBX1d.js → chunk-2J33WTMH-BWNSmQa8.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-DJO5Yx2A.js → chunk-4BX2VUAB-Blldhw0k.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-Dc117Qwf.js → chunk-55IACEB6-hTffyPXL.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-DsZmTw67.js → chunk-727SXJPM-Cd48r7c-.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-DgmF7gD6.js → chunk-AQP2D5EJ-BdDrOIL7.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-vgh3Erl0.js → chunk-FMBD7UC4-6XIvQYVJ.js} +1 -1
- package/dist/web/assets/{chunk-ND2GUHAM-Ogtl4wwd.js → chunk-ND2GUHAM-BJlcb_pB.js} +1 -1
- package/dist/web/assets/{chunk-QZHKN3VN-Cif7bPpF.js → chunk-QZHKN3VN-3TZzFkxK.js} +1 -1
- package/dist/web/assets/{circle-alert-B6ZM3dSn.js → circle-alert-oa-BQ5e7.js} +1 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-B0lQFDoP.js +1 -0
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-B0lQFDoP.js +1 -0
- package/dist/web/assets/{code-block-OCS4YCEC-BQnKZly0.js → code-block-OCS4YCEC-xJQWFyR7.js} +1 -1
- package/dist/web/assets/{confirm-dialog-BZeaAl5j.js → confirm-dialog-CRiC3xBd.js} +1 -1
- package/dist/web/assets/{copy-DqNqPI8d.js → copy-DaoAUKTO.js} +1 -1
- package/dist/web/assets/{cose-bilkent-S5V4N54A-rxJB-TPw.js → cose-bilkent-S5V4N54A-Cjcm-rW_.js} +1 -1
- package/dist/web/assets/cssMode-_D6u4YDd.js +1 -0
- package/dist/web/assets/{dagre-BM42HDAG-BkB6VpXn.js → dagre-BM42HDAG-YqubcAJi.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-BH2e-eqy.js → diagram-2AECGRRQ-Dk4ayFqg.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-B0I47Yhs.js → diagram-5GNKFQAL-CQ0joYfB.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-BVXUYRvI.js → diagram-KO2AKTUF-DmuZzIdW.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-C1fEOLke.js → diagram-LMA3HP47-YTPEfEZy.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-CVE8cOcO.js → diagram-OG6HWLK6-DmwRuz8-.js} +1 -1
- package/dist/web/assets/{erDiagram-TEJ5UH35-qCfnXmfp.js → erDiagram-TEJ5UH35-LRL_DMXa.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-KMhbF5QC.js → flowDiagram-I6XJVG4X-ELBDyOvk.js} +1 -1
- package/dist/web/assets/{folder-picker-C3BgqamQ.js → folder-picker-WUhXxIM5.js} +1 -1
- package/dist/web/assets/{freemarker2-tYgVOcFl.js → freemarker2-DxJFkRz5.js} +2 -2
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-CaX3qYV1.js → ganttDiagram-6RSMTGT7-D0e9ILuF.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-CBAlZX7C.js → gitGraphDiagram-PVQCEYII-Ck6OXXw0.js} +1 -1
- package/dist/web/assets/handlebars-djdO6ZKR.js +1 -0
- package/dist/web/assets/html-CHeBxmFi.js +1 -0
- package/dist/web/assets/htmlMode-SZh8FaPi.js +1 -0
- package/dist/web/assets/index-CjAJoGg5.css +1 -0
- package/dist/web/assets/{index-BuQDZjol.js → index-CjdqAcbu.js} +4 -4
- package/dist/web/assets/{index-DHKgc87I.js → index-D9kBRMR0.js} +18 -18
- package/dist/web/assets/{infoDiagram-5YYISTIA-Bp_GeFtA.js → infoDiagram-5YYISTIA-Cx8be-MV.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-BfBNEV_D.js → ishikawaDiagram-YF4QCWOH-Dlc7akl7.js} +1 -1
- package/dist/web/assets/javascript-l6fV6hf7.js +1 -0
- package/dist/web/assets/{journeyDiagram-JHISSGLW-DJkUQG4Z.js → journeyDiagram-JHISSGLW-BVDSbS9-.js} +1 -1
- package/dist/web/assets/{jsonMode-D4_2geMa.js → jsonMode-B5CJhoWd.js} +2 -2
- package/dist/web/assets/{kanban-definition-UN3LZRKU-Bk7sA26p.js → kanban-definition-UN3LZRKU-nFR0zB7B.js} +1 -1
- package/dist/web/assets/{layers-BFkjcml2.js → layers-Bo4aml98.js} +1 -1
- package/dist/web/assets/{linear-CmC68WUf.js → linear-BCWZjcpP.js} +1 -1
- package/dist/web/assets/liquid-DkFOeJ6J.js +1 -0
- package/dist/web/assets/{lspLanguageFeatures-vY541IS0.js → lspLanguageFeatures-Cbk7wEkj.js} +1 -1
- package/dist/web/assets/mdx-BOPNdexu.js +1 -0
- package/dist/web/assets/{mermaid-NOHMQCX5-C6wHDXti.js → mermaid-NOHMQCX5-DWQaYVL0.js} +59 -61
- package/dist/web/assets/{message-square-Wwixl9YA.js → message-square-DxbkpGqF.js} +1 -1
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-Ck1je2Hf.js → mindmap-definition-RKZ34NQL-LI7W1FIX.js} +1 -1
- package/dist/web/assets/{modal-kFSArLJi.js → modal-Cymk-6Df.js} +1 -1
- package/dist/web/assets/{monaco-D3FVQqPj.js → monaco-Bc-Z2u8N.js} +3 -3
- package/dist/web/assets/{pencil-5Kegi5_H.js → pencil-DOL2nlgg.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-6Ao8YaoY.js → pieDiagram-4H26LBE5-mgUzAFkk.js} +1 -1
- package/dist/web/assets/python-D_wlYLCL.js +1 -0
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-md1eBgjS.js → quadrantDiagram-W4KKPZXB-CaDF2kqA.js} +1 -1
- package/dist/web/assets/razor-DBFVg4JU.js +1 -0
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-DJOb4wf-.js → requirementDiagram-4Y6WPE33-DhmHdr_0.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-B7srXhHb.js → sankeyDiagram-5OEKKPKP-B7qnGfue.js} +1 -1
- package/dist/web/assets/{select-C2v0aFwN.js → select-BpKQyMvD.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-L0qwgf37.js → sequenceDiagram-3UESZ5HK-D2LhPgpM.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-DGbH9dJ5.js → stateDiagram-AJRCARHV-ClkO4Gu5.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-YnSeSD_N.js +1 -0
- package/dist/web/assets/{switch-DjnF5A3R.js → switch-BJ4Ar_OA.js} +1 -1
- package/dist/web/assets/{textarea-CbZnd5PO.js → textarea-DjwjAAG8.js} +1 -1
- package/dist/web/assets/{timeline-definition-PNZ67QCA-Dku_Yjmk.js → timeline-definition-PNZ67QCA-DhGIG-GC.js} +1 -1
- package/dist/web/assets/{trash-2-DA--Uikt.js → trash-2-R46i6Uus.js} +1 -1
- package/dist/web/assets/{tsMode-CRNPFR24.js → tsMode-CKaqpIrz.js} +2 -2
- package/dist/web/assets/typescript-DsixnGVt.js +1 -0
- package/dist/web/assets/{upload-hWnFHuhE.js → upload-CgWqsmdI.js} +1 -1
- package/dist/web/assets/{use-profiles-roHXR8He.js → use-profiles-BlGgvdYm.js} +1 -1
- package/dist/web/assets/{use-projects-DiJMGduU.js → use-projects-Cuv0A0RC.js} +1 -1
- package/dist/web/assets/{use-providers-efX4HCcr.js → use-providers-DLcAY6E1.js} +1 -1
- package/dist/web/assets/{useNormalizedLogs-xEobyuGF.js → useNormalizedLogs-tfxFAJOF.js} +1 -1
- package/dist/web/assets/{vennDiagram-CIIHVFJN-_ufI1hxk.js → vennDiagram-CIIHVFJN-BngQhsYJ.js} +1 -1
- package/dist/web/assets/{wardley-L42UT6IY-Dve662aX.js → wardley-L42UT6IY-BEY6sftT.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-Dn2x3M_Q.js → wardleyDiagram-YWT4CUSO-IH_Yh-cw.js} +1 -1
- package/dist/web/assets/xml-dEcQ1gzo.js +1 -0
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-BMe6JPre.js → xychartDiagram-2RQKCTM6-BdrGFDsr.js} +1 -1
- package/dist/web/assets/yaml-BfGVqGj4.js +1 -0
- package/dist/web/index.html +2 -2
- package/node_modules/@agent-tower/shared/dist/agent-runtime-support.d.ts +11 -0
- package/node_modules/@agent-tower/shared/dist/agent-runtime-support.d.ts.map +1 -0
- package/node_modules/@agent-tower/shared/dist/agent-runtime-support.js +26 -0
- package/node_modules/@agent-tower/shared/dist/agent-runtime-support.js.map +1 -0
- package/node_modules/@agent-tower/shared/dist/index.d.ts +1 -0
- package/node_modules/@agent-tower/shared/dist/index.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/index.js +1 -0
- package/node_modules/@agent-tower/shared/dist/index.js.map +1 -1
- package/node_modules/@agent-tower/shared/dist/log-adapter.d.ts +38 -3
- package/node_modules/@agent-tower/shared/dist/log-adapter.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/log-adapter.js +21 -2
- package/node_modules/@agent-tower/shared/dist/log-adapter.js.map +1 -1
- package/node_modules/@agent-tower/shared/dist/provider-capabilities.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/provider-capabilities.js +58 -0
- package/node_modules/@agent-tower/shared/dist/provider-capabilities.js.map +1 -1
- package/node_modules/@agent-tower/shared/dist/socket/events.d.ts +17 -0
- package/node_modules/@agent-tower/shared/dist/socket/events.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/socket/events.js +3 -0
- package/node_modules/@agent-tower/shared/dist/socket/events.js.map +1 -1
- package/node_modules/@agent-tower/shared/dist/types.d.ts +63 -2
- package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/types.js +12 -0
- package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
- package/package.json +9 -3
- package/prisma/migrations/20260723000000_add_agent_runtime/migration.sql +2 -0
- package/prisma/schema.prisma +2 -0
- package/scripts/patch-claude-agent-acp.mjs +77 -0
- package/scripts/postinstall.js +5 -0
- package/dist/web/assets/AgentLogo-DOc8QQHe.js +0 -1
- package/dist/web/assets/ConversationPage-sknzMK-I.js +0 -3
- package/dist/web/assets/CreateTaskInput-Cn0k5D8K.js +0 -1
- package/dist/web/assets/ProviderSettingsPage-n2AFrtkZ.js +0 -93
- package/dist/web/assets/channel-DpisCjBV.js +0 -1
- package/dist/web/assets/check-VMcYyBr4.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-ByrAXg7x.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-ByrAXg7x.js +0 -1
- package/dist/web/assets/cssMode-BvIKUSIl.js +0 -1
- package/dist/web/assets/handlebars-O23dzm6n.js +0 -1
- package/dist/web/assets/html-QaPgl834.js +0 -1
- package/dist/web/assets/htmlMode-CWyii3pY.js +0 -1
- package/dist/web/assets/index-BeEbDvc9.css +0 -1
- package/dist/web/assets/javascript-C_IpQDkn.js +0 -1
- package/dist/web/assets/liquid-C6FS7ekL.js +0 -1
- package/dist/web/assets/mdx-LQJToB8m.js +0 -1
- package/dist/web/assets/python-HMxhKAEt.js +0 -1
- package/dist/web/assets/razor-CfDc2DcT.js +0 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-Bq1LxgY_.js +0 -1
- package/dist/web/assets/typescript-CNopspEc.js +0 -1
- package/dist/web/assets/xml-dqhfYLW3.js +0 -1
- package/dist/web/assets/yaml-BOxNX83z.js +0 -1
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { prisma } from '../utils/index.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { SessionStatus, SessionPurpose, TaskStatus, SessionContext } from '../types/index.js';
|
|
3
|
+
import { getProviderById, ExecutionEnv, normalizeExecutorStartError, } from '../executors/index.js';
|
|
4
4
|
import { filterAgentSubprocessExternalEnv } from '../executors/execution-env.js';
|
|
5
|
-
import { sessionMsgStoreManager,
|
|
6
|
-
import { AgentPipeline } from '../pipeline/agent-pipeline.js';
|
|
5
|
+
import { sessionMsgStoreManager, createUserMessage, addNormalizedEntry, } from '../output/index.js';
|
|
7
6
|
import { execGit } from '../git/git-cli.js';
|
|
8
7
|
import { getCommitMessageService } from '../core/container.js';
|
|
9
8
|
import { TeamReconcilerService } from './team-reconciler.service.js';
|
|
10
|
-
import { NotFoundError } from '../errors.js';
|
|
9
|
+
import { NotFoundError, ValidationError } from '../errors.js';
|
|
11
10
|
import { ensureTaskNotDeleted } from './deleted-task-guard.js';
|
|
12
11
|
import { getWorkspaceWorkingDir, isMainDirectoryWorkspace, } from './workspace-kind.js';
|
|
13
12
|
import { writeErrorLog } from '../utils/error-log.js';
|
|
14
13
|
import { INTERNAL_API_TOKEN_ENV, readInternalApiTokenFromEnv } from '../utils/internal-api-token.js';
|
|
15
14
|
import { createHash } from 'node:crypto';
|
|
15
|
+
import { RuntimeType, supportsAgentRuntime } from '@agent-tower/shared';
|
|
16
|
+
import { getProviderRuntimeType } from '../executors/providers.js';
|
|
17
|
+
import { CliRuntimeDriver, AcpRuntimeDriver, RuntimeCoordinator, StaticRuntimeRegistry, setRuntimeStateSnapshot, } from '../runtime/index.js';
|
|
16
18
|
const DEBUG_SNAPSHOT = process.env.DEBUG_SNAPSHOT === 'true';
|
|
17
19
|
function hashForLog(value) {
|
|
18
20
|
return createHash('sha256').update(value).digest('hex').slice(0, 12);
|
|
@@ -46,12 +48,9 @@ function isAgentProgressPatch(patch) {
|
|
|
46
48
|
}
|
|
47
49
|
export class SessionManager {
|
|
48
50
|
eventBus;
|
|
49
|
-
pipelines = new Map();
|
|
50
|
-
pendingSpawns = new Map();
|
|
51
|
-
cancelTokens = new Map();
|
|
52
51
|
snapshotFlushTimers = new Map();
|
|
53
52
|
snapshotFlushChains = new Map();
|
|
54
|
-
|
|
53
|
+
sessionPersistenceWriterChain = Promise.resolve();
|
|
55
54
|
dirtySnapshots = new Set();
|
|
56
55
|
persistedSnapshotHashes = new Map();
|
|
57
56
|
pendingSnapshotStatus = new Map();
|
|
@@ -70,13 +69,15 @@ export class SessionManager {
|
|
|
70
69
|
followUpReservationReleases = new Set();
|
|
71
70
|
/** Incremented for every start/send cycle so late post-processing cannot affect a new turn. */
|
|
72
71
|
sessionGenerations = new Map();
|
|
73
|
-
logicalCompletionCleanupTimers = new Map();
|
|
74
72
|
// 每个 session 上次写入 TeamRun 心跳时间戳的时刻,用于节流 lastHeartbeatAt 落库。
|
|
75
73
|
heartbeatThrottle = new Map();
|
|
76
74
|
teamReconciler;
|
|
75
|
+
runtimeCoordinator;
|
|
76
|
+
runtimeProcessIds = new Map();
|
|
77
|
+
runtimePermissionStates = new Map();
|
|
78
|
+
externalSessionPersistence = new Map();
|
|
77
79
|
static SNAPSHOT_CHECKPOINT_MS = 15_000;
|
|
78
80
|
static HEARTBEAT_THROTTLE_MS = 30_000;
|
|
79
|
-
static CODEX_TURN_COMPLETION_GRACE_MS = 250;
|
|
80
81
|
constructor(eventBus, teamReconciler) {
|
|
81
82
|
this.eventBus = eventBus;
|
|
82
83
|
this.teamReconciler = teamReconciler ?? new TeamReconcilerService({
|
|
@@ -86,6 +87,14 @@ export class SessionManager {
|
|
|
86
87
|
// session 退出时的首次 reconcile(COMPLETED 判定 / 首次补催)仍即时执行,不依赖该定时器。
|
|
87
88
|
scheduleReminders: false,
|
|
88
89
|
});
|
|
90
|
+
this.runtimeCoordinator = new RuntimeCoordinator(new StaticRuntimeRegistry([
|
|
91
|
+
new CliRuntimeDriver(),
|
|
92
|
+
new AcpRuntimeDriver(),
|
|
93
|
+
]), {
|
|
94
|
+
onTurnEvent: (event) => this.handleRuntimeTurnEvent(event),
|
|
95
|
+
onRuntimeState: (state) => this.handleRuntimeState(state),
|
|
96
|
+
onProcessEvent: (event) => this.handleRuntimeProcessEvent(event),
|
|
97
|
+
});
|
|
89
98
|
// Patches only mark the snapshot dirty. A low-frequency checkpoint keeps the
|
|
90
99
|
// hot stream away from SQLite while terminal paths still force a final flush.
|
|
91
100
|
this.eventBus.on('session:patch', ({ sessionId, patch }) => {
|
|
@@ -105,7 +114,6 @@ export class SessionManager {
|
|
|
105
114
|
// The parser has already written raw stdout, the final assistant entry,
|
|
106
115
|
// usage and all other state from the turn.completed chunk at this point.
|
|
107
116
|
this.terminalSessions.set(sessionId, SessionStatus.COMPLETED);
|
|
108
|
-
this.scheduleLogicalCompletionCleanup(sessionId);
|
|
109
117
|
this.startSessionFinalization(sessionId, 0, { logicalCompletion: true });
|
|
110
118
|
});
|
|
111
119
|
this.eventBus.on('session:turn-failed', ({ sessionId }) => {
|
|
@@ -115,30 +123,8 @@ export class SessionManager {
|
|
|
115
123
|
// without an exit code. Use a synthetic non-zero code for the shared
|
|
116
124
|
// finalization path so success-only post-processing cannot run.
|
|
117
125
|
this.terminalSessions.set(sessionId, SessionStatus.FAILED);
|
|
118
|
-
this.scheduleLogicalCompletionCleanup(sessionId);
|
|
119
126
|
this.startSessionFinalization(sessionId, 1, { logicalCompletion: true });
|
|
120
127
|
});
|
|
121
|
-
this.eventBus.on('session:exit', ({ sessionId, exitCode }) => {
|
|
122
|
-
const terminalStatus = this.terminalSessions.get(sessionId);
|
|
123
|
-
this.clearLogicalCompletionCleanup(sessionId);
|
|
124
|
-
const pipeline = this.pipelines.get(sessionId);
|
|
125
|
-
if (DEBUG_SNAPSHOT) {
|
|
126
|
-
console.log(`[SessionManager:snapshot] session:exit sessionId=${sessionId} exitCode=${exitCode} hasPipeline=${Boolean(pipeline)}`);
|
|
127
|
-
}
|
|
128
|
-
if (pipeline) {
|
|
129
|
-
// Must call destroy() to remove MsgStore onPatch/onSessionId listeners.
|
|
130
|
-
// Without this, stale listeners accumulate across send/exit cycles and
|
|
131
|
-
// each subsequent pushPatch() forwards the same patch N times.
|
|
132
|
-
pipeline.destroy();
|
|
133
|
-
this.pipelines.delete(sessionId);
|
|
134
|
-
}
|
|
135
|
-
this.cancelTokens.delete(sessionId);
|
|
136
|
-
this.heartbeatThrottle.delete(sessionId);
|
|
137
|
-
if (terminalStatus)
|
|
138
|
-
return;
|
|
139
|
-
this.terminalSessions.set(sessionId, typeof exitCode === 'number' && exitCode !== 0 ? SessionStatus.FAILED : SessionStatus.COMPLETED);
|
|
140
|
-
this.startSessionFinalization(sessionId, exitCode);
|
|
141
|
-
});
|
|
142
128
|
// NOTE: checkTaskAutoRevert is called directly (awaited) inside start()
|
|
143
129
|
// and sendMessage() to guarantee the task status is updated before the
|
|
144
130
|
// HTTP response is sent. A fire-and-forget EventBus listener here caused
|
|
@@ -151,6 +137,12 @@ export class SessionManager {
|
|
|
151
137
|
include: { processes: true, workspace: true, conversation: true },
|
|
152
138
|
});
|
|
153
139
|
}
|
|
140
|
+
getRuntimeState(sessionId, runtimeType = RuntimeType.CLI) {
|
|
141
|
+
return this.runtimeCoordinator.getState(sessionId, runtimeType);
|
|
142
|
+
}
|
|
143
|
+
async resolveRuntimePermission(sessionId, requestId, optionId) {
|
|
144
|
+
await this.runtimeCoordinator.resolvePermission(sessionId, requestId, optionId);
|
|
145
|
+
}
|
|
154
146
|
async create(workspaceId, agentType, prompt, variant = 'DEFAULT', providerId) {
|
|
155
147
|
const workspace = await prisma.workspace.findUnique({
|
|
156
148
|
where: { id: workspaceId },
|
|
@@ -160,11 +152,23 @@ export class SessionManager {
|
|
|
160
152
|
throw new NotFoundError('Workspace', workspaceId);
|
|
161
153
|
}
|
|
162
154
|
ensureTaskNotDeleted(workspace.task);
|
|
155
|
+
const provider = providerId ? getProviderById(providerId) : null;
|
|
156
|
+
if (providerId && !provider) {
|
|
157
|
+
throw new ValidationError(`Provider not found: ${providerId}`);
|
|
158
|
+
}
|
|
159
|
+
if (provider && provider.agentType !== agentType) {
|
|
160
|
+
throw new ValidationError(`Provider '${provider.name}' belongs to agent '${provider.agentType}', not '${agentType}'`);
|
|
161
|
+
}
|
|
162
|
+
const runtimeType = provider ? getProviderRuntimeType(provider) : RuntimeType.CLI;
|
|
163
|
+
if (!supportsAgentRuntime(agentType, runtimeType)) {
|
|
164
|
+
throw new ValidationError(`Agent '${agentType}' does not support the '${runtimeType}' runtime`);
|
|
165
|
+
}
|
|
163
166
|
return prisma.session.create({
|
|
164
167
|
data: {
|
|
165
168
|
workspaceId,
|
|
166
169
|
context: SessionContext.WORKSPACE,
|
|
167
170
|
agentType,
|
|
171
|
+
runtimeType,
|
|
168
172
|
variant,
|
|
169
173
|
providerId: providerId ?? null,
|
|
170
174
|
prompt,
|
|
@@ -190,39 +194,7 @@ export class SessionManager {
|
|
|
190
194
|
prompt: summarizeTextForLog(session.prompt),
|
|
191
195
|
workingDir,
|
|
192
196
|
});
|
|
193
|
-
|
|
194
|
-
const executor = this.resolveExecutor(agentType, session.variant, session.providerId);
|
|
195
|
-
console.log('[SessionManager] ✅ Executor found, spawning process...');
|
|
196
|
-
const env = ExecutionEnv.default(workingDir);
|
|
197
|
-
// 如果有 provider,注入 provider 的环境变量
|
|
198
|
-
if (session.providerId) {
|
|
199
|
-
const provider = getProviderById(session.providerId);
|
|
200
|
-
if (provider && Object.keys(provider.env).length > 0) {
|
|
201
|
-
env.merge(filterAgentSubprocessExternalEnv(provider.env));
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
this.injectAgentTowerMcpServiceEnv(env);
|
|
205
|
-
if (!this.isConversationSession(session)) {
|
|
206
|
-
await this.injectTeamRunInvocationEnv(id, env);
|
|
207
|
-
}
|
|
208
|
-
let spawnResult;
|
|
209
|
-
try {
|
|
210
|
-
spawnResult = await executor.spawn({
|
|
211
|
-
workingDir,
|
|
212
|
-
prompt: session.prompt,
|
|
213
|
-
env,
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
catch (error) {
|
|
217
|
-
this.logSessionError('session.spawn', error, {
|
|
218
|
-
sessionId: id,
|
|
219
|
-
agentType: session.agentType,
|
|
220
|
-
providerId: session.providerId,
|
|
221
|
-
workingDir,
|
|
222
|
-
});
|
|
223
|
-
throw normalizeExecutorStartError(error);
|
|
224
|
-
}
|
|
225
|
-
await this.activateSpawnedSession(id, agentType, workingDir, spawnResult);
|
|
197
|
+
await this.startRuntimeTurn(session, session.prompt, session.externalSessionId);
|
|
226
198
|
return session;
|
|
227
199
|
}
|
|
228
200
|
async startFollowUp(id, resumeFromSessionId) {
|
|
@@ -238,10 +210,11 @@ export class SessionManager {
|
|
|
238
210
|
this.ensureExecutionRecordIsLive(session);
|
|
239
211
|
const resumeFromSession = await prisma.session.findUnique({
|
|
240
212
|
where: { id: resumeFromSessionId },
|
|
241
|
-
select: { logSnapshot: true },
|
|
213
|
+
select: { logSnapshot: true, externalSessionId: true },
|
|
242
214
|
});
|
|
243
215
|
const agentSessionId = resumeFromSession
|
|
244
|
-
?
|
|
216
|
+
? resumeFromSession.externalSessionId
|
|
217
|
+
?? this.resolveAgentSessionId(resumeFromSessionId, resumeFromSession.logSnapshot)
|
|
245
218
|
: null;
|
|
246
219
|
console.log('[SessionManager] Follow-up session details:', {
|
|
247
220
|
id: session.id,
|
|
@@ -252,76 +225,7 @@ export class SessionManager {
|
|
|
252
225
|
prompt: summarizeTextForLog(session.prompt),
|
|
253
226
|
workingDir: this.getExecutionWorkingDir(session),
|
|
254
227
|
});
|
|
255
|
-
|
|
256
|
-
const executor = this.resolveExecutor(agentType, session.variant, session.providerId);
|
|
257
|
-
const workingDir = this.getExecutionWorkingDir(session);
|
|
258
|
-
const env = ExecutionEnv.default(workingDir);
|
|
259
|
-
if (session.providerId) {
|
|
260
|
-
const provider = getProviderById(session.providerId);
|
|
261
|
-
if (provider && Object.keys(provider.env).length > 0) {
|
|
262
|
-
env.merge(filterAgentSubprocessExternalEnv(provider.env));
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
this.injectAgentTowerMcpServiceEnv(env);
|
|
266
|
-
if (!this.isConversationSession(session)) {
|
|
267
|
-
await this.injectTeamRunInvocationEnv(id, env);
|
|
268
|
-
}
|
|
269
|
-
const spawnConfig = {
|
|
270
|
-
workingDir,
|
|
271
|
-
prompt: session.prompt,
|
|
272
|
-
env,
|
|
273
|
-
};
|
|
274
|
-
let spawnResult;
|
|
275
|
-
if (agentSessionId && executor.spawnFollowUp) {
|
|
276
|
-
try {
|
|
277
|
-
spawnResult = await executor.spawnFollowUp(spawnConfig, agentSessionId);
|
|
278
|
-
}
|
|
279
|
-
catch (error) {
|
|
280
|
-
console.warn(`[SessionManager] Follow-up spawn failed for ${id}, falling back to a new agent session:`, error instanceof Error ? error.message : error);
|
|
281
|
-
writeErrorLog({
|
|
282
|
-
level: 'warn',
|
|
283
|
-
source: 'session.spawnFollowUpResume',
|
|
284
|
-
message: `Follow-up spawn failed for session ${id}; falling back to a new agent session`,
|
|
285
|
-
error,
|
|
286
|
-
metadata: {
|
|
287
|
-
sessionId: id,
|
|
288
|
-
resumeFromSessionId,
|
|
289
|
-
agentType: session.agentType,
|
|
290
|
-
providerId: session.providerId,
|
|
291
|
-
workingDir,
|
|
292
|
-
},
|
|
293
|
-
});
|
|
294
|
-
try {
|
|
295
|
-
spawnResult = await executor.spawn(spawnConfig);
|
|
296
|
-
}
|
|
297
|
-
catch (fallbackError) {
|
|
298
|
-
this.logSessionError('session.spawnFollowUpFallback', fallbackError, {
|
|
299
|
-
sessionId: id,
|
|
300
|
-
resumeFromSessionId,
|
|
301
|
-
agentType: session.agentType,
|
|
302
|
-
providerId: session.providerId,
|
|
303
|
-
workingDir,
|
|
304
|
-
});
|
|
305
|
-
throw normalizeExecutorStartError(fallbackError);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
else {
|
|
310
|
-
try {
|
|
311
|
-
spawnResult = await executor.spawn(spawnConfig);
|
|
312
|
-
}
|
|
313
|
-
catch (error) {
|
|
314
|
-
this.logSessionError('session.spawnFollowUp', error, {
|
|
315
|
-
sessionId: id,
|
|
316
|
-
resumeFromSessionId,
|
|
317
|
-
agentType: session.agentType,
|
|
318
|
-
providerId: session.providerId,
|
|
319
|
-
workingDir,
|
|
320
|
-
});
|
|
321
|
-
throw normalizeExecutorStartError(error);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
await this.activateSpawnedSession(id, agentType, workingDir, spawnResult);
|
|
228
|
+
await this.startRuntimeTurn(session, session.prompt, agentSessionId, session.providerId, 'resume');
|
|
325
229
|
return session;
|
|
326
230
|
}
|
|
327
231
|
async sendMessage(id, message, providerId) {
|
|
@@ -353,6 +257,9 @@ export class SessionManager {
|
|
|
353
257
|
if (String(effectiveProvider.agentType) !== session.agentType) {
|
|
354
258
|
throw new Error(`Cannot switch provider: agentType mismatch. Session uses '${session.agentType}', but provider '${effectiveProvider.name}' is for '${effectiveProvider.agentType}'`);
|
|
355
259
|
}
|
|
260
|
+
if (getProviderRuntimeType(effectiveProvider) !== this.normalizeRuntimeType(session.runtimeType)) {
|
|
261
|
+
throw new Error(`Cannot switch provider: runtimeType mismatch. Session uses '${session.runtimeType}', but provider '${effectiveProvider.name}' uses '${getProviderRuntimeType(effectiveProvider)}'`);
|
|
262
|
+
}
|
|
356
263
|
}
|
|
357
264
|
if (providerId && providerId !== session.providerId) {
|
|
358
265
|
const switchedProvider = getProviderById(providerId);
|
|
@@ -368,18 +275,18 @@ export class SessionManager {
|
|
|
368
275
|
// the old execution with the new one.
|
|
369
276
|
this.invalidateSessionGeneration(id);
|
|
370
277
|
this.beginSessionExecution(id);
|
|
371
|
-
//
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
|
|
375
|
-
if (existing) {
|
|
278
|
+
// Stop the previous turn before waiting for its auto-commit boundary. The
|
|
279
|
+
// coordinator suppresses that superseded turn's terminal event so it cannot
|
|
280
|
+
// finalize the newly reserved generation.
|
|
281
|
+
if (this.runtimeCoordinator.hasActiveTurn(id)) {
|
|
376
282
|
if (DEBUG_SNAPSHOT) {
|
|
377
|
-
console.log(`[SessionManager:snapshot] sendMessage checkpoint before
|
|
283
|
+
console.log(`[SessionManager:snapshot] sendMessage checkpoint before runtime turn replace sessionId=${id}`);
|
|
378
284
|
}
|
|
379
285
|
await this.flushSnapshotPersist(id);
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
286
|
+
const canReuseDriverSession = await this.runtimeCoordinator.abandonTurn(id);
|
|
287
|
+
if (!canReuseDriverSession) {
|
|
288
|
+
await this.runtimeCoordinator.disposeSession(id);
|
|
289
|
+
}
|
|
383
290
|
}
|
|
384
291
|
await this.waitForPendingAutoCommit(id);
|
|
385
292
|
const isNewStore = !sessionMsgStoreManager.has(id);
|
|
@@ -416,74 +323,12 @@ export class SessionManager {
|
|
|
416
323
|
// MsgStore's patchListeners are empty at this point. Without this line
|
|
417
324
|
// the user-message patch would never reach WebSocket subscribers.
|
|
418
325
|
this.eventBus.emit('session:patch', { sessionId: id, patch: userPatch, seq: userPatchSeq });
|
|
419
|
-
const agentSessionId =
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const env = ExecutionEnv.default(workingDir);
|
|
424
|
-
// 如果有 provider,注入 provider 的环境变量
|
|
425
|
-
if (effectiveProviderId) {
|
|
426
|
-
const provider = getProviderById(effectiveProviderId);
|
|
427
|
-
if (provider && Object.keys(provider.env).length > 0) {
|
|
428
|
-
env.merge(filterAgentSubprocessExternalEnv(provider.env));
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
this.injectAgentTowerMcpServiceEnv(env);
|
|
432
|
-
if (!this.isConversationSession(session)) {
|
|
433
|
-
await this.injectTeamRunInvocationEnv(id, env);
|
|
434
|
-
}
|
|
435
|
-
const spawnConfig = {
|
|
436
|
-
workingDir,
|
|
437
|
-
prompt: message,
|
|
438
|
-
env,
|
|
439
|
-
};
|
|
440
|
-
let spawnResult;
|
|
441
|
-
if (agentSessionId && executor.spawnFollowUp) {
|
|
442
|
-
try {
|
|
443
|
-
spawnResult = await executor.spawnFollowUp(spawnConfig, agentSessionId);
|
|
444
|
-
}
|
|
445
|
-
catch (resumeError) {
|
|
446
|
-
writeErrorLog({
|
|
447
|
-
level: 'warn',
|
|
448
|
-
source: 'session.messageSpawnResume',
|
|
449
|
-
message: `Message follow-up spawn failed for session ${id}; falling back to a new agent session`,
|
|
450
|
-
error: resumeError,
|
|
451
|
-
metadata: {
|
|
452
|
-
sessionId: id,
|
|
453
|
-
agentType: session.agentType,
|
|
454
|
-
providerId: effectiveProviderId,
|
|
455
|
-
workingDir,
|
|
456
|
-
},
|
|
457
|
-
});
|
|
458
|
-
try {
|
|
459
|
-
spawnResult = await executor.spawn(spawnConfig);
|
|
460
|
-
}
|
|
461
|
-
catch (error) {
|
|
462
|
-
this.logSessionError('session.messageSpawnFallback', error, {
|
|
463
|
-
sessionId: id,
|
|
464
|
-
agentType: session.agentType,
|
|
465
|
-
providerId: effectiveProviderId,
|
|
466
|
-
workingDir,
|
|
467
|
-
});
|
|
468
|
-
throw normalizeExecutorStartError(error);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
else {
|
|
473
|
-
try {
|
|
474
|
-
spawnResult = await executor.spawn(spawnConfig);
|
|
475
|
-
}
|
|
476
|
-
catch (error) {
|
|
477
|
-
this.logSessionError('session.messageSpawn', error, {
|
|
478
|
-
sessionId: id,
|
|
479
|
-
agentType: session.agentType,
|
|
480
|
-
providerId: effectiveProviderId,
|
|
481
|
-
workingDir,
|
|
482
|
-
});
|
|
483
|
-
throw normalizeExecutorStartError(error);
|
|
484
|
-
}
|
|
326
|
+
const agentSessionId = session.externalSessionId
|
|
327
|
+
?? this.resolveAgentSessionId(id, session.logSnapshot);
|
|
328
|
+
if (providerId && providerId !== session.providerId) {
|
|
329
|
+
await this.runtimeCoordinator.disposeSession(id);
|
|
485
330
|
}
|
|
486
|
-
await this.
|
|
331
|
+
await this.startRuntimeTurn(session, message, agentSessionId, effectiveProviderId);
|
|
487
332
|
return session;
|
|
488
333
|
}
|
|
489
334
|
finally {
|
|
@@ -495,19 +340,19 @@ export class SessionManager {
|
|
|
495
340
|
if (!session)
|
|
496
341
|
return null;
|
|
497
342
|
const terminalStatus = this.terminalSessions.get(id);
|
|
343
|
+
const hasActiveTurn = this.runtimeCoordinator.hasActiveTurn(id);
|
|
498
344
|
const persistedTerminal = [
|
|
499
345
|
SessionStatus.COMPLETED,
|
|
500
346
|
SessionStatus.FAILED,
|
|
501
347
|
SessionStatus.CANCELLED,
|
|
502
348
|
].includes(session.status);
|
|
503
|
-
if (terminalStatus || persistedTerminal) {
|
|
349
|
+
if (!hasActiveTurn && (terminalStatus || persistedTerminal)) {
|
|
504
350
|
const pendingFinalization = this.sessionFinalizations.get(id);
|
|
505
351
|
// A terminal transition already won the race. A late user stop may
|
|
506
352
|
// clean up the PTY, but it must not regress the persisted status. The
|
|
507
353
|
// backing TeamRun invocation may still be waiting for a room reply, so
|
|
508
354
|
// it must still pass through the cancellation reconciler.
|
|
509
|
-
this.
|
|
510
|
-
this.destroyActivePipeline(id);
|
|
355
|
+
await this.runtimeCoordinator.disposeSession(id);
|
|
511
356
|
this.maybeClearTerminalState(id);
|
|
512
357
|
await pendingFinalization;
|
|
513
358
|
if (!options.skipTeamRunReconcile && !this.isConversationSession(session)) {
|
|
@@ -515,34 +360,26 @@ export class SessionManager {
|
|
|
515
360
|
}
|
|
516
361
|
return session;
|
|
517
362
|
}
|
|
518
|
-
this.clearLogicalCompletionCleanup(id);
|
|
519
363
|
this.terminalSessions.set(id, SessionStatus.CANCELLED);
|
|
520
|
-
const
|
|
521
|
-
if (
|
|
522
|
-
this.
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
pendingSpawn.pty.kill();
|
|
531
|
-
}
|
|
532
|
-
catch {
|
|
533
|
-
// ignore kill errors for pending spawns
|
|
364
|
+
const runtimeType = this.normalizeRuntimeType(session.runtimeType);
|
|
365
|
+
if (runtimeType === RuntimeType.ACP && hasActiveTurn) {
|
|
366
|
+
const canReuseDriverSession = await this.runtimeCoordinator.abandonTurn(id).catch((error) => {
|
|
367
|
+
this.logSessionError('session.runtimeCancel', error, { sessionId: id });
|
|
368
|
+
return false;
|
|
369
|
+
});
|
|
370
|
+
if (!canReuseDriverSession) {
|
|
371
|
+
await this.runtimeCoordinator.disposeSession(id).catch((error) => {
|
|
372
|
+
this.logSessionError('session.runtimeDispose', error, { sessionId: id });
|
|
373
|
+
});
|
|
534
374
|
}
|
|
535
375
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
}
|
|
543
|
-
pipeline.destroy();
|
|
544
|
-
this.pipelines.delete(id);
|
|
545
|
-
this.cancelTokens.delete(id);
|
|
376
|
+
else {
|
|
377
|
+
await this.runtimeCoordinator.cancelTurn(id).catch((error) => {
|
|
378
|
+
this.logSessionError('session.runtimeCancel', error, { sessionId: id });
|
|
379
|
+
});
|
|
380
|
+
await this.runtimeCoordinator.disposeSession(id).catch((error) => {
|
|
381
|
+
this.logSessionError('session.runtimeDispose', error, { sessionId: id });
|
|
382
|
+
});
|
|
546
383
|
}
|
|
547
384
|
const msgStore = sessionMsgStoreManager.get(id);
|
|
548
385
|
if (msgStore) {
|
|
@@ -568,19 +405,21 @@ export class SessionManager {
|
|
|
568
405
|
await this.teamReconciler.handleSessionStopped(id);
|
|
569
406
|
}
|
|
570
407
|
this.eventBus.emit('session:stopped', { sessionId: id });
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
// 因此在这里释放 MsgStore。快照已在上方持久化(CANCELLED)。
|
|
408
|
+
// Cancellation does not run normal terminal finalization, so release the
|
|
409
|
+
// store after the CANCELLED snapshot has been persisted above.
|
|
574
410
|
sessionMsgStoreManager.delete(id);
|
|
575
411
|
this.releaseSnapshotPersistenceState(id);
|
|
576
412
|
return session;
|
|
577
413
|
}
|
|
578
|
-
/**
|
|
579
|
-
* 是否仍持有该 session 的活跃 PTY pipeline。
|
|
580
|
-
* 供 TeamRun 心跳 watchdog 判断 invocation 是真卡死(pipeline 存活)还是孤儿(进程已脱管)。
|
|
581
|
-
*/
|
|
414
|
+
/** @deprecated Use hasActiveTurn(). */
|
|
582
415
|
hasActivePipeline(sessionId) {
|
|
583
|
-
return this.
|
|
416
|
+
return this.runtimeCoordinator.hasActiveTurn(sessionId);
|
|
417
|
+
}
|
|
418
|
+
hasActiveTurn(sessionId) {
|
|
419
|
+
return this.runtimeCoordinator.hasActiveTurn(sessionId);
|
|
420
|
+
}
|
|
421
|
+
isAwaitingPermission(sessionId) {
|
|
422
|
+
return this.runtimeCoordinator.isAwaitingPermission(sessionId);
|
|
584
423
|
}
|
|
585
424
|
/**
|
|
586
425
|
* 节流写入 TeamRun invocation 的心跳时间戳。非 TeamRun session 无对应 invocation,updateMany 命中 0 行无副作用。
|
|
@@ -601,37 +440,16 @@ export class SessionManager {
|
|
|
601
440
|
});
|
|
602
441
|
}
|
|
603
442
|
writeInput(sessionId, data) {
|
|
604
|
-
|
|
605
|
-
if (!pipeline)
|
|
606
|
-
return;
|
|
607
|
-
pipeline.write(data);
|
|
443
|
+
this.runtimeCoordinator.writeInput(sessionId, data);
|
|
608
444
|
}
|
|
609
445
|
resize(sessionId, cols, rows) {
|
|
610
|
-
|
|
611
|
-
if (!pipeline)
|
|
612
|
-
return;
|
|
613
|
-
pipeline.resize(cols, rows);
|
|
446
|
+
this.runtimeCoordinator.resize(sessionId, cols, rows);
|
|
614
447
|
}
|
|
615
|
-
/**
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
console.log(`[SessionManager] Destroying all ${this.pipelines.size} active pipelines`);
|
|
621
|
-
}
|
|
622
|
-
for (const [sessionId, pipeline] of this.pipelines) {
|
|
623
|
-
const cancel = this.cancelTokens.get(sessionId);
|
|
624
|
-
if (cancel) {
|
|
625
|
-
cancel.cancel();
|
|
626
|
-
}
|
|
627
|
-
pipeline.destroy();
|
|
628
|
-
}
|
|
629
|
-
this.pipelines.clear();
|
|
630
|
-
this.cancelTokens.clear();
|
|
631
|
-
for (const timer of this.logicalCompletionCleanupTimers.values()) {
|
|
632
|
-
clearTimeout(timer);
|
|
633
|
-
}
|
|
634
|
-
this.logicalCompletionCleanupTimers.clear();
|
|
448
|
+
/** Close all runtime driver sessions during graceful server shutdown. */
|
|
449
|
+
async destroyAll() {
|
|
450
|
+
await this.runtimeCoordinator.destroyAll();
|
|
451
|
+
await Promise.allSettled(this.externalSessionPersistence.values());
|
|
452
|
+
this.externalSessionPersistence.clear();
|
|
635
453
|
this.terminalSessions.clear();
|
|
636
454
|
for (const resolve of this.pendingAutoCommitResolvers.values())
|
|
637
455
|
resolve();
|
|
@@ -661,163 +479,208 @@ export class SessionManager {
|
|
|
661
479
|
}
|
|
662
480
|
return null;
|
|
663
481
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
482
|
+
async startRuntimeTurn(session, prompt, resumeExternalSessionId, providerId = session.providerId, resumeMode = 'load') {
|
|
483
|
+
const workingDir = this.getExecutionWorkingDir(session);
|
|
484
|
+
const env = ExecutionEnv.default(workingDir);
|
|
485
|
+
if (providerId) {
|
|
486
|
+
const provider = getProviderById(providerId);
|
|
487
|
+
if (provider && Object.keys(provider.env).length > 0) {
|
|
488
|
+
env.merge(filterAgentSubprocessExternalEnv(provider.env));
|
|
489
|
+
}
|
|
670
490
|
}
|
|
671
|
-
|
|
672
|
-
|
|
491
|
+
this.injectAgentTowerMcpServiceEnv(env);
|
|
492
|
+
if (!this.isConversationSession(session)) {
|
|
493
|
+
await this.injectTeamRunInvocationEnv(session.id, env);
|
|
673
494
|
}
|
|
674
|
-
|
|
675
|
-
|
|
495
|
+
const isNewStore = !sessionMsgStoreManager.has(session.id);
|
|
496
|
+
const msgStore = sessionMsgStoreManager.getOrCreate(session.id);
|
|
497
|
+
if (isNewStore && session.logSnapshot) {
|
|
498
|
+
try {
|
|
499
|
+
msgStore.restoreFromSnapshot(JSON.parse(session.logSnapshot));
|
|
500
|
+
}
|
|
501
|
+
catch (error) {
|
|
502
|
+
this.logSessionError('session.snapshotRestore', error, { sessionId: session.id });
|
|
503
|
+
}
|
|
676
504
|
}
|
|
677
|
-
return executor;
|
|
678
|
-
}
|
|
679
|
-
async activateSpawnedSession(sessionId, agentType, workingDir, spawnResult) {
|
|
680
505
|
try {
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
506
|
+
// Session status follows the logical Runtime turn, not the lifetime of
|
|
507
|
+
// its backing OS process. ACP reuses one adapter process across turns,
|
|
508
|
+
// so a follow-up must become RUNNING even when no process starts.
|
|
509
|
+
await prisma.session.update({
|
|
510
|
+
where: { id: session.id },
|
|
511
|
+
data: { status: SessionStatus.RUNNING },
|
|
512
|
+
});
|
|
513
|
+
const handle = await this.runtimeCoordinator.startTurn({
|
|
514
|
+
towerSessionId: session.id,
|
|
515
|
+
agentType: session.agentType,
|
|
516
|
+
runtimeType: this.normalizeRuntimeType(session.runtimeType),
|
|
517
|
+
variant: session.variant ?? 'DEFAULT',
|
|
518
|
+
providerId,
|
|
519
|
+
workingDir,
|
|
520
|
+
env,
|
|
521
|
+
externalSessionId: session.externalSessionId,
|
|
522
|
+
msgStore,
|
|
523
|
+
prompt,
|
|
524
|
+
resumeExternalSessionId,
|
|
525
|
+
resumeMode,
|
|
526
|
+
});
|
|
527
|
+
// Terminal persistence is driven by Runtime turn events. Attach a catch
|
|
528
|
+
// so the public start/message methods do not leave a rejected handle
|
|
529
|
+
// unobserved after they have returned to the HTTP caller.
|
|
530
|
+
void handle.completion.catch(() => undefined);
|
|
531
|
+
this.eventBus.emit('session:started', { sessionId: session.id });
|
|
532
|
+
await this.checkTaskAutoRevert(session.id);
|
|
686
533
|
}
|
|
687
534
|
catch (error) {
|
|
688
|
-
await this.
|
|
689
|
-
|
|
535
|
+
await this.runtimeCoordinator.disposeSession(session.id).catch(() => undefined);
|
|
536
|
+
await prisma.session.update({
|
|
537
|
+
where: { id: session.id },
|
|
538
|
+
data: { status: SessionStatus.CANCELLED },
|
|
539
|
+
}).catch(() => undefined);
|
|
540
|
+
sessionMsgStoreManager.delete(session.id);
|
|
541
|
+
this.releaseSnapshotPersistenceState(session.id);
|
|
542
|
+
this.logSessionError('session.runtimeStart', error, {
|
|
543
|
+
sessionId: session.id,
|
|
544
|
+
agentType: session.agentType,
|
|
545
|
+
runtimeType: session.runtimeType,
|
|
546
|
+
providerId,
|
|
547
|
+
workingDir,
|
|
548
|
+
});
|
|
549
|
+
throw normalizeExecutorStartError(error);
|
|
690
550
|
}
|
|
691
551
|
}
|
|
692
|
-
|
|
693
|
-
const
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
// 重放的 exit 会让 pipeline 在构造期间就自毁;已死的 pipeline 不能进 map,
|
|
699
|
-
// 否则 hasActivePipeline 会误报存活。
|
|
700
|
-
if (pipeline.isAlive) {
|
|
701
|
-
this.pipelines.set(sessionId, pipeline);
|
|
702
|
-
if (spawnResult.cancel) {
|
|
703
|
-
this.cancelTokens.set(sessionId, spawnResult.cancel);
|
|
704
|
-
}
|
|
552
|
+
handleRuntimeTurnEvent(envelope) {
|
|
553
|
+
const sessionId = envelope.towerSessionId;
|
|
554
|
+
const event = envelope.event;
|
|
555
|
+
if (event.type === 'stdout') {
|
|
556
|
+
this.eventBus.emit('session:stdout', { sessionId, data: event.data });
|
|
557
|
+
return;
|
|
705
558
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
559
|
+
if (event.type === 'conversation_patch') {
|
|
560
|
+
this.eventBus.emit('session:patch', { sessionId, patch: event.patch, seq: event.seq });
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (event.type === 'external_session_id') {
|
|
564
|
+
const previous = this.externalSessionPersistence.get(sessionId) ?? Promise.resolve();
|
|
565
|
+
const persistence = previous
|
|
566
|
+
.then(() => this.enqueueSessionPersistenceWrite(async () => {
|
|
567
|
+
await prisma.session.update({
|
|
712
568
|
where: { id: sessionId },
|
|
713
|
-
|
|
569
|
+
data: { externalSessionId: event.externalSessionId },
|
|
714
570
|
});
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
if (!session.workspace) {
|
|
725
|
-
throw new NotFoundError('Workspace', session.workspaceId ?? sessionId);
|
|
726
|
-
}
|
|
727
|
-
ensureTaskNotDeleted(session.workspace.task);
|
|
571
|
+
}))
|
|
572
|
+
.then(() => undefined)
|
|
573
|
+
.catch((error) => {
|
|
574
|
+
this.logSessionError('session.externalSessionId', error, { sessionId });
|
|
575
|
+
});
|
|
576
|
+
this.externalSessionPersistence.set(sessionId, persistence);
|
|
577
|
+
void persistence.finally(() => {
|
|
578
|
+
if (this.externalSessionPersistence.get(sessionId) === persistence) {
|
|
579
|
+
this.externalSessionPersistence.delete(sessionId);
|
|
728
580
|
}
|
|
729
|
-
await tx.session.update({
|
|
730
|
-
where: { id: sessionId },
|
|
731
|
-
data: { status: SessionStatus.RUNNING },
|
|
732
|
-
});
|
|
733
|
-
await tx.executionProcess.create({
|
|
734
|
-
data: {
|
|
735
|
-
sessionId,
|
|
736
|
-
pid: spawnResult.pid,
|
|
737
|
-
},
|
|
738
|
-
});
|
|
739
581
|
});
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
582
|
+
this.eventBus.emit('session:sessionId', {
|
|
583
|
+
sessionId,
|
|
584
|
+
agentSessionId: event.externalSessionId,
|
|
743
585
|
});
|
|
744
|
-
|
|
745
|
-
throw new NotFoundError('Session', sessionId);
|
|
746
|
-
}
|
|
747
|
-
if (this.isConversationSession(session)) {
|
|
748
|
-
if (!session.conversation || session.conversation.deletedAt) {
|
|
749
|
-
throw new NotFoundError('Conversation', session.conversationId ?? sessionId);
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
else {
|
|
753
|
-
if (!session.workspace) {
|
|
754
|
-
throw new NotFoundError('Workspace', session.workspaceId ?? sessionId);
|
|
755
|
-
}
|
|
756
|
-
ensureTaskNotDeleted(session.workspace.task);
|
|
757
|
-
}
|
|
758
|
-
if (this.pendingSpawns.get(sessionId) !== spawnResult) {
|
|
759
|
-
throw new NotFoundError(this.isConversationSession(session) ? 'Conversation' : 'Task', this.isConversationSession(session)
|
|
760
|
-
? (session.conversationId ?? sessionId)
|
|
761
|
-
: (session.workspace?.task.id ?? sessionId));
|
|
762
|
-
}
|
|
586
|
+
return;
|
|
763
587
|
}
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
588
|
+
if (event.type === 'permission_requested') {
|
|
589
|
+
this.eventBus.emit('session:permission_requested', {
|
|
590
|
+
sessionId,
|
|
591
|
+
permission: event.request,
|
|
592
|
+
});
|
|
593
|
+
return;
|
|
767
594
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
595
|
+
if (event.type === 'permission_invalidated') {
|
|
596
|
+
this.eventBus.emit('session:permission_invalidated', {
|
|
597
|
+
sessionId,
|
|
598
|
+
turnId: envelope.turnId,
|
|
599
|
+
requestId: event.requestId,
|
|
600
|
+
});
|
|
601
|
+
return;
|
|
772
602
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
}
|
|
778
|
-
catch {
|
|
779
|
-
// Continue with explicit PTY cancellation even if pipeline teardown fails.
|
|
780
|
-
}
|
|
781
|
-
this.pipelines.delete(sessionId);
|
|
782
|
-
this.cancelTokens.delete(sessionId);
|
|
603
|
+
if (event.type === 'completed') {
|
|
604
|
+
this.eventBus.emit('session:turn-completed', { sessionId });
|
|
605
|
+
this.eventBus.emit('session:exit', { sessionId, exitCode: 0 });
|
|
606
|
+
return;
|
|
783
607
|
}
|
|
784
|
-
|
|
785
|
-
|
|
608
|
+
if (event.type === 'failed') {
|
|
609
|
+
this.eventBus.emit('session:turn-failed', { sessionId });
|
|
610
|
+
this.eventBus.emit('session:exit', { sessionId, exitCode: 1 });
|
|
786
611
|
}
|
|
787
|
-
|
|
788
|
-
|
|
612
|
+
}
|
|
613
|
+
handleRuntimeState(state) {
|
|
614
|
+
setRuntimeStateSnapshot(state);
|
|
615
|
+
this.eventBus.emit('session:runtime_state_changed', {
|
|
616
|
+
sessionId: state.sessionId,
|
|
617
|
+
state,
|
|
618
|
+
});
|
|
619
|
+
const awaitingPermission = state.turnState === 'AWAITING_PERMISSION';
|
|
620
|
+
const previous = this.runtimePermissionStates.get(state.sessionId) ?? false;
|
|
621
|
+
if (state.turnState === 'DISPOSED') {
|
|
622
|
+
this.runtimePermissionStates.delete(state.sessionId);
|
|
789
623
|
}
|
|
790
|
-
|
|
791
|
-
|
|
624
|
+
else {
|
|
625
|
+
this.runtimePermissionStates.set(state.sessionId, awaitingPermission);
|
|
792
626
|
}
|
|
793
|
-
|
|
794
|
-
|
|
627
|
+
if (previous !== awaitingPermission) {
|
|
628
|
+
void this.invalidateTeamRunRuntimeState(state.sessionId);
|
|
795
629
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
630
|
+
}
|
|
631
|
+
async invalidateTeamRunRuntimeState(sessionId) {
|
|
632
|
+
const invocation = await prisma.agentInvocation.findFirst({
|
|
633
|
+
where: { sessionId },
|
|
634
|
+
select: {
|
|
635
|
+
teamRunId: true,
|
|
636
|
+
teamRun: { select: { taskId: true, task: { select: { projectId: true } } } },
|
|
637
|
+
},
|
|
801
638
|
});
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
639
|
+
if (!invocation)
|
|
640
|
+
return;
|
|
641
|
+
this.eventBus.emit('team-run:invalidated', {
|
|
642
|
+
teamRunId: invocation.teamRunId,
|
|
643
|
+
taskId: invocation.teamRun.taskId,
|
|
644
|
+
projectId: invocation.teamRun.task.projectId,
|
|
645
|
+
scopes: ['team-members', 'agent-invocations', 'team-run'],
|
|
646
|
+
reason: 'agent-invocation-updated',
|
|
806
647
|
});
|
|
807
|
-
// 补偿路径同样释放 MsgStore(sendMessage 在 spawn 前已 getOrCreate)
|
|
808
|
-
sessionMsgStoreManager.delete(sessionId);
|
|
809
648
|
}
|
|
810
|
-
|
|
811
|
-
if (
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
649
|
+
async handleRuntimeProcessEvent(event) {
|
|
650
|
+
if (event.type === 'started') {
|
|
651
|
+
const processRecord = await prisma.$transaction(async (tx) => {
|
|
652
|
+
const session = await tx.session.findUnique({
|
|
653
|
+
where: { id: event.towerSessionId },
|
|
654
|
+
include: { workspace: { include: { task: true } }, conversation: true },
|
|
655
|
+
});
|
|
656
|
+
if (!session)
|
|
657
|
+
throw new NotFoundError('Session', event.towerSessionId);
|
|
658
|
+
this.ensureExecutionRecordIsLive(session);
|
|
659
|
+
return tx.executionProcess.create({
|
|
660
|
+
data: { sessionId: event.towerSessionId, pid: event.pid },
|
|
661
|
+
select: { id: true },
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
this.runtimeProcessIds.set(event.runtimeInstanceId, processRecord.id);
|
|
665
|
+
return;
|
|
819
666
|
}
|
|
820
|
-
|
|
667
|
+
const processId = this.runtimeProcessIds.get(event.runtimeInstanceId);
|
|
668
|
+
if (!processId)
|
|
669
|
+
return;
|
|
670
|
+
this.runtimeProcessIds.delete(event.runtimeInstanceId);
|
|
671
|
+
await prisma.executionProcess.update({
|
|
672
|
+
where: { id: processId },
|
|
673
|
+
data: { exitCode: event.exitCode },
|
|
674
|
+
}).catch((error) => {
|
|
675
|
+
this.logSessionError('session.processExit', error, {
|
|
676
|
+
sessionId: event.towerSessionId,
|
|
677
|
+
processId,
|
|
678
|
+
exitCode: event.exitCode,
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
normalizeRuntimeType(value) {
|
|
683
|
+
return value === RuntimeType.ACP ? RuntimeType.ACP : RuntimeType.CLI;
|
|
821
684
|
}
|
|
822
685
|
injectAgentTowerMcpServiceEnv(env) {
|
|
823
686
|
const serviceEnv = {};
|
|
@@ -954,7 +817,7 @@ export class SessionManager {
|
|
|
954
817
|
.catch(() => {
|
|
955
818
|
// Keep the chain alive even if previous flush failed.
|
|
956
819
|
})
|
|
957
|
-
.then(() => this.
|
|
820
|
+
.then(() => this.enqueueSessionPersistenceWrite(() => this.persistSnapshot(sessionId)));
|
|
958
821
|
this.snapshotFlushChains.set(sessionId, current);
|
|
959
822
|
try {
|
|
960
823
|
await current;
|
|
@@ -965,19 +828,19 @@ export class SessionManager {
|
|
|
965
828
|
}
|
|
966
829
|
}
|
|
967
830
|
}
|
|
968
|
-
async
|
|
969
|
-
const queued = this.
|
|
831
|
+
async enqueueSessionPersistenceWrite(write) {
|
|
832
|
+
const queued = this.sessionPersistenceWriterChain
|
|
970
833
|
.catch(() => {
|
|
971
834
|
// Keep the global writer alive after an isolated persistence failure.
|
|
972
835
|
})
|
|
973
836
|
.then(write);
|
|
974
|
-
this.
|
|
837
|
+
this.sessionPersistenceWriterChain = queued;
|
|
975
838
|
try {
|
|
976
839
|
await queued;
|
|
977
840
|
}
|
|
978
841
|
finally {
|
|
979
|
-
if (this.
|
|
980
|
-
this.
|
|
842
|
+
if (this.sessionPersistenceWriterChain === queued) {
|
|
843
|
+
this.sessionPersistenceWriterChain = Promise.resolve();
|
|
981
844
|
}
|
|
982
845
|
}
|
|
983
846
|
}
|
|
@@ -1201,6 +1064,7 @@ export class SessionManager {
|
|
|
1201
1064
|
*/
|
|
1202
1065
|
async handleSessionExit(sessionId, exitCode, options = {}) {
|
|
1203
1066
|
const generation = options.generation ?? this.sessionGenerations.get(sessionId);
|
|
1067
|
+
await this.externalSessionPersistence.get(sessionId);
|
|
1204
1068
|
const session = await prisma.session.findUnique({
|
|
1205
1069
|
where: { id: sessionId },
|
|
1206
1070
|
select: { purpose: true, context: true, conversationId: true },
|
|
@@ -1333,7 +1197,6 @@ export class SessionManager {
|
|
|
1333
1197
|
}
|
|
1334
1198
|
clearTerminalState(sessionId) {
|
|
1335
1199
|
this.terminalSessions.delete(sessionId);
|
|
1336
|
-
this.clearLogicalCompletionCleanup(sessionId);
|
|
1337
1200
|
}
|
|
1338
1201
|
startSessionFinalization(sessionId, exitCode, options = {}) {
|
|
1339
1202
|
if (options.logicalCompletion) {
|
|
@@ -1362,10 +1225,6 @@ export class SessionManager {
|
|
|
1362
1225
|
this.sessionFinalizations.set(sessionId, finalization);
|
|
1363
1226
|
}
|
|
1364
1227
|
maybeClearTerminalState(sessionId) {
|
|
1365
|
-
if (this.pipelines.has(sessionId))
|
|
1366
|
-
return;
|
|
1367
|
-
if (this.logicalCompletionCleanupTimers.has(sessionId))
|
|
1368
|
-
return;
|
|
1369
1228
|
if (this.sessionFinalizations.has(sessionId))
|
|
1370
1229
|
return;
|
|
1371
1230
|
this.terminalSessions.delete(sessionId);
|
|
@@ -1426,30 +1285,6 @@ export class SessionManager {
|
|
|
1426
1285
|
return;
|
|
1427
1286
|
}
|
|
1428
1287
|
}
|
|
1429
|
-
destroyActivePipeline(sessionId) {
|
|
1430
|
-
const pipeline = this.pipelines.get(sessionId);
|
|
1431
|
-
if (pipeline) {
|
|
1432
|
-
pipeline.destroy();
|
|
1433
|
-
this.pipelines.delete(sessionId);
|
|
1434
|
-
}
|
|
1435
|
-
this.cancelTokens.delete(sessionId);
|
|
1436
|
-
this.heartbeatThrottle.delete(sessionId);
|
|
1437
|
-
}
|
|
1438
|
-
clearLogicalCompletionCleanup(sessionId) {
|
|
1439
|
-
const timer = this.logicalCompletionCleanupTimers.get(sessionId);
|
|
1440
|
-
if (timer)
|
|
1441
|
-
clearTimeout(timer);
|
|
1442
|
-
this.logicalCompletionCleanupTimers.delete(sessionId);
|
|
1443
|
-
}
|
|
1444
|
-
scheduleLogicalCompletionCleanup(sessionId) {
|
|
1445
|
-
this.clearLogicalCompletionCleanup(sessionId);
|
|
1446
|
-
const timer = setTimeout(() => {
|
|
1447
|
-
this.logicalCompletionCleanupTimers.delete(sessionId);
|
|
1448
|
-
this.destroyActivePipeline(sessionId);
|
|
1449
|
-
this.maybeClearTerminalState(sessionId);
|
|
1450
|
-
}, SessionManager.CODEX_TURN_COMPLETION_GRACE_MS);
|
|
1451
|
-
this.logicalCompletionCleanupTimers.set(sessionId, timer);
|
|
1452
|
-
}
|
|
1453
1288
|
logSessionError(source, error, metadata) {
|
|
1454
1289
|
writeErrorLog({
|
|
1455
1290
|
level: 'error',
|