agent-tower 0.7.4 → 0.7.5-beta.5
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/app.d.ts.map +1 -1
- package/dist/app.js +6 -1
- package/dist/app.js.map +1 -1
- package/dist/core/container.d.ts +2 -0
- package/dist/core/container.d.ts.map +1 -1
- package/dist/core/container.js +25 -0
- package/dist/core/container.js.map +1 -1
- package/dist/executors/__tests__/base.executor.test.js +19 -0
- package/dist/executors/__tests__/base.executor.test.js.map +1 -1
- package/dist/executors/__tests__/claude-code-mcp.test.d.ts +2 -0
- package/dist/executors/__tests__/claude-code-mcp.test.d.ts.map +1 -0
- package/dist/executors/__tests__/claude-code-mcp.test.js +211 -0
- package/dist/executors/__tests__/claude-code-mcp.test.js.map +1 -0
- package/dist/executors/__tests__/codex-mcp-windows.test.d.ts +2 -0
- package/dist/executors/__tests__/codex-mcp-windows.test.d.ts.map +1 -0
- package/dist/executors/__tests__/codex-mcp-windows.test.js +128 -0
- package/dist/executors/__tests__/codex-mcp-windows.test.js.map +1 -0
- package/dist/executors/__tests__/codex.executor.test.js +394 -1
- package/dist/executors/__tests__/codex.executor.test.js.map +1 -1
- package/dist/executors/base.executor.d.ts.map +1 -1
- package/dist/executors/base.executor.js +13 -0
- package/dist/executors/base.executor.js.map +1 -1
- package/dist/executors/claude-code.executor.d.ts +1 -0
- package/dist/executors/claude-code.executor.d.ts.map +1 -1
- package/dist/executors/claude-code.executor.js +52 -3
- package/dist/executors/claude-code.executor.js.map +1 -1
- package/dist/executors/codex.executor.d.ts +6 -6
- package/dist/executors/codex.executor.d.ts.map +1 -1
- package/dist/executors/codex.executor.js +118 -32
- package/dist/executors/codex.executor.js.map +1 -1
- package/dist/git/worktree.manager.d.ts.map +1 -1
- package/dist/git/worktree.manager.js +5 -2
- package/dist/git/worktree.manager.js.map +1 -1
- package/dist/mcp/http-client.d.ts +8 -1
- package/dist/mcp/http-client.d.ts.map +1 -1
- package/dist/mcp/http-client.js +6 -0
- package/dist/mcp/http-client.js.map +1 -1
- package/dist/mcp/tools/__tests__/workspace-services.test.js +34 -1
- package/dist/mcp/tools/__tests__/workspace-services.test.js.map +1 -1
- package/dist/mcp/tools/tasks.js +1 -1
- package/dist/mcp/tools/tasks.js.map +1 -1
- package/dist/mcp/tools/workspace-services.d.ts.map +1 -1
- package/dist/mcp/tools/workspace-services.js +39 -2
- package/dist/mcp/tools/workspace-services.js.map +1 -1
- package/dist/mcp/types.d.ts +20 -0
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js +12 -0
- package/dist/mcp/types.js.map +1 -1
- package/dist/middleware/__tests__/preview-fallback.test.d.ts +2 -0
- package/dist/middleware/__tests__/preview-fallback.test.d.ts.map +1 -0
- package/dist/middleware/__tests__/preview-fallback.test.js +93 -0
- package/dist/middleware/__tests__/preview-fallback.test.js.map +1 -0
- package/dist/middleware/preview-fallback.d.ts +5 -0
- package/dist/middleware/preview-fallback.d.ts.map +1 -0
- package/dist/middleware/preview-fallback.js +91 -0
- package/dist/middleware/preview-fallback.js.map +1 -0
- package/dist/output/__tests__/claude-code-parser-continuation.test.d.ts +2 -0
- package/dist/output/__tests__/claude-code-parser-continuation.test.d.ts.map +1 -0
- package/dist/output/__tests__/claude-code-parser-continuation.test.js +89 -0
- package/dist/output/__tests__/claude-code-parser-continuation.test.js.map +1 -0
- package/dist/output/__tests__/claude-code-parser-lifecycle.test.js +4 -1
- package/dist/output/__tests__/claude-code-parser-lifecycle.test.js.map +1 -1
- package/dist/output/claude-code-parser.d.ts +36 -1
- package/dist/output/claude-code-parser.d.ts.map +1 -1
- package/dist/output/claude-code-parser.js +149 -18
- package/dist/output/claude-code-parser.js.map +1 -1
- package/dist/output/types.d.ts +2 -0
- package/dist/output/types.d.ts.map +1 -1
- package/dist/output/types.js.map +1 -1
- package/dist/prompts/workspace-background-service-policy.d.ts +6 -2
- package/dist/prompts/workspace-background-service-policy.d.ts.map +1 -1
- package/dist/prompts/workspace-background-service-policy.js +12 -3
- package/dist/prompts/workspace-background-service-policy.js.map +1 -1
- package/dist/prompts/workspace-background-service-policy.test.js +11 -0
- package/dist/prompts/workspace-background-service-policy.test.js.map +1 -1
- package/dist/routes/__tests__/previews.integration.test.js +117 -6
- package/dist/routes/__tests__/previews.integration.test.js.map +1 -1
- package/dist/routes/__tests__/previews.test.js +223 -194
- package/dist/routes/__tests__/previews.test.js.map +1 -1
- package/dist/routes/__tests__/task-handoff.test.js +25 -16
- package/dist/routes/__tests__/task-handoff.test.js.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +3 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/previews.d.ts +11 -9
- package/dist/routes/previews.d.ts.map +1 -1
- package/dist/routes/previews.js +137 -516
- package/dist/routes/previews.js.map +1 -1
- package/dist/routes/session-wakeups.d.ts +7 -0
- package/dist/routes/session-wakeups.d.ts.map +1 -0
- package/dist/routes/session-wakeups.js +86 -0
- package/dist/routes/session-wakeups.js.map +1 -0
- package/dist/routes/tasks.d.ts.map +1 -1
- package/dist/routes/tasks.js +3 -1
- package/dist/routes/tasks.js.map +1 -1
- package/dist/runtime/__tests__/acp-process-manager.test.js +65 -0
- package/dist/runtime/__tests__/acp-process-manager.test.js.map +1 -1
- package/dist/runtime/__tests__/claude-cli-driver.test.js +96 -2
- package/dist/runtime/__tests__/claude-cli-driver.test.js.map +1 -1
- package/dist/runtime/__tests__/claude-run-lifecycle.test.d.ts +2 -0
- package/dist/runtime/__tests__/claude-run-lifecycle.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/claude-run-lifecycle.test.js +88 -0
- package/dist/runtime/__tests__/claude-run-lifecycle.test.js.map +1 -0
- package/dist/runtime/__tests__/cli-driver-lifecycle.test.js +14 -7
- package/dist/runtime/__tests__/cli-driver-lifecycle.test.js.map +1 -1
- package/dist/runtime/acp/agents/pi-coding-agent.d.ts.map +1 -1
- package/dist/runtime/acp/agents/pi-coding-agent.js +3 -0
- package/dist/runtime/acp/agents/pi-coding-agent.js.map +1 -1
- package/dist/runtime/claude-run-lifecycle.d.ts +77 -0
- package/dist/runtime/claude-run-lifecycle.d.ts.map +1 -0
- package/dist/runtime/claude-run-lifecycle.js +147 -0
- package/dist/runtime/claude-run-lifecycle.js.map +1 -0
- package/dist/runtime/cli-driver.d.ts.map +1 -1
- package/dist/runtime/cli-driver.js +89 -3
- package/dist/runtime/cli-driver.js.map +1 -1
- package/dist/runtime/contracts.d.ts +6 -1
- package/dist/runtime/contracts.d.ts.map +1 -1
- package/dist/runtime/process-scope/__tests__/process-scope.test.d.ts +2 -0
- package/dist/runtime/process-scope/__tests__/process-scope.test.d.ts.map +1 -0
- package/dist/runtime/process-scope/__tests__/process-scope.test.js +156 -0
- package/dist/runtime/process-scope/__tests__/process-scope.test.js.map +1 -0
- package/dist/runtime/process-scope/process-scope.d.ts +46 -0
- package/dist/runtime/process-scope/process-scope.d.ts.map +1 -0
- package/dist/runtime/process-scope/process-scope.js +68 -0
- package/dist/runtime/process-scope/process-scope.js.map +1 -0
- package/dist/runtime/process-scope/scope-helper.d.ts +28 -0
- package/dist/runtime/process-scope/scope-helper.d.ts.map +1 -0
- package/dist/runtime/process-scope/scope-helper.js +78 -0
- package/dist/runtime/process-scope/scope-helper.js.map +1 -0
- package/dist/runtime/runtime-coordinator.d.ts.map +1 -1
- package/dist/runtime/runtime-coordinator.js +6 -0
- package/dist/runtime/runtime-coordinator.js.map +1 -1
- package/dist/services/__tests__/preview-runtime-manager.test.js +13 -112
- package/dist/services/__tests__/preview-runtime-manager.test.js.map +1 -1
- package/dist/services/__tests__/preview.service.test.js +13 -14
- package/dist/services/__tests__/preview.service.test.js.map +1 -1
- package/dist/services/__tests__/session-manager.lifecycle.test.js +147 -1
- package/dist/services/__tests__/session-manager.lifecycle.test.js.map +1 -1
- package/dist/services/__tests__/session-wakeup.service.test.d.ts +2 -0
- package/dist/services/__tests__/session-wakeup.service.test.d.ts.map +1 -0
- package/dist/services/__tests__/session-wakeup.service.test.js +173 -0
- package/dist/services/__tests__/session-wakeup.service.test.js.map +1 -0
- package/dist/services/__tests__/terminal-manager.test.js +129 -51
- package/dist/services/__tests__/terminal-manager.test.js.map +1 -1
- package/dist/services/__tests__/workspace.service.test.js +31 -0
- package/dist/services/__tests__/workspace.service.test.js.map +1 -1
- package/dist/services/message-delivery.service.d.ts +3 -2
- package/dist/services/message-delivery.service.d.ts.map +1 -1
- package/dist/services/message-delivery.service.js +11 -7
- package/dist/services/message-delivery.service.js.map +1 -1
- package/dist/services/preview-bridge.d.ts +50 -0
- package/dist/services/preview-bridge.d.ts.map +1 -0
- package/dist/services/preview-bridge.js +537 -0
- package/dist/services/preview-bridge.js.map +1 -0
- package/dist/services/preview-cookies.d.ts +28 -0
- package/dist/services/preview-cookies.d.ts.map +1 -0
- package/dist/services/preview-cookies.js +133 -0
- package/dist/services/preview-cookies.js.map +1 -0
- package/dist/services/preview-proxy.d.ts +71 -0
- package/dist/services/preview-proxy.d.ts.map +1 -0
- package/dist/services/preview-proxy.js +462 -0
- package/dist/services/preview-proxy.js.map +1 -0
- package/dist/services/preview-runtime-manager.d.ts +11 -25
- package/dist/services/preview-runtime-manager.d.ts.map +1 -1
- package/dist/services/preview-runtime-manager.js +46 -363
- package/dist/services/preview-runtime-manager.js.map +1 -1
- package/dist/services/preview.service.d.ts.map +1 -1
- package/dist/services/preview.service.js +11 -9
- package/dist/services/preview.service.js.map +1 -1
- package/dist/services/project.service.d.ts +13 -13
- package/dist/services/session-manager.d.ts +2 -0
- package/dist/services/session-manager.d.ts.map +1 -1
- package/dist/services/session-manager.js +4 -2
- package/dist/services/session-manager.js.map +1 -1
- package/dist/services/session-wakeup.service.d.ts +105 -0
- package/dist/services/session-wakeup.service.d.ts.map +1 -0
- package/dist/services/session-wakeup.service.js +319 -0
- package/dist/services/session-wakeup.service.js.map +1 -0
- package/dist/services/task-handoff.service.d.ts +3 -2
- package/dist/services/task-handoff.service.d.ts.map +1 -1
- package/dist/services/task-handoff.service.js +7 -6
- package/dist/services/task-handoff.service.js.map +1 -1
- package/dist/services/team-room-delivery.d.ts +4 -4
- package/dist/services/terminal-manager.d.ts +16 -0
- package/dist/services/terminal-manager.d.ts.map +1 -1
- package/dist/services/terminal-manager.js +64 -7
- package/dist/services/terminal-manager.js.map +1 -1
- package/dist/services/workspace-background-service.service.d.ts +15 -1
- package/dist/services/workspace-background-service.service.d.ts.map +1 -1
- package/dist/services/workspace-background-service.service.js +41 -2
- package/dist/services/workspace-background-service.service.js.map +1 -1
- package/dist/services/workspace.service.d.ts.map +1 -1
- package/dist/services/workspace.service.js +2 -1
- package/dist/services/workspace.service.js.map +1 -1
- package/dist/utils/database-schema.d.ts.map +1 -1
- package/dist/utils/database-schema.js +9 -4
- package/dist/utils/database-schema.js.map +1 -1
- package/dist/utils/database-schema.test.js +21 -0
- package/dist/utils/database-schema.test.js.map +1 -1
- package/dist/utils/owned-child-process.d.ts +32 -3
- package/dist/utils/owned-child-process.d.ts.map +1 -1
- package/dist/utils/owned-child-process.js +138 -1
- package/dist/utils/owned-child-process.js.map +1 -1
- package/dist/web/assets/{AgentDemoPage-D9bIiALx.js → AgentDemoPage-D6clLNZx.js} +1 -1
- package/dist/web/assets/{AgentEnvironmentSettingsPage-BE0RC6fR.js → AgentEnvironmentSettingsPage-O1hlpsgp.js} +1 -1
- package/dist/web/assets/AttachmentPreview-hD3g_T_y.js +2 -0
- package/dist/web/assets/{BranchSelect-Kv3M1YhZ.js → BranchSelect-BkuXnEG2.js} +1 -1
- package/dist/web/assets/{BrandLogo-fR2lCwEo.js → BrandLogo-BSwQa2SF.js} +1 -1
- package/dist/web/assets/{ConversationPage-BZiMHI9T.js → ConversationPage-DvWjqFF2.js} +1 -1
- package/dist/web/assets/CreateTaskInput-Bric0GeW.js +1 -0
- package/dist/web/assets/{DemoPage-DwpThe16.js → DemoPage-Cwy3EmOm.js} +1 -1
- package/dist/web/assets/{DesignSystemPage-DH9_cygJ.js → DesignSystemPage-C1HrGwpV.js} +1 -1
- package/dist/web/assets/{ExecutionPermissionSelector-C-akTOVg.js → ExecutionPermissionSelector-BgrxbvBB.js} +1 -1
- package/dist/web/assets/{GeneralSettingsPage-v2w1eofz.js → GeneralSettingsPage-qdeAdBkW.js} +1 -1
- package/dist/web/assets/{LoadingPreviewPage-pgk3PWM9.js → LoadingPreviewPage-qeZ8nuiQ.js} +1 -1
- package/dist/web/assets/{McpSettingsPage-w1DOlY5V.js → McpSettingsPage-Bo-auo0X.js} +1 -1
- package/dist/web/assets/{MemberAvatar-CJtylxwu.js → MemberAvatar-DBab_9EO.js} +1 -1
- package/dist/web/assets/{MobileTaskDetail-D5Czlr2O.js → MobileTaskDetail-Dm64ie99.js} +2 -2
- package/dist/web/assets/{NotificationSettingsPage-CkLg4WvT.js → NotificationSettingsPage-BVJpo76k.js} +1 -1
- package/dist/web/assets/{ProfileSettingsPage-B3yzbY1V.js → ProfileSettingsPage-CbSRYGcp.js} +1 -1
- package/dist/web/assets/{ProjectKanbanPage-CuGSLvrU.js → ProjectKanbanPage-Cp3rdFik.js} +6 -6
- package/dist/web/assets/{ProjectSettingsPage-DoklOfna.js → ProjectSettingsPage-DLs5-Gk3.js} +1 -1
- package/dist/web/assets/{ProviderSettingsPage-Cm0hbX5n.js → ProviderSettingsPage-BnBcgAMQ.js} +1 -1
- package/dist/web/assets/{SettingsMasterDetail-CGCUalkW.js → SettingsMasterDetail-fu-_yejj.js} +1 -1
- package/dist/web/assets/{TaskDetail-BiZbuXcE.js → TaskDetail-C2_Fg59e.js} +1 -1
- package/dist/web/assets/TaskHandoffPanel-3Y0gcjyN.js +70 -0
- package/dist/web/assets/{TeamSettingsPage-CyppRTaY.js → TeamSettingsPage-CAjitaBe.js} +1 -1
- package/dist/web/assets/{TranslationSettingsPage-BoQXEZmn.js → TranslationSettingsPage-DKD6Uw2F.js} +1 -1
- package/dist/web/assets/{arc-5NPBSbiL.js → arc-BiuUFTzU.js} +1 -1
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-CqPT-IsX.js → architectureDiagram-3BPJPVTR-Dcwk905A.js} +1 -1
- package/dist/web/assets/{at-sign-Cf39WHCg.js → at-sign-CZAKOH3i.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-BN-1z9KN.js → blockDiagram-GPEHLZMM-Cfc5JDN-.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-CkeLbmQp.js → c4Diagram-AAUBKEIU-B4v5PUdt.js} +1 -1
- package/dist/web/assets/channel-DThSYkmR.js +1 -0
- package/dist/web/assets/check-C3vuSPKz.js +1 -0
- package/dist/web/assets/{chevron-down-Ct7FZbKm.js → chevron-down-CtAk9uZw.js} +1 -1
- package/dist/web/assets/{chevron-up-ARFlXMyO.js → chevron-up-DAc3NX1J.js} +1 -1
- package/dist/web/assets/{chip-BV_1-tIb.js → chip-CDUNxQW2.js} +1 -1
- package/dist/web/assets/{chunk-2J33WTMH-CWR7tRuT.js → chunk-2J33WTMH-BYRdUeyt.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-CAulp4D5.js → chunk-4BX2VUAB-3si_kPmV.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-DqkKXsbD.js → chunk-55IACEB6-JiMm9u33.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-B19m45Sq.js → chunk-727SXJPM-CMCFIUyF.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-CuODwkza.js → chunk-AQP2D5EJ-CzvnZjH5.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-CbUAjtCT.js → chunk-FMBD7UC4-hsabZkPl.js} +1 -1
- package/dist/web/assets/{chunk-ND2GUHAM-C54VxArl.js → chunk-ND2GUHAM-D2o-Reeo.js} +1 -1
- package/dist/web/assets/{chunk-QZHKN3VN-D54Uw5fD.js → chunk-QZHKN3VN-ASJk-E7d.js} +1 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-CjJqgIpK.js +1 -0
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-CjJqgIpK.js +1 -0
- package/dist/web/assets/{code-block-OCS4YCEC-CtAySD1h.js → code-block-OCS4YCEC-5bkFBowa.js} +1 -1
- package/dist/web/assets/{confirm-dialog-CCjHVFTv.js → confirm-dialog-Bs80kXB9.js} +1 -1
- package/dist/web/assets/{content-DZozZbBD.js → content-DAccroCT.js} +1 -1
- package/dist/web/assets/{copy-C-jvx2et.js → copy-D1XK3OqK.js} +1 -1
- package/dist/web/assets/{cose-bilkent-S5V4N54A-BaB2EVcU.js → cose-bilkent-S5V4N54A-C52UKnOd.js} +1 -1
- package/dist/web/assets/{cssMode-wd7JR1S4.js → cssMode-BFq_c5xU.js} +1 -1
- package/dist/web/assets/{dagre-BM42HDAG-C2j1Y8pO.js → dagre-BM42HDAG-CeEute93.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-CF-EvpV9.js → diagram-2AECGRRQ-CEcq4W4S.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-rBNso835.js → diagram-5GNKFQAL-DqQe7Rr7.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-6NW-oos1.js → diagram-KO2AKTUF-C1KVZphS.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-DTfRxRLT.js → diagram-LMA3HP47-D1BL7GQh.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-CS4Ae-hD.js → diagram-OG6HWLK6-ngyXooqC.js} +1 -1
- package/dist/web/assets/{dialog-DV4wfpNU.js → dialog-XkZ8wMQa.js} +1 -1
- package/dist/web/assets/{erDiagram-TEJ5UH35-Dw9QQctN.js → erDiagram-TEJ5UH35-D1zIFVqE.js} +1 -1
- package/dist/web/assets/{file-Btui_I8Z.js → file-CXCll7Pm.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-BGZ9q86S.js → flowDiagram-I6XJVG4X-BZu49qcj.js} +1 -1
- package/dist/web/assets/{folder-picker-DiR-8Qdl.js → folder-picker-CVkaEJEL.js} +1 -1
- package/dist/web/assets/{freemarker2-yVWXzD3l.js → freemarker2-CmhadAeB.js} +1 -1
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-CkGun0no.js → ganttDiagram-6RSMTGT7-eJ8yBxfS.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-Bz3oiYvi.js → gitGraphDiagram-PVQCEYII-DpeUUyH_.js} +1 -1
- package/dist/web/assets/{globe-DAFqpAcW.js → globe-WnxN1JzY.js} +1 -1
- package/dist/web/assets/{handlebars-BPf0z6eN.js → handlebars-DtjmH1qR.js} +1 -1
- package/dist/web/assets/{html-HCTCjfUg.js → html-DOYAlzlD.js} +1 -1
- package/dist/web/assets/{htmlMode-BDkbsknt.js → htmlMode-ORxd7KRN.js} +1 -1
- package/dist/web/assets/{index-BMcLxIvZ.js → index-6mINSZng.js} +1 -1
- package/dist/web/assets/{index-BPqjxXmH.js → index-BrjvxKaf.js} +1 -1
- package/dist/web/assets/{index-B8xoIIa6.js → index-CqIyOiVN.js} +4 -4
- package/dist/web/assets/{index-DgUtx9rV.js → index-D9w1zMaz.js} +1 -1
- package/dist/web/assets/{index-DkSPjZm4.js → index-DHDgbH3t.js} +1 -1
- package/dist/web/assets/{index-BszkzNS4.js → index-Dz9SKopM.js} +13 -13
- package/dist/web/assets/{index-GuczxPxb.js → index-eIXz5Hbk.js} +1 -1
- package/dist/web/assets/{index-DuM6iAxG.js → index-iOIxdF6J.js} +1 -1
- package/dist/web/assets/{index-lZLy5rkr.js → index-wbMiSmsY.js} +1 -1
- package/dist/web/assets/{infoDiagram-5YYISTIA-DYFufUYC.js → infoDiagram-5YYISTIA-BiVrC__7.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-IOkrKRwu.js → ishikawaDiagram-YF4QCWOH-B1kPjO1J.js} +1 -1
- package/dist/web/assets/javascript-CHlWHrtz.js +1 -0
- package/dist/web/assets/{journeyDiagram-JHISSGLW-Btl3yxne.js → journeyDiagram-JHISSGLW-DoTAPPAQ.js} +1 -1
- package/dist/web/assets/{jsonMode-DJPdvFcw.js → jsonMode-BkeQ_C6g.js} +1 -1
- package/dist/web/assets/{kanban-definition-UN3LZRKU-CDryhPbA.js → kanban-definition-UN3LZRKU-CBRmLcce.js} +1 -1
- package/dist/web/assets/{kbd-8RRRm9jd.js → kbd-CTfS0G5-.js} +1 -1
- package/dist/web/assets/{key-round-wojrCrDP.js → key-round-B7gTfpDR.js} +1 -1
- package/dist/web/assets/{layers-BH_LL4b8.js → layers-DE0xXedq.js} +1 -1
- package/dist/web/assets/{linear-BLuwKHze.js → linear-VLiA8Vcw.js} +1 -1
- package/dist/web/assets/{link-jWQOSQOQ.js → link-BSE_nRDT.js} +1 -1
- package/dist/web/assets/{liquid-1y-xg637.js → liquid-BZeg6p1K.js} +1 -1
- package/dist/web/assets/{lspLanguageFeatures-cYkILdCn.js → lspLanguageFeatures-CuFQ2_f9.js} +1 -1
- package/dist/web/assets/{mdx-CRg4Q6aP.js → mdx-C7xLyFFX.js} +1 -1
- package/dist/web/assets/{menu-CMqbaDni.js → menu-Dp4RDxvP.js} +1 -1
- package/dist/web/assets/{mermaid-NOHMQCX5-CYeAhOl-.js → mermaid-NOHMQCX5-C2enCJxR.js} +62 -62
- package/dist/web/assets/{message-square-DykJ2lWP.js → message-square-4lyHjyHe.js} +1 -1
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-B5wtw1Rv.js → mindmap-definition-RKZ34NQL-C1OOtqma.js} +1 -1
- package/dist/web/assets/{modal-BwOwTzij.js → modal-BGglpgrR.js} +1 -1
- package/dist/web/assets/{monaco-CcHqpOKe.js → monaco-B-6VmJO0.js} +3 -3
- package/dist/web/assets/{nav-row-BmtdNLxR.js → nav-row-DaR8yP7p.js} +1 -1
- package/dist/web/assets/{paperclip-C4t49vX2.js → paperclip-DORF2rEM.js} +1 -1
- package/dist/web/assets/{pencil-BQ5_raiB.js → pencil-BG033B74.js} +1 -1
- package/dist/web/assets/{people-Dr0v8HK0.js → people-1qyKeYq6.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-BZ3VoaNI.js → pieDiagram-4H26LBE5-BYYFVqOS.js} +1 -1
- package/dist/web/assets/{popover-CTqvKqTR.js → popover-CvjMbMb4.js} +1 -1
- package/dist/web/assets/{python-Huq_acyd.js → python-TTfnYnjF.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-C6ArHmnG.js → quadrantDiagram-W4KKPZXB-DTllM2j9.js} +1 -1
- package/dist/web/assets/{razor-Clc87Ivb.js → razor-BFVXAHVd.js} +1 -1
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-DiLzBRQP.js → requirementDiagram-4Y6WPE33-DeJDRYUP.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-QlYYd7IZ.js → sankeyDiagram-5OEKKPKP-DzTvEKv8.js} +1 -1
- package/dist/web/assets/{search-CMIAPUEt.js → search-DDRcBORc.js} +1 -1
- package/dist/web/assets/{select-DqB7YF38.js → select-CXC17bJx.js} +1 -1
- package/dist/web/assets/{select-Bg7NI8i0.js → select-CzXL3ZbG.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-COKEtqc0.js → sequenceDiagram-3UESZ5HK-CIYrA1ww.js} +1 -1
- package/dist/web/assets/{server-D7knl8H9.js → server-KqeWHfgr.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-1RZZZdOe.js → stateDiagram-AJRCARHV-Dg_UXkEq.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-Lc4r0bC3.js +1 -0
- package/dist/web/assets/{textarea-BCY2J2ca.js → textarea-Cmbv5l35.js} +1 -1
- package/dist/web/assets/{timeline-definition-PNZ67QCA-IJl1LY6J.js → timeline-definition-PNZ67QCA-CfSPMqxF.js} +1 -1
- package/dist/web/assets/{toggles-C4LmZwyo.js → toggles-t5v4QKn6.js} +1 -1
- package/dist/web/assets/{tooltip-Cirpotu3.js → tooltip-Cp8tovqH.js} +1 -1
- package/dist/web/assets/{track-Cv7h6oaE.js → track-C1rUV49K.js} +1 -1
- package/dist/web/assets/{trash-2-BEOQ1ICJ.js → trash-2-Ca2ken6q.js} +1 -1
- package/dist/web/assets/{tsMode-CHIbCBQc.js → tsMode-DBbiXlyu.js} +1 -1
- package/dist/web/assets/{typescript-CScj0bbb.js → typescript-jv_3P9x_.js} +1 -1
- package/dist/web/assets/{upload-tLVD0xMT.js → upload-CgGjN1FH.js} +1 -1
- package/dist/web/assets/{use-profiles-TkoP4Yw9.js → use-profiles-BdL-U8GZ.js} +1 -1
- package/dist/web/assets/{use-providers-Bz6YNiYj.js → use-providers-B6Mj5qeG.js} +1 -1
- package/dist/web/assets/{use-translation-settings-CCpQNS30.js → use-translation-settings-FIP674t9.js} +1 -1
- package/dist/web/assets/{useNormalizedLogs-Bstb98cF.js → useNormalizedLogs-FpVMUHeq.js} +1 -1
- package/dist/web/assets/useTokenUsage-Ay3Yy0_V.js +1 -0
- package/dist/web/assets/{vennDiagram-CIIHVFJN-DzVayy4U.js → vennDiagram-CIIHVFJN-DKREAIRT.js} +1 -1
- package/dist/web/assets/{wardley-L42UT6IY-CULSKh55.js → wardley-L42UT6IY-BMpDotAz.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-C5_h3lAA.js → wardleyDiagram-YWT4CUSO-DAPccQsF.js} +1 -1
- package/dist/web/assets/{xml-XuycSwh6.js → xml-DnxIQwRS.js} +1 -1
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-F65naXcR.js → xychartDiagram-2RQKCTM6-o06g8vo8.js} +1 -1
- package/dist/web/assets/{yaml-BWm159lw.js → yaml-BV5yQTRd.js} +1 -1
- package/dist/web/index.html +1 -1
- package/node_modules/@agent-tower/shared/dist/log-adapter.d.ts +4 -0
- package/node_modules/@agent-tower/shared/dist/log-adapter.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/log-adapter.js +1 -0
- package/node_modules/@agent-tower/shared/dist/log-adapter.js.map +1 -1
- package/node_modules/@agent-tower/shared/dist/translation.d.ts +4 -1
- package/node_modules/@agent-tower/shared/dist/translation.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/translation.js +4 -1
- package/node_modules/@agent-tower/shared/dist/translation.js.map +1 -1
- package/node_modules/@agent-tower/shared/dist/types.d.ts +38 -2
- package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
- package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
- package/package.json +2 -1
- package/prisma/migrations/20261106000000_add_session_wakeups/migration.sql +28 -0
- package/prisma/schema.prisma +28 -0
- package/scripts/postinstall.js +23 -0
- package/dist/web/assets/AttachmentPreview-TPd4gdfb.js +0 -2
- package/dist/web/assets/CreateTaskInput-TcZpjBe-.js +0 -1
- package/dist/web/assets/TaskHandoffPanel-CJahUKNo.js +0 -70
- package/dist/web/assets/channel-CDkuQ_7m.js +0 -1
- package/dist/web/assets/check-yNWhPvMH.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-eHY73FGu.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-eHY73FGu.js +0 -1
- package/dist/web/assets/javascript-DKmxrNeR.js +0 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-CCLmVs34.js +0 -1
- package/dist/web/assets/useTokenUsage-D7SEwXb0.js +0 -1
|
@@ -1,123 +1,50 @@
|
|
|
1
|
-
import { EventEmitter } from 'node:events';
|
|
2
1
|
import http from 'node:http';
|
|
3
|
-
import { afterEach, describe, expect, it
|
|
2
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
4
3
|
import { normalizePreviewTarget } from '../preview.service.js';
|
|
5
4
|
import { PreviewRuntimeManager, rewriteTargetCookie } from '../preview-runtime-manager.js';
|
|
6
|
-
class FakeTunnel extends EventEmitter {
|
|
7
|
-
stopped = false;
|
|
8
|
-
stop() {
|
|
9
|
-
this.stopped = true;
|
|
10
|
-
this.emit('exit', 0, null);
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
5
|
const managers = [];
|
|
15
6
|
afterEach(async () => {
|
|
16
7
|
await Promise.all(managers.splice(0).map((manager) => manager.stopAll()));
|
|
17
8
|
});
|
|
18
9
|
describe('PreviewRuntimeManager lifecycle', () => {
|
|
19
|
-
it('starts one remote tunnel per workspace and reuses it across active leases', async () => {
|
|
20
|
-
const tunnels = [];
|
|
21
|
-
const ensureTunnelBinary = vi.fn(async () => { });
|
|
22
|
-
const manager = new PreviewRuntimeManager({
|
|
23
|
-
listenHost: '127.0.0.1',
|
|
24
|
-
ensureTunnelBinary,
|
|
25
|
-
createTunnel: () => {
|
|
26
|
-
const tunnel = new FakeTunnel();
|
|
27
|
-
tunnels.push(tunnel);
|
|
28
|
-
setTimeout(() => tunnel.emit('url', 'https://preview-test.trycloudflare.com'), 0);
|
|
29
|
-
return tunnel;
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
managers.push(manager);
|
|
33
|
-
const target = normalizePreviewTarget('http://127.0.0.1:3000');
|
|
34
|
-
const [first, second] = await Promise.all([
|
|
35
|
-
manager.acquire('workspace-1', target, 'remote', 'localhost'),
|
|
36
|
-
manager.acquire('workspace-1', target, 'remote', 'localhost'),
|
|
37
|
-
]);
|
|
38
|
-
expect(first.viewBaseUrl).toBe('https://preview-test.trycloudflare.com');
|
|
39
|
-
expect(second.viewBaseUrl).toBe(first.viewBaseUrl);
|
|
40
|
-
expect(tunnels).toHaveLength(1);
|
|
41
|
-
expect(ensureTunnelBinary).toHaveBeenCalledTimes(1);
|
|
42
|
-
});
|
|
43
10
|
it('reclaims every gateway after simultaneous first acquisition', async () => {
|
|
44
11
|
const manager = new PreviewRuntimeManager({ listenHost: '127.0.0.1' });
|
|
45
12
|
managers.push(manager);
|
|
46
13
|
const target = normalizePreviewTarget('http://127.0.0.1:3000');
|
|
47
14
|
const sessions = await Promise.all([
|
|
48
|
-
manager.acquire('same-workspace', target, '
|
|
49
|
-
manager.acquire('same-workspace', target, '
|
|
15
|
+
manager.acquire('same-workspace', target, 'localhost'),
|
|
16
|
+
manager.acquire('same-workspace', target, 'localhost'),
|
|
50
17
|
]);
|
|
51
18
|
expect(sessions[0].viewBaseUrl).toBe(sessions[1].viewBaseUrl);
|
|
52
19
|
expect(await manager.heartbeat('same-workspace', sessions[0].id)).not.toBeNull();
|
|
53
20
|
await manager.stopAll();
|
|
54
21
|
await expect(fetch(sessions[0].viewBaseUrl)).rejects.toThrow();
|
|
55
22
|
});
|
|
56
|
-
it('cancels binary setup on invalidation and never launches the old tunnel', async () => {
|
|
57
|
-
let finishSetup;
|
|
58
|
-
const setup = new Promise((resolve) => { finishSetup = resolve; });
|
|
59
|
-
const ensureTunnelBinary = vi.fn(() => setup);
|
|
60
|
-
const createTunnel = vi.fn(() => new FakeTunnel());
|
|
61
|
-
const manager = new PreviewRuntimeManager({ listenHost: '127.0.0.1', ensureTunnelBinary, createTunnel });
|
|
62
|
-
managers.push(manager);
|
|
63
|
-
const opening = manager.acquire('invalidated', normalizePreviewTarget('http://127.0.0.1:3000'), 'remote', 'localhost');
|
|
64
|
-
const rejected = expect(opening).rejects.toThrow('cancelled');
|
|
65
|
-
await vi.waitFor(() => expect(ensureTunnelBinary).toHaveBeenCalled());
|
|
66
|
-
await manager.invalidate('invalidated');
|
|
67
|
-
finishSetup();
|
|
68
|
-
await rejected;
|
|
69
|
-
expect(createTunnel).not.toHaveBeenCalled();
|
|
70
|
-
});
|
|
71
23
|
it('closes a gateway invalidated before listen completes and rejects shutdown opens', async () => {
|
|
72
24
|
const manager = new PreviewRuntimeManager({ listenHost: '127.0.0.1' });
|
|
73
25
|
managers.push(manager);
|
|
74
26
|
const target = normalizePreviewTarget('http://127.0.0.1:3000');
|
|
75
|
-
const opening = manager.acquire('pending-listen', target, '
|
|
27
|
+
const opening = manager.acquire('pending-listen', target, 'localhost');
|
|
76
28
|
const rejected = expect(opening).rejects.toThrow('invalidated');
|
|
77
29
|
await manager.invalidate('pending-listen');
|
|
78
30
|
await rejected;
|
|
79
31
|
await manager.stopAll();
|
|
80
|
-
await expect(manager.acquire('after-stop', target, '
|
|
32
|
+
await expect(manager.acquire('after-stop', target, 'localhost')).rejects.toThrow('stopped');
|
|
81
33
|
});
|
|
82
|
-
it('
|
|
83
|
-
const
|
|
84
|
-
const manager = new PreviewRuntimeManager({ listenHost: '127.0.0.1', createTunnel: () => {
|
|
85
|
-
queueMicrotask(() => tunnel.emit('url', 'https://retry.trycloudflare.com'));
|
|
86
|
-
return tunnel;
|
|
87
|
-
} });
|
|
34
|
+
it('formats IPv6 browser hostnames for the gateway URL', async () => {
|
|
35
|
+
const manager = new PreviewRuntimeManager({ listenHost: '127.0.0.1' });
|
|
88
36
|
managers.push(manager);
|
|
89
|
-
await manager.acquire('
|
|
90
|
-
|
|
91
|
-
vi.useFakeTimers();
|
|
92
|
-
try {
|
|
93
|
-
const invalidating = manager.invalidate('retry-workspace');
|
|
94
|
-
const rejected = expect(invalidating).rejects.toThrow('did not exit');
|
|
95
|
-
await vi.advanceTimersByTimeAsync(3000);
|
|
96
|
-
await rejected;
|
|
97
|
-
stop.mockRestore();
|
|
98
|
-
await manager.stopAll();
|
|
99
|
-
expect(tunnel.stopped).toBe(true);
|
|
100
|
-
}
|
|
101
|
-
finally {
|
|
102
|
-
stop.mockRestore();
|
|
103
|
-
vi.useRealTimers();
|
|
104
|
-
}
|
|
37
|
+
const session = await manager.acquire('ipv6', normalizePreviewTarget('http://127.0.0.1:3000'), '::1');
|
|
38
|
+
expect(session.viewBaseUrl).toMatch(/^http:\/\/\[::1\]:\d+$/);
|
|
105
39
|
});
|
|
106
|
-
it('stops the
|
|
40
|
+
it('stops the local gateway after the last lease becomes idle', async () => {
|
|
107
41
|
let now = 0;
|
|
108
|
-
const tunnels = [];
|
|
109
42
|
const manager = new PreviewRuntimeManager({
|
|
110
43
|
listenHost: '127.0.0.1',
|
|
111
44
|
idleTtlMs: 20,
|
|
112
45
|
leaseTtlMs: 20,
|
|
113
46
|
sweepIntervalMs: 5,
|
|
114
47
|
now: () => now,
|
|
115
|
-
createTunnel: () => {
|
|
116
|
-
const tunnel = new FakeTunnel();
|
|
117
|
-
tunnels.push(tunnel);
|
|
118
|
-
setTimeout(() => tunnel.emit('url', 'https://preview-idle.trycloudflare.com'), 0);
|
|
119
|
-
return tunnel;
|
|
120
|
-
},
|
|
121
48
|
});
|
|
122
49
|
managers.push(manager);
|
|
123
50
|
const targetServer = http.createServer((_request, response) => response.end('ok'));
|
|
@@ -127,42 +54,16 @@ describe('PreviewRuntimeManager lifecycle', () => {
|
|
|
127
54
|
throw new Error('Target server did not start');
|
|
128
55
|
try {
|
|
129
56
|
const target = normalizePreviewTarget(`http://127.0.0.1:${address.port}`);
|
|
130
|
-
const session = await manager.acquire('workspace-idle', target, '
|
|
57
|
+
const session = await manager.acquire('workspace-idle', target, 'localhost');
|
|
131
58
|
manager.release('workspace-idle', session.id);
|
|
132
59
|
now = 100;
|
|
133
|
-
await expect.poll(() =>
|
|
134
|
-
|
|
60
|
+
await expect.poll(async () => manager.heartbeat('workspace-idle', session.id), { timeout: 500 }).toBeNull();
|
|
61
|
+
await expect(fetch(session.viewBaseUrl)).rejects.toThrow();
|
|
135
62
|
}
|
|
136
63
|
finally {
|
|
137
64
|
await new Promise((resolve) => targetServer.close(() => resolve()));
|
|
138
65
|
}
|
|
139
66
|
});
|
|
140
|
-
it('reclaims an idle remote tunnel while keeping an active local gateway session', async () => {
|
|
141
|
-
let now = 0;
|
|
142
|
-
const tunnels = [];
|
|
143
|
-
const manager = new PreviewRuntimeManager({
|
|
144
|
-
listenHost: '127.0.0.1',
|
|
145
|
-
idleTtlMs: 20,
|
|
146
|
-
leaseTtlMs: 1_000,
|
|
147
|
-
sweepIntervalMs: 5,
|
|
148
|
-
now: () => now,
|
|
149
|
-
createTunnel: () => {
|
|
150
|
-
const tunnel = new FakeTunnel();
|
|
151
|
-
tunnels.push(tunnel);
|
|
152
|
-
setTimeout(() => tunnel.emit('url', 'https://preview-mixed.trycloudflare.com'), 0);
|
|
153
|
-
return tunnel;
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
managers.push(manager);
|
|
157
|
-
const target = normalizePreviewTarget('http://127.0.0.1:3000');
|
|
158
|
-
const localSession = await manager.acquire('workspace-mixed', target, 'local', 'localhost');
|
|
159
|
-
const remoteSession = await manager.acquire('workspace-mixed', target, 'remote', 'localhost');
|
|
160
|
-
manager.release('workspace-mixed', remoteSession.id);
|
|
161
|
-
now = 100;
|
|
162
|
-
await expect.poll(() => tunnels[0]?.stopped, { timeout: 500 }).toBe(true);
|
|
163
|
-
expect(await manager.heartbeat('workspace-mixed', localSession.id)).not.toBeNull();
|
|
164
|
-
expect(tunnels).toHaveLength(1);
|
|
165
|
-
});
|
|
166
67
|
});
|
|
167
68
|
describe('preview target cookies', () => {
|
|
168
69
|
const target = normalizePreviewTarget('http://127.0.0.1:3000/app');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-runtime-manager.test.js","sourceRoot":"","sources":["../../../src/services/__tests__/preview-runtime-manager.test.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"preview-runtime-manager.test.js","sourceRoot":"","sources":["../../../src/services/__tests__/preview-runtime-manager.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAE3F,MAAM,QAAQ,GAA4B,EAAE,CAAC;AAE7C,SAAS,CAAC,KAAK,IAAI,EAAE;IACnB,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AAC5E,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,OAAO,GAAG,IAAI,qBAAqB,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC;QACvE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,MAAM,MAAM,GAAG,sBAAsB,CAAC,uBAAuB,CAAC,CAAC;QAC/D,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACjC,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC;YACtD,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC;SACvD,CAAC,CAAC;QACH,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QAC9D,MAAM,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACjF,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iFAAiF,EAAE,KAAK,IAAI,EAAE;QAC/F,MAAM,OAAO,GAAG,IAAI,qBAAqB,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC;QACvE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,MAAM,MAAM,GAAG,sBAAsB,CAAC,uBAAuB,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QACvE,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAChE,MAAM,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;QAC3C,MAAM,QAAQ,CAAC;QACf,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC9F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,OAAO,GAAG,IAAI,qBAAqB,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC;QACvE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,sBAAsB,CAAC,uBAAuB,CAAC,EAAE,KAAK,CAAC,CAAC;QACtG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,IAAI,GAAG,GAAG,CAAC,CAAC;QACZ,MAAM,OAAO,GAAG,IAAI,qBAAqB,CAAC;YACxC,UAAU,EAAE,WAAW;YACvB,SAAS,EAAE,EAAE;YACb,UAAU,EAAE,EAAE;YACd,eAAe,EAAE,CAAC;YAClB,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG;SACf,CAAC,CAAC;QACH,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACnF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QACnF,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,CAAC;QACvC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;QAE5F,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,sBAAsB,CAAC,oBAAoB,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1E,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;YAC7E,OAAO,CAAC,OAAO,CAAC,gBAAgB,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;YAC9C,GAAG,GAAG,GAAG,CAAC;YAEV,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,gBAAgB,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC5G,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAC7D,CAAC;gBAAS,CAAC;YACT,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,MAAM,MAAM,GAAG,sBAAsB,CAAC,2BAA2B,CAAC,CAAC;IAEnE,EAAE,CAAC,mEAAmE,EAAE,GAAG,EAAE;QAC3E,MAAM,CAAC,mBAAmB,CACxB,kEAAkE,EAClE,MAAM,EACN,+BAA+B,EAC/B,IAAI,CACL,CAAC,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACvF,MAAM,SAAS,GAAG,mBAAmB,CACnC,2DAA2D,EAC3D,MAAM,EACN,+BAA+B,EAC/B,KAAK,CACN,CAAC;QAEF,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,uCAAuC,CAAC,CAAC;QACrE,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;QACtD,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,SAAS,GAAG,mBAAmB,CACnC,4EAA4E,EAC5E,MAAM,EACN,+BAA+B,EAC/B,KAAK,CACN,CAAC;QAEF,MAAM,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,uCAAuC,CAAC,CAAC;QACrE,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,CAAC,mBAAmB,CACxB,mEAAmE,EACnE,MAAM,EACN,+BAA+B,EAC/B,KAAK,CACN,CAAC,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { ServiceError } from '../../errors.js';
|
|
3
2
|
import { normalizePreviewTarget } from '../preview.service.js';
|
|
4
3
|
describe('normalizePreviewTarget', () => {
|
|
5
4
|
it('accepts shorthand ports and loopback URLs', () => {
|
|
6
5
|
expect(normalizePreviewTarget('3000').target).toBe('http://127.0.0.1:3000');
|
|
7
6
|
expect(normalizePreviewTarget('localhost:5173').target).toBe('http://127.0.0.1:5173');
|
|
8
|
-
expect(normalizePreviewTarget('http://
|
|
9
|
-
expect(normalizePreviewTarget('https://
|
|
7
|
+
expect(normalizePreviewTarget('http://[::1]:8080/app/').target).toBe('http://[::1]:8080/app');
|
|
8
|
+
expect(normalizePreviewTarget('https://127.0.0.1:8443/dash').basePath).toBe('/dash');
|
|
10
9
|
});
|
|
11
|
-
it('normalizes
|
|
12
|
-
expect(normalizePreviewTarget('0.0.0.0:3000').target).toBe('http://127.0.0.1:3000');
|
|
10
|
+
it('normalizes wildcard listen addresses to loopback', () => {
|
|
11
|
+
expect(normalizePreviewTarget('http://0.0.0.0:3000').target).toBe('http://127.0.0.1:3000');
|
|
13
12
|
});
|
|
14
|
-
it('
|
|
15
|
-
expect(
|
|
16
|
-
expect(
|
|
17
|
-
expect(
|
|
13
|
+
it('accepts any host the Agent Tower server can reach', () => {
|
|
14
|
+
expect(normalizePreviewTarget('192.168.1.20:8080').target).toBe('http://192.168.1.20:8080');
|
|
15
|
+
expect(normalizePreviewTarget('http://api:4000/health').target).toBe('http://api:4000/health');
|
|
16
|
+
expect(normalizePreviewTarget('https://Staging.Example.com').origin).toBe('https://staging.example.com');
|
|
18
17
|
});
|
|
19
18
|
it('rejects unsupported schemes and credentials', () => {
|
|
20
|
-
expect(() => normalizePreviewTarget('
|
|
21
|
-
expect(() => normalizePreviewTarget('
|
|
22
|
-
expect(() => normalizePreviewTarget('
|
|
19
|
+
expect(() => normalizePreviewTarget('ftp://127.0.0.1:21')).toThrow('must use http or https');
|
|
20
|
+
expect(() => normalizePreviewTarget('http://user:pass@127.0.0.1:3000')).toThrow('credentials');
|
|
21
|
+
expect(() => normalizePreviewTarget(' ')).toThrow('required');
|
|
23
22
|
});
|
|
24
23
|
it('rejects invalid ports', () => {
|
|
25
|
-
expect(() => normalizePreviewTarget('
|
|
26
|
-
expect(() => normalizePreviewTarget('
|
|
24
|
+
expect(() => normalizePreviewTarget('http://127.0.0.1:70000')).toThrow();
|
|
25
|
+
expect(() => normalizePreviewTarget('http://127.0.0.1:0')).toThrow();
|
|
27
26
|
});
|
|
28
27
|
});
|
|
29
28
|
//# sourceMappingURL=preview.service.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.service.test.js","sourceRoot":"","sources":["../../../src/services/__tests__/preview.service.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"preview.service.test.js","sourceRoot":"","sources":["../../../src/services/__tests__/preview.service.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAC5E,MAAM,CAAC,sBAAsB,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACtF,MAAM,CAAC,sBAAsB,CAAC,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QAC9F,MAAM,CAAC,sBAAsB,CAAC,6BAA6B,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,sBAAsB,CAAC,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC7F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,CAAC,sBAAsB,CAAC,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC5F,MAAM,CAAC,sBAAsB,CAAC,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;QAC/F,MAAM,CAAC,sBAAsB,CAAC,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC3G,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC,oBAAoB,CAAC,CAAC,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;QAC7F,MAAM,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC,iCAAiC,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAC/F,MAAM,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC,wBAAwB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACzE,MAAM,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC,oBAAoB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;IACvE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1209,6 +1209,70 @@ describe('SessionManager session status vs real process state', () => {
|
|
|
1209
1209
|
})).toBe(0);
|
|
1210
1210
|
await manager.destroyAll();
|
|
1211
1211
|
});
|
|
1212
|
+
it.each([
|
|
1213
|
+
['start', 'entry'], ['startFollowUp', 'entry'],
|
|
1214
|
+
['start', 'backend'], ['startFollowUp', 'backend'],
|
|
1215
|
+
])('resolves a Claude MCP %s failure with missing %s and permits cleanup and retry', async (method, missing) => {
|
|
1216
|
+
const { ClaudeCodeExecutor } = await import('../../executors/claude-code.executor.js');
|
|
1217
|
+
// Exercise the real configuration builder and CLI Driver; only substitute
|
|
1218
|
+
// the PTY transport after configuration succeeds.
|
|
1219
|
+
class TestClaudeExecutor extends ClaudeCodeExecutor {
|
|
1220
|
+
async spawnWithStdin() { return spawnMock(); }
|
|
1221
|
+
}
|
|
1222
|
+
const executor = new TestClaudeExecutor({ executionPermission: 'FULL_ACCESS' });
|
|
1223
|
+
const provider = {
|
|
1224
|
+
id: 'claude-mcp-recovery', name: 'Claude MCP recovery', agentType: AgentType.CLAUDE_CODE,
|
|
1225
|
+
runtimeType: RuntimeType.CLI, executionPermission: 'FULL_ACCESS',
|
|
1226
|
+
config: {}, env: {}, isDefault: false,
|
|
1227
|
+
};
|
|
1228
|
+
getProviderByIdMock.mockReturnValue(provider);
|
|
1229
|
+
getExecutorByProviderMock.mockReturnValue(executor);
|
|
1230
|
+
const { workspace, session } = await createSessionFixture({ providerId: provider.id, agentType: AgentType.CLAUDE_CODE });
|
|
1231
|
+
const resumeSource = await prisma.session.create({ data: {
|
|
1232
|
+
workspaceId: workspace.id, agentType: AgentType.CLAUDE_CODE, prompt: 'previous turn',
|
|
1233
|
+
status: SessionStatus.COMPLETED, externalSessionId: 'previous-claude-session',
|
|
1234
|
+
} });
|
|
1235
|
+
const entry = path.join(serverRoot, 'src', 'mcp', 'index.ts');
|
|
1236
|
+
const manager = new SessionManager(new EventBus());
|
|
1237
|
+
const pty = new ControlledPty();
|
|
1238
|
+
spawnMock.mockReset();
|
|
1239
|
+
spawnMock.mockResolvedValueOnce(spawnResultFor(pty));
|
|
1240
|
+
vi.stubEnv('AGENT_TOWER_DESKTOP_RUNTIME_MODE', undefined);
|
|
1241
|
+
vi.stubEnv('AGENT_TOWER_MCP_ENTRY', missing === 'entry' ? path.join(testDir, 'missing-mcp.js') : entry);
|
|
1242
|
+
vi.stubEnv('AGENT_TOWER_URL', missing === 'backend' ? undefined : 'http://127.0.0.1:12580');
|
|
1243
|
+
vi.stubEnv('AGENT_TOWER_PORT', undefined);
|
|
1244
|
+
try {
|
|
1245
|
+
const starting = method === 'start' ? manager.start(session.id) : manager.startFollowUp(session.id, resumeSource.id);
|
|
1246
|
+
await expect(starting).rejects.toThrow(missing === 'entry'
|
|
1247
|
+
? 'Agent Tower MCP entry not found'
|
|
1248
|
+
: 'AGENT_TOWER_URL or AGENT_TOWER_PORT is required');
|
|
1249
|
+
expect(spawnMock).not.toHaveBeenCalled();
|
|
1250
|
+
expect(await prisma.executionProcess.count({ where: { sessionId: session.id } })).toBe(0);
|
|
1251
|
+
await expect(prisma.session.findUniqueOrThrow({ where: { id: session.id } })).resolves.toMatchObject({
|
|
1252
|
+
status: SessionStatus.CANCELLED, runtimeLaunchState: 'SAFE_PRE_CHILD_FAILURE',
|
|
1253
|
+
runtimeLaunchClaimCount: 1, runtimeLaunchResolvedCount: 1, runtimeLaunchProcessCount: 0,
|
|
1254
|
+
});
|
|
1255
|
+
expect(await manager.isRuntimeCleanupConfirmed(session.id)).toBe(true);
|
|
1256
|
+
await expect(manager.stop(session.id)).resolves.toMatchObject({ id: session.id });
|
|
1257
|
+
vi.stubEnv('AGENT_TOWER_MCP_ENTRY', entry);
|
|
1258
|
+
vi.stubEnv('AGENT_TOWER_URL', 'http://127.0.0.1:12580');
|
|
1259
|
+
await expect(manager.sendMessage(session.id, 'retry after correcting MCP configuration')).resolves.toMatchObject({ id: session.id });
|
|
1260
|
+
expect(spawnMock).toHaveBeenCalledOnce();
|
|
1261
|
+
await expect(prisma.session.findUniqueOrThrow({ where: { id: session.id } })).resolves.toMatchObject({
|
|
1262
|
+
status: SessionStatus.RUNNING, runtimeLaunchState: 'PROCESS_RECORDED',
|
|
1263
|
+
runtimeLaunchClaimCount: 2, runtimeLaunchResolvedCount: 2, runtimeLaunchProcessCount: 1,
|
|
1264
|
+
});
|
|
1265
|
+
await manager.stop(session.id);
|
|
1266
|
+
expect(pty.killed).toBe(true);
|
|
1267
|
+
expect(await manager.isRuntimeCleanupConfirmed(session.id)).toBe(true);
|
|
1268
|
+
expect(await prisma.executionProcess.findMany({ where: { sessionId: session.id } }))
|
|
1269
|
+
.toMatchObject([{ launchClaimNumber: 2, cleanupState: 'CONFIRMED' }]);
|
|
1270
|
+
}
|
|
1271
|
+
finally {
|
|
1272
|
+
vi.unstubAllEnvs();
|
|
1273
|
+
await manager.destroyAll();
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1212
1276
|
it.each(['start', 'startFollowUp'])('revokes a public %s before runtime admission when stop wins during preparation', async (method) => {
|
|
1213
1277
|
const { workspace, session } = await createSessionFixture();
|
|
1214
1278
|
const resumeSource = await prisma.session.create({
|
|
@@ -1358,6 +1422,8 @@ describe('SessionManager session status vs real process state', () => {
|
|
|
1358
1422
|
expect(checkpoint).toHaveBeenCalledWith(session.id);
|
|
1359
1423
|
feed({ type: 'stream_event', event: { type: 'content_block_start', index: 2, content_block: { type: 'text', text: 'B after tool' } } });
|
|
1360
1424
|
feed({ type: 'result', subtype: 'success', num_turns: 1 });
|
|
1425
|
+
// A Claude run ends when its process exits, not at the result.
|
|
1426
|
+
pty.emitExit(0);
|
|
1361
1427
|
await completed;
|
|
1362
1428
|
const row = await prisma.session.findUniqueOrThrow({ where: { id: session.id } });
|
|
1363
1429
|
expect(JSON.parse(row.logSnapshot).entries.at(-1)).toMatchObject({ content: 'B after tool', metadata: { textBlock: { state: 'SEALED', sealedReason: 'TURN_END' } } });
|
|
@@ -1499,6 +1565,78 @@ describe('SessionManager session status vs real process state', () => {
|
|
|
1499
1565
|
expect(snapshot.entries.map((entry) => entry.content))
|
|
1500
1566
|
.toContain('fast logical completion');
|
|
1501
1567
|
});
|
|
1568
|
+
it('keeps a Claude Code session running after its result while background work is pending and completes on exit', async () => {
|
|
1569
|
+
const { session } = await createSessionFixture({ agentType: AgentType.CLAUDE_CODE });
|
|
1570
|
+
const pty = new ControlledPty();
|
|
1571
|
+
spawnMock.mockResolvedValueOnce(spawnResultFor(pty));
|
|
1572
|
+
const eventBus = new EventBus();
|
|
1573
|
+
const manager = new SessionManager(eventBus);
|
|
1574
|
+
const completed = waitForEvent(eventBus, 'session:completed');
|
|
1575
|
+
const states = [];
|
|
1576
|
+
eventBus.on('session:runtime_state_changed', ({ state }) => states.push(state));
|
|
1577
|
+
try {
|
|
1578
|
+
await manager.start(session.id);
|
|
1579
|
+
const line = (message) => pty.emitData(JSON.stringify(message) + '\n');
|
|
1580
|
+
line({ type: 'system', subtype: 'init', session_id: 'claude-native' });
|
|
1581
|
+
line({ type: 'assistant', message: { id: 'm1', role: 'assistant', content: [{ type: 'text', text: 'WAITING for the router' }] } });
|
|
1582
|
+
line({ type: 'system', subtype: 'background_tasks_changed', tasks: [{ task_id: 'bg-1', task_type: 'local_bash', description: 'router watch' }] });
|
|
1583
|
+
line({ type: 'result', subtype: 'success', result: 'WAITING for the router', num_turns: 1, result_index: 0, uuid: 'r0', origin: null });
|
|
1584
|
+
await vi.waitFor(() => {
|
|
1585
|
+
expect(states.some((state) => state.backgroundTasks?.some((task) => task.taskId === 'bg-1'))).toBe(true);
|
|
1586
|
+
}, { timeout: 2000 });
|
|
1587
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
1588
|
+
expect(manager.hasActiveTurn(session.id)).toBe(true);
|
|
1589
|
+
expect(pty.killed).toBe(false);
|
|
1590
|
+
expect((await prisma.session.findUnique({ where: { id: session.id } }))?.status).toBe(SessionStatus.RUNNING);
|
|
1591
|
+
line({ type: 'system', subtype: 'task_notification', task_id: 'bg-1', status: 'completed', summary: 'Background command "router watch" completed (exit code 0)' });
|
|
1592
|
+
line({ type: 'system', subtype: 'background_tasks_changed', tasks: [] });
|
|
1593
|
+
line({ type: 'system', subtype: 'init', session_id: 'claude-native' });
|
|
1594
|
+
line({ type: 'assistant', message: { id: 'm2', role: 'assistant', content: [{ type: 'text', text: 'FINISHED routing' }] } });
|
|
1595
|
+
line({ type: 'result', subtype: 'success', result: 'FINISHED routing', num_turns: 1, result_index: 1, uuid: 'r1', origin: { kind: 'task-notification' } });
|
|
1596
|
+
pty.emitExit(0);
|
|
1597
|
+
const payload = await completed;
|
|
1598
|
+
expect(payload.status).toBe(SessionStatus.COMPLETED);
|
|
1599
|
+
await vi.waitFor(() => expect(manager.hasActiveTurn(session.id)).toBe(false), { timeout: 2000 });
|
|
1600
|
+
const persisted = await prisma.session.findUnique({ where: { id: session.id } });
|
|
1601
|
+
expect(persisted?.status).toBe(SessionStatus.COMPLETED);
|
|
1602
|
+
const contents = JSON.parse(persisted?.logSnapshot ?? '{}').entries.map((entry) => entry.content);
|
|
1603
|
+
expect(contents).toContain('FINISHED routing');
|
|
1604
|
+
expect(contents).toContain('Background command "router watch" completed (exit code 0)');
|
|
1605
|
+
}
|
|
1606
|
+
finally {
|
|
1607
|
+
await manager.destroyAll();
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
it('stops a waiting Claude Code run when a follow-up arrives and tags wake-up messages as system events', async () => {
|
|
1611
|
+
const { session } = await createSessionFixture({ agentType: AgentType.CLAUDE_CODE });
|
|
1612
|
+
const first = new ControlledPty();
|
|
1613
|
+
const second = new ControlledPty();
|
|
1614
|
+
second.pid = 4343;
|
|
1615
|
+
spawnMock.mockResolvedValueOnce(spawnResultFor(first)).mockResolvedValueOnce(spawnResultFor(second));
|
|
1616
|
+
const eventBus = new EventBus();
|
|
1617
|
+
const manager = new SessionManager(eventBus);
|
|
1618
|
+
try {
|
|
1619
|
+
await manager.start(session.id);
|
|
1620
|
+
const line = (pty, message) => pty.emitData(JSON.stringify(message) + '\n');
|
|
1621
|
+
line(first, { type: 'system', subtype: 'background_tasks_changed', tasks: [{ task_id: 'bg-1', description: 'watch' }] });
|
|
1622
|
+
line(first, { type: 'result', subtype: 'success', result: 'WAITING', num_turns: 1, result_index: 0, uuid: 'r0', origin: null });
|
|
1623
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
1624
|
+
expect(first.killed).toBe(false);
|
|
1625
|
+
await manager.sendMessage(session.id, 'wake-up text', undefined, undefined, { origin: 'SYSTEM_EVENT' });
|
|
1626
|
+
expect(first.killed).toBe(true);
|
|
1627
|
+
const completed = waitForEvent(eventBus, 'session:completed');
|
|
1628
|
+
line(second, { type: 'result', subtype: 'success', result: 'done', result_index: 0, uuid: 'r0', origin: null });
|
|
1629
|
+
second.emitExit(0);
|
|
1630
|
+
await completed;
|
|
1631
|
+
const persisted = await prisma.session.findUnique({ where: { id: session.id } });
|
|
1632
|
+
const entries = JSON.parse(persisted?.logSnapshot ?? '{}').entries;
|
|
1633
|
+
const wakeup = entries.find((entry) => entry.content === 'wake-up text');
|
|
1634
|
+
expect(wakeup).toMatchObject({ entryType: 'user_message', metadata: { origin: 'SYSTEM_EVENT' } });
|
|
1635
|
+
}
|
|
1636
|
+
finally {
|
|
1637
|
+
await manager.destroyAll();
|
|
1638
|
+
}
|
|
1639
|
+
});
|
|
1502
1640
|
it('waits for the completed generation auto-commit before starting an immediate follow-up', async () => {
|
|
1503
1641
|
const { session } = await createSessionFixture();
|
|
1504
1642
|
const firstPty = new ControlledPty();
|
|
@@ -1722,7 +1860,7 @@ describe('SessionManager session status vs real process state', () => {
|
|
|
1722
1860
|
expect(spawnMock).toHaveBeenCalledTimes(2);
|
|
1723
1861
|
await manager.destroyAll();
|
|
1724
1862
|
});
|
|
1725
|
-
it.each(['success', 'error_during_execution'])('persists Claude final %s and
|
|
1863
|
+
it.each(['success', 'error_during_execution'])('persists Claude final %s and reclaims a CLI process that lingers after its result', async (subtype) => {
|
|
1726
1864
|
const { task, session } = await createSessionFixture({ agentType: AgentType.CLAUDE_CODE });
|
|
1727
1865
|
const pty = new ControlledPty();
|
|
1728
1866
|
spawnMock.mockResolvedValueOnce(spawnResultFor(pty));
|
|
@@ -1730,6 +1868,9 @@ describe('SessionManager session status vs real process state', () => {
|
|
|
1730
1868
|
const manager = new SessionManager(eventBus);
|
|
1731
1869
|
const completed = waitForEvent(eventBus, 'session:completed');
|
|
1732
1870
|
const expectedStatus = subtype === 'success' ? SessionStatus.COMPLETED : SessionStatus.FAILED;
|
|
1871
|
+
// The process reports no background work and never exits: Tower treats it as hung after the idle grace.
|
|
1872
|
+
const previousGrace = process.env.AGENT_TOWER_CLAUDE_IDLE_EXIT_GRACE_MS;
|
|
1873
|
+
process.env.AGENT_TOWER_CLAUDE_IDLE_EXIT_GRACE_MS = '200';
|
|
1733
1874
|
try {
|
|
1734
1875
|
await manager.start(session.id);
|
|
1735
1876
|
pty.emitData(JSON.stringify({
|
|
@@ -1765,8 +1906,13 @@ describe('SessionManager session status vs real process state', () => {
|
|
|
1765
1906
|
}));
|
|
1766
1907
|
expect((await prisma.task.findUniqueOrThrow({ where: { id: task.id } })).status).not.toBe('IN_REVIEW');
|
|
1767
1908
|
}
|
|
1909
|
+
expect(snapshot.entries).toContainEqual(expect.objectContaining({ entryType: 'warning_message' }));
|
|
1768
1910
|
}
|
|
1769
1911
|
finally {
|
|
1912
|
+
if (previousGrace === undefined)
|
|
1913
|
+
delete process.env.AGENT_TOWER_CLAUDE_IDLE_EXIT_GRACE_MS;
|
|
1914
|
+
else
|
|
1915
|
+
process.env.AGENT_TOWER_CLAUDE_IDLE_EXIT_GRACE_MS = previousGrace;
|
|
1770
1916
|
await manager.destroyAll();
|
|
1771
1917
|
}
|
|
1772
1918
|
});
|