specrails-desktop 2.58.0 → 2.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -5
- package/client/dist/assets/{ActivityFeedPage-BQGppIR0.js → ActivityFeedPage-DBFt8ZHP.js} +1 -1
- package/client/dist/assets/AgentBrowserCapture-DiQx0W67.js +1 -0
- package/client/dist/assets/AgentModeAnalyticsPane-BmDSXp_J.js +2 -0
- package/client/dist/assets/AgentModeCodePane-BLQLPSwr.js +2 -0
- package/client/dist/assets/{AgentModeJobsPane-DR_06oqH.js → AgentModeJobsPane-BbTXdKUa.js} +1 -1
- package/client/dist/assets/{AgentsPage-BKIKjm_1.js → AgentsPage-Dei1jGR8.js} +3 -3
- package/client/dist/assets/{AnalyticsPage-069npRVo.js → AnalyticsPage-BhkeGIGn.js} +1 -1
- package/client/dist/assets/CodePage-B_9yYWUx.js +3 -0
- package/client/dist/assets/{DesktopAnalyticsPage-qtWAPM47.js → DesktopAnalyticsPage-DAcfEJac.js} +1 -1
- package/client/dist/assets/DocsDialog-WCuiYgsc.js +11 -0
- package/client/dist/assets/DocsPage-bGVYTMTU.js +11 -0
- package/client/dist/assets/{ExportDropdown-BcMpvV89.js → ExportDropdown-CsodZ-yb.js} +1 -1
- package/client/dist/assets/InteractiveJobComposer-D5fh_eVL.js +19 -0
- package/client/dist/assets/JobDetailModal-DlpXF61O.js +1 -0
- package/client/dist/assets/JobDetailPage-2yR19_Lb.js +1 -0
- package/client/dist/assets/JobsPage-CS9IR6Dq.js +1 -0
- package/client/dist/assets/LoopBuilderPage-BwiAtmOr.js +1 -0
- package/client/dist/assets/{LoopPreviewModal-je5pSCf-.js → LoopPreviewModal--upwvgTv.js} +1 -1
- package/client/dist/assets/LoopWindowSurface-DRnUixtl.js +1 -0
- package/client/dist/assets/LoopsPage-Xm3nRToQ.js +2 -0
- package/client/dist/assets/MinimizedChatsContext-D_8L4EWZ.js +1 -0
- package/client/dist/assets/PluginsPage-CAFQ1B4Q.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-DYSO4v90.js +1 -0
- package/client/dist/assets/{RepositoryDeliveries-BiwJuS8r.js → RepositoryDeliveries-CbQmW7Qx.js} +1 -1
- package/client/dist/assets/RepositoryScopeSelector-DKzvIGSZ.js +2 -0
- package/client/dist/assets/ReviewPacketPage-SDBhYdQB.js +4 -0
- package/client/dist/assets/TemplatePreviewModal-DHBUds51.js +1 -0
- package/client/dist/assets/TicketDetailModalContext-UAeNm7BB.js +16 -0
- package/client/dist/assets/{Trans-D3AfDylf.js → Trans-DExd3Z00.js} +1 -1
- package/client/dist/assets/agent-BElLtdKP.js +1 -0
- package/client/dist/assets/agent-C-m9VugX.js +1 -0
- package/client/dist/assets/agent-CGSKxfZk.js +1 -0
- package/client/dist/assets/agent-CW74ODDP.js +1 -0
- package/client/dist/assets/agent-DeCAZDhL.js +1 -0
- package/client/dist/assets/agent-DrM4qned.js +1 -0
- package/client/dist/assets/agent-KdIXq3w5.js +1 -0
- package/client/dist/assets/agent-jK8mCisM.js +1 -0
- package/client/dist/assets/{agentRuntime-DGz_IqKW.js → agentRuntime-15Pkk6we.js} +1 -1
- package/client/dist/assets/agentRuntime-Bpx3yWHK.js +1 -0
- package/client/dist/assets/{agentRuntime-Bg9tbmqm.js → agentRuntime-C78n13To.js} +1 -1
- package/client/dist/assets/{agentRuntime-D-GJVGkj.js → agentRuntime-CCI7XF4c.js} +1 -1
- package/client/dist/assets/{agentRuntime-D2__CVI_.js → agentRuntime-CKDHe2-a.js} +1 -1
- package/client/dist/assets/{agentRuntime-D35dGVOP.js → agentRuntime-Ddqvp2bP.js} +1 -1
- package/client/dist/assets/{agentRuntime-hInH9Fzj.js → agentRuntime-TXPQowKJ.js} +1 -1
- package/client/dist/assets/{agentRuntime-O1liDt6G.js → agentRuntime-hqZRy08E.js} +1 -1
- package/client/dist/assets/{builder-DGETbtZj.js → builder-B9gorTnT.js} +1 -1
- package/client/dist/assets/{builder-DyQperXU.js → builder-Bvw5s3Mi.js} +1 -1
- package/client/dist/assets/{builder-C1neIYGR.js → builder-CL5l4dU3.js} +1 -1
- package/client/dist/assets/{builder-DXEJUlC0.js → builder-CVDTVrQX.js} +1 -1
- package/client/dist/assets/{builder-CzW5ovyI.js → builder-CpS9liE9.js} +1 -1
- package/client/dist/assets/{builder-CvnFm0im.js → builder-DNctIL9Q.js} +1 -1
- package/client/dist/assets/{builder-D06L7iPy.js → builder-De2NxX6o.js} +1 -1
- package/client/dist/assets/{builder-C0FyvM83.js → builder-DeYT7Y-m.js} +1 -1
- package/client/dist/assets/{button-Ccc9Dc-D.js → button-D1xExSTF.js} +1 -1
- package/client/dist/assets/{common-DmsclwjP.js → common-Bdi0qN4S.js} +1 -1
- package/client/dist/assets/{common-C4Yvvhp6.js → common-Bhb9zN1P.js} +1 -1
- package/client/dist/assets/{common-od8WThlf.js → common-BykKlZg1.js} +1 -1
- package/client/dist/assets/{common-C0BOdeNi.js → common-CvnNNSJA.js} +1 -1
- package/client/dist/assets/{common-BCK7c9Pv.js → common-D5zk_mqq.js} +1 -1
- package/client/dist/assets/{common-DpLWwgI1.js → common-DQNO_al5.js} +1 -1
- package/client/dist/assets/{common-OCvl3c8t.js → common-Dax30Vmp.js} +1 -1
- package/client/dist/assets/{common-c0aujEZC.js → common-DkllpVSb.js} +1 -1
- package/client/dist/assets/{dialog-xXB2q5HJ.js → dialog-CMgps2MY.js} +1 -1
- package/client/dist/assets/external-link-D6XxC_Cu.js +1 -0
- package/client/dist/assets/file-text-BcLCfcWZ.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-aBss1Nai.js → formatDistanceToNow-aw7H5ORC.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-DOGj1Mfz.js → getTimezoneOffsetInMilliseconds-NyC7gZJX.js} +1 -1
- package/client/dist/assets/index-CahchzS5.css +2 -0
- package/client/dist/assets/index-JSTZVJfe.js +83 -0
- package/client/dist/assets/integrations-BkpRUbvp.js +1 -0
- package/client/dist/assets/integrations-Bo8qbJf4.js +1 -0
- package/client/dist/assets/integrations-CmSrKzZb.js +1 -0
- package/client/dist/assets/integrations-DGtq26Rr.js +1 -0
- package/client/dist/assets/integrations-De38GFnQ.js +1 -0
- package/client/dist/assets/integrations-Dw6zdtb-.js +1 -0
- package/client/dist/assets/integrations-Dwl9KQp0.js +1 -0
- package/client/dist/assets/integrations-Refg_d2I.js +1 -0
- package/client/dist/assets/jira-api-DlXaRwA3.js +1 -0
- package/client/dist/assets/{jobs-KG4Sn12X.js → jobs-1G3wodhl.js} +1 -1
- package/client/dist/assets/{jobs-6_-r75yC.js → jobs-B4pu8TdF.js} +1 -1
- package/client/dist/assets/{jobs-pPv4KWjb.js → jobs-BD47Ov11.js} +1 -1
- package/client/dist/assets/{jobs-B1yqyId5.js → jobs-BLOoVRRl.js} +1 -1
- package/client/dist/assets/{jobs-BKiK-ZWh.js → jobs-CSrRx5lD.js} +1 -1
- package/client/dist/assets/{jobs-BFuV7ttH.js → jobs-DZAbh1m2.js} +1 -1
- package/client/dist/assets/{jobs-DmKV7b7d.js → jobs-epP7gt8W.js} +1 -1
- package/client/dist/assets/{jobs-BKa8LuwA.js → jobs-tWjcDF_6.js} +1 -1
- package/client/dist/assets/loops--KWr0X-w.js +1 -0
- package/client/dist/assets/{loops-BO3V8244.js → loops-3IutojWt.js} +1 -1
- package/client/dist/assets/{loops-C-hZK8pf.js → loops-BAosWtXE.js} +1 -1
- package/client/dist/assets/{loops-eUnCfLjC.js → loops-BISDssL5.js} +1 -1
- package/client/dist/assets/{loops-Dv849ckF.js → loops-Cm55CXJ4.js} +1 -1
- package/client/dist/assets/{loops-CDOWZ0ci.js → loops-D7tL_Saw.js} +1 -1
- package/client/dist/assets/{loops-BJDY9FTh.js → loops-DHQtnfze.js} +1 -1
- package/client/dist/assets/{loops-Q9LIgGkq.js → loops-kIJJkaxf.js} +1 -1
- package/client/dist/assets/nav--u7iPYlS.js +1 -0
- package/client/dist/assets/nav-C5Jgfaby.js +1 -0
- package/client/dist/assets/nav-CAna_Wpt.js +1 -0
- package/client/dist/assets/nav-CCLqeyHo.js +1 -0
- package/client/dist/assets/nav-Cf3c90Jk.js +1 -0
- package/client/dist/assets/nav-DMYQaQjR.js +1 -0
- package/client/dist/assets/nav-Df_PkZ9F.js +1 -0
- package/client/dist/assets/nav-VyNGBJwj.js +1 -0
- package/client/dist/assets/{project-repositories-CSnfzUpg.js → project-repositories-DXhb9FP4.js} +1 -1
- package/client/dist/assets/provider-capabilities-lePfeT_Y.js +1 -0
- package/client/dist/assets/save-OsUqVn99.js +1 -0
- package/client/dist/assets/settings-B5Hy_Kol.js +1 -0
- package/client/dist/assets/settings-BBj_kZaD.js +1 -0
- package/client/dist/assets/settings-ByVE7jdZ.js +1 -0
- package/client/dist/assets/settings-COlH_P33.js +1 -0
- package/client/dist/assets/settings-D77mC_Lb.js +1 -0
- package/client/dist/assets/settings-DZKy7MqN.js +1 -0
- package/client/dist/assets/settings-Dbyd6gmj.js +1 -0
- package/client/dist/assets/settings-Dn-pXukr.js +1 -0
- package/client/dist/assets/setup-Be485n2u.js +1 -0
- package/client/dist/assets/setup-CKGUxKr-.js +1 -0
- package/client/dist/assets/setup-Ck1Uwd7_.js +1 -0
- package/client/dist/assets/setup-Cm774a6v.js +1 -0
- package/client/dist/assets/setup-DIqMQpxt.js +1 -0
- package/client/dist/assets/setup-DfCsoNFL.js +1 -0
- package/client/dist/assets/setup-DkeE8mP0.js +1 -0
- package/client/dist/assets/setup-xVntVaz1.js +1 -0
- package/client/dist/assets/{spending-HBSk-6jv.js → spending-BdcyVkcd.js} +1 -1
- package/client/dist/assets/subscriptionUsage-Cf87DeS_.js +1 -0
- package/client/dist/assets/subscriptionUsage-D6u7VBdz.js +1 -0
- package/client/dist/assets/subscriptionUsage-DQ02J25O.js +1 -0
- package/client/dist/assets/subscriptionUsage-DWF3zKp4.js +1 -0
- package/client/dist/assets/subscriptionUsage-DkIf6Zuh.js +1 -0
- package/client/dist/assets/subscriptionUsage-DoWQv7l1.js +1 -0
- package/client/dist/assets/subscriptionUsage-N9hB-ARf.js +1 -0
- package/client/dist/assets/subscriptionUsage-zRcRPHkP.js +1 -0
- package/client/dist/assets/tauri-shell-DE6LfGPw.js +2 -0
- package/client/dist/assets/{useDesktop-0unHDBJJ.js → useDesktop-D07TvesS.js} +1 -1
- package/client/dist/assets/{useRuntimeRuns-BN1rhyEm.js → useRuntimeRuns-pGLmtAC6.js} +1 -1
- package/client/dist/assets/useSharedWebSocket-TzKtPRp9.js +2 -0
- package/client/dist/index.html +33 -32
- package/docs/README.md +1 -9
- package/docs/agent-live-steering.md +0 -6
- package/docs/agents/README.md +0 -2
- package/docs/agents/mcp.md +1 -14
- package/docs/cli.md +0 -13
- package/docs/codex.md +2 -75
- package/docs/creating-specs.md +0 -39
- package/docs/customizing.md +1 -15
- package/docs/getting-started.md +0 -23
- package/docs/guide/de/agents/2-profiles-and-the-balanced-default.md +0 -8
- package/docs/guide/de/agents/4-custom-agents-catalog.md +0 -15
- package/docs/guide/de/getting-started/1-what-is-specrails.md +0 -2
- package/docs/guide/de/getting-started/2-installing-and-first-run.md +0 -2
- package/docs/guide/de/getting-started/3-adding-your-first-project.md +0 -4
- package/docs/guide/de/getting-started/4-the-dashboard-tour.md +0 -5
- package/docs/guide/de/integrations/1-ai-providers.md +2 -50
- package/docs/guide/de/integrations/2-plugins.md +0 -8
- package/docs/guide/de/integrations/5-mcp-server.md +1 -14
- package/docs/guide/de/integrations/6-agent-chat.md +0 -11
- package/docs/guide/de/integrations/7-local-engines.md +0 -4
- package/docs/guide/de/pipeline/1-rails-and-jobs.md +0 -13
- package/docs/guide/de/pipeline/2-the-job-detail-view.md +0 -3
- package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +0 -20
- package/docs/guide/de/pipeline/5-the-loop-builder.md +0 -6
- package/docs/guide/de/specs/2-add-spec-quick-mode.md +0 -7
- package/docs/guide/en/agents/2-profiles-and-the-balanced-default.md +0 -10
- package/docs/guide/en/agents/4-custom-agents-catalog.md +0 -25
- package/docs/guide/en/getting-started/1-what-is-specrails.md +0 -2
- package/docs/guide/en/getting-started/2-installing-and-first-run.md +0 -2
- package/docs/guide/en/getting-started/3-adding-your-first-project.md +0 -4
- package/docs/guide/en/getting-started/4-the-dashboard-tour.md +0 -5
- package/docs/guide/en/insights/3-code-explorer.md +0 -5
- package/docs/guide/en/integrations/1-ai-providers.md +10 -99
- package/docs/guide/en/integrations/2-plugins.md +0 -8
- package/docs/guide/en/integrations/5-mcp-server.md +1 -1
- package/docs/guide/en/integrations/6-agent-chat.md +0 -17
- package/docs/guide/en/integrations/7-local-engines.md +0 -4
- package/docs/guide/en/pipeline/1-rails-and-jobs.md +2 -24
- package/docs/guide/en/pipeline/2-the-job-detail-view.md +0 -3
- package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +10 -65
- package/docs/guide/en/pipeline/5-the-loop-builder.md +0 -7
- package/docs/guide/en/specs/2-add-spec-quick-mode.md +0 -11
- package/docs/guide/es/agents/2-profiles-and-the-balanced-default.md +0 -8
- package/docs/guide/es/agents/4-custom-agents-catalog.md +0 -15
- package/docs/guide/es/getting-started/1-what-is-specrails.md +0 -2
- package/docs/guide/es/getting-started/2-installing-and-first-run.md +0 -2
- package/docs/guide/es/getting-started/3-adding-your-first-project.md +0 -4
- package/docs/guide/es/getting-started/4-the-dashboard-tour.md +0 -5
- package/docs/guide/es/integrations/1-ai-providers.md +10 -75
- package/docs/guide/es/integrations/2-plugins.md +0 -8
- package/docs/guide/es/integrations/5-mcp-server.md +1 -1
- package/docs/guide/es/integrations/6-agent-chat.md +0 -11
- package/docs/guide/es/integrations/7-local-engines.md +0 -4
- package/docs/guide/es/pipeline/1-rails-and-jobs.md +3 -13
- package/docs/guide/es/pipeline/2-the-job-detail-view.md +0 -3
- package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +10 -53
- package/docs/guide/es/pipeline/5-the-loop-builder.md +1 -6
- package/docs/guide/es/specs/2-add-spec-quick-mode.md +0 -7
- package/docs/guide/fr/agents/2-profiles-and-the-balanced-default.md +0 -8
- package/docs/guide/fr/agents/4-custom-agents-catalog.md +0 -15
- package/docs/guide/fr/getting-started/1-what-is-specrails.md +0 -2
- package/docs/guide/fr/getting-started/2-installing-and-first-run.md +0 -2
- package/docs/guide/fr/getting-started/3-adding-your-first-project.md +0 -4
- package/docs/guide/fr/getting-started/4-the-dashboard-tour.md +0 -5
- package/docs/guide/fr/integrations/1-ai-providers.md +2 -50
- package/docs/guide/fr/integrations/2-plugins.md +0 -8
- package/docs/guide/fr/integrations/5-mcp-server.md +1 -14
- package/docs/guide/fr/integrations/6-agent-chat.md +0 -11
- package/docs/guide/fr/integrations/7-local-engines.md +0 -4
- package/docs/guide/fr/pipeline/1-rails-and-jobs.md +0 -13
- package/docs/guide/fr/pipeline/2-the-job-detail-view.md +0 -3
- package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +0 -22
- package/docs/guide/fr/pipeline/5-the-loop-builder.md +0 -6
- package/docs/guide/fr/specs/2-add-spec-quick-mode.md +0 -7
- package/docs/guide/it/agents/2-profiles-and-the-balanced-default.md +0 -8
- package/docs/guide/it/agents/4-custom-agents-catalog.md +0 -15
- package/docs/guide/it/getting-started/1-what-is-specrails.md +0 -2
- package/docs/guide/it/getting-started/2-installing-and-first-run.md +0 -2
- package/docs/guide/it/getting-started/3-adding-your-first-project.md +0 -4
- package/docs/guide/it/getting-started/4-the-dashboard-tour.md +0 -5
- package/docs/guide/it/integrations/1-ai-providers.md +2 -48
- package/docs/guide/it/integrations/2-plugins.md +0 -8
- package/docs/guide/it/integrations/5-mcp-server.md +1 -14
- package/docs/guide/it/integrations/6-agent-chat.md +0 -11
- package/docs/guide/it/integrations/7-local-engines.md +0 -4
- package/docs/guide/it/pipeline/1-rails-and-jobs.md +2 -13
- package/docs/guide/it/pipeline/2-the-job-detail-view.md +0 -3
- package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +0 -20
- package/docs/guide/it/pipeline/5-the-loop-builder.md +1 -6
- package/docs/guide/it/specs/2-add-spec-quick-mode.md +0 -7
- package/docs/guide/ja/agents/2-profiles-and-the-balanced-default.md +0 -9
- package/docs/guide/ja/agents/4-custom-agents-catalog.md +0 -15
- package/docs/guide/ja/getting-started/1-what-is-specrails.md +0 -2
- package/docs/guide/ja/getting-started/2-installing-and-first-run.md +0 -2
- package/docs/guide/ja/getting-started/3-adding-your-first-project.md +0 -4
- package/docs/guide/ja/getting-started/4-the-dashboard-tour.md +0 -5
- package/docs/guide/ja/integrations/1-ai-providers.md +1 -69
- package/docs/guide/ja/integrations/2-plugins.md +0 -8
- package/docs/guide/ja/integrations/5-mcp-server.md +1 -14
- package/docs/guide/ja/integrations/6-agent-chat.md +0 -10
- package/docs/guide/ja/integrations/7-local-engines.md +0 -4
- package/docs/guide/ja/pipeline/1-rails-and-jobs.md +0 -13
- package/docs/guide/ja/pipeline/2-the-job-detail-view.md +0 -3
- package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +0 -21
- package/docs/guide/ja/pipeline/5-the-loop-builder.md +0 -6
- package/docs/guide/ja/specs/2-add-spec-quick-mode.md +0 -7
- package/docs/guide/pt/agents/2-profiles-and-the-balanced-default.md +0 -8
- package/docs/guide/pt/agents/4-custom-agents-catalog.md +0 -15
- package/docs/guide/pt/getting-started/1-what-is-specrails.md +0 -2
- package/docs/guide/pt/getting-started/2-installing-and-first-run.md +0 -2
- package/docs/guide/pt/getting-started/3-adding-your-first-project.md +0 -4
- package/docs/guide/pt/getting-started/4-the-dashboard-tour.md +0 -5
- package/docs/guide/pt/integrations/1-ai-providers.md +2 -48
- package/docs/guide/pt/integrations/2-plugins.md +0 -8
- package/docs/guide/pt/integrations/5-mcp-server.md +1 -14
- package/docs/guide/pt/integrations/6-agent-chat.md +0 -10
- package/docs/guide/pt/integrations/7-local-engines.md +0 -4
- package/docs/guide/pt/pipeline/1-rails-and-jobs.md +0 -13
- package/docs/guide/pt/pipeline/2-the-job-detail-view.md +0 -2
- package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +0 -21
- package/docs/guide/pt/pipeline/5-the-loop-builder.md +0 -6
- package/docs/guide/pt/specs/2-add-spec-quick-mode.md +0 -7
- package/docs/guide/zh/agents/2-profiles-and-the-balanced-default.md +0 -7
- package/docs/guide/zh/agents/4-custom-agents-catalog.md +0 -15
- package/docs/guide/zh/getting-started/1-what-is-specrails.md +0 -2
- package/docs/guide/zh/getting-started/2-installing-and-first-run.md +0 -2
- package/docs/guide/zh/getting-started/3-adding-your-first-project.md +0 -4
- package/docs/guide/zh/getting-started/4-the-dashboard-tour.md +0 -5
- package/docs/guide/zh/integrations/1-ai-providers.md +1 -67
- package/docs/guide/zh/integrations/2-plugins.md +0 -8
- package/docs/guide/zh/integrations/5-mcp-server.md +1 -14
- package/docs/guide/zh/integrations/6-agent-chat.md +0 -9
- package/docs/guide/zh/integrations/7-local-engines.md +0 -4
- package/docs/guide/zh/pipeline/1-rails-and-jobs.md +0 -13
- package/docs/guide/zh/pipeline/2-the-job-detail-view.md +0 -2
- package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +0 -20
- package/docs/guide/zh/pipeline/5-the-loop-builder.md +0 -6
- package/docs/guide/zh/specs/2-add-spec-quick-mode.md +0 -7
- package/docs/internals/adding-a-provider.md +7 -0
- package/docs/internals/desktop-owned-workflows.md +121 -0
- package/docs/internals/operations-runbook.md +20 -0
- package/docs/internals/programmatic-agent-runtime.md +34 -28
- package/docs/internals/project-builder.md +34 -11
- package/docs/internals/source-map.md +51 -3
- package/docs/internals/spec-addenda.md +11 -5
- package/docs/local-providers.md +8 -13
- package/docs/mcp.md +1 -17
- package/docs/platforms/macos.md +0 -40
- package/docs/platforms/windows-parity.md +1 -1
- package/docs/platforms/windows.md +0 -8
- package/docs/running-pipelines.md +1 -46
- package/docs/subscription-usage.md +19 -0
- package/docs/terminal.md +0 -2
- package/docs/tracking-cost.md +2 -33
- package/package.json +1 -1
- package/server/dist/core-execution.js +24 -15
- package/server/dist/desktop-db.js +23 -10
- package/server/dist/desktop-router.js +8 -4
- package/server/dist/index.js +10 -5
- package/server/dist/mcp/guide.js +1 -0
- package/server/dist/mcp/tools/jobs.js +1 -1
- package/server/dist/mcp/tools/loops.js +2 -0
- package/server/dist/mcp/tools/meta.js +6 -7
- package/server/dist/mcp/tools/projects.js +31 -6
- package/server/dist/mcp/tools/rails.js +6 -3
- package/server/dist/mcp/tools/setup.js +10 -0
- package/server/dist/mcp/tools/specs.js +1 -1
- package/server/dist/modules/accounting/runtime/pricing.js +6 -1
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +55 -12
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-metrics.js +1 -1
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings-router.js +1 -1
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings.js +42 -0
- package/server/dist/modules/agent-runtime/runtime/agent-runtime-verification-suggestions.js +10 -4
- package/server/dist/modules/builder/runtime/milestone-chain.js +76 -10
- package/server/dist/modules/delivery/runtime/delivery-evidence.js +3 -2
- package/server/dist/modules/delivery/runtime/multi-repo-execution.js +3 -0
- package/server/dist/modules/delivery/runtime/rail-isolated-launch.js +1 -0
- package/server/dist/modules/delivery/runtime/rail-launch-parser.js +8 -0
- package/server/dist/modules/delivery/runtime/rails-router.js +48 -26
- package/server/dist/modules/delivery/runtime/review-packet.js +1 -1
- package/server/dist/modules/loops/runtime/loop-agent-defaults.json +6 -0
- package/server/dist/modules/loops/runtime/loop-agents.js +21 -0
- package/server/dist/modules/loops/runtime/loop-command-catalog.js +1 -1
- package/server/dist/modules/loops/runtime/loop-core-factory.js +19 -3
- package/server/dist/modules/loops/runtime/loop-definition.js +79 -12
- package/server/dist/modules/loops/runtime/loop-effect.js +1 -1
- package/server/dist/modules/loops/runtime/loop-executors.js +24 -9
- package/server/dist/modules/loops/runtime/loop-factory.js +1 -1
- package/server/dist/modules/loops/runtime/loop-graph.js +5 -14
- package/server/dist/modules/loops/runtime/loop-implement-recipe.js +49 -0
- package/server/dist/modules/loops/runtime/loop-run-manager.js +3 -3
- package/server/dist/modules/loops/runtime/loop-templates.js +29 -5
- package/server/dist/modules/loops/runtime/loops-router.js +17 -0
- package/server/dist/modules/missions/runtime/agent-context-resolver.js +1 -1
- package/server/dist/modules/missions/runtime/agent-operator-prompt.js +17 -8
- package/server/dist/modules/specs/runtime/spec-models.js +4 -7
- package/server/dist/modules/subscription-usage/adapters/claude.js +113 -0
- package/server/dist/modules/subscription-usage/adapters/codex.js +164 -0
- package/server/dist/modules/subscription-usage/adapters/errors.js +15 -0
- package/server/dist/modules/subscription-usage/adapters/http.js +20 -0
- package/server/dist/modules/subscription-usage/adapters/local-auth.js +61 -0
- package/server/dist/modules/subscription-usage/application.js +11 -0
- package/server/dist/modules/subscription-usage/domain.js +70 -0
- package/server/dist/modules/subscription-usage/index.js +2 -0
- package/server/dist/modules/subscription-usage/ports.js +2 -0
- package/server/dist/modules/subscription-usage/runtime/composition.js +20 -0
- package/server/dist/modules/subscription-usage/runtime/usage-service.js +111 -0
- package/server/dist/project-registry.js +16 -1
- package/server/dist/project-repositories.js +51 -2
- package/server/dist/project-router-helpers.js +1 -1
- package/server/dist/project-router-loop-runs.js +4 -1
- package/server/dist/providers/claude-adapter.js +6 -4
- package/server/dist/providers/codex-adapter.js +4 -5
- package/server/dist/schemas/workflow-definition.schema.json +2 -0
- package/client/dist/assets/AgentBrowserCapture-PI-cxyxi.js +0 -1
- package/client/dist/assets/AgentModeAnalyticsPane-mQsOqdXx.js +0 -2
- package/client/dist/assets/AgentModeCodePane-BL-vpmlY.js +0 -2
- package/client/dist/assets/CodePage-Alitorrv.js +0 -3
- package/client/dist/assets/DocsDialog-DwL6rgcT.js +0 -11
- package/client/dist/assets/DocsPage-BA1hiJZ-.js +0 -11
- package/client/dist/assets/InteractiveJobComposer-B3H1YHr6.js +0 -19
- package/client/dist/assets/JobDetailModal-XZQ9eAAy.js +0 -1
- package/client/dist/assets/JobDetailPage-B-SIaq_q.js +0 -1
- package/client/dist/assets/JobsPage-iQXAv2mx.js +0 -1
- package/client/dist/assets/LoopBuilderPage-B_gbFAHn.js +0 -1
- package/client/dist/assets/LoopsPage-BZwcY5cu.js +0 -2
- package/client/dist/assets/MinimizedChatsContext-CNx_0bZu.js +0 -1
- package/client/dist/assets/PluginsPage-DFocnoHX.js +0 -2
- package/client/dist/assets/ProjectSettingsDialog-Bh3S6EBB.js +0 -1
- package/client/dist/assets/RepositoryScopeSelector-C2mvWBQ6.js +0 -1
- package/client/dist/assets/ReviewPacketPage-CrAf-WDy.js +0 -4
- package/client/dist/assets/TemplatePreviewModal-C7DordpN.js +0 -1
- package/client/dist/assets/TicketDetailModalContext-B-DySpKL.js +0 -16
- package/client/dist/assets/agent-BaG10AN6.js +0 -1
- package/client/dist/assets/agent-CH4aJ884.js +0 -1
- package/client/dist/assets/agent-CLc9fA7v.js +0 -1
- package/client/dist/assets/agent-DHh9-AdL.js +0 -1
- package/client/dist/assets/agent-DkKnd6Wv.js +0 -1
- package/client/dist/assets/agent-aiVhqFW1.js +0 -1
- package/client/dist/assets/agent-dbznD3eO.js +0 -1
- package/client/dist/assets/agent-sMVFcPwL.js +0 -1
- package/client/dist/assets/agentRuntime-BeUb3g9g.js +0 -1
- package/client/dist/assets/file-text-CFT-xXKN.js +0 -1
- package/client/dist/assets/index-BS7KCWkV.css +0 -2
- package/client/dist/assets/index-C4Mv6tV6.js +0 -80
- package/client/dist/assets/integrations-CH0zy1Mw.js +0 -1
- package/client/dist/assets/integrations-CIAjhd7L.js +0 -1
- package/client/dist/assets/integrations-CZdwOe8A.js +0 -1
- package/client/dist/assets/integrations-Ct7omMld.js +0 -1
- package/client/dist/assets/integrations-D6MSxy3-.js +0 -1
- package/client/dist/assets/integrations-DbZa-Zyc.js +0 -1
- package/client/dist/assets/integrations-b8pH-vfl.js +0 -1
- package/client/dist/assets/integrations-mB-7zoyT.js +0 -1
- package/client/dist/assets/jira-api-BYbEPlkr.js +0 -1
- package/client/dist/assets/layers-eDMjG41I.js +0 -1
- package/client/dist/assets/loops-B4_D-nbg.js +0 -1
- package/client/dist/assets/nav-B-tpjyc2.js +0 -1
- package/client/dist/assets/nav-BJNXGg29.js +0 -1
- package/client/dist/assets/nav-BdhtRLP7.js +0 -1
- package/client/dist/assets/nav-ByYgyN5T.js +0 -1
- package/client/dist/assets/nav-DA0Mva2c.js +0 -1
- package/client/dist/assets/nav-DcJm_Jg_.js +0 -1
- package/client/dist/assets/nav-DhMPtAXc.js +0 -1
- package/client/dist/assets/nav-DhPb-Isc.js +0 -1
- package/client/dist/assets/provider-capabilities-Vi1ZGvAK.js +0 -1
- package/client/dist/assets/save-B6wGWwBR.js +0 -1
- package/client/dist/assets/settings-3o4Ntzi5.js +0 -1
- package/client/dist/assets/settings-B97jlkwD.js +0 -1
- package/client/dist/assets/settings-BTaT_bsS.js +0 -1
- package/client/dist/assets/settings-CT8w7WiC.js +0 -1
- package/client/dist/assets/settings-Cvr8_tvh.js +0 -1
- package/client/dist/assets/settings-Dn1qgAC8.js +0 -1
- package/client/dist/assets/settings-EShDdg4J.js +0 -1
- package/client/dist/assets/settings-tfLhQZRN.js +0 -1
- package/client/dist/assets/setup-CBMu3tqH.js +0 -1
- package/client/dist/assets/setup-CPh-e6J2.js +0 -1
- package/client/dist/assets/setup-CRKLp_ke.js +0 -1
- package/client/dist/assets/setup-Cy302v4J.js +0 -1
- package/client/dist/assets/setup-D681vTPf.js +0 -1
- package/client/dist/assets/setup-DYK0VBkE.js +0 -1
- package/client/dist/assets/setup-KAuiIiVs.js +0 -1
- package/client/dist/assets/setup-OGX-oCfI.js +0 -1
- package/client/dist/assets/useSharedWebSocket-BZMrX4jr.js +0 -2
- package/client/dist/assets/wrench-BfmNO9Vw.js +0 -1
- package/docs/agy-cli-provider-study.md +0 -257
- package/docs/gemini-cli-provider-study.md +0 -301
- package/docs/gemini-core-support-evaluation.md +0 -160
- package/docs/gemini.md +0 -257
- package/docs/kimi.md +0 -291
|
@@ -25,17 +25,42 @@ function projectsTools() {
|
|
|
25
25
|
name: 'specrails_projects',
|
|
26
26
|
title: 'Projects',
|
|
27
27
|
description: 'List and inspect registered Specrails projects, resolve a project by filesystem path, or unregister one. ' +
|
|
28
|
-
'Actions: list, get, resolve (by path), unregister (destructive — removes the project and its workspace).',
|
|
28
|
+
'Actions: list, get, resolve (by project/repository/workspace path), repositories (inventory), repository_add / repository_update (write, configure registered code workspacePaths), repository_remove (destructive), unregister (destructive — removes the project and its workspace).',
|
|
29
29
|
hintTier: 'read',
|
|
30
|
-
tier: (args) => (args.action
|
|
30
|
+
tier: (args) => ['unregister', 'repository_remove'].includes(String(args.action)) ? 'destructive' : ['repository_add', 'repository_update'].includes(String(args.action)) ? 'write' : 'read',
|
|
31
31
|
inputSchema: {
|
|
32
|
-
action: zod_1.z.enum(['list', 'get', 'resolve', 'unregister']).describe('Operation to perform'),
|
|
33
|
-
projectId: zod_1.z.string().optional().describe('Project id
|
|
34
|
-
path: zod_1.z.string().optional().describe('Filesystem path
|
|
32
|
+
action: zod_1.z.enum(['list', 'get', 'resolve', 'repositories', 'repository_add', 'repository_update', 'repository_remove', 'unregister']).describe('Operation to perform'),
|
|
33
|
+
projectId: zod_1.z.string().optional().describe('Project id; defaults to the active project for get and repository operations; required for unregister'),
|
|
34
|
+
path: zod_1.z.string().optional().describe('Filesystem path for resolve or repository_add/update; repository root retains its Git/delivery identity'),
|
|
35
|
+
repositoryId: zod_1.z.string().min(1).optional().describe('Required for repository_update/remove; discover from repositories/get'),
|
|
36
|
+
name: zod_1.z.string().min(1).optional().describe('Repository display name'),
|
|
37
|
+
integrationBranch: zod_1.z.string().nullable().optional().describe('Repository integration branch; null clears it'),
|
|
38
|
+
workspacePaths: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(50).nullable().optional().describe('repository_add/update: code directories inside the repository, absolute or root-relative. null resets to repository root. Independent Git roots need separate memberships. Changes are rejected while active runs depend on this scope.'),
|
|
35
39
|
},
|
|
36
40
|
handler: (ctx, args) => {
|
|
37
41
|
const action = args.action;
|
|
38
42
|
switch (action) {
|
|
43
|
+
case 'repositories':
|
|
44
|
+
return (0, types_1.apiCall)(ctx, 'GET', `${(0, types_1.projectPath)(ctx, args.projectId)}/repositories`);
|
|
45
|
+
case 'repository_add':
|
|
46
|
+
case 'repository_update':
|
|
47
|
+
case 'repository_remove': {
|
|
48
|
+
const base = `${(0, types_1.projectPath)(ctx, args.projectId)}/repositories`;
|
|
49
|
+
const id = args.repositoryId;
|
|
50
|
+
if (action !== 'repository_add' && !id)
|
|
51
|
+
throw new Error(`${action} requires repositoryId.`);
|
|
52
|
+
if (action === 'repository_remove')
|
|
53
|
+
return (0, types_1.apiCall)(ctx, 'DELETE', `${base}/${encodeURIComponent(id)}`);
|
|
54
|
+
if (action === 'repository_add' && !args.path)
|
|
55
|
+
throw new Error('repository_add requires path.');
|
|
56
|
+
const body = {};
|
|
57
|
+
for (const key of ['path', 'name', 'integrationBranch', 'workspacePaths'])
|
|
58
|
+
if (args[key] !== undefined)
|
|
59
|
+
body[key] = args[key];
|
|
60
|
+
if (!Object.keys(body).length)
|
|
61
|
+
throw new Error('repository_update requires at least one field to update.');
|
|
62
|
+
return (0, types_1.apiCall)(ctx, action === 'repository_add' ? 'POST' : 'PATCH', action === 'repository_add' ? base : `${base}/${encodeURIComponent(id)}`, body);
|
|
63
|
+
}
|
|
39
64
|
case 'list':
|
|
40
65
|
return ctx.registry.listProjects().map((p) => ({ ...serializeProject(p), available: !!ctx.registry.getContext(p.id) }));
|
|
41
66
|
case 'get': {
|
|
@@ -53,7 +78,7 @@ function projectsTools() {
|
|
|
53
78
|
throw new Error('resolve requires a "path".');
|
|
54
79
|
const selected = args.projectId;
|
|
55
80
|
const key = (0, project_repositories_1.repositoryPathKey)((0, project_repositories_1.canonicalRepositoryPath)(p));
|
|
56
|
-
const matches = ctx.registry.listProjects().filter(row => !selected || row.id === selected).flatMap(project => (0, project_repositories_1.getProjectRepositories)(project).filter(repository => (0, project_repositories_1.repositoryPathKey)((0, project_repositories_1.canonicalRepositoryPath)(
|
|
81
|
+
const matches = ctx.registry.listProjects().filter(row => !selected || row.id === selected).flatMap(project => (0, project_repositories_1.getProjectRepositories)(project).filter(repository => [repository.path, ...(repository.workspacePaths ?? (repository.workspacePath ? [repository.workspacePath] : []))].some(root => (0, project_repositories_1.repositoryPathKey)((0, project_repositories_1.canonicalRepositoryPath)(root)) === key))
|
|
57
82
|
.map(repository => ({ project, repository })));
|
|
58
83
|
if (matches.length > 1)
|
|
59
84
|
return { resolved: false, ambiguous: true, matches: matches.map(({ project, repository }) => ({ projectId: project.id, projectName: project.name, repositoryId: repository.id, repositoryName: repository.name })) };
|
|
@@ -49,7 +49,7 @@ function railsTools() {
|
|
|
49
49
|
'When launched from the in-app agent chat without an explicit aiEngine, the engine defaults to your conversation\'s provider. On that same provider, an omitted model inherits the conversation model; an explicit model wins (pass aiEngine to override; launch_all always uses each rail\'s stored engine). ' +
|
|
50
50
|
'For Implement, the selected launch provider applies to ALL runtime roles (architect, developer, reviewer); an explicit launch model/effort also applies to each role. Resuming an existing execution keeps its original frozen role configuration; start a new launch to change providers. ' +
|
|
51
51
|
'User-facing naming: call the free-form autonomous mode "Freestyle"; use "freestyle" as the canonical API enum value for that same capability. ' +
|
|
52
|
-
'
|
|
52
|
+
'Always honor an explicit workflow selection, including Implement for addenda or delivery changes. Without a preference, for small OpenSpec-governed work, recommend "SDD Quick (OpenSpec)" and launch with mode "loop" plus loopId "factory:sdd-quick-openspec"; keep Freestyle for ticket-local implementation-only work. ' +
|
|
53
53
|
'NAMING: railIndex is the 0-BASED internal identity; the dashboard shows rails 1-based ("Rail N" = railIndex N-1). When talking to the user, ALWAYS say "Rail <railIndex + 1>" (or the rail\'s custom name) — results include railLabel with the correct user-facing label.',
|
|
54
54
|
hintTier: 'read',
|
|
55
55
|
tier: (a) => {
|
|
@@ -68,6 +68,7 @@ function railsTools() {
|
|
|
68
68
|
.describe('Operation to perform'),
|
|
69
69
|
projectId: zod_1.z.string().optional().describe('Project id (defaults to the active project)'),
|
|
70
70
|
repositoryIds: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(50).optional().describe('launch only: explicit affected repository memberships for the coordinated run; must include every repository required by the assigned specs. Omission uses the assigned specs\' repository selections.'),
|
|
71
|
+
workspaceSelection: zod_1.z.record(zod_1.z.string(), zod_1.z.array(zod_1.z.string().min(1)).min(1)).optional().describe('launch only: map selected repository IDs to registered code workspace paths; discover workspacePaths through project context. Omission verifies all configured code workspaces. Never use the parent project path unless it is itself a registered code workspace.'),
|
|
71
72
|
repositoryId: zod_1.z.string().min(1).optional().describe('review_packet only: member of a grouped delivery to inspect; omission returns the public parent with grouped outcomes.'),
|
|
72
73
|
railIndex: zod_1.z
|
|
73
74
|
.number()
|
|
@@ -129,11 +130,11 @@ function railsTools() {
|
|
|
129
130
|
revisionOfDeliveryId: zod_1.z
|
|
130
131
|
.string()
|
|
131
132
|
.optional()
|
|
132
|
-
.describe('Revise a delivery that is ALREADY awaiting the user\'s decision (launch). Continues with Quick SDD (the Revision loop is retired): pass its prDeliveryId (from rails.prDeliveries) together with revisionNote. Use it whenever the user asks for a change to work you already delivered — never publish/discard/merge first. The rail must still carry exactly that delivery\'s specs.'),
|
|
133
|
+
.describe('Revise a delivery that is ALREADY awaiting the user\'s decision (launch). Continues with the explicitly selected loop, defaulting to Quick SDD (the Revision loop is retired): pass its prDeliveryId (from rails.prDeliveries) together with revisionNote. Use it whenever the user asks for a change to work you already delivered — never publish/discard/merge first. The rail must still carry exactly that delivery\'s specs.'),
|
|
133
134
|
revisionNote: zod_1.z
|
|
134
135
|
.string()
|
|
135
136
|
.optional()
|
|
136
|
-
.describe('What to change, in the user\'s own words (required with revisionOfDeliveryId). It is injected into every
|
|
137
|
+
.describe('What to change, in the user\'s own words (required with revisionOfDeliveryId). It is injected into every selected workflow phase and shown on the updated review packet as "what you asked to change".'),
|
|
137
138
|
followUp: zod_1.z
|
|
138
139
|
.object({
|
|
139
140
|
comments: zod_1.z.array(zod_1.z.object({
|
|
@@ -257,6 +258,8 @@ function railsTools() {
|
|
|
257
258
|
body.targetPrNumber = args.targetPrNumber;
|
|
258
259
|
if (args.repositoryIds !== undefined)
|
|
259
260
|
body.repositoryIds = args.repositoryIds;
|
|
261
|
+
if (args.workspaceSelection !== undefined)
|
|
262
|
+
body.workspaceSelection = args.workspaceSelection;
|
|
260
263
|
if (args.baseBranch !== undefined)
|
|
261
264
|
body.baseBranch = args.baseBranch;
|
|
262
265
|
// Revision of a delivery already awaiting the user's decision: the
|
|
@@ -88,6 +88,8 @@ function setupTools() {
|
|
|
88
88
|
.array(zod_1.z.string())
|
|
89
89
|
.optional()
|
|
90
90
|
.describe('add_project: providers to install (first = primary/default); de-duped server-side'),
|
|
91
|
+
workspacePaths: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(50).optional().describe('add_project: code workspace directories inside the primary project root'),
|
|
92
|
+
repositories: zod_1.z.array(zod_1.z.object({ path: zod_1.z.string().min(1), name: zod_1.z.string().optional(), workspacePaths: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(50).optional(), integrationBranch: zod_1.z.string().nullable().optional() })).max(50).optional().describe('add_project: additional repository memberships with their code workspaces'),
|
|
91
93
|
// install_config
|
|
92
94
|
config: zod_1.z
|
|
93
95
|
.record(zod_1.z.unknown())
|
|
@@ -115,6 +117,10 @@ function setupTools() {
|
|
|
115
117
|
body.provider = args.provider;
|
|
116
118
|
if (Array.isArray(args.providers))
|
|
117
119
|
body.providers = args.providers;
|
|
120
|
+
if (args.workspacePaths !== undefined)
|
|
121
|
+
body.workspacePaths = args.workspacePaths;
|
|
122
|
+
if (args.repositories !== undefined)
|
|
123
|
+
body.repositories = args.repositories;
|
|
118
124
|
return (0, types_1.apiCall)(ctx, 'POST', '/projects', body);
|
|
119
125
|
}
|
|
120
126
|
// ─── Per-project ────────────────────────────────────────────────────
|
|
@@ -137,6 +143,10 @@ function setupTools() {
|
|
|
137
143
|
const body = {};
|
|
138
144
|
if (Array.isArray(args.providers))
|
|
139
145
|
body.providers = args.providers;
|
|
146
|
+
if (args.workspacePaths !== undefined)
|
|
147
|
+
body.workspacePaths = args.workspacePaths;
|
|
148
|
+
if (args.repositories !== undefined)
|
|
149
|
+
body.repositories = args.repositories;
|
|
140
150
|
else if (typeof args.provider === 'string')
|
|
141
151
|
body.provider = args.provider;
|
|
142
152
|
else
|
|
@@ -313,7 +313,7 @@ function specsTools() {
|
|
|
313
313
|
});
|
|
314
314
|
return {
|
|
315
315
|
...r,
|
|
316
|
-
hint: 'The addendum is open:
|
|
316
|
+
hint: 'The addendum is open: respect the user\'s explicit loop selection, including factory:implement; otherwise recommend factory:sdd-quick-openspec. Rail launches continue a same-spec pending delivery, and brief every AI step with the full delta and a fresh OpenSpec target. Do not use Revision or edit openspecChangeName. Do not edit the spec description to repeat it.',
|
|
317
317
|
};
|
|
318
318
|
}
|
|
319
319
|
case 'update_addendum': {
|
|
@@ -76,7 +76,10 @@ exports.PRICING = {
|
|
|
76
76
|
// Opus 5.5: https://platform.claude.com/docs/en/models/opus-5-5/overview
|
|
77
77
|
'claude:opus': { inputPer1M: 4.00, outputPer1M: 20.00, cacheReadPer1M: 0.20, cacheWritePer1M: 5.00, inputIncludesCacheReads: false, lastReviewedAt: '2026-09-24' },
|
|
78
78
|
'claude:claude-opus-5-5': { inputPer1M: 4.00, outputPer1M: 20.00, cacheReadPer1M: 0.20, cacheWritePer1M: 5.00, inputIncludesCacheReads: false, lastReviewedAt: '2026-09-24' },
|
|
79
|
-
'claude:sonnet': { inputPer1M: 3.00, outputPer1M: 15.00, cacheReadPer1M: 0.30, cacheWritePer1M: 3.75, inputIncludesCacheReads: false, lastReviewedAt: '2026-07-02' },
|
|
79
|
+
'claude:claude-sonnet-5': { inputPer1M: 3.00, outputPer1M: 15.00, cacheReadPer1M: 0.30, cacheWritePer1M: 3.75, inputIncludesCacheReads: false, lastReviewedAt: '2026-07-02' },
|
|
80
|
+
// Sonnet 5.5: https://platform.claude.com/docs/en/models/sonnet-5-5/overview
|
|
81
|
+
'claude:sonnet': { inputPer1M: 2.00, outputPer1M: 10.00, cacheReadPer1M: 0.20, cacheWritePer1M: 2.50, inputIncludesCacheReads: false, lastReviewedAt: '2026-09-30' },
|
|
82
|
+
'claude:claude-sonnet-5-5': { inputPer1M: 2.00, outputPer1M: 10.00, cacheReadPer1M: 0.20, cacheWritePer1M: 2.50, inputIncludesCacheReads: false, lastReviewedAt: '2026-09-30' },
|
|
80
83
|
'claude:haiku': { inputPer1M: 1.00, outputPer1M: 5.00, cacheReadPer1M: 0.10, cacheWritePer1M: 1.25, inputIncludesCacheReads: false, lastReviewedAt: '2026-07-02' },
|
|
81
84
|
'claude:fable': { inputPer1M: 10.00, outputPer1M: 50.00, cacheReadPer1M: 1.00, cacheWritePer1M: 12.50, inputIncludesCacheReads: false, lastReviewedAt: '2026-07-02' },
|
|
82
85
|
};
|
|
@@ -96,6 +99,8 @@ function resolvePriceEntry(providerId, model) {
|
|
|
96
99
|
// Keep the existing estimates for older concrete Opus generations.
|
|
97
100
|
if (/^claude-opus-(?:4(?:-|$)|5$)/.test(model))
|
|
98
101
|
return exports.PRICING['claude:claude-opus-5'];
|
|
102
|
+
if (/^claude-sonnet-(?:4(?:-|$)|5$)/.test(model))
|
|
103
|
+
return exports.PRICING['claude:claude-sonnet-5'];
|
|
99
104
|
const family = CLAUDE_MODEL_FAMILY.exec(model);
|
|
100
105
|
if (family)
|
|
101
106
|
return exports.PRICING[`claude:${family[1]}`];
|
|
@@ -32,13 +32,39 @@ function runtimeChangeName(runId) {
|
|
|
32
32
|
* cwd is relative to the registered directory; one already inside the scope is kept.
|
|
33
33
|
*/
|
|
34
34
|
function scopedHostChecks(checks, repositories = []) {
|
|
35
|
-
return checks.
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
return checks.flatMap(check => {
|
|
36
|
+
const repository = repositories.find(repository => repository.id === check.repositoryId);
|
|
37
|
+
const scopes = repository?.scope;
|
|
38
|
+
if (!repository || !scopes?.length)
|
|
39
|
+
return [check];
|
|
40
|
+
if (check.cwd === undefined)
|
|
41
|
+
return scopes.map((scope, index) => ({ ...check, cwd: scope,
|
|
42
|
+
...(check.key && scopes.length > 1 ? { key: check.key.slice(0, 90) + '-workspace-' + index } : {}),
|
|
43
|
+
}));
|
|
44
|
+
const declared = repository.registeredScope;
|
|
45
|
+
const inputCwd = check.cwd.replace(/\\/g, '/');
|
|
46
|
+
const rawCwd = (0, node_path_1.isAbsolute)(inputCwd) && repository.path ? (0, node_path_1.relative)(repository.path, inputCwd).split(node_path_1.sep).join('/') : inputCwd;
|
|
47
|
+
if (!(0, node_path_1.isAbsolute)(rawCwd) && declared?.some(root => rawCwd === root || rawCwd.startsWith(root + '/')) && !scopes.some(root => rawCwd === root || rawCwd.startsWith(root + '/')))
|
|
48
|
+
return [];
|
|
49
|
+
let cwd = check.cwd.replace(/\\/g, '/');
|
|
50
|
+
if ((0, node_path_1.isAbsolute)(cwd)) {
|
|
51
|
+
if (!repository.path)
|
|
52
|
+
throw new Error('Verification workspace root is unavailable');
|
|
53
|
+
cwd = (0, node_path_1.relative)(repository.path, cwd).split(node_path_1.sep).join('/');
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const normalized = node_path_1.posix.normalize(cwd);
|
|
57
|
+
if (scopes.some(scope => cwd === scope || cwd.startsWith(scope + '/')))
|
|
58
|
+
cwd = normalized;
|
|
59
|
+
else if (scopes.length === 1)
|
|
60
|
+
cwd = node_path_1.posix.join(scopes[0], normalized);
|
|
61
|
+
else
|
|
62
|
+
throw new Error('Verification must select an explicit workspace cwd when several scopes are configured');
|
|
63
|
+
}
|
|
64
|
+
cwd = node_path_1.posix.normalize(cwd);
|
|
65
|
+
if (!scopes.some(root => cwd === root || cwd.startsWith(root + '/')))
|
|
66
|
+
throw new Error(`Verification cwd ${JSON.stringify(check.cwd)} escapes the selected code workspace ${scopes.join(', ')}`);
|
|
67
|
+
return [{ ...check, cwd }];
|
|
42
68
|
});
|
|
43
69
|
}
|
|
44
70
|
exports.RUNTIME_HOST_ENV_KEYS = [
|
|
@@ -80,7 +106,7 @@ function readFrozenRuntimeHost(contextPath, baseEnv, runId) {
|
|
|
80
106
|
* rail's cancellation and worktree ownership remain in Desktop. */
|
|
81
107
|
async function runAgentRuntimeInvocation(options) {
|
|
82
108
|
const definitionEngine = options.engineVersion === 2 || options.definitionPath !== undefined || options.prepareDefinition !== undefined;
|
|
83
|
-
if (options.resume && (options.definitionPath || options.prepareDefinition || options.workflowRoleBindings))
|
|
109
|
+
if (options.resume && (options.definitionPath || options.prepareDefinition || options.workflowRoleBindings || options.loopConfig))
|
|
84
110
|
throw new Error('Resume must use the frozen workflow definition and role bindings');
|
|
85
111
|
const selectedCli = options.resume ? (0, agent_runtime_package_1.resolveRetainedAgentRuntime)(options.contextPath) : (0, agent_runtime_loader_1.findCoreAgentRuntimeCli)();
|
|
86
112
|
let cli = selectedCli;
|
|
@@ -96,11 +122,12 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
96
122
|
if (!Array.isArray(admittedContext.repositories) || !admittedContext.repositories.length || admittedContext.repositories.some(repo => !repo || typeof repo.id !== 'string' || !repo.id))
|
|
97
123
|
throw new Error('Core context is missing its repository scope');
|
|
98
124
|
const source = (0, node_fs_1.existsSync)(options.configPath) ? 'project-role' : 'default';
|
|
99
|
-
const { config, origins } = (0, agent_runtime_effective_config_1.resolveEffectiveRuntimeConfig)((0, agent_runtime_settings_1.loadRuntimeConfigFile)(options.configPath, options.defaultProvider), {
|
|
100
|
-
repositoryIds: admittedContext.repositories.map(repo => repo.id), source, providerOverride: options.providerOverride,
|
|
125
|
+
const { config, origins } = (0, agent_runtime_effective_config_1.resolveEffectiveRuntimeConfig)(options.loopConfig ? (0, agent_runtime_settings_1.loadLoopRuntimeConfig)(options.configPath, options.loopConfig, options.providerOverride ?? { provider: options.defaultProvider ?? 'claude' }) : (0, agent_runtime_settings_1.loadRuntimeConfigFile)(options.configPath, options.defaultProvider), {
|
|
126
|
+
repositoryIds: admittedContext.repositories.map(repo => repo.id), source: options.loopConfig ? 'loop-role' : source, providerOverride: options.loopConfig ? undefined : options.providerOverride,
|
|
101
127
|
});
|
|
102
128
|
config.verification = scopedHostChecks(config.verification, admittedContext.repositories);
|
|
103
|
-
|
|
129
|
+
if (!options.loopConfig)
|
|
130
|
+
config.rolePrompts = { ...(await (0, agent_runtime_loader_1.loadCoreAgentRuntime)()).rolePromptDefaults(), ...(0, agent_runtime_settings_1.loadRuntimeRolePrompts)(), ...config.rolePrompts };
|
|
104
131
|
// Core rejects unknown connection keys: drop the desktop-only local-engine
|
|
105
132
|
// fields (label/defaultModel/rates/supportsReasoningEffort) before Core sees it.
|
|
106
133
|
const runtime = await (0, agent_runtime_loader_1.loadCoreAgentRuntime)();
|
|
@@ -120,8 +147,13 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
120
147
|
if (definitionEngine && !draft)
|
|
121
148
|
throw new Error('A new Core workflow requires a definition');
|
|
122
149
|
const selectedOrigins = (0, agent_runtime_effective_config_1.bindWorkflowRoleSelections)(config, draft, options.workflowRoleBindings);
|
|
150
|
+
if (options.loopConfig && draft?.roles?.includes('loop-decider') && !config.roles?.['loop-decider'])
|
|
151
|
+
throw new Error('The loop must define its loop-decider agent');
|
|
123
152
|
const workflowOrigins = { ...(0, agent_runtime_effective_config_1.bindWorkflowRoleDefaults)(config, draft), ...selectedOrigins };
|
|
124
|
-
|
|
153
|
+
if (options.loopConfig)
|
|
154
|
+
for (const role of Object.keys(config.roles ?? {}))
|
|
155
|
+
workflowOrigins[role] = 'loop-role';
|
|
156
|
+
const override = options.loopConfig ? undefined : options.providerOverride;
|
|
125
157
|
runtime.validateRuntimeConfig(JSON.parse(JSON.stringify(config)));
|
|
126
158
|
(0, agent_runtime_loader_1.validateRequestedRoleEfforts)(runtime, config);
|
|
127
159
|
cli = (0, agent_runtime_package_1.retainAgentRuntime)(cli, options.contextPath);
|
|
@@ -169,6 +201,17 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
169
201
|
const definitionFile = (0, node_path_1.join)((0, node_path_1.dirname)(options.contextPath), 'desktop-workflow-definition.json');
|
|
170
202
|
const requiresVerified = definitionEngine && JSON.parse((0, node_fs_1.readFileSync)(definitionFile, 'utf8')).delivery?.requiresVerified === true;
|
|
171
203
|
const frozenPath = (0, node_path_1.join)((0, node_path_1.dirname)(options.contextPath), 'desktop-runtime-config.json');
|
|
204
|
+
if ((0, node_fs_1.existsSync)(frozenPath)) {
|
|
205
|
+
const checks = JSON.parse((0, node_fs_1.readFileSync)(frozenPath, 'utf8')).verification;
|
|
206
|
+
for (const check of checks) {
|
|
207
|
+
const repository = admittedContext.repositories?.find(repo => repo.id === check.repositoryId);
|
|
208
|
+
const cwd = repository?.path ? (0, node_path_1.resolve)(repository.path, check.cwd ?? repository.scope?.[0] ?? '.') : check.cwd;
|
|
209
|
+
try {
|
|
210
|
+
options.onLine?.(`[verification scope] ${JSON.stringify({ repositoryId: check.repositoryId, workspace: repository?.name, cwd, command: check.command, args: check.args })}\n`);
|
|
211
|
+
}
|
|
212
|
+
catch { /* Advisory log observer. */ }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
172
215
|
if ((0, node_fs_1.existsSync)(frozenPath)) {
|
|
173
216
|
const frozen = JSON.parse((0, node_fs_1.readFileSync)(frozenPath, 'utf8'));
|
|
174
217
|
for (const [role, assignment] of [...Object.entries(frozen.agents), ...(frozen.fixer ? [['fixer', frozen.fixer]] : [])]) {
|
|
@@ -121,7 +121,7 @@ function applyRuntimeSelectionOrigins(summary, selection, runId) {
|
|
|
121
121
|
const record = object(selection), origins = object(record?.origins);
|
|
122
122
|
if (!summary || record?.schemaVersion !== 1 || record.runId !== runId || !origins)
|
|
123
123
|
return summary;
|
|
124
|
-
if (!['architect', 'developer', 'reviewer'].every(role => ['project-role', 'default', ...(role === 'developer' ? ['explicit-launch-override'] : [])].includes(String(origins[role]))))
|
|
124
|
+
if (!['architect', 'developer', 'reviewer'].every(role => ['loop-role', 'project-role', 'default', ...(role === 'developer' ? ['explicit-launch-override'] : [])].includes(String(origins[role]))))
|
|
125
125
|
return summary;
|
|
126
126
|
return { ...summary, roles: summary.roles.map(role => {
|
|
127
127
|
const origin = origins[role.role];
|
|
@@ -84,7 +84,7 @@ function registerAgentRuntimeSettingsRoutes({ router, ctx }) {
|
|
|
84
84
|
router.get('/:projectId/agent-runtime/verification-suggestions', (req, res) => {
|
|
85
85
|
try {
|
|
86
86
|
const project = ctx(req).project;
|
|
87
|
-
const repositories = (0, project_repositories_1.getProjectRepositories)(project).map((repository) => ({ id: repository.id, name: repository.name, path: repository.path }));
|
|
87
|
+
const repositories = (0, project_repositories_1.getProjectRepositories)(project).map((repository) => ({ id: repository.id, name: repository.name, path: repository.path, workspacePath: repository.workspacePath, workspacePaths: repository.workspacePaths }));
|
|
88
88
|
res.json({ repositories: repositories.map(({ id, name }) => ({ id, name })), suggestions: (0, agent_runtime_verification_suggestions_1.suggestVerificationCommands)(repositories) });
|
|
89
89
|
}
|
|
90
90
|
catch {
|
|
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.AgentRuntimeConfigError = exports.REVIEW_THRESHOLD_FLOORS = exports.MAX_OUTPUT_TOKENS_MAX = exports.MAX_OUTPUT_TOKENS_MIN = exports.CONTEXT_WINDOW_TOKENS_MAX = exports.CONTEXT_WINDOW_TOKENS_MIN = exports.RUNTIME_ROLE_ID = void 0;
|
|
7
7
|
exports.stripDesktopConnectionFields = stripDesktopConnectionFields;
|
|
8
8
|
exports.coreConnectionFieldGates = coreConnectionFieldGates;
|
|
9
|
+
exports.validateLoopRuntimeSettings = validateLoopRuntimeSettings;
|
|
10
|
+
exports.loadLoopRuntimeConfig = loadLoopRuntimeConfig;
|
|
9
11
|
exports.validateAgentRuntimeConfig = validateAgentRuntimeConfig;
|
|
10
12
|
exports.defaultAgentRuntimeConfig = defaultAgentRuntimeConfig;
|
|
11
13
|
exports.agentRuntimeConfigPath = agentRuntimeConfigPath;
|
|
@@ -121,6 +123,46 @@ exports.REVIEW_THRESHOLD_FLOORS = {
|
|
|
121
123
|
minScore: 70,
|
|
122
124
|
aspects: { type_correctness: 60, pattern_adherence: 60, test_coverage: 60, security: 75, architectural_alignment: 60 },
|
|
123
125
|
};
|
|
126
|
+
/** Validate a portable recipe without accepting host-owned fields or project fallbacks. */
|
|
127
|
+
function validateLoopRuntimeSettings(input, providers = loadRuntimeProviders()) {
|
|
128
|
+
if (!input || typeof input !== 'object' || Array.isArray(input) || Object.keys(input).some(key => ![
|
|
129
|
+
'schemaVersion', 'agents', 'fixer', 'roles', 'rolePrompts', 'limits', 'review', 'architect', 'approvalBeforeArchive', 'efficiency', 'guardrails',
|
|
130
|
+
].includes(key)))
|
|
131
|
+
throw new AgentRuntimeConfigError('Invalid loop agent configuration');
|
|
132
|
+
// "inherit" is a recipe-only assignment, resolved before Core sees it.
|
|
133
|
+
const portableProviders = [...providers, { id: 'inherit', kind: 'cli', cli: 'claude' }];
|
|
134
|
+
const config = validateAgentRuntimeConfig({ ...input, enabled: true, providers: portableProviders, verification: [] });
|
|
135
|
+
if (['architect', 'developer', 'reviewer', 'fixer'].some(role => !config.rolePrompts?.[role]?.trim()))
|
|
136
|
+
throw new AgentRuntimeConfigError('Loop agents require an explicit definition for architect, developer, reviewer and fixer');
|
|
137
|
+
const { enabled: _enabled, providers: _providers, verification: _checks, ...settings } = config;
|
|
138
|
+
return settings;
|
|
139
|
+
}
|
|
140
|
+
/** Read only repository checks from the old project file. Role migration is explicit. */
|
|
141
|
+
function loadLoopRuntimeConfig(file, input, selection = { provider: 'claude' }) {
|
|
142
|
+
let verification = [];
|
|
143
|
+
try {
|
|
144
|
+
const saved = JSON.parse(node_fs_1.default.readFileSync(file, 'utf8'));
|
|
145
|
+
if (!saved || typeof saved !== 'object' || Array.isArray(saved))
|
|
146
|
+
throw new AgentRuntimeConfigError('Saved runtime configuration must be an object');
|
|
147
|
+
verification = saved.verification ?? [];
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (error.code !== 'ENOENT')
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
const providers = loadRuntimeProviders();
|
|
154
|
+
const settings = validateLoopRuntimeSettings(input, providers);
|
|
155
|
+
const inherit = (agent) => agent.provider !== 'inherit' ? agent : {
|
|
156
|
+
...agent, provider: selection.provider, model: agent.model ?? selection.model, effort: agent.effort ?? selection.effort,
|
|
157
|
+
};
|
|
158
|
+
for (const role of ROLES)
|
|
159
|
+
settings.agents[role] = inherit(settings.agents[role]);
|
|
160
|
+
if (settings.fixer)
|
|
161
|
+
settings.fixer = inherit(settings.fixer);
|
|
162
|
+
for (const [role, agent] of Object.entries(settings.roles ?? {}))
|
|
163
|
+
settings.roles[role] = inherit(agent);
|
|
164
|
+
return validateAgentRuntimeConfig({ ...settings, providers, enabled: true, verification });
|
|
165
|
+
}
|
|
124
166
|
const CLI_PROVIDERS = ['claude', 'codex', 'gemini', 'kimi'];
|
|
125
167
|
const ROLES = ['architect', 'developer', 'reviewer'];
|
|
126
168
|
const schemaValidator = new ajv_1.default({ allErrors: true }).compile(agent_runtime_schema_json_1.default);
|
|
@@ -110,10 +110,16 @@ function otherSuggestions(repository) {
|
|
|
110
110
|
function suggestVerificationCommands(repositories) {
|
|
111
111
|
const out = [];
|
|
112
112
|
for (const repository of repositories) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
const workspaces = repository.workspacePaths ?? (repository.workspacePath ? [repository.workspacePath] : [repository.path]);
|
|
114
|
+
for (const workspace of workspaces) {
|
|
115
|
+
if (!workspace || !node_fs_1.default.existsSync(workspace))
|
|
116
|
+
continue;
|
|
117
|
+
const member = { ...repository, path: workspace };
|
|
118
|
+
const node = nodeSuggestions(member);
|
|
119
|
+
const checks = node.length ? node : otherSuggestions(member);
|
|
120
|
+
const cwd = node_path_1.default.relative(repository.path, workspace).split(node_path_1.default.sep).join('/');
|
|
121
|
+
out.push(...checks.map(check => cwd ? { ...check, cwd, reason: `${cwd}: ${check.reason}` } : check));
|
|
122
|
+
}
|
|
117
123
|
}
|
|
118
124
|
return out;
|
|
119
125
|
}
|
|
@@ -4,14 +4,11 @@
|
|
|
4
4
|
// "Launch Milestone N": order the milestone's todo specs by their declared
|
|
5
5
|
// dependencies (prerequisites, then execution_order), put ONE spec on each
|
|
6
6
|
// rail (mode `implement`), launch the first through the ORDINARY rails launch
|
|
7
|
-
// route, and
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// `rail.pr_state` broadcast tapped in the project's bound broadcast — the
|
|
13
|
-
// engine's `onLoopRunFinished` fires BEFORE the delivery row leaves
|
|
14
|
-
// `building`, so it is only a fallback for delivery-less shared-cwd runs),
|
|
7
|
+
// route, and integrate each completed delivery through the guarded local decision
|
|
8
|
+
// path BEFORE allocating the next worktree from the integration branch. Older
|
|
9
|
+
// adapters without integration retain their historical stacking behavior.
|
|
10
|
+
// Launches are sequential; auto-continue is on by default. The plan lives in
|
|
11
|
+
// SQLite and retains the current delivery through integration and recovery.
|
|
15
12
|
// pauses with a typed reason on any failure (never skips ahead), and is
|
|
16
13
|
// visible/controllable (resume / cancel) from every surface.
|
|
17
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -77,6 +74,7 @@ class MilestoneChainManager {
|
|
|
77
74
|
db;
|
|
78
75
|
projectId;
|
|
79
76
|
io;
|
|
77
|
+
integrating = new Set();
|
|
80
78
|
constructor(db, projectId, io) {
|
|
81
79
|
this.db = db;
|
|
82
80
|
this.projectId = projectId;
|
|
@@ -218,7 +216,7 @@ class MilestoneChainManager {
|
|
|
218
216
|
}
|
|
219
217
|
/**
|
|
220
218
|
* Launch a milestone. Always sequential: one spec per rail, each stacked on
|
|
221
|
-
* the
|
|
219
|
+
* the accumulated integration branch when integration is bound. `autoAdvance` defaults to ON; turning it off
|
|
222
220
|
* parks the chain at a checkpoint after every delivered rail.
|
|
223
221
|
*/
|
|
224
222
|
async start(n, opts = {}) {
|
|
@@ -308,7 +306,7 @@ class MilestoneChainManager {
|
|
|
308
306
|
return;
|
|
309
307
|
}
|
|
310
308
|
const earlierDeliveries = (0, milestone_chain_store_1.parseLaunched)(row).filter((entry) => entry.chunk <= chunkIndex && entry.deliveryId).map((entry) => entry.deliveryId);
|
|
311
|
-
const multiRepositoryHistory = earlierDeliveries.some((id) => this.io.getDelivery(id)?.executionManifest);
|
|
309
|
+
const multiRepositoryHistory = !this.io.integrateDelivery && earlierDeliveries.some((id) => this.io.getDelivery(id)?.executionManifest);
|
|
312
310
|
if (!multiRepositoryHistory && head && !(await this.branchExistsSafe(head))) {
|
|
313
311
|
this.pause(row, 'head_missing', { headBranch: head });
|
|
314
312
|
return;
|
|
@@ -371,6 +369,56 @@ class MilestoneChainManager {
|
|
|
371
369
|
return 'chunk_stopped';
|
|
372
370
|
return 'chunk_failed';
|
|
373
371
|
}
|
|
372
|
+
completeIntegrated(row) {
|
|
373
|
+
if (!row.current_delivery_id || !(0, milestone_chain_store_1.isActiveChainStatus)(row.status))
|
|
374
|
+
return;
|
|
375
|
+
(0, milestone_chain_store_1.updateChain)(this.db, row.id, row.status, { status: 'waiting', currentDeliveryId: null, currentRunIds: [], headBranch: row.integration_branch, pauseReason: 'integration_complete' }, this.now());
|
|
376
|
+
this.afterChunkSuccess(row.id, row.integration_branch);
|
|
377
|
+
}
|
|
378
|
+
/** Keep the delivery attached until its guarded integration is durably confirmed. */
|
|
379
|
+
async integrateChunk(chainId) {
|
|
380
|
+
if (!this.io.integrateDelivery || this.integrating.has(chainId))
|
|
381
|
+
return;
|
|
382
|
+
const row = (0, milestone_chain_store_1.getChain)(this.db, chainId);
|
|
383
|
+
if (!row?.current_delivery_id || !(0, milestone_chain_store_1.isActiveChainStatus)(row.status))
|
|
384
|
+
return;
|
|
385
|
+
const snap = this.io.getDelivery(row.current_delivery_id);
|
|
386
|
+
if (!snap) {
|
|
387
|
+
this.pause(row, 'integration_failed:delivery_missing');
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (snap.implementationOutcome === 'partially_succeeded' || snap.implementationOutcome === 'failed') {
|
|
391
|
+
this.pauseFailedChunk(row, 'chunk_failed');
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (snap.decision === 'merged' || snap.decision === 'completed') {
|
|
395
|
+
this.completeIntegrated(row);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
this.integrating.add(chainId);
|
|
399
|
+
(0, milestone_chain_store_1.updateChain)(this.db, row.id, row.status, { status: 'waiting', pauseReason: 'integration_pending' }, this.now());
|
|
400
|
+
const pending = (0, milestone_chain_store_1.getChain)(this.db, chainId);
|
|
401
|
+
this.emit(pending);
|
|
402
|
+
try {
|
|
403
|
+
const result = await this.io.integrateDelivery(snap);
|
|
404
|
+
const current = (0, milestone_chain_store_1.getChain)(this.db, chainId);
|
|
405
|
+
if (!current || !(0, milestone_chain_store_1.isActiveChainStatus)(current.status) || current.current_delivery_id !== snap.id)
|
|
406
|
+
return;
|
|
407
|
+
const settled = this.io.getDelivery(snap.id);
|
|
408
|
+
if (settled?.decision === 'merged' || settled?.decision === 'completed')
|
|
409
|
+
this.completeIntegrated(current);
|
|
410
|
+
else
|
|
411
|
+
this.pause(current, 'integration_failed:' + (result.ok ? 'not_confirmed' : result.error));
|
|
412
|
+
}
|
|
413
|
+
catch {
|
|
414
|
+
const current = (0, milestone_chain_store_1.getChain)(this.db, chainId);
|
|
415
|
+
if (current && (0, milestone_chain_store_1.isActiveChainStatus)(current.status) && current.current_delivery_id === snap.id)
|
|
416
|
+
this.pause(current, 'integration_failed:unavailable');
|
|
417
|
+
}
|
|
418
|
+
finally {
|
|
419
|
+
this.integrating.delete(chainId);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
374
422
|
/** The delivery-settle chokepoint: a `rail.pr_state` for the in-flight chunk. */
|
|
375
423
|
handleDeliveryState(snap) {
|
|
376
424
|
for (const row of (0, milestone_chain_store_1.listActiveChains)(this.db)) {
|
|
@@ -378,6 +426,13 @@ class MilestoneChainManager {
|
|
|
378
426
|
continue;
|
|
379
427
|
if (snap.decision === 'building')
|
|
380
428
|
continue;
|
|
429
|
+
if (this.io.integrateDelivery && SUCCESS_DECISIONS.has(snap.decision)) {
|
|
430
|
+
if (row.status === 'running' || row.status === 'waiting' && row.pause_reason === 'integration_pending')
|
|
431
|
+
void this.integrateChunk(row.id);
|
|
432
|
+
else if (row.pause_reason?.startsWith('integration_') && ['merged', 'completed'].includes(snap.decision))
|
|
433
|
+
this.completeIntegrated(row);
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
381
436
|
if (SUCCESS_DECISIONS.has(snap.decision)) {
|
|
382
437
|
const delivered = snap.executionManifest ? null : snap.decision === 'no_changes'
|
|
383
438
|
? row.head_branch
|
|
@@ -457,6 +512,10 @@ class MilestoneChainManager {
|
|
|
457
512
|
return { ok: false, status: 404, error: 'chain_not_found' };
|
|
458
513
|
if (row.status !== 'paused' && row.status !== 'awaiting_approval')
|
|
459
514
|
return { ok: false, status: 409, error: 'chain_not_paused', detail: `chain is ${row.status}` };
|
|
515
|
+
if (this.io.integrateDelivery && row.current_delivery_id && row.pause_reason?.startsWith('integration_')) {
|
|
516
|
+
await this.integrateChunk(id);
|
|
517
|
+
return { ok: true, status: 202, chain: (0, milestone_chain_store_1.toChainSnapshot)((0, milestone_chain_store_1.getChain)(this.db, id)) };
|
|
518
|
+
}
|
|
460
519
|
if (row.head_branch && !(await this.branchExistsSafe(row.head_branch))) {
|
|
461
520
|
(0, milestone_chain_store_1.updateChain)(this.db, row.id, row.status, { status: 'paused', pauseReason: 'head_missing' }, this.now());
|
|
462
521
|
const fresh = (0, milestone_chain_store_1.getChain)(this.db, row.id);
|
|
@@ -503,6 +562,13 @@ class MilestoneChainManager {
|
|
|
503
562
|
*/
|
|
504
563
|
async recoverOnStartup() {
|
|
505
564
|
for (const row of (0, milestone_chain_store_1.listActiveChains)(this.db)) {
|
|
565
|
+
if (this.io.integrateDelivery && row.status === 'waiting' && row.pause_reason?.startsWith('integration_')) {
|
|
566
|
+
if (row.current_delivery_id)
|
|
567
|
+
await this.integrateChunk(row.id);
|
|
568
|
+
else if (row.pause_reason === 'integration_complete')
|
|
569
|
+
this.afterChunkSuccess(row.id, row.integration_branch);
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
506
572
|
if (row.status !== 'running')
|
|
507
573
|
continue;
|
|
508
574
|
if (row.current_delivery_id) {
|
|
@@ -475,7 +475,7 @@ function projectDefinitionRuntimeEvidence(status, reviewerStepId) {
|
|
|
475
475
|
if (!['succeeded', 'failed'].includes(scope.status) || !scope.attemptId || !isRecord(scope.output))
|
|
476
476
|
continue;
|
|
477
477
|
const output = scope.output;
|
|
478
|
-
if (scope.kind === 'verify' && typeof output.receiptId === 'string' && Array.isArray(output.commands)) {
|
|
478
|
+
if ((scope.kind === 'verify' || scope.kind === 'implementation-step' && output.phase === 'verify') && typeof output.receiptId === 'string' && Array.isArray(output.commands)) {
|
|
479
479
|
for (const raw of output.commands) {
|
|
480
480
|
if (commands.length >= RUNTIME_LIST_CAP)
|
|
481
481
|
break;
|
|
@@ -487,7 +487,8 @@ function projectDefinitionRuntimeEvidence(status, reviewerStepId) {
|
|
|
487
487
|
}
|
|
488
488
|
}
|
|
489
489
|
const review = scope.status !== 'succeeded' ? null : scope.kind === 'implementation' ? parseRuntimeReview(output.review)
|
|
490
|
-
:
|
|
490
|
+
: scope.kind === 'implementation-step' && output.phase === 'reviewer' ? parseRuntimeReview(output)
|
|
491
|
+
: reviewerStepId === scope.nodePath && scope.kind === 'role-turn' ? parseRuntimeReview(output.structured ?? output) : null;
|
|
491
492
|
if (review)
|
|
492
493
|
scopedReviews.push({ nodePath: scope.nodePath, scopeId: scope.scopeId, attemptId: scope.attemptId, review });
|
|
493
494
|
}
|
|
@@ -389,6 +389,9 @@ async function launchMultiRepositoryRail(input, io = {}) {
|
|
|
389
389
|
if (head.code !== 0 || !/^[a-f0-9]{40,64}$/i.test(head.stdout.trim()))
|
|
390
390
|
throw new Error(`Cannot freeze HEAD for ${repository.name}`);
|
|
391
391
|
manifest.repositories.push({ repositoryId: repository.id, name: repository.name, sourcePath: repository.path,
|
|
392
|
+
...(repository.workspacePath ? { workspacePath: repository.workspacePath } : {}),
|
|
393
|
+
...(repository.workspacePaths ? { workspacePaths: [...repository.workspacePaths] } : {}),
|
|
394
|
+
...((input.workspaceSelection?.[repository.id] ?? previousRepository?.selectedWorkspacePaths) ? { selectedWorkspacePaths: [...(input.workspaceSelection?.[repository.id] ?? previousRepository.selectedWorkspacePaths)] } : {}),
|
|
392
395
|
gitCommonDir: (0, repo_lock_1.repositoryLockKey)(repository.path), baseBranch: child.base_branch, integrationBranch: integrationBranches.get(repository.id) ?? previousRepository?.integrationBranch ?? child.base_branch, baseSha: head.stdout.trim(),
|
|
393
396
|
worktreePath: worktree.worktree_path, branch: worktree.branch, worktreeId: worktree.id });
|
|
394
397
|
}
|
|
@@ -1275,6 +1275,7 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1275
1275
|
runId: a.runId, loopId, loopName, graph: loopGraph, projectId: ctx.project.id,
|
|
1276
1276
|
cwd: a.handle.worktreePath, repoDir: a.handle.worktreePath,
|
|
1277
1277
|
repositoryId: singleRepositoryId,
|
|
1278
|
+
workspacePaths: singleRepositoryId ? input.workspaceSelection?.[singleRepositoryId] : undefined,
|
|
1278
1279
|
isolation: { branch: a.handle.branch, worktreePath: a.handle.worktreePath },
|
|
1279
1280
|
railIndex, ticketId: a.ticketIds.length ? a.ticketId : null,
|
|
1280
1281
|
spec: {
|
|
@@ -75,6 +75,12 @@ function coerceRailLaunchProposal(value) {
|
|
|
75
75
|
const mode = modeRaw === 'batch' || modeRaw === 'batch-implement' ? 'implement' : modeRaw;
|
|
76
76
|
if (!exports.RAIL_LAUNCH_MODES.includes(mode))
|
|
77
77
|
return { ok: false, reason: 'invalid_mode' };
|
|
78
|
+
const repositoryIds = value.repositoryIds;
|
|
79
|
+
const workspaceSelection = value.workspaceSelection;
|
|
80
|
+
if (repositoryIds !== undefined && (!Array.isArray(repositoryIds) || !repositoryIds.length || repositoryIds.length > 50 || repositoryIds.some(id => typeof id !== 'string' || !id.trim()) || new Set(repositoryIds).size !== repositoryIds.length))
|
|
81
|
+
return { ok: false, reason: 'invalid_workspace_selection' };
|
|
82
|
+
if (workspaceSelection !== undefined && (!isRecord(workspaceSelection) || !Object.keys(workspaceSelection).length || Object.keys(workspaceSelection).length > 50 || Object.entries(workspaceSelection).some(([id, paths]) => !id.trim() || !Array.isArray(paths) || !paths.length || paths.length > 50 || paths.some(item => typeof item !== 'string' || !item.trim() || item.length > 4096) || new Set(paths).size !== paths.length)))
|
|
83
|
+
return { ok: false, reason: 'invalid_workspace_selection' };
|
|
78
84
|
const newRailRaw = value.newRail;
|
|
79
85
|
const newRail = newRailRaw === true
|
|
80
86
|
? { name: null }
|
|
@@ -89,6 +95,8 @@ function coerceRailLaunchProposal(value) {
|
|
|
89
95
|
railIndex,
|
|
90
96
|
newRail,
|
|
91
97
|
ticketIds,
|
|
98
|
+
...(repositoryIds !== undefined ? { repositoryIds: repositoryIds } : {}),
|
|
99
|
+
...(workspaceSelection !== undefined ? { workspaceSelection: workspaceSelection } : {}),
|
|
92
100
|
mode: mode,
|
|
93
101
|
loopId: optString(value.loopId, 120),
|
|
94
102
|
aiEngine: optString(value.aiEngine ?? value.provider ?? value.engine, 80),
|