specrails-desktop 2.29.1 → 2.29.2
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/client/dist/assets/ActivityFeedPage-ClpD5CFj.js +1 -0
- package/client/dist/assets/AgentBrowserCapture-CSPs9Ofc.js +1 -0
- package/client/dist/assets/AgentModeCodePane-DwcyklA5.js +2 -0
- package/client/dist/assets/AgentModeJobsPane-C69bqlMa.js +1 -0
- package/client/dist/assets/AgentsPage-pRiRr9-W.js +86 -0
- package/client/dist/assets/AnalyticsPage-C14exKsC.js +1 -0
- package/client/dist/assets/BarChart-nBOMNfiy.js +33 -0
- package/client/dist/assets/CodePage-CK76JKm9.js +2 -0
- package/client/dist/assets/DesktopAnalyticsPage-Cnv0f3XQ.js +1 -0
- package/client/dist/assets/DocsDialog-CHUm_Ym0.js +11 -0
- package/client/dist/assets/DocsPage-COI-oDok.js +11 -0
- package/client/dist/assets/ExportDropdown-BrtfWzEG.js +1 -0
- package/client/dist/assets/IntegrationsPage-Bs00_tl4.js +1 -0
- package/client/dist/assets/InteractiveJobComposer-CypM7R1F.js +19 -0
- package/client/dist/assets/JobDetailModal-B_W0H9dw.js +1 -0
- package/client/dist/assets/JobDetailPage-CeeXOoKS.js +1 -0
- package/client/dist/assets/JobsPage-CwhJG9jw.js +1 -0
- package/client/dist/assets/LoopBuilderPage-C51t1WOM.js +7 -0
- package/client/dist/assets/LoopPreviewModal-Y2HgtIJu.js +1 -0
- package/client/dist/assets/LoopsPage-Bvn1Kw5V.js +1 -0
- package/client/dist/assets/MinimizedChatsContext-DiFvZDPW.js +1 -0
- package/client/dist/assets/PluginsPage-BGPRJpdf.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-D_GTyGLc.js +1 -0
- package/client/dist/assets/TemplatePreviewModal-szwBhd7h.js +1 -0
- package/client/dist/assets/TicketDetailModal-9UEyK974.js +14 -0
- package/client/dist/assets/Trans-B_vKZ-Hb.js +1 -0
- package/client/dist/assets/activity-DiyjsgQV.js +1 -0
- package/client/dist/assets/addon-image-BRmmRHgQ.js +1 -0
- package/client/dist/assets/addon-ligatures-qIZUPvRV.js +2 -0
- package/client/dist/assets/{addon-webgl-DMbGeI_u.js → addon-webgl-C2d7lriQ.js} +3 -3
- package/client/dist/assets/addspec-Rw8uVBC1.js +1 -0
- package/client/dist/assets/agent-4rmXoo_p.js +1 -0
- package/client/dist/assets/agent-BxZuNGih.js +1 -0
- package/client/dist/assets/agent-C3uXOYv_.js +1 -0
- package/client/dist/assets/agent-CA8Y4wbw.js +1 -0
- package/client/dist/assets/agent-CASKrhG12.js +1 -0
- package/client/dist/assets/agent-Cv-jud5n2.js +1 -0
- package/client/dist/assets/agent-DitUxYh2.js +1 -0
- package/client/dist/assets/agent-Doow4X7o.js +1 -0
- package/client/dist/assets/agents-DhG7mg6-.js +1 -0
- package/client/dist/assets/agentstudio-BIUe1Im2.js +1 -0
- package/client/dist/assets/aiedit-iIjz2_Qk.js +1 -0
- package/client/dist/assets/analytics-DZ8Aoq3I.js +1 -0
- package/client/dist/assets/api-B2M7CtDw.js +1 -0
- package/client/dist/assets/arrow-left-CpIR4j7G.js +1 -0
- package/client/dist/assets/arrow-up-right-9DoMn7fD.js +1 -0
- package/client/dist/assets/attachments-B53PG733.js +1 -0
- package/client/dist/assets/brain-DI9lHkKY.js +1 -0
- package/client/dist/assets/browser-CXUcnTMI.js +1 -0
- package/client/dist/assets/button-BUIIBQPT.js +1 -0
- package/client/dist/assets/chat-BGD7MXAy.js +1 -0
- package/client/dist/assets/chevron-right-CViMxucY.js +1 -0
- package/client/dist/assets/chunk-KS7C4IRE-Cx5sqopX.js +3 -0
- package/client/dist/assets/circle-x-CMYsN4ZH.js +1 -0
- package/client/dist/assets/code-BDXEM4ml.js +1 -0
- package/client/dist/assets/commands-DyUZQ1FI.js +1 -0
- package/client/dist/assets/common-CD1QHriA2.js +1 -0
- package/client/dist/assets/common-CwwPaRxx.js +1 -0
- package/client/dist/assets/common-DVQuhs-m.js +1 -0
- package/client/dist/assets/common-Dg2jaX7d.js +1 -0
- package/client/dist/assets/common-DuBwj6tK.js +1 -0
- package/client/dist/assets/common-MRj9MHcc.js +1 -0
- package/client/dist/assets/common-O4ZpnMLp.js +1 -0
- package/client/dist/assets/common-fyeF0v7C2.js +1 -0
- package/client/dist/assets/createLucideIcon-Bjbbp_KS.js +1 -0
- package/client/dist/assets/{css.worker-Wv5dxAWO.js → css.worker-yKcq13f2.js} +13 -13
- package/client/dist/assets/{cssMode-DBHbkBvI.js → cssMode-BM4z9OX0.js} +1 -1
- package/client/dist/assets/dashboard-B_NkSHYm.js +1 -0
- package/client/dist/assets/dashboard-ClBb_3pq.js +1 -0
- package/client/dist/assets/dashboard-CpCmJvMB.js +1 -0
- package/client/dist/assets/dashboard-Czohu35O2.js +1 -0
- package/client/dist/assets/dashboard-DcjbD0UK.js +1 -0
- package/client/dist/assets/dashboard-Dvx5F3we.js +1 -0
- package/client/dist/assets/dashboard-R1bOPjBh2.js +1 -0
- package/client/dist/assets/dashboard-owcDucDi.js +1 -0
- package/client/dist/assets/dialog-CzIekCw0.js +45 -0
- package/client/dist/assets/dist-Cez5CRkX.js +1 -0
- package/client/dist/assets/dist-bVqfYmby.js +1 -0
- package/client/dist/assets/{dist-js-CgpP6tKI.js → dist-js-CcBCbb32.js} +1 -1
- package/client/dist/assets/{dist-js-DHiN-A_E.js → dist-js-VwztRkGj.js} +1 -1
- package/client/dist/assets/download-CFHyyHcw.js +1 -0
- package/client/dist/assets/editor.api2-4ndOupAA.js +872 -0
- package/client/dist/assets/{editor.main-C9J2ikJK.js → editor.main-DxrmGfoq.js} +2 -2
- package/client/dist/assets/editor.worker-tytyn86m.js +26 -0
- package/client/dist/assets/explore-DgBACn39.js +1 -0
- package/client/dist/assets/external-link-BcU5EWZp.js +1 -0
- package/client/dist/assets/file-text-CFT-xXKN.js +1 -0
- package/client/dist/assets/formatDistanceToNow-ByHIauTQ.js +1 -0
- package/client/dist/assets/{freemarker2-J5iIIIK-.js → freemarker2-CELlTDJ2.js} +1 -1
- package/client/dist/assets/getTimezoneOffsetInMilliseconds-BxHAksaU.js +1 -0
- package/client/dist/assets/git-branch-BRM0F5GN.js +1 -0
- package/client/dist/assets/{handlebars-DSw1l_tw.js → handlebars-NEjHJ7o_.js} +1 -1
- package/client/dist/assets/{html-h2-wE1Lj.js → html-CA10zN5A.js} +1 -1
- package/client/dist/assets/{html.worker-CQP8QQsS.js → html.worker-DSJ9zmd7.js} +12 -12
- package/client/dist/assets/{htmlMode-Cc_ij7lW.js → htmlMode-89rPYiyq.js} +1 -1
- package/client/dist/assets/i18n-CgaRxpII.js +2 -0
- package/client/dist/assets/index-BflYQpWm.css +2 -0
- package/client/dist/assets/index-C1_-BTXy.js +72 -0
- package/client/dist/assets/integrations-mB-7zoyT.js +1 -0
- package/client/dist/assets/{javascript-C1q3xv6N.js → javascript-B1gDmwzS.js} +1 -1
- package/client/dist/assets/jira-CmKWERE0.js +1 -0
- package/client/dist/assets/jira-api-BgFIr8eD.js +1 -0
- package/client/dist/assets/job-time-BCL7wee9.js +1 -0
- package/client/dist/assets/jobs-B-sDHEay2.js +1 -0
- package/client/dist/assets/jobs-BCtGfgUA.js +1 -0
- package/client/dist/assets/jobs-BHR_EnDI.js +1 -0
- package/client/dist/assets/jobs-BMbZJjJJ.js +1 -0
- package/client/dist/assets/jobs-CVFD8rar.js +1 -0
- package/client/dist/assets/jobs-DoSaFzAa.js +1 -0
- package/client/dist/assets/jobs-DzSIR8V62.js +1 -0
- package/client/dist/assets/jobs-lk1_Dq1U.js +1 -0
- package/client/dist/assets/json.worker-Oa9UrkKE.js +58 -0
- package/client/dist/assets/{jsonMode-CeaCdEhr.js → jsonMode-xHLMfzhF.js} +1 -1
- package/client/dist/assets/jsx-runtime-zu2_FqZY.js +1 -0
- package/client/dist/assets/lib-C7Mhm-gT.js +7 -0
- package/client/dist/assets/lib-CGdyxIWg.js +29 -0
- package/client/dist/assets/{liquid-eFsVK0V-.js → liquid-BY6hCUXG.js} +1 -1
- package/client/dist/assets/loader-circle-DcygOQpC.js +1 -0
- package/client/dist/assets/loops-BzfXWEwO.js +1 -0
- package/client/dist/assets/{lspLanguageFeatures-DT3-bbhn.js → lspLanguageFeatures-ira2aEJh.js} +1 -1
- package/client/dist/assets/mcp-uDkXYu4L.js +1 -0
- package/client/dist/assets/{mdx-Fuwnh1cX.js → mdx-BNx0xr3N.js} +1 -1
- package/client/dist/assets/monaco.contribution-Bt92R3Vw.js +2 -0
- package/client/dist/assets/nav-myFaXRHD.js +1 -0
- package/client/dist/assets/pencil-C9RkrIPZ.js +1 -0
- package/client/dist/assets/play-DNlrawbS.js +1 -0
- package/client/dist/assets/plus-CsnjfkmX.js +1 -0
- package/client/dist/assets/preload-helper-Czpn1I53.js +1 -0
- package/client/dist/assets/{python-BgoEs6Cj.js → python-DLdbtmNw.js} +1 -1
- package/client/dist/assets/{razor-CM1sPMMq.js → razor-B9gdhAbR.js} +1 -1
- package/client/dist/assets/react-dom-DiWzBDP9.js +1 -0
- package/client/dist/assets/rolldown-runtime-CNC7AqOf.js +1 -0
- package/client/dist/assets/save-28sIlCdr.js +1 -0
- package/client/dist/assets/search-Cq_gE3Ik.js +1 -0
- package/client/dist/assets/settings-B0ypw9uw.js +1 -0
- package/client/dist/assets/setup-CmK87LuS.js +1 -0
- package/client/dist/assets/sparkles-DrY8hb0X.js +1 -0
- package/client/dist/assets/specs-DII0C8gw.js +1 -0
- package/client/dist/assets/spending-C4p06nen.js +1 -0
- package/client/dist/assets/terminal-BFRZnMPP.js +1 -0
- package/client/dist/assets/tickets-B8BuAaj5.js +1 -0
- package/client/dist/assets/tickets-B_yh4qQk.js +1 -0
- package/client/dist/assets/tickets-Bs37scwY2.js +1 -0
- package/client/dist/assets/tickets-CFk2n2Or.js +1 -0
- package/client/dist/assets/tickets-DQp7JS2R2.js +1 -0
- package/client/dist/assets/tickets-D_yDquEw.js +1 -0
- package/client/dist/assets/tickets-DpmdE8tv2.js +1 -0
- package/client/dist/assets/tickets-pAUJkDTu.js +1 -0
- package/client/dist/assets/trash-2-BOU3WdZZ.js +1 -0
- package/client/dist/assets/triangle-alert-H9T4jAmh.js +1 -0
- package/client/dist/assets/{ts.worker-METxwbDZ.js → ts.worker-BtjBFNHX.js} +48 -33
- package/client/dist/assets/{tsMode-BmT8WogI.js → tsMode-DEjSThU9.js} +1 -1
- package/client/dist/assets/{typescript-CQrQPsoW.js → typescript-CRRnD2Ts.js} +1 -1
- package/client/dist/assets/upload-DYMjAQ6M.js +1 -0
- package/client/dist/assets/useBrowserCaptureSession-2OqX0vap.js +1 -0
- package/client/dist/assets/useDesktop-X8YB9xzv.js +1 -0
- package/client/dist/assets/useSharedWebSocket-DaPh7QBL.js +1 -0
- package/client/dist/assets/utils-BC4DHlz2.js +1 -0
- package/client/dist/assets/value-s-gzBeyF.js +1 -0
- package/client/dist/assets/{workers-U0UXyLUf.js → workers-uOo7WLaa.js} +1 -1
- package/client/dist/assets/x-CBMI9ejn.js +1 -0
- package/client/dist/assets/{xml-WRxJs8_I.js → xml-EDREH2X7.js} +1 -1
- package/client/dist/assets/{yaml-CjpSTjJU.js → yaml-BjaDVX1E.js} +1 -1
- package/client/dist/assets/zap-CHo3aCD1.js +1 -0
- package/client/dist/index.html +73 -32
- package/docs/guide/en/integrations/5-mcp-server.md +2 -2
- package/docs/internals/safe-pr-review-flow.md +190 -132
- package/docs/mcp.md +15 -5
- package/package.json +11 -7
- package/server/dist/active-pr-continuation.js +16 -1
- package/server/dist/agent-chat-manager.js +360 -181
- package/server/dist/agent-cwd-manager.js +52 -8
- package/server/dist/agent-generator.js +90 -48
- package/server/dist/agent-mcp-config.js +69 -36
- package/server/dist/agent-operator-prompt.js +3 -2
- package/server/dist/agent-refine-manager.js +47 -6
- package/server/dist/agent-tier.js +13 -11
- package/server/dist/artifact-registry.js +33 -2
- package/server/dist/auth.js +5 -4
- package/server/dist/browser-capture-manager.js +92 -29
- package/server/dist/chat-manager.js +302 -46
- package/server/dist/contract-refine-runner.js +19 -6
- package/server/dist/db.js +665 -8
- package/server/dist/desktop-router.js +1 -2
- package/server/dist/headroom-manager.js +1310 -121
- package/server/dist/headroom-routing.js +120 -2
- package/server/dist/index.js +48 -13
- package/server/dist/interactive-job-session.js +237 -82
- package/server/dist/jira/jira-adf.js +10 -0
- package/server/dist/jira/jira-db.js +15 -3
- package/server/dist/jira/jira-sync-manager.js +182 -13
- package/server/dist/job-listing.js +140 -0
- package/server/dist/job-spawn-idempotency.js +67 -0
- package/server/dist/loop-run-manager.js +508 -98
- package/server/dist/loop-runs-store.js +266 -13
- package/server/dist/mcp/agent-capability.js +71 -0
- package/server/dist/mcp/guide.js +5 -2
- package/server/dist/mcp/mcp-server.js +5 -10
- package/server/dist/mcp/mcp-token.js +5 -5
- package/server/dist/mcp/tools/jobs.js +20 -13
- package/server/dist/mcp/tools/types.js +20 -42
- package/server/dist/mobile/mobile-router.js +1 -1
- package/server/dist/pr-lifecycle.js +77 -0
- package/server/dist/pr-publisher.js +94 -27
- package/server/dist/process-admission.js +82 -0
- package/server/dist/project-registry.js +572 -174
- package/server/dist/project-router-git.js +13 -2
- package/server/dist/project-router-jobs.js +111 -55
- package/server/dist/project-router-tickets.js +32 -2
- package/server/dist/providers/claude-adapter.js +21 -13
- package/server/dist/queue-manager.js +2332 -623
- package/server/dist/rail-isolated-launch.js +1137 -290
- package/server/dist/rail-isolation.js +4 -2
- package/server/dist/rail-pr-decision.js +740 -266
- package/server/dist/rail-pr-delivery.js +2 -1
- package/server/dist/rail-pr-store.js +317 -18
- package/server/dist/rail-pr-ticket-effects.js +328 -0
- package/server/dist/rail-worktree-release.js +18 -2
- package/server/dist/rails-router.js +149 -119
- package/server/dist/rails-store.js +106 -3
- package/server/dist/ticket-store.js +23 -1
- package/server/dist/transient-children.js +13 -0
- package/server/dist/util/cli-prompt.js +42 -8
- package/server/dist/util/secure-token-file.js +84 -0
- package/server/dist/worktree-manager.js +6 -5
- package/client/dist/assets/ActivityFeedPage-D8RKRRoM.js +0 -1
- package/client/dist/assets/AgentBrowserCapture-BUHPHNSN.js +0 -1
- package/client/dist/assets/AgentModeCodePane-GZc8bNGT.js +0 -2
- package/client/dist/assets/AgentModeJobsPane-DWtQqj3k.js +0 -1
- package/client/dist/assets/AgentsPage-CKPtHkRP.js +0 -86
- package/client/dist/assets/AnalyticsPage-CZW8c77k.js +0 -1
- package/client/dist/assets/BarChart-DugEoe2q.js +0 -33
- package/client/dist/assets/CodePage-CQ-uKETG.js +0 -2
- package/client/dist/assets/DesktopAnalyticsPage-Bgo4SyCH.js +0 -1
- package/client/dist/assets/DocsDialog-DxpWw_La.js +0 -11
- package/client/dist/assets/DocsPage-CmcOkK19.js +0 -11
- package/client/dist/assets/ExportDropdown-DvTS2wc7.js +0 -1
- package/client/dist/assets/IntegrationsPage-B72WxJ3Z.js +0 -1
- package/client/dist/assets/InteractiveJobComposer-B5gh4Qoy.js +0 -19
- package/client/dist/assets/JobDetailModal-D-BeqsJ6.js +0 -1
- package/client/dist/assets/JobDetailPage-DwdozwLO.js +0 -1
- package/client/dist/assets/JobsPage-BDkinklt.js +0 -1
- package/client/dist/assets/LoopBuilderPage-CclJ3VZA.js +0 -7
- package/client/dist/assets/LoopPreviewModal-Dw4UgoLu.js +0 -1
- package/client/dist/assets/LoopsPage-D7ac4M24.js +0 -1
- package/client/dist/assets/PluginsPage-wdC-qa40.js +0 -2
- package/client/dist/assets/ProjectSettingsDialog-BlTlRtGI.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-CeMx2Red.js +0 -1
- package/client/dist/assets/activity-Dbl71S-E.js +0 -1
- package/client/dist/assets/addon-image-CwoZj17F.js +0 -1
- package/client/dist/assets/addon-ligatures-CWDrED80.js +0 -2
- package/client/dist/assets/addspec-DrXqP8ip.js +0 -1
- package/client/dist/assets/agent-B8BXmlUQ2.js +0 -1
- package/client/dist/assets/agent-BEXYxDbs.js +0 -1
- package/client/dist/assets/agent-BYc3-6Ln2.js +0 -1
- package/client/dist/assets/agent-BbJorPdT.js +0 -1
- package/client/dist/assets/agent-CewFImgL.js +0 -1
- package/client/dist/assets/agent-DfhYoUmp.js +0 -1
- package/client/dist/assets/agent-TK_rrK4d.js +0 -1
- package/client/dist/assets/agent-bHjA9R_j2.js +0 -1
- package/client/dist/assets/agents-CGDn_3kB.js +0 -1
- package/client/dist/assets/agentstudio-Dxe_94PK.js +0 -1
- package/client/dist/assets/aiedit-DrfHQnlz.js +0 -1
- package/client/dist/assets/analytics-CxDYcBl9.js +0 -1
- package/client/dist/assets/attachments-DvxQbBTC.js +0 -1
- package/client/dist/assets/brain-C92rjjYP.js +0 -1
- package/client/dist/assets/browser-DM8G151q.js +0 -1
- package/client/dist/assets/chat-0dg5hOo_.js +0 -1
- package/client/dist/assets/chunk-BNv3lrIs.js +0 -1
- package/client/dist/assets/code-B62fClk-.js +0 -1
- package/client/dist/assets/commands-DCJ-R9E4.js +0 -1
- package/client/dist/assets/common-BM3LFLCE2.js +0 -1
- package/client/dist/assets/common-Bwafujnc2.js +0 -1
- package/client/dist/assets/common-C-vU-j-I.js +0 -1
- package/client/dist/assets/common-C5HKSbbz.js +0 -1
- package/client/dist/assets/common-DXrmj6WD.js +0 -1
- package/client/dist/assets/common-DhWTkocg.js +0 -1
- package/client/dist/assets/common-DiPTT7V_2.js +0 -1
- package/client/dist/assets/common-DmqBPwZG.js +0 -1
- package/client/dist/assets/dashboard-B5ang-ZS.js +0 -1
- package/client/dist/assets/dashboard-CHHcOWVL2.js +0 -1
- package/client/dist/assets/dashboard-DBD4wPGY2.js +0 -1
- package/client/dist/assets/dashboard-DBuLfWo9.js +0 -1
- package/client/dist/assets/dashboard-DSnztwWH.js +0 -1
- package/client/dist/assets/dashboard-D_aP_InU.js +0 -1
- package/client/dist/assets/dashboard-DmBcdl1B.js +0 -1
- package/client/dist/assets/dashboard-RIcUKY1Y2.js +0 -1
- package/client/dist/assets/editor.api2-C3B0tp2x.js +0 -872
- package/client/dist/assets/editor.worker-Bd9IXS8d.js +0 -26
- package/client/dist/assets/explore-_XyU5e7R.js +0 -1
- package/client/dist/assets/index-BVbgKXUu.css +0 -2
- package/client/dist/assets/index-DyY1QyIn.js +0 -158
- package/client/dist/assets/integrations-By80EBkf.js +0 -1
- package/client/dist/assets/jira-DeIjwCK3.js +0 -1
- package/client/dist/assets/jobs-1GV2-4zd2.js +0 -1
- package/client/dist/assets/jobs-26F9gGcE.js +0 -1
- package/client/dist/assets/jobs-BgQzB0Sx2.js +0 -1
- package/client/dist/assets/jobs-BqWui9_f.js +0 -1
- package/client/dist/assets/jobs-CE8qeIYj.js +0 -1
- package/client/dist/assets/jobs-DrYmsKy5.js +0 -1
- package/client/dist/assets/jobs-DzeuASRf.js +0 -1
- package/client/dist/assets/jobs-Wh7dn28q2.js +0 -1
- package/client/dist/assets/json.worker-DzV-CpCQ.js +0 -58
- package/client/dist/assets/lib-0PFDyMxJ.js +0 -7
- package/client/dist/assets/loops-CO6q75W8.js +0 -1
- package/client/dist/assets/mcp-HwBbQDk0.js +0 -1
- package/client/dist/assets/monaco.contribution-Desq7oq4.js +0 -2
- package/client/dist/assets/nav-BTg8s-cD.js +0 -1
- package/client/dist/assets/preload-helper-DSXbuxSR.js +0 -1
- package/client/dist/assets/settings-cJNynGIZ.js +0 -1
- package/client/dist/assets/setup-D2CmgAro.js +0 -1
- package/client/dist/assets/specs-PGIOzMtc.js +0 -1
- package/client/dist/assets/terminal-CFt56A5f.js +0 -1
- package/client/dist/assets/tickets-B7zT4IRf.js +0 -1
- package/client/dist/assets/tickets-BHv3ewvq.js +0 -1
- package/client/dist/assets/tickets-BoXom7l8.js +0 -1
- package/client/dist/assets/tickets-C3b8cHhd2.js +0 -1
- package/client/dist/assets/tickets-CFXnJsYF.js +0 -1
- package/client/dist/assets/tickets-CWYFvx1W2.js +0 -1
- package/client/dist/assets/tickets-DTHo9c3C.js +0 -1
- package/client/dist/assets/tickets-SuX_HkK4.js +0 -1
- package/client/dist/assets/upload-CmR4h9H1.js +0 -1
- package/client/dist/assets/value-JA3bf47v.js +0 -1
- /package/client/dist/assets/{abap-D0E4FfVW.js → abap-08VXUWAP.js} +0 -0
- /package/client/dist/assets/{activity--iN4Ad2R2.js → activity-B-rp4h_v.js} +0 -0
- /package/client/dist/assets/{activity-6hM_ZSg9.js → activity-C6kJpm9q2.js} +0 -0
- /package/client/dist/assets/{activity-CIOLxGvh.js → activity-CGx4kb_j.js} +0 -0
- /package/client/dist/assets/{activity-BZNfOdiS.js → activity-CnoX2bH7.js} +0 -0
- /package/client/dist/assets/{activity-BuVo5sPB.js → activity-DAyekdlt2.js} +0 -0
- /package/client/dist/assets/{activity-D2r2h3SE2.js → activity-DCxtpT7j.js} +0 -0
- /package/client/dist/assets/{activity-DtxrAUMo2.js → activity-JzwRaWPD.js} +0 -0
- /package/client/dist/assets/{addspec-BMtLZLW62.js → addspec-BXjvkh5q.js} +0 -0
- /package/client/dist/assets/{addspec-CRuvWn8d.js → addspec-C4bwrmpi.js} +0 -0
- /package/client/dist/assets/{addspec-D-JLDsUV.js → addspec-CArHXUDM.js} +0 -0
- /package/client/dist/assets/{addspec-mgPOt5q02.js → addspec-DTKJcXiR.js} +0 -0
- /package/client/dist/assets/{addspec-DfhDbg5c.js → addspec-DbNBbrha2.js} +0 -0
- /package/client/dist/assets/{addspec-CP0HRuxY.js → addspec-htHGZHOe2.js} +0 -0
- /package/client/dist/assets/{addspec-bsNDdqRm2.js → addspec-lr_LP6jk.js} +0 -0
- /package/client/dist/assets/{agents-78bTZGZh.js → agents-6ZOJXWVk2.js} +0 -0
- /package/client/dist/assets/{agents-DDtCJ5M02.js → agents-BZ0XQKjg.js} +0 -0
- /package/client/dist/assets/{agents-CWo-KyAB.js → agents-BqWklnHR.js} +0 -0
- /package/client/dist/assets/{agents-BUdEi3-N.js → agents-Bz-thHkB2.js} +0 -0
- /package/client/dist/assets/{agents-bvyRLczL.js → agents-CJclElx6.js} +0 -0
- /package/client/dist/assets/{agents-BpoUaoW92.js → agents-D_l9L0VA.js} +0 -0
- /package/client/dist/assets/{agents-BLhWCmDS2.js → agents-u_C0jPB5.js} +0 -0
- /package/client/dist/assets/{agentstudio-BMQxc_bz.js → agentstudio-BF19hbXj.js} +0 -0
- /package/client/dist/assets/{agentstudio-C_Jq8Tl4.js → agentstudio-BLGvxWBx2.js} +0 -0
- /package/client/dist/assets/{agentstudio-Dtq3dGYk.js → agentstudio-D9UKiWTj2.js} +0 -0
- /package/client/dist/assets/{agentstudio-CWgXG5W52.js → agentstudio-Drs37nIM.js} +0 -0
- /package/client/dist/assets/{agentstudio-sI27TpBu2.js → agentstudio-nEnKkcXM.js} +0 -0
- /package/client/dist/assets/{agentstudio-jKMTdT70.js → agentstudio-pyFcSBKy.js} +0 -0
- /package/client/dist/assets/{agentstudio-COZriimI2.js → agentstudio-zfTzQh3U.js} +0 -0
- /package/client/dist/assets/{aiedit-hknZlly82.js → aiedit-B0KUbpB6.js} +0 -0
- /package/client/dist/assets/{aiedit-eMN4eQPF.js → aiedit-BoG24nlg2.js} +0 -0
- /package/client/dist/assets/{aiedit-pc5JDUq1.js → aiedit-CjlSRQVP.js} +0 -0
- /package/client/dist/assets/{aiedit-B03pD_Na.js → aiedit-DCjA7jLJ.js} +0 -0
- /package/client/dist/assets/{aiedit-6CHdb_iv2.js → aiedit-DNXxq_Dr.js} +0 -0
- /package/client/dist/assets/{aiedit-igmw-ZoZ2.js → aiedit-DQ-0_pvE.js} +0 -0
- /package/client/dist/assets/{aiedit-BtRqRxML.js → aiedit-olnv3QSt2.js} +0 -0
- /package/client/dist/assets/{analytics-CCprAqk3.js → analytics-BC7mwR4s.js} +0 -0
- /package/client/dist/assets/{analytics-CR1TJqUi2.js → analytics-BiNg8edP.js} +0 -0
- /package/client/dist/assets/{analytics-Bj-SmKHa.js → analytics-C0s_T2GN.js} +0 -0
- /package/client/dist/assets/{analytics-C5dpiS0o.js → analytics-CdsAPAwZ2.js} +0 -0
- /package/client/dist/assets/{analytics-BWMUZ2Pq2.js → analytics-Cr1_q-lC.js} +0 -0
- /package/client/dist/assets/{analytics-DfNdXR0y2.js → analytics-IEsKZvpW2.js} +0 -0
- /package/client/dist/assets/{analytics-rmHuJv0V.js → analytics-SRPf84OT.js} +0 -0
- /package/client/dist/assets/{apex-D_X_8J4d.js → apex-BWPQTe0t.js} +0 -0
- /package/client/dist/assets/{atom-one-dark-BMDPFwRZ.js → atom-one-dark-qiGhVQxg.js} +0 -0
- /package/client/dist/assets/{attachments-BgBNSqQd.js → attachments-D0wXXt9T2.js} +0 -0
- /package/client/dist/assets/{attachments-C9GTTJsx2.js → attachments-DenIjuVI.js} +0 -0
- /package/client/dist/assets/{attachments-BjyMmrwE.js → attachments-DhO7LPfy.js} +0 -0
- /package/client/dist/assets/{attachments-DPCmkFZu2.js → attachments-DoH5w0x4.js} +0 -0
- /package/client/dist/assets/{attachments-DCZOL1RU.js → attachments-Q9yNtiEM.js} +0 -0
- /package/client/dist/assets/{attachments-rNn1ekRy2.js → attachments-V_WZAYXx2.js} +0 -0
- /package/client/dist/assets/{attachments-DL-bPy9E.js → attachments-eaeScpjM.js} +0 -0
- /package/client/dist/assets/{auth-DpZq5p1e.js → auth-D_5bQv5W.js} +0 -0
- /package/client/dist/assets/{azcli-PZLnZ3jH.js → azcli-Bc_sGQ0U.js} +0 -0
- /package/client/dist/assets/{bat-KlBOxpO4.js → bat-i0X4ZdIN.js} +0 -0
- /package/client/dist/assets/{bicep-CgG9oF3V.js → bicep-B5-_aFwp.js} +0 -0
- /package/client/dist/assets/{browser-BPkhILSJ.js → browser-C25DkQiO.js} +0 -0
- /package/client/dist/assets/{browser-BqLqJVVT.js → browser-CiJ7-L32.js} +0 -0
- /package/client/dist/assets/{browser-rWKj-RR_2.js → browser-CvgDySd9.js} +0 -0
- /package/client/dist/assets/{browser-Cvg-QHvd2.js → browser-CyR1Kp3m2.js} +0 -0
- /package/client/dist/assets/{browser-FRv8P02E.js → browser-DwZljcRh2.js} +0 -0
- /package/client/dist/assets/{browser-Cc0iDqUi.js → browser-DxP-alBK.js} +0 -0
- /package/client/dist/assets/{browser-BijVYG0Y2.js → browser-rRCwi7Y7.js} +0 -0
- /package/client/dist/assets/{cameligo-6R45j6Ph.js → cameligo-DMUM7wLl.js} +0 -0
- /package/client/dist/assets/{chat-Bom6bqK3.js → chat-2rIB8HhT.js} +0 -0
- /package/client/dist/assets/{chat-vylxqR0P2.js → chat-BR8oe53x2.js} +0 -0
- /package/client/dist/assets/{chat-Dnwg-NpQ.js → chat-Bua03tty.js} +0 -0
- /package/client/dist/assets/{chat-BgL3zv6x2.js → chat-CZpAQan1.js} +0 -0
- /package/client/dist/assets/{chat-DsbwJhus.js → chat-CdY5SGvS.js} +0 -0
- /package/client/dist/assets/{chat-Bh3NdgJs.js → chat-DZyu3ImY2.js} +0 -0
- /package/client/dist/assets/{chat-DdwZjT8K2.js → chat-D_I7nU5K.js} +0 -0
- /package/client/dist/assets/{clojure-CLAAMO7J.js → clojure-Cm7r79vr.js} +0 -0
- /package/client/dist/assets/{clsx-CNmW0Nip.js → clsx-CjueKrWZ.js} +0 -0
- /package/client/dist/assets/{code-UPVvmAKM2.js → code-76gS0IdL.js} +0 -0
- /package/client/dist/assets/{code-BRcDlze52.js → code-C1VwxlML.js} +0 -0
- /package/client/dist/assets/{code-CeQ0FZpC.js → code-CnMZyJpr.js} +0 -0
- /package/client/dist/assets/{code-DgeHz9CB.js → code-D20ef9Cf2.js} +0 -0
- /package/client/dist/assets/{code-DzC1rMk12.js → code-DdOXaWRY.js} +0 -0
- /package/client/dist/assets/{code-CwnBPPyA.js → code-Duohuf9l.js} +0 -0
- /package/client/dist/assets/{code-BdMy3Ffq.js → code-geHmknB92.js} +0 -0
- /package/client/dist/assets/{coffee-AruSygo-.js → coffee-Ba7i2nA0.js} +0 -0
- /package/client/dist/assets/{commands-DkU0qliq.js → commands-B9RZO4qS.js} +0 -0
- /package/client/dist/assets/{commands-u-4eEHCN.js → commands-BEwbAjtD2.js} +0 -0
- /package/client/dist/assets/{commands-JWMfGDmr.js → commands-Bbxu8kLU.js} +0 -0
- /package/client/dist/assets/{commands-C4Y3C-PF2.js → commands-CDU3qv68.js} +0 -0
- /package/client/dist/assets/{commands-xHDDGfcP2.js → commands-CzezgtQj.js} +0 -0
- /package/client/dist/assets/{commands-CSDs9q_2.js → commands-DTQBF_q52.js} +0 -0
- /package/client/dist/assets/{commands-RHOchN4n2.js → commands-hOtKuOXc.js} +0 -0
- /package/client/dist/assets/{cpp-DEE5r4Pw.js → cpp-C7h46wYY.js} +0 -0
- /package/client/dist/assets/{csharp-BVXJUvbK.js → csharp-BKxtCVv1.js} +0 -0
- /package/client/dist/assets/{csp-cXOF0JoV.js → csp-bTuwJoIa.js} +0 -0
- /package/client/dist/assets/{css-C_zQuAoy.js → css-DIMkf-bt.js} +0 -0
- /package/client/dist/assets/{cypher-t95m_Q-O.js → cypher-CVaqCwHa.js} +0 -0
- /package/client/dist/assets/{dart-B4r1Tg-e.js → dart-onAF5SnQ.js} +0 -0
- /package/client/dist/assets/{dockerfile-6mt7zHQw.js → dockerfile-DZFCIeNp.js} +0 -0
- /package/client/dist/assets/{ecl-DEY_Fz_Q.js → ecl-D05T4iGw.js} +0 -0
- /package/client/dist/assets/{elixir-R6-q1c5t.js → elixir-6RTg0lbw.js} +0 -0
- /package/client/dist/assets/{explore-B7fblXBb2.js → explore-BG99VGpL.js} +0 -0
- /package/client/dist/assets/{explore-CNBzrEPS.js → explore-C0TjprJW2.js} +0 -0
- /package/client/dist/assets/{explore-C0k8HrkN.js → explore-C2dgEo7q.js} +0 -0
- /package/client/dist/assets/{explore-BHtEpoNN2.js → explore-C8NDHbob.js} +0 -0
- /package/client/dist/assets/{explore-Dahc1lOx.js → explore-CWWgzQGR.js} +0 -0
- /package/client/dist/assets/{explore-D_mWBYCS2.js → explore-CcQXpdtS.js} +0 -0
- /package/client/dist/assets/{explore-UvW1jEMa.js → explore-DsXAgnmJ2.js} +0 -0
- /package/client/dist/assets/{flow9-CNQvHBOt.js → flow9-C5_-GSwl.js} +0 -0
- /package/client/dist/assets/{format-command-CuWuRvka.js → format-command-Dfa1o2d-.js} +0 -0
- /package/client/dist/assets/{fsharp-vPZkFXwv.js → fsharp-C8Ef5oNN.js} +0 -0
- /package/client/dist/assets/{go-KUzBMYJ_.js → go-C-y9NEjX.js} +0 -0
- /package/client/dist/assets/{graphql-C3W0rCsD.js → graphql-fmXr3nnJ.js} +0 -0
- /package/client/dist/assets/{hcl-BOOMFSNX.js → hcl-CpzslTdj.js} +0 -0
- /package/client/dist/assets/{ini-CFat1WJr.js → ini-sBoK_t0W.js} +0 -0
- /package/client/dist/assets/{integrations-TAcYNh0b.js → integrations-CH0zy1Mw2.js} +0 -0
- /package/client/dist/assets/{integrations-C1ThkTC52.js → integrations-CIAjhd7L.js} +0 -0
- /package/client/dist/assets/{integrations-2YcNyAl3.js → integrations-CZdwOe8A.js} +0 -0
- /package/client/dist/assets/{integrations-BXCvXUlG2.js → integrations-Ct7omMld.js} +0 -0
- /package/client/dist/assets/{integrations-Crk2rFhL2.js → integrations-D6MSxy3-.js} +0 -0
- /package/client/dist/assets/{integrations-Dn4JiGtW.js → integrations-DbZa-Zyc.js} +0 -0
- /package/client/dist/assets/{integrations-D9YrJ_Ep.js → integrations-b8pH-vfl2.js} +0 -0
- /package/client/dist/assets/{java-Dee-CNAM.js → java-BEtHBSE6.js} +0 -0
- /package/client/dist/assets/{jira-D5efgwp_.js → jira-BySfUuQZ.js} +0 -0
- /package/client/dist/assets/{jira-bDs7Im2C.js → jira-Byw2C0F8.js} +0 -0
- /package/client/dist/assets/{jira-BHAKtc4B.js → jira-CNeYi06w2.js} +0 -0
- /package/client/dist/assets/{jira-Be369RIR2.js → jira-D11gPhxz.js} +0 -0
- /package/client/dist/assets/{jira-CPpgA-5z2.js → jira-D38Gj7AT.js} +0 -0
- /package/client/dist/assets/{jira-uNH1gETL2.js → jira-gAvOyije.js} +0 -0
- /package/client/dist/assets/{jira-DS7ownDy.js → jira-pRaL7JQo2.js} +0 -0
- /package/client/dist/assets/{julia-CGNlLYuU.js → julia-Bri6UV-V.js} +0 -0
- /package/client/dist/assets/{kotlin-CY7lhknJ.js → kotlin-BOotOW0E.js} +0 -0
- /package/client/dist/assets/{less-D0KN7hfJ.js → less-B9JPFI3C.js} +0 -0
- /package/client/dist/assets/{lexon-CuRJufPs.js → lexon-CfSJPG6W.js} +0 -0
- /package/client/dist/assets/{loops-LIgO_-eM.js → loops-CPpgMapU.js} +0 -0
- /package/client/dist/assets/{loops-DJNhp17p2.js → loops-CWWbKuVL.js} +0 -0
- /package/client/dist/assets/{loops-D6D3XHjj.js → loops-Crc9It8o2.js} +0 -0
- /package/client/dist/assets/{loops-CvOWsi5r.js → loops-DH2hZcbK2.js} +0 -0
- /package/client/dist/assets/{loops-vhJdbcz92.js → loops-DS5TmvIU2.js} +0 -0
- /package/client/dist/assets/{loops-K5_HXfJa.js → loops-P2f0cN8-.js} +0 -0
- /package/client/dist/assets/{loops-BvqQ-bHR2.js → loops-fJV-9hH7.js} +0 -0
- /package/client/dist/assets/{lua-DHvH62Zf.js → lua-CsQS60Ue.js} +0 -0
- /package/client/dist/assets/{m3-tlthQ8Fo.js → m3-D-oSqn_W.js} +0 -0
- /package/client/dist/assets/{markdown-Dbx1LgUZ.js → markdown-Cimd5fb3.js} +0 -0
- /package/client/dist/assets/{mcp-Dcl-XxkR.js → mcp-Czd_iyp02.js} +0 -0
- /package/client/dist/assets/{mcp-XJNRfxVp2.js → mcp-D2pc5GGx.js} +0 -0
- /package/client/dist/assets/{mcp-sRmWE8WJ.js → mcp-D4ZjtIx32.js} +0 -0
- /package/client/dist/assets/{mcp-DtbxuH3V2.js → mcp-DIfBUv2D2.js} +0 -0
- /package/client/dist/assets/{mcp-H-gnQUdN.js → mcp-DsTwmjST.js} +0 -0
- /package/client/dist/assets/{mcp-N1nzp8oL.js → mcp-SMkn8boG.js} +0 -0
- /package/client/dist/assets/{mcp-CjVKXAhD2.js → mcp-fSfiVYAc.js} +0 -0
- /package/client/dist/assets/{mips-D_O3IUz_.js → mips-CIPQ_RoX.js} +0 -0
- /package/client/dist/assets/{msdax-C_ZmCnRt.js → msdax-DauUninz.js} +0 -0
- /package/client/dist/assets/{mysql-C-dCEYSX.js → mysql-SOo6toE5.js} +0 -0
- /package/client/dist/assets/{nav-BElxljIF2.js → nav-BRJBxaGt2.js} +0 -0
- /package/client/dist/assets/{nav-COu2E_-L.js → nav-CAGKv3_F.js} +0 -0
- /package/client/dist/assets/{nav-BXO7PJJG2.js → nav-CNskRJKQ2.js} +0 -0
- /package/client/dist/assets/{nav-CW9pxWfC2.js → nav-DY93irBL.js} +0 -0
- /package/client/dist/assets/{nav-CXHUA4uf.js → nav-DhjvYgqt.js} +0 -0
- /package/client/dist/assets/{nav-DdHF5VWJ.js → nav-Gb-r4wop.js} +0 -0
- /package/client/dist/assets/{nav-C8kOuGh8.js → nav-TvumAFD22.js} +0 -0
- /package/client/dist/assets/{objective-c-DZY9Aqxd.js → objective-c-FvmIjYaQ.js} +0 -0
- /package/client/dist/assets/{pascal-BVHLXoor.js → pascal-DrH0SRf2.js} +0 -0
- /package/client/dist/assets/{pascaligo-XdG_6u3k.js → pascaligo-D-ptJ9y-.js} +0 -0
- /package/client/dist/assets/{perl-b9QV0BcD.js → perl-oz_6vUea.js} +0 -0
- /package/client/dist/assets/{pgsql-Du5FDOgT.js → pgsql-DTj74zXo.js} +0 -0
- /package/client/dist/assets/{php-WJD0z2Z_.js → php-nr791fC2.js} +0 -0
- /package/client/dist/assets/{pla-CbciTf2w.js → pla-CopQ2nXW.js} +0 -0
- /package/client/dist/assets/{postiats-C1QxmvVq.js → postiats-43DmfD33.js} +0 -0
- /package/client/dist/assets/{powerquery-BEYDhPLZ.js → powerquery-D3hlyOfw.js} +0 -0
- /package/client/dist/assets/{powershell-CfaYw4XL.js → powershell-DmHpPYUd.js} +0 -0
- /package/client/dist/assets/{protobuf-pwZEDOYa.js → protobuf-C531GsRP.js} +0 -0
- /package/client/dist/assets/{pug-5ftqSPVe.js → pug-Z5eAx3Zn.js} +0 -0
- /package/client/dist/assets/{qsharp-uI6qZMq4.js → qsharp-DkqhCAOL.js} +0 -0
- /package/client/dist/assets/{r-BK54P1EG.js → r-BwWrilGY.js} +0 -0
- /package/client/dist/assets/{redis-DI0yd7kN.js → redis-ClamHrr6.js} +0 -0
- /package/client/dist/assets/{redshift-Dov26eyI.js → redshift-DT7zqm-g.js} +0 -0
- /package/client/dist/assets/{restructuredtext-QqJ_1dC-.js → restructuredtext-BYgofb2h.js} +0 -0
- /package/client/dist/assets/{ruby-5WjveeVb.js → ruby-DezsRK8O.js} +0 -0
- /package/client/dist/assets/{rust-BzVSfqFH.js → rust-DdL9SqIa.js} +0 -0
- /package/client/dist/assets/{sb-DMIUfT-3.js → sb-CcwsVR0C.js} +0 -0
- /package/client/dist/assets/{scala-CSKA7lqt.js → scala-DHpiXF5c.js} +0 -0
- /package/client/dist/assets/{scheme-CojOP2Qc.js → scheme-BeGwcela.js} +0 -0
- /package/client/dist/assets/{scss-DoBZGJG6.js → scss-gp-XZpBa.js} +0 -0
- /package/client/dist/assets/{settings-1w1O5Og82.js → settings-B9ilOSYO.js} +0 -0
- /package/client/dist/assets/{settings-Jc46vUhd.js → settings-BqlWHRHA.js} +0 -0
- /package/client/dist/assets/{settings-BSihkr0e.js → settings-D8dlqt4d.js} +0 -0
- /package/client/dist/assets/{settings-BiDOB3hV2.js → settings-DAZqjX2a2.js} +0 -0
- /package/client/dist/assets/{settings-DNL68KTE.js → settings-DRh39v1S2.js} +0 -0
- /package/client/dist/assets/{settings-BoubJhDP2.js → settings-Mj1OJbfd2.js} +0 -0
- /package/client/dist/assets/{settings-CB-EZ_W8.js → settings-lRReaSYm.js} +0 -0
- /package/client/dist/assets/{setup-D84y3wx-.js → setup-CGZjy2IF2.js} +0 -0
- /package/client/dist/assets/{setup-BuQNKztt2.js → setup-CXa0Sx16.js} +0 -0
- /package/client/dist/assets/{setup-BwMovM9z2.js → setup-D2ktKKMF2.js} +0 -0
- /package/client/dist/assets/{setup-DU0ULX8q2.js → setup-DG2H7ha5.js} +0 -0
- /package/client/dist/assets/{setup-BXpw07r-.js → setup-DK5bHDUq2.js} +0 -0
- /package/client/dist/assets/{setup-7J8TjG4O.js → setup-DrWlIQTZ.js} +0 -0
- /package/client/dist/assets/{setup-BiduSX9d.js → setup-rzHUVgAu.js} +0 -0
- /package/client/dist/assets/{shell-YOaE4udl.js → shell-CC2rA5mh.js} +0 -0
- /package/client/dist/assets/{solidity-l3jSyVLu.js → solidity-BEEn4gHE.js} +0 -0
- /package/client/dist/assets/{sophia-BhSGTaQJ.js → sophia-CRfGWb83.js} +0 -0
- /package/client/dist/assets/{sparql-qhk6QD9w.js → sparql-D_Lu-MrJ.js} +0 -0
- /package/client/dist/assets/{specs-CWtKcVDC.js → specs-BcyxaCwf2.js} +0 -0
- /package/client/dist/assets/{specs-DTplt2XM.js → specs-C1q9geAi.js} +0 -0
- /package/client/dist/assets/{specs-PozUNhqb.js → specs-CLZKysq-2.js} +0 -0
- /package/client/dist/assets/{specs-C9Sd7BhH2.js → specs-DZMS9o54.js} +0 -0
- /package/client/dist/assets/{specs-D2clrt2c2.js → specs-Dm6zUI-R.js} +0 -0
- /package/client/dist/assets/{specs-CBDvEYKv.js → specs-V3TvhKLX.js} +0 -0
- /package/client/dist/assets/{specs-CIPr9reA2.js → specs-dV3Mq5Mu2.js} +0 -0
- /package/client/dist/assets/{sql-DoO3y21-.js → sql-NEE52Syq.js} +0 -0
- /package/client/dist/assets/{st-ChZAY8oG.js → st-DbInun42.js} +0 -0
- /package/client/dist/assets/{swift-D290Tr7_.js → swift-Bxkupp3x.js} +0 -0
- /package/client/dist/assets/{systemverilog-DsxzgJ-o.js → systemverilog-Bz4Y3fRF.js} +0 -0
- /package/client/dist/assets/{tcl-BKDUHAAR.js → tcl-DISqw1ZD.js} +0 -0
- /package/client/dist/assets/{terminal-De2Brd23.js → terminal-BZiIu3rf.js} +0 -0
- /package/client/dist/assets/{terminal-cqfd3EDV.js → terminal-C2NAd7BW2.js} +0 -0
- /package/client/dist/assets/{terminal-WSAvHXoU.js → terminal-IahE4LW92.js} +0 -0
- /package/client/dist/assets/{terminal-Z7KsDM_n.js → terminal-QpXZU2Bn2.js} +0 -0
- /package/client/dist/assets/{terminal-xGbCZJHg.js → terminal-ScjyXNxQ.js} +0 -0
- /package/client/dist/assets/{terminal-BRm03iFA2.js → terminal-d7GadXbN.js} +0 -0
- /package/client/dist/assets/{terminal-DxQWCqSD2.js → terminal-tITa81iz.js} +0 -0
- /package/client/dist/assets/{twig-UpZQaM8G.js → twig-De2hgUGE.js} +0 -0
- /package/client/dist/assets/{typespec-DzaKvN8i.js → typespec-B8J7ngcE.js} +0 -0
- /package/client/dist/assets/{vb-CZpbb7tk.js → vb-DV3o63ZY.js} +0 -0
- /package/client/dist/assets/{wgsl-CNt3OIvl.js → wgsl-DpFanUEy.js} +0 -0
|
@@ -7,6 +7,7 @@ exports.LoopRunManager = void 0;
|
|
|
7
7
|
exports.truncate = truncate;
|
|
8
8
|
exports.resolveRunVars = resolveRunVars;
|
|
9
9
|
exports.extractChangeId = extractChangeId;
|
|
10
|
+
exports.recoverOrphanLoopStepAccounting = recoverOrphanLoopStepAccounting;
|
|
10
11
|
const tree_kill_1 = __importDefault(require("tree-kill"));
|
|
11
12
|
const db_1 = require("./db");
|
|
12
13
|
const interactive_job_session_1 = require("./interactive-job-session");
|
|
@@ -17,6 +18,9 @@ const loop_decider_1 = require("./loop-decider");
|
|
|
17
18
|
const loop_runs_store_1 = require("./loop-runs-store");
|
|
18
19
|
const ai_invocations_1 = require("./ai-invocations");
|
|
19
20
|
const ids_1 = require("./ids");
|
|
21
|
+
const providers_1 = require("./providers");
|
|
22
|
+
const result_event_1 = require("./result-event");
|
|
23
|
+
const rails_store_1 = require("./rails-store");
|
|
20
24
|
const IMPLEMENT_CMD_TOKEN_RE = /\{\{\s*cmd:(?:implement|batch)\s*\}\}/;
|
|
21
25
|
const IMPLEMENT_CMD_TEXT_RE = /(?:^|\s)(?:\/(?:specrails:|sr:)?(?:implement|batch-implement)|\$(?:implement|batch-implement))\b/;
|
|
22
26
|
const LOOP_BLOCKED_LINE_RE = /(?:^|\n)\s*LOOP_BLOCKED:\s*(.+?)(?:\n|$)/i;
|
|
@@ -93,11 +97,211 @@ function extractChangeId(text) {
|
|
|
93
97
|
const m = CHANGE_ID_RE.exec(text);
|
|
94
98
|
return m ? m[1] : undefined;
|
|
95
99
|
}
|
|
100
|
+
function insertLoopInvocation(db, payload, result, finishedAt) {
|
|
101
|
+
const ticketIds = Array.isArray(payload.ticketIds)
|
|
102
|
+
? [...new Set(payload.ticketIds)]
|
|
103
|
+
: (payload.ticketId == null ? [] : [payload.ticketId]);
|
|
104
|
+
const targets = ticketIds.length > 0 ? ticketIds : [null];
|
|
105
|
+
const splitInt = (value) => {
|
|
106
|
+
if (value === undefined)
|
|
107
|
+
return targets.map(() => undefined);
|
|
108
|
+
const base = Math.floor(value / targets.length);
|
|
109
|
+
const remainder = value - base * targets.length;
|
|
110
|
+
return targets.map((_target, index) => base + (index < remainder ? 1 : 0));
|
|
111
|
+
};
|
|
112
|
+
const tokensIn = splitInt(result.tokensIn);
|
|
113
|
+
const tokensOut = splitInt(result.tokensOut ?? (result.tokensIn === undefined && result.tokensOut === undefined
|
|
114
|
+
? result.tokens
|
|
115
|
+
: undefined));
|
|
116
|
+
const cacheRead = splitInt(result.tokensCacheRead);
|
|
117
|
+
const cacheCreate = splitInt(result.tokensCacheCreate);
|
|
118
|
+
const turns = splitInt(result.numTurns);
|
|
119
|
+
targets.forEach((ticketId, index) => {
|
|
120
|
+
const suffix = ticketId == null || targets.length === 1 ? '' : `:t${ticketId}`;
|
|
121
|
+
(0, ai_invocations_1.recordInvocation)(db, {
|
|
122
|
+
id: `${payload.invocationId}${suffix}`,
|
|
123
|
+
project_id: payload.projectId,
|
|
124
|
+
provider: result.provider ?? payload.provider,
|
|
125
|
+
surface: 'loop',
|
|
126
|
+
surface_ref_id: `${payload.surfaceRefId}${suffix}`,
|
|
127
|
+
ticket_id: ticketId,
|
|
128
|
+
status: result.failed ? 'failed' : 'success',
|
|
129
|
+
started_at: payload.startedAt,
|
|
130
|
+
finished_at: finishedAt,
|
|
131
|
+
total_cost_usd: result.cost === undefined ? undefined : result.cost / targets.length,
|
|
132
|
+
total_cost_usd_estimated: result.estimated ?? false,
|
|
133
|
+
tokens_in: tokensIn[index],
|
|
134
|
+
tokens_out: tokensOut[index],
|
|
135
|
+
tokens_cache_read: cacheRead[index],
|
|
136
|
+
tokens_cache_create: cacheCreate[index],
|
|
137
|
+
num_turns: turns[index],
|
|
138
|
+
session_id: result.sessionId,
|
|
139
|
+
duration_ms: result.durationMs === undefined ? undefined : result.durationMs / targets.length,
|
|
140
|
+
duration_api_ms: result.durationApiMs === undefined ? undefined : result.durationApiMs / targets.length,
|
|
141
|
+
model: result.model ?? payload.model ?? undefined,
|
|
142
|
+
loop_run_id: payload.runId,
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
const aggregate = db.prepare(`
|
|
146
|
+
SELECT COALESCE(SUM(total_cost_usd), 0) AS cost,
|
|
147
|
+
COALESCE(SUM(tokens_in), 0) AS tokens_in,
|
|
148
|
+
COALESCE(SUM(tokens_out), 0) AS tokens_out,
|
|
149
|
+
COALESCE(SUM(tokens_cache_read), 0) AS cache_read,
|
|
150
|
+
COALESCE(SUM(tokens_cache_create), 0) AS cache_create,
|
|
151
|
+
COALESCE(SUM(duration_ms), 0) AS duration,
|
|
152
|
+
COALESCE(SUM(num_turns), 0) AS turns
|
|
153
|
+
FROM ai_invocations
|
|
154
|
+
WHERE surface = 'loop' AND loop_run_id = ?
|
|
155
|
+
`).get(payload.runId);
|
|
156
|
+
db.prepare(`
|
|
157
|
+
UPDATE jobs
|
|
158
|
+
SET total_cost_usd = ?, tokens_in = ?, tokens_out = ?,
|
|
159
|
+
tokens_cache_read = ?, tokens_cache_create = ?,
|
|
160
|
+
duration_ms = ?, num_turns = ?
|
|
161
|
+
WHERE id = ? AND owner = 'loop'
|
|
162
|
+
`).run(aggregate.cost, aggregate.tokens_in, aggregate.tokens_out, aggregate.cache_read, aggregate.cache_create, aggregate.duration, aggregate.turns, payload.runId);
|
|
163
|
+
}
|
|
164
|
+
/** Recover every AI/decider step that was staged before provider spawn but did
|
|
165
|
+
* not atomically land its invocation. Completed interactive turns are the jobs
|
|
166
|
+
* delta from the durable baseline; raw events after completedEventSeq are the
|
|
167
|
+
* unfinished/failed-to-checkpoint turn. The insert and checkpoint removal share
|
|
168
|
+
* one transaction, so a crash between them replays without duplication. */
|
|
169
|
+
function boundedInflightDurationMs(payload, finishedAt) {
|
|
170
|
+
const startedAt = payload.activeTurnStartedAtMs;
|
|
171
|
+
const lastActivityAt = payload.lastActivityAtMs;
|
|
172
|
+
if (!Number.isFinite(startedAt) || !Number.isFinite(lastActivityAt))
|
|
173
|
+
return 0;
|
|
174
|
+
const finishedAtMs = Date.parse(finishedAt);
|
|
175
|
+
const boundedEnd = Number.isFinite(finishedAtMs)
|
|
176
|
+
? Math.min(lastActivityAt, finishedAtMs)
|
|
177
|
+
: lastActivityAt;
|
|
178
|
+
return Math.max(0, boundedEnd - startedAt);
|
|
179
|
+
}
|
|
180
|
+
function recoverOrphanLoopStepAccounting(db, finishedAt = new Date().toISOString(), onlyRunId) {
|
|
181
|
+
let recovered = 0;
|
|
182
|
+
for (const row of (0, loop_runs_store_1.listLoopStepRecoveries)(db).filter((candidate) => !onlyRunId || candidate.run_id === onlyRunId)) {
|
|
183
|
+
let payload;
|
|
184
|
+
try {
|
|
185
|
+
payload = JSON.parse(row.payload);
|
|
186
|
+
if (!payload || payload.runId !== row.run_id || payload.stepKey !== row.step_key)
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (payload.settledResult) {
|
|
193
|
+
const settled = payload.settledResult;
|
|
194
|
+
const didRecover = (0, loop_runs_store_1.completeLoopStepRecovery)(db, payload.runId, payload.stepKey, (stable) => {
|
|
195
|
+
insertLoopInvocation(db, stable, settled, finishedAt);
|
|
196
|
+
const aggregate = (0, loop_runs_store_1.readLoopJobUsage)(db, payload.runId);
|
|
197
|
+
const recoveredLoopDuration = (payload.loopDurationBaseline ?? 0) + (settled.durationMs ?? 0);
|
|
198
|
+
db.prepare(`
|
|
199
|
+
UPDATE loop_runs
|
|
200
|
+
SET total_cost_usd = ?, total_tokens = ?, total_duration_ms = ?,
|
|
201
|
+
iteration_count = MAX(iteration_count, COALESCE(?, iteration_count))
|
|
202
|
+
WHERE id = ?
|
|
203
|
+
`).run(aggregate.totalCostUsd, aggregate.tokensIn + aggregate.tokensOut, recoveredLoopDuration, stable.iterationCount ?? null, payload.runId);
|
|
204
|
+
// ai_invocations contains AI-active duration only. The backing job is
|
|
205
|
+
// the whole loop and must retain shell duration already represented by
|
|
206
|
+
// loopDurationBaseline across this recovery transaction.
|
|
207
|
+
db.prepare(`UPDATE jobs SET duration_ms = ? WHERE id = ? AND owner = 'loop'`)
|
|
208
|
+
.run(recoveredLoopDuration, payload.runId);
|
|
209
|
+
});
|
|
210
|
+
if (didRecover)
|
|
211
|
+
recovered += 1;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const current = (0, loop_runs_store_1.readLoopJobUsage)(db, payload.runId);
|
|
215
|
+
const completed = {
|
|
216
|
+
tokensIn: Math.max(0, current.tokensIn - (payload.baseline?.tokensIn ?? 0)),
|
|
217
|
+
tokensOut: Math.max(0, current.tokensOut - (payload.baseline?.tokensOut ?? 0)),
|
|
218
|
+
tokensCacheRead: Math.max(0, current.tokensCacheRead - (payload.baseline?.tokensCacheRead ?? 0)),
|
|
219
|
+
tokensCacheCreate: Math.max(0, current.tokensCacheCreate - (payload.baseline?.tokensCacheCreate ?? 0)),
|
|
220
|
+
totalCostUsd: Math.max(0, current.totalCostUsd - (payload.baseline?.totalCostUsd ?? 0)),
|
|
221
|
+
numTurns: Math.max(0, current.numTurns - (payload.baseline?.numTurns ?? 0)),
|
|
222
|
+
};
|
|
223
|
+
const rawRows = db.prepare(`
|
|
224
|
+
SELECT seq, event_type, payload FROM events
|
|
225
|
+
WHERE job_id = ? AND seq > ? AND source = 'stdout'
|
|
226
|
+
ORDER BY seq
|
|
227
|
+
`).all(payload.runId, payload.completedEventSeq ?? -1);
|
|
228
|
+
const adapter = (0, providers_1.getAdapter)(payload.provider);
|
|
229
|
+
const events = rawRows
|
|
230
|
+
.map((raw) => adapter.parseStreamLine(raw.payload))
|
|
231
|
+
.filter((event) => event != null);
|
|
232
|
+
const { result: partial, estimated } = (0, result_event_1.finaliseInvocationResult)(adapter, events, {
|
|
233
|
+
fallbackModel: payload.model ?? undefined,
|
|
234
|
+
});
|
|
235
|
+
const hasResult = rawRows.some((raw) => raw.event_type === 'result');
|
|
236
|
+
const partialCost = hasResult && partial.total_cost_usd != null
|
|
237
|
+
? Math.max(0, partial.total_cost_usd - (payload.providerCostBaseline ?? 0))
|
|
238
|
+
: (partial.total_cost_usd ?? 0);
|
|
239
|
+
const partialTurns = hasResult && partial.num_turns != null
|
|
240
|
+
? Math.max(0, partial.num_turns - (payload.providerTurnsBaseline ?? 0))
|
|
241
|
+
: (partial.num_turns ?? (events.length > 0 ? 1 : 0));
|
|
242
|
+
const partialUsage = {
|
|
243
|
+
tokens_in: partial.tokens_in ?? 0,
|
|
244
|
+
tokens_out: partial.tokens_out ?? 0,
|
|
245
|
+
tokens_cache_read: partial.tokens_cache_read ?? 0,
|
|
246
|
+
tokens_cache_create: partial.tokens_cache_create ?? 0,
|
|
247
|
+
total_cost_usd: partialCost,
|
|
248
|
+
num_turns: partialTurns,
|
|
249
|
+
model: partial.model,
|
|
250
|
+
session_id: partial.session_id,
|
|
251
|
+
estimated: partialCost > 0 && estimated,
|
|
252
|
+
};
|
|
253
|
+
const partialDurationMs = hasResult
|
|
254
|
+
? (partial.duration_ms ?? 0)
|
|
255
|
+
: ((partial.duration_ms ?? 0) > 0
|
|
256
|
+
? partial.duration_ms
|
|
257
|
+
: boundedInflightDurationMs(payload, finishedAt));
|
|
258
|
+
// completedDurationMs contains only fully-checkpointed turns. The current
|
|
259
|
+
// raw turn contributes either provider duration OR the bounded wall fallback,
|
|
260
|
+
// never both.
|
|
261
|
+
const recoveredDurationMs = (payload.completedDurationMs ?? 0) + partialDurationMs;
|
|
262
|
+
const didRecover = (0, loop_runs_store_1.completeLoopStepRecovery)(db, payload.runId, payload.stepKey, (stable) => {
|
|
263
|
+
if (partialUsage.tokens_in || partialUsage.tokens_out ||
|
|
264
|
+
partialUsage.tokens_cache_read || partialUsage.tokens_cache_create ||
|
|
265
|
+
partialUsage.total_cost_usd || partialUsage.num_turns) {
|
|
266
|
+
(0, db_1.accumulateInteractiveTurn)(db, payload.runId, partialUsage);
|
|
267
|
+
}
|
|
268
|
+
insertLoopInvocation(db, stable, {
|
|
269
|
+
cost: completed.totalCostUsd + partialUsage.total_cost_usd,
|
|
270
|
+
tokens: completed.tokensIn + completed.tokensOut + partialUsage.tokens_in + partialUsage.tokens_out,
|
|
271
|
+
tokensIn: completed.tokensIn + partialUsage.tokens_in,
|
|
272
|
+
tokensOut: completed.tokensOut + partialUsage.tokens_out,
|
|
273
|
+
tokensCacheRead: completed.tokensCacheRead + partialUsage.tokens_cache_read,
|
|
274
|
+
tokensCacheCreate: completed.tokensCacheCreate + partialUsage.tokens_cache_create,
|
|
275
|
+
numTurns: completed.numTurns + partialUsage.num_turns,
|
|
276
|
+
durationMs: recoveredDurationMs,
|
|
277
|
+
model: partial.model ?? payload.model ?? undefined,
|
|
278
|
+
sessionId: partial.session_id,
|
|
279
|
+
provider: payload.provider,
|
|
280
|
+
estimated: partialUsage.estimated,
|
|
281
|
+
failed: true,
|
|
282
|
+
}, finishedAt);
|
|
283
|
+
const aggregate = (0, loop_runs_store_1.readLoopJobUsage)(db, payload.runId);
|
|
284
|
+
const recoveredLoopDuration = (payload.loopDurationBaseline ?? 0) + recoveredDurationMs;
|
|
285
|
+
db.prepare(`
|
|
286
|
+
UPDATE loop_runs
|
|
287
|
+
SET total_cost_usd = ?, total_tokens = ?, total_duration_ms = ?,
|
|
288
|
+
iteration_count = MAX(iteration_count, COALESCE(?, iteration_count))
|
|
289
|
+
WHERE id = ?
|
|
290
|
+
`).run(aggregate.totalCostUsd, aggregate.tokensIn + aggregate.tokensOut, recoveredLoopDuration, stable.iterationCount ?? null, payload.runId);
|
|
291
|
+
db.prepare(`UPDATE jobs SET duration_ms = ? WHERE id = ? AND owner = 'loop'`)
|
|
292
|
+
.run(recoveredLoopDuration, payload.runId);
|
|
293
|
+
});
|
|
294
|
+
if (didRecover)
|
|
295
|
+
recovered += 1;
|
|
296
|
+
}
|
|
297
|
+
return recovered;
|
|
298
|
+
}
|
|
96
299
|
class LoopRunManager {
|
|
97
300
|
db;
|
|
98
301
|
broadcast;
|
|
99
302
|
executors;
|
|
100
303
|
now;
|
|
304
|
+
_disposed = false;
|
|
101
305
|
_cancelled = new Set();
|
|
102
306
|
/** The currently-spawned child per run, so cancel/stop can actually KILL a
|
|
103
307
|
* blocked spawn (the cooperative `_cancelled` flag can't interrupt an await). */
|
|
@@ -108,6 +312,7 @@ class LoopRunManager {
|
|
|
108
312
|
* id, which IS the backing job row's id — the manager-agnostic turn routing
|
|
109
313
|
* (project-router-jobs) addresses sessions by that job id. */
|
|
110
314
|
_interactiveSteps = new Map();
|
|
315
|
+
_activeStepRecovery = new Map();
|
|
111
316
|
_pausedHumanDecisions = new Map();
|
|
112
317
|
constructor(db, broadcast, executors, now = () => Date.now()) {
|
|
113
318
|
this.db = db;
|
|
@@ -115,6 +320,14 @@ class LoopRunManager {
|
|
|
115
320
|
this.executors = executors;
|
|
116
321
|
this.now = now;
|
|
117
322
|
}
|
|
323
|
+
/** WebSocket delivery is advisory. A disconnected/misbehaving listener must
|
|
324
|
+
* never change launch, accounting, traversal, or terminal outcomes. */
|
|
325
|
+
_emit(msg) {
|
|
326
|
+
try {
|
|
327
|
+
this.broadcast(msg);
|
|
328
|
+
}
|
|
329
|
+
catch { /* persisted state remains authoritative */ }
|
|
330
|
+
}
|
|
118
331
|
/** Cancel an in-flight run: flag it AND kill the active spawned child so a
|
|
119
332
|
* blocked AI Step / Shell returns immediately (the engine then settles
|
|
120
333
|
* 'stopped' at the next boundary). An interactive step is torn down through
|
|
@@ -183,7 +396,57 @@ class LoopRunManager {
|
|
|
183
396
|
* runs — the startup orphan sweeps (jobs + loop_runs) reconcile the rows on
|
|
184
397
|
* the next boot, exactly as they do for a crash today. */
|
|
185
398
|
shutdown() {
|
|
186
|
-
|
|
399
|
+
if (this._disposed)
|
|
400
|
+
return;
|
|
401
|
+
this._disposed = true;
|
|
402
|
+
for (const runId of this._activeChild.keys())
|
|
403
|
+
this._cancelled.add(runId);
|
|
404
|
+
for (const runId of this._interactiveSteps.keys())
|
|
405
|
+
this._cancelled.add(runId);
|
|
406
|
+
for (const [runId, session] of this._interactiveSteps) {
|
|
407
|
+
try {
|
|
408
|
+
const snapshot = session.snapshotForAbort();
|
|
409
|
+
const stepKey = this._activeStepRecovery.get(runId);
|
|
410
|
+
if (stepKey) {
|
|
411
|
+
let recoveredProjectId = null;
|
|
412
|
+
(0, loop_runs_store_1.completeLoopStepRecovery)(this.db, runId, stepKey, (payload) => {
|
|
413
|
+
recoveredProjectId = payload.projectId;
|
|
414
|
+
insertLoopInvocation(this.db, payload, {
|
|
415
|
+
cost: snapshot.totals.total_cost_usd,
|
|
416
|
+
tokens: snapshot.totals.tokens_in + snapshot.totals.tokens_out,
|
|
417
|
+
tokensIn: snapshot.totals.tokens_in,
|
|
418
|
+
tokensOut: snapshot.totals.tokens_out,
|
|
419
|
+
tokensCacheRead: snapshot.totals.tokens_cache_read,
|
|
420
|
+
tokensCacheCreate: snapshot.totals.tokens_cache_create,
|
|
421
|
+
numTurns: snapshot.totals.num_turns,
|
|
422
|
+
durationMs: snapshot.activeDurationMs,
|
|
423
|
+
sessionId: snapshot.sessionId ?? undefined,
|
|
424
|
+
model: snapshot.model ?? undefined,
|
|
425
|
+
provider: payload.provider,
|
|
426
|
+
estimated: snapshot.estimated,
|
|
427
|
+
failed: true,
|
|
428
|
+
}, new Date(this.now()).toISOString());
|
|
429
|
+
const aggregate = (0, loop_runs_store_1.readLoopJobUsage)(this.db, runId);
|
|
430
|
+
const recoveredLoopDuration = (payload.loopDurationBaseline ?? 0) + snapshot.activeDurationMs;
|
|
431
|
+
this.db.prepare(`
|
|
432
|
+
UPDATE loop_runs
|
|
433
|
+
SET total_cost_usd = ?, total_tokens = ?, total_duration_ms = ?,
|
|
434
|
+
iteration_count = MAX(iteration_count, COALESCE(?, iteration_count))
|
|
435
|
+
WHERE id = ?
|
|
436
|
+
`).run(aggregate.totalCostUsd, aggregate.tokensIn + aggregate.tokensOut, recoveredLoopDuration, payload.iterationCount ?? null, runId);
|
|
437
|
+
this.db.prepare(`UPDATE jobs SET duration_ms = ? WHERE id = ? AND owner = 'loop'`)
|
|
438
|
+
.run(recoveredLoopDuration, runId);
|
|
439
|
+
});
|
|
440
|
+
this._activeStepRecovery.delete(runId);
|
|
441
|
+
if (recoveredProjectId) {
|
|
442
|
+
this._emit({ type: 'spending.invalidated', projectId: recoveredProjectId });
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch (err) {
|
|
447
|
+
// Leave the durable step checkpoint for startup raw-event recovery.
|
|
448
|
+
console.error(`[loop] shutdown accounting checkpoint failed for ${runId}:`, err);
|
|
449
|
+
}
|
|
187
450
|
try {
|
|
188
451
|
session.dispose();
|
|
189
452
|
}
|
|
@@ -209,6 +472,9 @@ class LoopRunManager {
|
|
|
209
472
|
this._pausedHumanDecisions.clear();
|
|
210
473
|
}
|
|
211
474
|
async run(req) {
|
|
475
|
+
if (this._disposed)
|
|
476
|
+
throw new Error('LoopRunManager is shut down');
|
|
477
|
+
const neverAfterDispose = () => new Promise(() => { });
|
|
212
478
|
const runId = req.runId ?? (0, ids_1.newId)();
|
|
213
479
|
const maxIterations = req.graph.config.maxIterations;
|
|
214
480
|
const timeoutMs = req.graph.config.timeoutMinutes * 60_000;
|
|
@@ -226,20 +492,41 @@ class LoopRunManager {
|
|
|
226
492
|
const maxCostUsd = typeof req.graph.config.maxCostUsd === 'number' && req.graph.config.maxCostUsd > 0
|
|
227
493
|
? req.graph.config.maxCostUsd
|
|
228
494
|
: undefined;
|
|
229
|
-
(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
495
|
+
const launchStartedAt = new Date(this.now()).toISOString();
|
|
496
|
+
const jobCommand = `loop: ${req.loopName ?? req.loopId}${req.ticketId != null ? ` #${req.ticketId}` : ''}`;
|
|
497
|
+
const exactTicketIds = req.spec?.ticketIds ?? (req.ticketId == null ? [] : [req.ticketId]);
|
|
498
|
+
const persistLaunch = this.db.transaction(() => {
|
|
499
|
+
(0, rails_store_1.claimTicketOutcomeOwners)(this.db, exactTicketIds, runId);
|
|
500
|
+
if (req.railIndex != null && exactTicketIds.length > 0) {
|
|
501
|
+
(0, rails_store_1.claimRailTickets)(this.db, req.railIndex, exactTicketIds, runId);
|
|
502
|
+
}
|
|
503
|
+
(0, loop_runs_store_1.createLoopRun)(this.db, {
|
|
504
|
+
id: runId,
|
|
505
|
+
projectId: req.projectId,
|
|
506
|
+
loopId: req.loopId,
|
|
507
|
+
loopName: req.loopName ?? null,
|
|
508
|
+
railIndex: req.railIndex ?? null,
|
|
509
|
+
ticketId: req.ticketId ?? null,
|
|
510
|
+
provider: req.provider,
|
|
511
|
+
model: req.model,
|
|
512
|
+
reasoningEffort: req.effort ?? null,
|
|
513
|
+
ticketIds: exactTicketIds,
|
|
514
|
+
ticketCompletionStatus: req.ticketCompletionStatus ?? 'done',
|
|
515
|
+
causalOwnership: true,
|
|
516
|
+
iterationLimit: maxIterations,
|
|
517
|
+
startedAt: launchStartedAt,
|
|
518
|
+
});
|
|
519
|
+
(0, db_1.createJob)(this.db, {
|
|
520
|
+
id: runId,
|
|
521
|
+
command: jobCommand,
|
|
522
|
+
started_at: launchStartedAt,
|
|
523
|
+
provider: req.provider,
|
|
524
|
+
owner: 'loop',
|
|
525
|
+
causal_ownership: true,
|
|
526
|
+
});
|
|
241
527
|
});
|
|
242
|
-
|
|
528
|
+
persistLaunch();
|
|
529
|
+
this._emit({
|
|
243
530
|
type: 'loop.run_started',
|
|
244
531
|
projectId: req.projectId,
|
|
245
532
|
loopRunId: runId,
|
|
@@ -247,14 +534,7 @@ class LoopRunManager {
|
|
|
247
534
|
railIndex: req.railIndex ?? null,
|
|
248
535
|
});
|
|
249
536
|
// Surface the run as a JOB so the full session streams live in the Jobs list
|
|
250
|
-
// + JobDetail
|
|
251
|
-
// createJob is idempotent. Synchronous (before the first await) so the row
|
|
252
|
-
// exists by the time the launch responds → "View Log" never 404s.
|
|
253
|
-
const jobCommand = `loop: ${req.loopName ?? req.loopId}${req.ticketId != null ? ` #${req.ticketId}` : ''}`;
|
|
254
|
-
try {
|
|
255
|
-
(0, db_1.createJob)(this.db, { id: runId, command: jobCommand, started_at: new Date(this.now()).toISOString() });
|
|
256
|
-
}
|
|
257
|
-
catch { /* best-effort; the run still proceeds */ }
|
|
537
|
+
// + JobDetail. Both identities committed together above, before any spawn.
|
|
258
538
|
let seq = 0;
|
|
259
539
|
// Monotonic event-seq allocator for THIS run's job row. Shared with an
|
|
260
540
|
// interactive step session (which persists its own provider events/logs on
|
|
@@ -277,7 +557,7 @@ class LoopRunManager {
|
|
|
277
557
|
(0, db_1.appendEvent)(this.db, runId, s, { event_type: eventType, source: 'stdout', payload: json });
|
|
278
558
|
}
|
|
279
559
|
catch { /* best-effort */ }
|
|
280
|
-
this.
|
|
560
|
+
this._emit({ type: 'event', jobId: runId, event_type: eventType, source: 'stdout', payload: json, seq: s, timestamp: new Date(this.now()).toISOString() });
|
|
281
561
|
};
|
|
282
562
|
// Emit a structured step-boundary event (for a segmented/collapsible client
|
|
283
563
|
// view) AND a visual divider line in the flat log, so each loop step is a
|
|
@@ -316,7 +596,7 @@ class LoopRunManager {
|
|
|
316
596
|
(0, db_1.appendEvent)(this.db, runId, s, { event_type: 'log', source, payload: JSON.stringify({ line }) });
|
|
317
597
|
}
|
|
318
598
|
catch { /* events table best-effort */ }
|
|
319
|
-
this.
|
|
599
|
+
this._emit({ type: 'log', source, line, timestamp: new Date(this.now()).toISOString(), processId: runId });
|
|
320
600
|
};
|
|
321
601
|
// Forward a RAW provider stdout line (claude/codex JSONL) as an `event` —
|
|
322
602
|
// identical to QueueManager — so JobStatusPanel parses real activity
|
|
@@ -341,7 +621,16 @@ class LoopRunManager {
|
|
|
341
621
|
(0, db_1.appendEvent)(this.db, runId, s, { event_type: eventType, source: 'stdout', payload: line });
|
|
342
622
|
}
|
|
343
623
|
catch { /* best-effort */ }
|
|
344
|
-
|
|
624
|
+
const recoveryStepKey = this._activeStepRecovery.get(runId);
|
|
625
|
+
if (recoveryStepKey) {
|
|
626
|
+
try {
|
|
627
|
+
(0, loop_runs_store_1.updateLoopStepActivityCheckpoint)(this.db, runId, recoveryStepKey, undefined, this.now());
|
|
628
|
+
}
|
|
629
|
+
catch (err) {
|
|
630
|
+
console.error(`[loop] raw activity checkpoint failed for ${runId}/${recoveryStepKey}:`, err);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
this._emit({ type: 'event', jobId: runId, event_type: eventType, source: 'stdout', payload: line, seq: s, timestamp: new Date(this.now()).toISOString() });
|
|
345
634
|
};
|
|
346
635
|
logLine(`▶ Loop "${req.loopName ?? req.loopId}" started${req.spec?.title ? ` — spec: ${req.spec.title}` : ''}`);
|
|
347
636
|
if (req.isolation)
|
|
@@ -365,6 +654,13 @@ class LoopRunManager {
|
|
|
365
654
|
let totalCost = 0;
|
|
366
655
|
let totalTokens = 0;
|
|
367
656
|
let totalDuration = 0;
|
|
657
|
+
let finalJobUsage = {
|
|
658
|
+
tokensIn: 0,
|
|
659
|
+
tokensOut: 0,
|
|
660
|
+
tokensCacheRead: 0,
|
|
661
|
+
tokensCacheCreate: 0,
|
|
662
|
+
numTurns: 0,
|
|
663
|
+
};
|
|
368
664
|
let aiSessionId;
|
|
369
665
|
// Consecutive AI steps that hard-failed with no output (provider down /
|
|
370
666
|
// out of quota / crashing). Reset on any step that runs or produces output.
|
|
@@ -409,7 +705,7 @@ class LoopRunManager {
|
|
|
409
705
|
const record = (refSuffix, r,
|
|
410
706
|
// The REAL turn-start instant, captured before the step/decider await. The
|
|
411
707
|
// row is bucketed/ordered by started_at, so it must be the start, not finish.
|
|
412
|
-
startedAt) => {
|
|
708
|
+
startedAt, stepKey) => {
|
|
413
709
|
totalCost += r.cost ?? 0;
|
|
414
710
|
// A cost-bearing step that produced work (tokens) or hard-failed but reports
|
|
415
711
|
// no priced cost → its real spend is missing from the total. The common case
|
|
@@ -428,53 +724,80 @@ class LoopRunManager {
|
|
|
428
724
|
costUnknownWarned = true;
|
|
429
725
|
logLine('⚠️ A step reported no priced cost (non-Claude estimate / unknown model / failed step) — the cost cap may under-count.', 'stderr');
|
|
430
726
|
}
|
|
431
|
-
totalTokens += r.tokens ?? 0;
|
|
727
|
+
totalTokens += r.tokens ?? ((r.tokensIn ?? 0) + (r.tokensOut ?? 0));
|
|
432
728
|
totalDuration += r.durationMs ?? 0;
|
|
433
|
-
(
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
model: r.model ?? req.model,
|
|
467
|
-
loop_run_id: runId,
|
|
468
|
-
});
|
|
729
|
+
const finishedAt = new Date(this.now()).toISOString();
|
|
730
|
+
if (stepKey) {
|
|
731
|
+
(0, loop_runs_store_1.setLoopStepSettledResult)(this.db, runId, stepKey, r);
|
|
732
|
+
const completed = (0, loop_runs_store_1.completeLoopStepRecovery)(this.db, runId, stepKey, (payload) => {
|
|
733
|
+
insertLoopInvocation(this.db, payload, r, finishedAt);
|
|
734
|
+
(0, loop_runs_store_1.updateLoopRunCounters)(this.db, runId, {
|
|
735
|
+
iterationCount: iteration,
|
|
736
|
+
totalCostUsd: totalCost,
|
|
737
|
+
totalTokens,
|
|
738
|
+
totalDurationMs: totalDuration,
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
if (!completed)
|
|
742
|
+
throw new Error(`Missing loop step checkpoint ${runId}/${stepKey}`);
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
// Compatibility for synthetic/test call paths that predate staged steps.
|
|
746
|
+
(0, ai_invocations_1.recordInvocation)(this.db, {
|
|
747
|
+
id: (0, ids_1.newId)(), project_id: req.projectId,
|
|
748
|
+
provider: r.provider ?? req.provider, surface: 'loop',
|
|
749
|
+
surface_ref_id: refSuffix, ticket_id: req.ticketId ?? null,
|
|
750
|
+
status: r.failed ? 'failed' : 'success', started_at: startedAt,
|
|
751
|
+
finished_at: finishedAt, total_cost_usd: r.cost,
|
|
752
|
+
total_cost_usd_estimated: r.estimated ?? false,
|
|
753
|
+
tokens_in: r.tokensIn,
|
|
754
|
+
tokens_out: r.tokensOut ?? (r.tokensIn === undefined && r.tokensOut === undefined ? r.tokens : undefined),
|
|
755
|
+
tokens_cache_read: r.tokensCacheRead,
|
|
756
|
+
tokens_cache_create: r.tokensCacheCreate,
|
|
757
|
+
num_turns: r.numTurns, session_id: r.sessionId,
|
|
758
|
+
duration_ms: r.durationMs, duration_api_ms: r.durationApiMs,
|
|
759
|
+
model: r.model ?? req.model, loop_run_id: runId,
|
|
760
|
+
});
|
|
761
|
+
}
|
|
469
762
|
// BUG-07: the loop path is the only recordInvocation callsite that never
|
|
470
763
|
// invalidated open spending dashboards — they'd freeze for the whole (often
|
|
471
764
|
// multi-hour) run. Broadcast after each row, wrapped so a broadcast failure
|
|
472
765
|
// can't break traversal (mirrors the file-summary callsite).
|
|
473
766
|
try {
|
|
474
|
-
this.
|
|
767
|
+
this._emit({ type: 'spending.invalidated', projectId: req.projectId });
|
|
475
768
|
}
|
|
476
769
|
catch { /* best-effort — never break traversal on a broadcast failure */ }
|
|
477
770
|
};
|
|
771
|
+
const stageAiAccounting = (kind, nodeId, surfaceRefId, startedAt) => {
|
|
772
|
+
const stepKey = `${kind}:${stepNum}:${nodeId}`;
|
|
773
|
+
const startedAtMs = Date.parse(startedAt);
|
|
774
|
+
const payload = {
|
|
775
|
+
version: 1,
|
|
776
|
+
runId,
|
|
777
|
+
stepKey,
|
|
778
|
+
invocationId: (0, ids_1.newId)(),
|
|
779
|
+
projectId: req.projectId,
|
|
780
|
+
provider: req.provider,
|
|
781
|
+
model: req.model ?? null,
|
|
782
|
+
surfaceRefId,
|
|
783
|
+
ticketIds: exactTicketIds,
|
|
784
|
+
startedAt,
|
|
785
|
+
baseline: (0, loop_runs_store_1.readLoopJobUsage)(this.db, runId),
|
|
786
|
+
completedEventSeq: seq - 1,
|
|
787
|
+
providerCostBaseline: 0,
|
|
788
|
+
providerTurnsBaseline: 0,
|
|
789
|
+
loopDurationBaseline: this.db.prepare(`SELECT total_duration_ms FROM loop_runs WHERE id = ?`)
|
|
790
|
+
.get(runId)?.total_duration_ms ?? totalDuration,
|
|
791
|
+
completedDurationMs: 0,
|
|
792
|
+
iterationCount: iteration,
|
|
793
|
+
...(Number.isFinite(startedAtMs)
|
|
794
|
+
? { activeTurnStartedAtMs: startedAtMs, lastActivityAtMs: startedAtMs }
|
|
795
|
+
: {}),
|
|
796
|
+
};
|
|
797
|
+
(0, loop_runs_store_1.stageLoopStepRecovery)(this.db, payload);
|
|
798
|
+
this._activeStepRecovery.set(runId, stepKey);
|
|
799
|
+
return stepKey;
|
|
800
|
+
};
|
|
478
801
|
const composeHistory = () => {
|
|
479
802
|
const joined = history.join('\n');
|
|
480
803
|
return joined.length > HISTORY_MAX_CHARS ? '…' + joined.slice(-HISTORY_MAX_CHARS) : joined;
|
|
@@ -502,15 +825,15 @@ class LoopRunManager {
|
|
|
502
825
|
totalDurationMs: totalDuration,
|
|
503
826
|
});
|
|
504
827
|
const pausedAt = new Date(this.now()).toISOString();
|
|
505
|
-
this.
|
|
828
|
+
this._emit({
|
|
506
829
|
type: 'loop.run_paused',
|
|
507
830
|
projectId: req.projectId,
|
|
508
831
|
loopRunId: runId,
|
|
509
832
|
railIndex: req.railIndex ?? null,
|
|
510
833
|
reason,
|
|
511
|
-
ticketIds:
|
|
834
|
+
ticketIds: exactTicketIds,
|
|
512
835
|
});
|
|
513
|
-
this.
|
|
836
|
+
this._emit({
|
|
514
837
|
type: 'job.interactive',
|
|
515
838
|
projectId: req.projectId,
|
|
516
839
|
jobId: runId,
|
|
@@ -519,6 +842,8 @@ class LoopRunManager {
|
|
|
519
842
|
timestamp: pausedAt,
|
|
520
843
|
});
|
|
521
844
|
const decision = await decisionPromise;
|
|
845
|
+
if (this._disposed)
|
|
846
|
+
return new Promise(() => { });
|
|
522
847
|
if (decision.action === 'stop' || this._cancelled.has(runId)) {
|
|
523
848
|
logLine(`\n■ Loop stop requested while paused.`, 'stderr');
|
|
524
849
|
return { action: 'stop' };
|
|
@@ -528,14 +853,14 @@ class LoopRunManager {
|
|
|
528
853
|
const resumedAt = new Date(this.now()).toISOString();
|
|
529
854
|
logLine(`\n▶ Loop resumed — human decision received${answer ? `:\n${answer}` : '.'}`);
|
|
530
855
|
history.push(`Human decision: ${truncate(answer)}`);
|
|
531
|
-
this.
|
|
856
|
+
this._emit({
|
|
532
857
|
type: 'loop.run_resumed',
|
|
533
858
|
projectId: req.projectId,
|
|
534
859
|
loopRunId: runId,
|
|
535
860
|
railIndex: req.railIndex ?? null,
|
|
536
|
-
ticketIds:
|
|
861
|
+
ticketIds: exactTicketIds,
|
|
537
862
|
});
|
|
538
|
-
this.
|
|
863
|
+
this._emit({
|
|
539
864
|
type: 'job.interactive',
|
|
540
865
|
projectId: req.projectId,
|
|
541
866
|
jobId: runId,
|
|
@@ -618,6 +943,7 @@ class LoopRunManager {
|
|
|
618
943
|
// bucketed/ordered by started_at, which must be the turn-start, not the
|
|
619
944
|
// finish time (this.now() evaluated after the await would be the finish).
|
|
620
945
|
const aiStepStart = new Date(this.now()).toISOString();
|
|
946
|
+
const aiRecoveryKey = stageAiAccounting('ai', node.id, `loop:${runId}`, aiStepStart);
|
|
621
947
|
// Interactive upgrade (default for persistent-stdin providers): the
|
|
622
948
|
// executors return a resident-session plan when the kill-switch is on
|
|
623
949
|
// and the provider is capable (claude); null falls through to the
|
|
@@ -640,8 +966,11 @@ class LoopRunManager {
|
|
|
640
966
|
prompt,
|
|
641
967
|
fallbackModel: nodeModel,
|
|
642
968
|
nextEventSeq: takeSeq,
|
|
969
|
+
recoveryStepKey: aiRecoveryKey,
|
|
643
970
|
})
|
|
644
971
|
: await this.executors.runAiStep({ prompt, sessionId: aiSessionId, provider: nodeProvider, model: nodeModel, effort: nodeEffort, cwd: req.cwd, repoDir: req.repoDir, onLine: logLine, onRawLine, onSpawn: (c) => this._activeChild.set(runId, c), aiStepTimeoutMs });
|
|
972
|
+
if (this._disposed)
|
|
973
|
+
return neverAfterDispose();
|
|
645
974
|
this._activeChild.delete(runId);
|
|
646
975
|
// Zero-work strictness (run 01f41203): a settle that consumed NO
|
|
647
976
|
// model work — the claude CLI's synthetic `Unknown command:` result
|
|
@@ -690,7 +1019,8 @@ class LoopRunManager {
|
|
|
690
1019
|
if (res.sessionId && (!stepFailed || (blockedReason !== null && !res.failed && !zeroWork)))
|
|
691
1020
|
aiSessionId = res.sessionId;
|
|
692
1021
|
history.push(`AI Step: ${truncate(res.text)}`);
|
|
693
|
-
record(`loop:${runId}`, { ...res, failed: stepFailed }, aiStepStart);
|
|
1022
|
+
record(`loop:${runId}`, { ...res, failed: stepFailed }, aiStepStart, aiRecoveryKey);
|
|
1023
|
+
this._activeStepRecovery.delete(runId);
|
|
694
1024
|
if (blockedReason) {
|
|
695
1025
|
const decision = await awaitHumanDecision(blockedReason);
|
|
696
1026
|
if (decision.action === 'stop') {
|
|
@@ -760,10 +1090,18 @@ class LoopRunManager {
|
|
|
760
1090
|
emitStep('shell', `⚡ ${nodeLabel || 'Shell'}`, node.id, iteration + 1);
|
|
761
1091
|
logLine(`$ ${command}`);
|
|
762
1092
|
const sh = await this.executors.runShell({ command, cwd: req.cwd, onLine: logLine, onSpawn: (c) => this._activeChild.set(runId, c) });
|
|
1093
|
+
if (this._disposed)
|
|
1094
|
+
return neverAfterDispose();
|
|
763
1095
|
this._activeChild.delete(runId);
|
|
764
1096
|
logLine(`(exit ${sh.exitCode})`);
|
|
765
1097
|
emitStepEnd({ status: sh.exitCode === 0 ? 'ok' : 'failed', exitCode: sh.exitCode, durationMs: sh.durationMs });
|
|
766
1098
|
totalDuration += sh.durationMs ?? 0;
|
|
1099
|
+
(0, loop_runs_store_1.updateLoopRunCounters)(this.db, runId, {
|
|
1100
|
+
iterationCount: iteration,
|
|
1101
|
+
totalCostUsd: totalCost,
|
|
1102
|
+
totalTokens,
|
|
1103
|
+
totalDurationMs: totalDuration,
|
|
1104
|
+
});
|
|
767
1105
|
history.push(`Shell \`${command}\` exit=${sh.exitCode}: ${truncate(sh.stdout || sh.stderr)}`);
|
|
768
1106
|
nodeId = succs[0]?.id;
|
|
769
1107
|
break;
|
|
@@ -781,6 +1119,7 @@ class LoopRunManager {
|
|
|
781
1119
|
logLine(`Goal: ${goal}`);
|
|
782
1120
|
// BUG-32: capture the real Decider start BEFORE the await (see AI step).
|
|
783
1121
|
const deciderStart = new Date(this.now()).toISOString();
|
|
1122
|
+
const deciderRecoveryKey = stageAiAccounting('decider', node.id, `loop:${runId}:decider`, deciderStart);
|
|
784
1123
|
const dec = await this.executors.runDecider({
|
|
785
1124
|
systemPrompt: (0, loop_decider_1.buildDeciderSystemPrompt)(),
|
|
786
1125
|
// Give the Decider the spec so it can verify completeness against the
|
|
@@ -795,6 +1134,8 @@ class LoopRunManager {
|
|
|
795
1134
|
onRawLine,
|
|
796
1135
|
onSpawn: (c) => this._activeChild.set(runId, c),
|
|
797
1136
|
});
|
|
1137
|
+
if (this._disposed)
|
|
1138
|
+
return neverAfterDispose();
|
|
798
1139
|
this._activeChild.delete(runId);
|
|
799
1140
|
// A parseable Decider verdict means this AI invocation (SAME
|
|
800
1141
|
// provider/model) succeeded → the provider is alive, so clear any
|
|
@@ -806,7 +1147,8 @@ class LoopRunManager {
|
|
|
806
1147
|
const verdictWord = dec.blocked ? 'blocked' : dec.continue ? 'continue' : 'stop';
|
|
807
1148
|
// BUG-03: a Decider that couldn't parse a verdict (dec.parsed === false)
|
|
808
1149
|
// is a failed AI invocation — record it as such, not as success.
|
|
809
|
-
record(`loop:${runId}:decider`, { ...dec, failed: !dec.parsed }, deciderStart);
|
|
1150
|
+
record(`loop:${runId}:decider`, { ...dec, failed: !dec.parsed }, deciderStart, deciderRecoveryKey);
|
|
1151
|
+
this._activeStepRecovery.delete(runId);
|
|
810
1152
|
logLine(`Decision: ${verdictWord} — ${dec.reasoning}`);
|
|
811
1153
|
// The verdict line above is the decider step's last output. `decision`
|
|
812
1154
|
// is the route actually taken (an unparseable verdict defaults to
|
|
@@ -823,7 +1165,7 @@ class LoopRunManager {
|
|
|
823
1165
|
totalTokens,
|
|
824
1166
|
totalDurationMs: totalDuration,
|
|
825
1167
|
});
|
|
826
|
-
this.
|
|
1168
|
+
this._emit({
|
|
827
1169
|
type: 'loop.run_progress',
|
|
828
1170
|
projectId: req.projectId,
|
|
829
1171
|
loopRunId: runId,
|
|
@@ -930,21 +1272,65 @@ class LoopRunManager {
|
|
|
930
1272
|
}
|
|
931
1273
|
}
|
|
932
1274
|
catch (err) {
|
|
1275
|
+
if (this._disposed)
|
|
1276
|
+
return neverAfterDispose();
|
|
933
1277
|
outcome = 'failed';
|
|
934
1278
|
console.error(`[loop] run=${runId} traversal threw:`, err);
|
|
935
1279
|
logLine(`error: ${err?.message ?? String(err)}`, 'stderr');
|
|
936
1280
|
}
|
|
1281
|
+
if (this._disposed)
|
|
1282
|
+
return neverAfterDispose();
|
|
937
1283
|
// Backstop: a traversal exception (or any future path that breaks out
|
|
938
1284
|
// mid-step) must not leave the last step dangling — close it as failed.
|
|
939
1285
|
// The ONLY way a persisted stream ends with an open step is a mid-flight
|
|
940
1286
|
// dispose (shutdown/project removal), where this code never runs.
|
|
941
1287
|
emitStepEnd({ status: 'failed' });
|
|
1288
|
+
// A traversal exception can bypass record(). Reconcile the staged step
|
|
1289
|
+
// BEFORE writing aggregate job totals, while its jobs baseline is still
|
|
1290
|
+
// valid. If reconciliation itself fails, finishLoopRunAndJob detects the
|
|
1291
|
+
// surviving checkpoint and preserves those baseline columns for startup.
|
|
1292
|
+
try {
|
|
1293
|
+
const recoveredSteps = recoverOrphanLoopStepAccounting(this.db, new Date(this.now()).toISOString(), runId);
|
|
1294
|
+
this._activeStepRecovery.delete(runId);
|
|
1295
|
+
const aggregate = this.db.prepare(`
|
|
1296
|
+
SELECT COALESCE(SUM(total_cost_usd), 0) AS cost,
|
|
1297
|
+
COALESCE(SUM(tokens_in), 0) AS tokens_in,
|
|
1298
|
+
COALESCE(SUM(tokens_out), 0) AS tokens_out,
|
|
1299
|
+
COALESCE(SUM(tokens_cache_read), 0) AS cache_read,
|
|
1300
|
+
COALESCE(SUM(tokens_cache_create), 0) AS cache_create,
|
|
1301
|
+
COALESCE(SUM(duration_ms), 0) AS duration,
|
|
1302
|
+
COALESCE(SUM(num_turns), 0) AS turns
|
|
1303
|
+
FROM ai_invocations
|
|
1304
|
+
WHERE surface = 'loop' AND loop_run_id = ?
|
|
1305
|
+
`).get(runId);
|
|
1306
|
+
totalCost = aggregate.cost;
|
|
1307
|
+
totalTokens = aggregate.tokens_in + aggregate.tokens_out;
|
|
1308
|
+
if (recoveredSteps > 0) {
|
|
1309
|
+
// Recovery already committed the authoritative D+X totals. Refresh the
|
|
1310
|
+
// traversal locals before finishLoopRunAndJob; otherwise a catch path
|
|
1311
|
+
// that never called record() writes its stale D baseline back over the
|
|
1312
|
+
// recovered row (duration was the visible casualty, but keep all
|
|
1313
|
+
// counters/iteration coherent).
|
|
1314
|
+
const durable = (0, loop_runs_store_1.getLoopRun)(this.db, runId);
|
|
1315
|
+
if (durable) {
|
|
1316
|
+
totalCost = durable.total_cost_usd;
|
|
1317
|
+
totalTokens = durable.total_tokens;
|
|
1318
|
+
totalDuration = durable.total_duration_ms;
|
|
1319
|
+
iteration = Math.max(iteration, durable.iteration_count);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
finalJobUsage = {
|
|
1323
|
+
tokensIn: aggregate.tokens_in,
|
|
1324
|
+
tokensOut: aggregate.tokens_out,
|
|
1325
|
+
tokensCacheRead: aggregate.cache_read,
|
|
1326
|
+
tokensCacheCreate: aggregate.cache_create,
|
|
1327
|
+
numTurns: aggregate.turns,
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
catch (err) {
|
|
1331
|
+
console.error(`[loop] staged accounting reconciliation failed for ${runId}:`, err);
|
|
1332
|
+
}
|
|
942
1333
|
console.log(`[loop] settle run=${runId} outcome=${outcome} iterations=${iteration} cost=$${totalCost.toFixed(4)}`);
|
|
943
|
-
(0, loop_runs_store_1.finishLoopRun)(this.db, runId, {
|
|
944
|
-
outcome,
|
|
945
|
-
finishedAt: new Date(this.now()).toISOString(),
|
|
946
|
-
counters: { iterationCount: iteration, totalCostUsd: totalCost, totalTokens, totalDurationMs: totalDuration },
|
|
947
|
-
});
|
|
948
1334
|
// Settle the backing job so the Jobs list + JobDetail reflect the final
|
|
949
1335
|
// status, and emit job.finalized so an open JobDetail re-fetches + stops the
|
|
950
1336
|
// live stream (mirrors QueueManager).
|
|
@@ -957,39 +1343,52 @@ class LoopRunManager {
|
|
|
957
1343
|
// `≥` when any cost-bearing step ended unpriced (timeout/crash) — the figure
|
|
958
1344
|
// is a lower bound, not exact.
|
|
959
1345
|
logLine(`\n■ Loop finished: ${outcome} — ${iteration} iteration${iteration === 1 ? '' : 's'}, ${costUncertain ? '≥ ' : ''}$${totalCost.toFixed(4)}`);
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1346
|
+
const finishedAt = new Date(this.now()).toISOString();
|
|
1347
|
+
(0, loop_runs_store_1.finishLoopRunAndJob)(this.db, runId, {
|
|
1348
|
+
outcome,
|
|
1349
|
+
finishedAt,
|
|
1350
|
+
counters: {
|
|
1351
|
+
iterationCount: iteration,
|
|
1352
|
+
totalCostUsd: totalCost,
|
|
1353
|
+
totalTokens,
|
|
1354
|
+
totalDurationMs: totalDuration,
|
|
1355
|
+
},
|
|
1356
|
+
job: {
|
|
1357
|
+
exitCode: outcome === 'success' ? 0 : 1,
|
|
963
1358
|
status: jobStatus,
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1359
|
+
totalCostUsd: totalCost,
|
|
1360
|
+
tokensIn: finalJobUsage.tokensIn,
|
|
1361
|
+
tokensOut: finalJobUsage.tokensOut,
|
|
1362
|
+
tokensCacheRead: finalJobUsage.tokensCacheRead,
|
|
1363
|
+
tokensCacheCreate: finalJobUsage.tokensCacheCreate,
|
|
1364
|
+
durationMs: totalDuration,
|
|
1365
|
+
numTurns: finalJobUsage.numTurns,
|
|
1366
|
+
},
|
|
1367
|
+
callbackOutcome: req.deferTerminalOutcome ? 'failed' : outcome,
|
|
1368
|
+
outcomeFinalized: !req.deferTerminalOutcome,
|
|
1369
|
+
});
|
|
1370
|
+
this._emit({
|
|
972
1371
|
type: 'job.finalized',
|
|
973
1372
|
projectId: req.projectId,
|
|
974
1373
|
jobId: runId,
|
|
975
1374
|
status: jobStatus,
|
|
976
1375
|
totals: {
|
|
977
|
-
tokens_in:
|
|
978
|
-
tokens_out:
|
|
979
|
-
tokens_cache_read:
|
|
980
|
-
tokens_cache_create:
|
|
1376
|
+
tokens_in: finalJobUsage.tokensIn,
|
|
1377
|
+
tokens_out: finalJobUsage.tokensOut,
|
|
1378
|
+
tokens_cache_read: finalJobUsage.tokensCacheRead,
|
|
1379
|
+
tokens_cache_create: finalJobUsage.tokensCacheCreate,
|
|
981
1380
|
total_cost_usd: totalCost,
|
|
982
|
-
num_turns:
|
|
1381
|
+
num_turns: finalJobUsage.numTurns,
|
|
983
1382
|
},
|
|
984
1383
|
timestamp: new Date(this.now()).toISOString(),
|
|
985
1384
|
});
|
|
986
|
-
this.
|
|
1385
|
+
this._emit({
|
|
987
1386
|
type: 'loop.run_completed',
|
|
988
1387
|
projectId: req.projectId,
|
|
989
1388
|
loopRunId: runId,
|
|
990
1389
|
railIndex: req.railIndex ?? null,
|
|
991
1390
|
status: outcome,
|
|
992
|
-
ticketIds:
|
|
1391
|
+
ticketIds: exactTicketIds,
|
|
993
1392
|
});
|
|
994
1393
|
return { runId, outcome, iterations: iteration, totalCostUsd: totalCost };
|
|
995
1394
|
}
|
|
@@ -1032,7 +1431,18 @@ class LoopRunManager {
|
|
|
1032
1431
|
// The step timeout below is the sole watchdog — the one-shot loop path
|
|
1033
1432
|
// has no zombie detector either (byte-parity), so none is armed here.
|
|
1034
1433
|
nextEventSeq: input.nextEventSeq,
|
|
1434
|
+
persistTurnUsage: (usage, completedEventSeq, checkpoint) => {
|
|
1435
|
+
const checkpointTurn = this.db.transaction(() => {
|
|
1436
|
+
(0, db_1.accumulateInteractiveTurn)(this.db, input.runId, usage);
|
|
1437
|
+
(0, loop_runs_store_1.updateLoopStepEventCheckpoint)(this.db, input.runId, input.recoveryStepKey, completedEventSeq, checkpoint?.cost, checkpoint?.turns, checkpoint?.activeDurationMs);
|
|
1438
|
+
});
|
|
1439
|
+
checkpointTurn();
|
|
1440
|
+
},
|
|
1441
|
+
persistTurnActivity: (turnStartedAtMs, activityAtMs) => {
|
|
1442
|
+
(0, loop_runs_store_1.updateLoopStepActivityCheckpoint)(this.db, input.runId, input.recoveryStepKey, turnStartedAtMs, activityAtMs);
|
|
1443
|
+
},
|
|
1035
1444
|
spawn: input.plan.spawn,
|
|
1445
|
+
killTree: input.plan.killTree,
|
|
1036
1446
|
onSettle: (info) => {
|
|
1037
1447
|
if (timer) {
|
|
1038
1448
|
clearTimeout(timer);
|
|
@@ -1041,7 +1451,7 @@ class LoopRunManager {
|
|
|
1041
1451
|
this._interactiveSteps.delete(input.runId);
|
|
1042
1452
|
// The step's session is gone — the composer flips to its gentle
|
|
1043
1453
|
// "waiting for the next step" state instead of erroring on 409.
|
|
1044
|
-
this.
|
|
1454
|
+
this._emit({
|
|
1045
1455
|
type: 'job.interactive',
|
|
1046
1456
|
projectId: input.projectId,
|
|
1047
1457
|
jobId: input.runId,
|
|
@@ -1095,7 +1505,7 @@ class LoopRunManager {
|
|
|
1095
1505
|
catch { /* best-effort */ }
|
|
1096
1506
|
// A resident step session is live — an open Job Detail composer re-enables
|
|
1097
1507
|
// without polling (mirror flip of the onSettle broadcast above).
|
|
1098
|
-
this.
|
|
1508
|
+
this._emit({
|
|
1099
1509
|
type: 'job.interactive',
|
|
1100
1510
|
projectId: input.projectId,
|
|
1101
1511
|
jobId: input.runId,
|