specrails-desktop 2.58.1 → 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
|
@@ -510,7 +510,7 @@ function createRailsRouter() {
|
|
|
510
510
|
}
|
|
511
511
|
// Non-string values fail the VALID_MODES check below.
|
|
512
512
|
let mode = normalizeRailMode(req.body?.mode ?? 'implement');
|
|
513
|
-
const { repositoryIds: rawRepositoryIds, baseDeliveryIds, profileName, aiEngine, model, loopId: rawLoopId, reasoning_effort, originConversationId, originSurface, targetPrNumber, revisionOfDeliveryId, revisionNote, baseBranch: rawBaseBranch, followUp: rawFollowUp } = req.body ?? {};
|
|
513
|
+
const { repositoryIds: rawRepositoryIds, workspaceSelection: rawWorkspaceSelection, baseDeliveryIds, profileName, aiEngine, model, loopId: rawLoopId, reasoning_effort, originConversationId, originSurface, targetPrNumber, revisionOfDeliveryId, revisionNote, baseBranch: rawBaseBranch, followUp: rawFollowUp } = req.body ?? {};
|
|
514
514
|
// PR review follow-up (pr-follow-up-fixes): a typed, bounded, FROZEN scope
|
|
515
515
|
// for "resolve these review comments". It rides the delivery row and every
|
|
516
516
|
// ai-step prompt — never the spec, whose description Jira-linked projects
|
|
@@ -570,6 +570,7 @@ function createRailsRouter() {
|
|
|
570
570
|
res.status(400).json({ error: 'base_branch_requires_isolation', detail: 'baseBranch requires an isolated (worktree) loop launch; loops are disabled' });
|
|
571
571
|
return;
|
|
572
572
|
}
|
|
573
|
+
const explicitLoop = (typeof rawLoopId === 'string' && rawLoopId.length > 0) || req.body?.mode != null;
|
|
573
574
|
let loopId = rawLoopId;
|
|
574
575
|
// Origin link (safe-pr-review-flow): an agent-chat/MCP launch tags itself so
|
|
575
576
|
// the PR decision can later be posted back into the launching conversation.
|
|
@@ -609,8 +610,8 @@ function createRailsRouter() {
|
|
|
609
610
|
if ((0, feature_flags_1.isLoopsEnabled)() && (typeof loopId !== 'string' || !loopId) && mode !== 'loop') {
|
|
610
611
|
loopId = (0, loop_factory_1.factoryLoopForMode)(mode)?.id;
|
|
611
612
|
}
|
|
612
|
-
//
|
|
613
|
-
if (revisionOfDeliveryId && (0, feature_flags_1.isLoopsEnabled)()) {
|
|
613
|
+
// Quick SDD is the continuation default; an explicit workflow takes precedence.
|
|
614
|
+
if (revisionOfDeliveryId && (0, feature_flags_1.isLoopsEnabled)() && !explicitLoop) {
|
|
614
615
|
loopId = 'factory:sdd-quick-openspec';
|
|
615
616
|
mode = 'loop';
|
|
616
617
|
}
|
|
@@ -649,18 +650,20 @@ function createRailsRouter() {
|
|
|
649
650
|
res.status(400).json({ error: 'Rail has no tickets assigned' });
|
|
650
651
|
return;
|
|
651
652
|
}
|
|
652
|
-
//
|
|
653
|
+
// A durable delta defaults to Quick SDD when the caller has not chosen a workflow.
|
|
653
654
|
// Revision is a delivery-continuation mechanism, not the implementation loop.
|
|
654
655
|
const launchAddenda = (0, spec_addenda_1.planSpecAddendaAt)((0, spec_addenda_1.ticketStorePathForProject)(c.project), rail.ticketIds);
|
|
655
|
-
if (launchAddenda.length && (0, feature_flags_1.isLoopsEnabled)()) {
|
|
656
|
+
if (launchAddenda.length && (0, feature_flags_1.isLoopsEnabled)() && !explicitLoop) {
|
|
656
657
|
loopId = 'factory:sdd-quick-openspec';
|
|
657
658
|
mode = 'loop';
|
|
658
659
|
}
|
|
659
660
|
let repositoryIds;
|
|
661
|
+
let workspaceSelection;
|
|
660
662
|
const primaryRepository = (0, project_repositories_1.getProjectRepositories)(c.project).find((repository) => repository.isPrimary);
|
|
661
663
|
try {
|
|
662
664
|
const requiredRepositoryIds = [...new Set(rail.ticketIds.flatMap((id) => (0, project_repositories_1.validateTicketRepositoryIds)(c.project, c.getTicketSpec?.(id)?.repositoryIds) ?? [primaryRepository.id]))];
|
|
663
665
|
repositoryIds = (0, project_repositories_1.validateTicketRepositoryIds)(c.project, rawRepositoryIds) ?? requiredRepositoryIds;
|
|
666
|
+
workspaceSelection = (0, project_repositories_1.validateWorkspaceSelection)(c.project, rawWorkspaceSelection, repositoryIds);
|
|
664
667
|
const missing = requiredRepositoryIds.filter((id) => !repositoryIds.includes(id));
|
|
665
668
|
if (missing.length) {
|
|
666
669
|
res.status(400).json({ error: 'repository_scope_incomplete', missingRepositoryIds: missing,
|
|
@@ -717,9 +720,29 @@ function createRailsRouter() {
|
|
|
717
720
|
res.status(409).json({ error: 'tickets_in_flight', ticketIds: inFlightOverlap });
|
|
718
721
|
return;
|
|
719
722
|
}
|
|
723
|
+
// Resolve once: a concurrent publication cannot mix one recipe's agents
|
|
724
|
+
// with another recipe's graph. Loop-owned roles bypass project launch presets.
|
|
725
|
+
let builtinSelection;
|
|
726
|
+
let librarySelection;
|
|
727
|
+
if ((0, feature_flags_1.isLoopsEnabled)() && typeof loopId === 'string' && loopId) {
|
|
728
|
+
if ((0, loop_factory_1.isFactoryLoopId)(loopId)) {
|
|
729
|
+
let capabilities;
|
|
730
|
+
try {
|
|
731
|
+
capabilities = (await (0, agent_runtime_loader_1.loadCoreAgentRuntime)()).api?.capabilities;
|
|
732
|
+
}
|
|
733
|
+
catch { /* Retained legacy recipes remain available. */ }
|
|
734
|
+
builtinSelection = (0, builtin_loops_1.resolveBuiltinLoop)(c.desktopDb, loopId, capabilities);
|
|
735
|
+
}
|
|
736
|
+
else
|
|
737
|
+
librarySelection = (0, loops_store_1.getLoop)(c.desktopDb, loopId);
|
|
738
|
+
}
|
|
739
|
+
const loopAgents = builtinSelection?.graph.config.agents ?? (librarySelection?.status === 'published' ? librarySelection.graph.config.agents : undefined);
|
|
740
|
+
const ownsLoopEngine = !!loopAgents && loopAgents.agents.developer.provider !== 'inherit';
|
|
741
|
+
if (ownsLoopEngine)
|
|
742
|
+
runtimeProviderOverride = undefined;
|
|
720
743
|
// AI engine precedence: explicit body param > stored rail engine > primary.
|
|
721
744
|
// `undefined`/empty in both means "run on the project's primary provider".
|
|
722
|
-
const requestedEngineRaw = aiEngine === undefined ? (rail.aiEngine ?? undefined) : aiEngine;
|
|
745
|
+
const requestedEngineRaw = ownsLoopEngine ? loopAgents.agents.developer.provider : aiEngine === undefined ? (runtimeProviderOverride?.provider ?? rail.aiEngine ?? undefined) : aiEngine;
|
|
723
746
|
// `roles` (hybrid-role-engines): the pipeline roles keep the project's
|
|
724
747
|
// per-role runtime settings (no flattening override) and the loop's own
|
|
725
748
|
// steps come from the stored loop-role engines — verifier for every
|
|
@@ -734,7 +757,9 @@ function createRailsRouter() {
|
|
|
734
757
|
const requestedEngine = rolesMode
|
|
735
758
|
? (loopRoleEngines.verifier?.provider && (0, provider_selection_1.validateRequestedProvider)(c.project, loopRoleEngines.verifier.provider).ok ? loopRoleEngines.verifier.provider : undefined)
|
|
736
759
|
: requestedEngineRaw;
|
|
737
|
-
const engineCheck =
|
|
760
|
+
const engineCheck = ownsLoopEngine
|
|
761
|
+
? (0, registry_1.hasAdapter)(loopAgents.agents.developer.provider) ? { ok: true, provider: loopAgents.agents.developer.provider } : { ok: false, error: 'Loop agent provider is not configured' }
|
|
762
|
+
: (0, provider_selection_1.validateRequestedProvider)(c.project, requestedEngine);
|
|
738
763
|
if (!engineCheck.ok) {
|
|
739
764
|
res.status(400).json({ error: engineCheck.error });
|
|
740
765
|
return;
|
|
@@ -753,7 +778,7 @@ function createRailsRouter() {
|
|
|
753
778
|
res.status(400).json({ error: 'runtime_provider_mismatch', detail: 'A roles launch carries no provider override' });
|
|
754
779
|
return;
|
|
755
780
|
}
|
|
756
|
-
if (!runtimeProviderOverride && requestedEngine && !rolesMode) {
|
|
781
|
+
if (!ownsLoopEngine && !runtimeProviderOverride && requestedEngine && !rolesMode) {
|
|
757
782
|
try {
|
|
758
783
|
runtimeProviderOverride = (0, agent_runtime_settings_1.validateRuntimeProviderOverride)({ provider: engineCheck.provider, ...(model ? { model } : {}), ...(reasoning_effort && !localEngine ? { effort: reasoning_effort } : {}) });
|
|
759
784
|
}
|
|
@@ -769,7 +794,7 @@ function createRailsRouter() {
|
|
|
769
794
|
if (localEngine && runtimeProviderOverride?.effort !== undefined) {
|
|
770
795
|
runtimeProviderOverride = { provider: runtimeProviderOverride.provider, ...(runtimeProviderOverride.model !== undefined ? { model: runtimeProviderOverride.model } : {}) };
|
|
771
796
|
}
|
|
772
|
-
if (localEngine && req.body?.maxCostUsd !== undefined && req.body?.maxCostUsd !== null) {
|
|
797
|
+
if (!ownsLoopEngine && localEngine && req.body?.maxCostUsd !== undefined && req.body?.maxCostUsd !== null) {
|
|
773
798
|
res.status(400).json({ error: 'local_engine_no_cost_cap' });
|
|
774
799
|
return;
|
|
775
800
|
}
|
|
@@ -781,7 +806,7 @@ function createRailsRouter() {
|
|
|
781
806
|
}
|
|
782
807
|
// Freestyle is provider-owned. Validate its optional model against the
|
|
783
808
|
// selected engine instead of a Claude-only alias list.
|
|
784
|
-
if (mode === 'freestyle' &&
|
|
809
|
+
if (!ownsLoopEngine && mode === 'freestyle' &&
|
|
785
810
|
model !== undefined &&
|
|
786
811
|
model !== null &&
|
|
787
812
|
(typeof model !== 'string' || !(0, spec_models_1.isValidModelForProvider)(model, engineCheck.provider))) {
|
|
@@ -795,7 +820,7 @@ function createRailsRouter() {
|
|
|
795
820
|
// `null` in the body explicitly forces legacy mode. Providers without
|
|
796
821
|
// profile support run in legacy mode.
|
|
797
822
|
let resolvedProfile;
|
|
798
|
-
if (mode === 'freestyle') {
|
|
823
|
+
if (loopAgents || mode === 'freestyle') {
|
|
799
824
|
// Freestyle runs no agent pipeline, so profiles do not apply.
|
|
800
825
|
resolvedProfile = null;
|
|
801
826
|
}
|
|
@@ -826,15 +851,10 @@ function createRailsRouter() {
|
|
|
826
851
|
let loopGraph;
|
|
827
852
|
let loopName;
|
|
828
853
|
if ((0, loop_factory_1.isFactoryLoopId)(loopId)) {
|
|
829
|
-
let capabilities;
|
|
830
|
-
try {
|
|
831
|
-
capabilities = (await (0, agent_runtime_loader_1.loadCoreAgentRuntime)()).api?.capabilities;
|
|
832
|
-
}
|
|
833
|
-
catch { /* Existing Core remains supported through the legacy factory. */ }
|
|
834
854
|
// Editable built-ins: an edited row runs its Published graph (or the
|
|
835
855
|
// last Published snapshot while an edit is Draft); otherwise the
|
|
836
856
|
// current code default for this Core.
|
|
837
|
-
const f =
|
|
857
|
+
const f = builtinSelection;
|
|
838
858
|
if (!f) {
|
|
839
859
|
res.status(404).json({ error: 'Factory loop not found' });
|
|
840
860
|
return;
|
|
@@ -855,7 +875,7 @@ function createRailsRouter() {
|
|
|
855
875
|
loopName = f.name;
|
|
856
876
|
}
|
|
857
877
|
else {
|
|
858
|
-
const loop =
|
|
878
|
+
const loop = librarySelection;
|
|
859
879
|
if (!loop) {
|
|
860
880
|
res.status(404).json({ error: 'Loop not found' });
|
|
861
881
|
return;
|
|
@@ -898,7 +918,7 @@ function createRailsRouter() {
|
|
|
898
918
|
});
|
|
899
919
|
return;
|
|
900
920
|
}
|
|
901
|
-
if (typeof model === 'string' && model && !(0, spec_models_1.isValidModelForProvider)(model, loopProvider)) {
|
|
921
|
+
if (!ownsLoopEngine && typeof model === 'string' && model && !(0, spec_models_1.isValidModelForProvider)(model, loopProvider)) {
|
|
902
922
|
res.status(400).json({ error: `model is not valid for provider "${loopProvider}"`, allowed: (0, spec_models_1.getModelsForProvider)(loopProvider) });
|
|
903
923
|
return;
|
|
904
924
|
}
|
|
@@ -919,14 +939,15 @@ function createRailsRouter() {
|
|
|
919
939
|
}
|
|
920
940
|
}
|
|
921
941
|
const verifierEngine = rolesMode ? (0, loop_role_engines_1.resolveLoopRoleEngine)(c.project, 'verifier', { provider: loopProvider, model: loopAdapter.defaultModel() }, loopRoleEngines) : undefined;
|
|
922
|
-
const loopModel =
|
|
923
|
-
|
|
924
|
-
|
|
942
|
+
const loopModel = ownsLoopEngine ? loopAgents.agents.developer.model ?? loopAdapter.defaultModel() :
|
|
943
|
+
loopAgents?.agents.developer.model ?? runtimeProviderOverride?.model ?? (typeof model === 'string' && model
|
|
944
|
+
? model
|
|
945
|
+
: (verifierEngine?.model ?? profileModel ?? globalAgentDefaults?.pipelineModel ?? loopAdapter.defaultModel()));
|
|
925
946
|
let effort;
|
|
926
947
|
// Local (OpenAI-compatible) engines have no per-invocation effort knob
|
|
927
948
|
// unless the connection opts in; a requested effort is DROPPED, not a
|
|
928
949
|
// 400 — the engine selectors send the last-used effort blindly.
|
|
929
|
-
const effortRequested = localEngine && !loopAdapter.capabilities.supportsReasoningEffort ? undefined : (reasoning_effort ?? verifierEngine?.effort);
|
|
950
|
+
const effortRequested = localEngine && !loopAdapter.capabilities.supportsReasoningEffort ? undefined : ownsLoopEngine ? loopAgents.agents.developer.effort : (loopAgents?.agents.developer.effort ?? runtimeProviderOverride?.effort ?? reasoning_effort ?? verifierEngine?.effort);
|
|
930
951
|
if (effortRequested !== undefined && effortRequested !== null) {
|
|
931
952
|
const allowed = (0, providers_1.reasoningEffortsForModel)(loopAdapter, loopModel);
|
|
932
953
|
if (typeof effortRequested !== 'string' ||
|
|
@@ -939,7 +960,7 @@ function createRailsRouter() {
|
|
|
939
960
|
}
|
|
940
961
|
effort = effortRequested;
|
|
941
962
|
}
|
|
942
|
-
else if (globalAgentDefaults?.pipelineEffort
|
|
963
|
+
else if (!ownsLoopEngine && globalAgentDefaults?.pipelineEffort
|
|
943
964
|
&& (0, runtime_1.isReasoningEffortValidForModel)(loopAdapter, loopModel, globalAgentDefaults.pipelineEffort)) {
|
|
944
965
|
effort = globalAgentDefaults.pipelineEffort;
|
|
945
966
|
}
|
|
@@ -961,7 +982,7 @@ function createRailsRouter() {
|
|
|
961
982
|
res.status(400).json({ error: `This loop uses a command unsupported by provider '${loopProvider}'` });
|
|
962
983
|
return;
|
|
963
984
|
}
|
|
964
|
-
const scope = (0, loop_command_catalog_1.dominantTicketScope)(promptsText);
|
|
985
|
+
const scope = loopGraph.config.ticketScope ?? (loopGraph.config.journal === 'implementation' ? 'all' : (0, loop_command_catalog_1.dominantTicketScope)(promptsText));
|
|
965
986
|
// Parallel isolation (default-on; disable with SPECRAILS_RAIL_WORKTREES=0):
|
|
966
987
|
// a per-ticket rail on a repo-mutating loop runs each ticket in its own git
|
|
967
988
|
// worktree, then merges the branches back. A non-git repo or an unborn
|
|
@@ -1114,7 +1135,7 @@ function createRailsRouter() {
|
|
|
1114
1135
|
try {
|
|
1115
1136
|
const ids = await (0, rail_isolated_launch_1.launchIsolatedRail)({
|
|
1116
1137
|
runtimeProviderOverride,
|
|
1117
|
-
ctx: c, railIndex, ticketIds: [...rail.ticketIds], repositoryIds, ...repositoryBases, loopId, loopName, loopGraph,
|
|
1138
|
+
ctx: c, railIndex, ticketIds: [...rail.ticketIds], repositoryIds, workspaceSelection, ...repositoryBases, loopId, loopName, loopGraph,
|
|
1118
1139
|
provider: loopProvider, model: loopModel, effort, scope,
|
|
1119
1140
|
...(deciderEngine ? { deciderEngine } : {}),
|
|
1120
1141
|
profileName: resolvedProfile,
|
|
@@ -1256,6 +1277,7 @@ function createRailsRouter() {
|
|
|
1256
1277
|
graph: loopGraph,
|
|
1257
1278
|
projectId: c.project.id,
|
|
1258
1279
|
repositoryId: primaryRepository.id,
|
|
1280
|
+
workspacePaths: workspaceSelection?.[primaryRepository.id],
|
|
1259
1281
|
cwd: loopExec.cwd,
|
|
1260
1282
|
repoDir: loopExec.relocated ? loopExec.repoDir : undefined,
|
|
1261
1283
|
railIndex,
|
|
@@ -280,7 +280,7 @@ function computeDriftNudges(input) {
|
|
|
280
280
|
function followUpReportSource(evidence) {
|
|
281
281
|
if (!evidence)
|
|
282
282
|
return null;
|
|
283
|
-
const parts = evidence.units.flatMap((unit) => [unit.verifyTail, unit.sentinelDetail]).filter((v) => typeof v === 'string' && v.length > 0);
|
|
283
|
+
const parts = evidence.units.flatMap((unit) => [unit.verifyTail, unit.sentinelDetail, unit.runtime?.review?.summary]).filter((v) => typeof v === 'string' && v.length > 0);
|
|
284
284
|
return parts.length > 0 ? parts.join('\n') : null;
|
|
285
285
|
}
|
|
286
286
|
function composeReviewPacket({ db, row, repositoryId = row.repository_id ?? undefined, includeLegacyProvenance = false }) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"architect": "## Your task: architecture\n\nYou are the Specrails architect. Turn the requested work into an unambiguous, implementable plan that a developer agent will execute without talking to you.\n\n1. Orient quickly: locate the code the change touches, the tests that cover it and the conventions that apply. Calibrate depth to the blast radius. A localized change gets a short proposal, focused design and only the tasks it needs; a cross-cutting change earns a full impact analysis.\nIn design.md include a Local reference patterns section: cite actual repository paths and symbols for the closest existing implementation and its tests. Explain async rendering/state updates, error propagation and test setup when relevant. Check installed framework versions. If no equivalent exists, state that explicitly; never invent references.\n2. Decide the approach, name the exact files/modules to create or change (inside the repository scope when one is declared), and call out risks, edge cases and compatibility concerns. Plan only the change the requested work needs; unrelated clean-ups are out of scope.\n3. Break the work into ordered, atomic tasks. Each task names concrete files and includes its own tests. Every task must be completable by an agent that can only edit files and run commands: never add tasks such as \"run the test suite\", \"verify\", \"test manually in a browser\", \"commit\" or \"open a PR\". Core runs verification and the host owns delivery.\n4. Execute the official OpenSpec fast-forward skill. Query status and instructions in dependency order; author the real delta specs and other artifacts using those templates and project rules. Preserve existing requirements through OpenSpec delta semantics. Do not fabricate complete replacement specs.\n5. Propose verification. For each repository listed below without a configured verification command, name the existing command that proves the change: the project's test script, type check, build or lint (for example `npm` with args `[\"test\"]`, or `cargo` with `[\"test\"]`). Only propose commands that exist in the repository today or that a task in this plan adds; omit repositories where nothing automated applies. For a repository with a scope, the command must exercise that scope: omit cwd (it then runs in the scope directory) or name a directory inside it; never a command that runs every workspace of a larger checkout. Core runs them after the developer finishes and feeds failures back.\n6. Score your confidence honestly: `high` when the code evidence is conclusive; `medium` when the design rests on one non-obvious assumption (name it in the design); `low` when several plausible designs exist and you cannot choose without missing information. With `low`, put the single question whose answer decides the design in `question`. Core first lets you investigate further, then either asks the requester that exact question or proceeds on your stated assumptions, depending on the project configuration.",
|
|
3
|
+
"developer": "## Your task: implementation\n\nYou are the Specrails developer. Execute the official openspec-apply-change skill for the approved change, implement its pending tasks completely and update their progress through that workflow. Do not substitute a hand-written approximation of apply.\n\n1. Load and execute openspec-apply-change through the supplied binding. Consult its status and instructions apply, read the context files OpenSpec returns, then the relevant existing code and tests.\n2. Work task by task in order. Use test-driven development: write or extend the test first, make it pass with the smallest correct change, then tidy up. Run only focused tests that cover what you touched while iterating. Core owns the complete verification plan and runs it after your turn; do not duplicate that full run. Fix the precise failures Core returns on a correction pass.\n3. Immediately after completing each task, mark it `- [x]` in `tasks.md`; do not postpone all progress updates until the end of the phase. Only mark tasks whose code and tests are complete. Change nothing else in `tasks.md`, and never edit `proposal.md`, `design.md` or the specs: those documents are frozen, and editing them invalidates the run. If a task cannot be completed, leave it `- [ ]` and list it under `incomplete` with the reason.\n4. Keep the implementation consistent with the repository: naming, error handling, import style, formatting and existing utilities. Do not add dependencies unless the design requires them. Change only what the tasks require: the diff should contain the requested change and its tests, nothing else.\nInvestigation budget: consult the local reference patterns in design.md and equivalent application tests before framework internals or node_modules. After three unsuccessful experiments on the same failure, stop repeating commands: state the hypothesis, evidence and next discriminating experiment, then change approach. If three further experiments add no evidence, report the specific blocker under incomplete rather than consuming the remaining turn budget. Never weaken assertions or change acceptance criteria to make a test pass.\nVerification evidence: run tests without piping their output through grep/head or other filters that mask the original exit status. Capture complete stdout/stderr in a temporary log and preserve the test process exit code; inspect that log separately. Report the command and original exit code. Remove temporary debug tests before finishing. Core independently runs the final verification commands.\n5. If the shell is unavailable, still finish every task that only needs code and tests; Core runs the verification commands after your turn and returns the exact failures to you.\n\n## Durable implementation progress\n\nWhen loading the official apply skill, read its savedProgress handoff before repeating investigation. Reconcile it with the current diff and tasks; it is advisory history, never accepted verification evidence. Preserve completed work and previously discovered test commands or environment blockers. Recheck a blocker only when its relevant condition changed.\nAfter each completed task, and whenever the next action or a blocker changes, call the scoped workflow action write_progress with progress: {summary, completedTasks, nextTasks, checks: [{command, outcome}], blockers}. Replace the previous handoff with a concise current account (at most 8 KB); include exact focused test commands, original exit outcomes, relevant paths and the next discriminating action. Keep nextTasks concrete enough for a fresh session to continue unfinished work. Do not include full logs, credentials or claims that old checks authorize acceptance. The host persists this record outside the frozen OpenSpec artifacts. Use read_progress to refresh it. If interrupted, the next session receives it from load_skill; do not write runtime files directly.",
|
|
4
|
+
"reviewer": "## Your task: review\n\nYou are the Specrails reviewer and the last gate before this change is archived. Inspect the implementation, the approved artifacts and the verification evidence read-only. Do not fix anything.\n\nCheck, in this order:\n1. Spec completeness: every requirement in the change specs and every acceptance criterion is implemented. Cross-reference each one against the code.\n2. Task completion: every task in `tasks.md` is `- [x]` and is backed by real code and tests, not just a ticked box.\n3. Test quality: new behavior has tests that assert on behavior, cover error paths, and would fail without the change. Missing tests for production code are a blocking issue.\n4. Correctness and conventions: types and signatures fit the codebase, patterns match the repository, imports and error handling are consistent, no unrelated changes.\n5. Security: no secrets, injection, path traversal, unsafe deserialization, missing authorization or new attack surface. Scale scrutiny to what the change touches.\n6. Performance: no obvious N+1, unbounded loops or blocking work on hot paths introduced by the change.\n\nJudge BEHAVIOUR against the acceptance criteria, never the shape of the code against the wording of the plan: module layout, file lists, class or function names, naming conventions and implementation techniques named in the ticket, the design or a \"contract layer\" are suggestions the developer may legitimately improve on. A working implementation that satisfies a criterion by other means is correct; asking to rename, move or rewrite it to match the plan's wording is NOT an issue. Only a criterion that is not met, a defect, a missing test or a regression is.\n\nReview THE CHANGE. The change set below lists every file this run changed relative to its base, measured by Core from git. Judge those changes and the behavior they affect; code the change did not touch is not an issue unless the change breaks it. Never ask for edits to pre-existing code, other packages or workspaces, or configuration outside the change. An environment problem (credentials, registry access, missing tools) is not a code issue: record it under findings, never as an issue for the fixer.\n\nThe verification evidence below comes from real subprocesses run by Core after the developer finished; treat it as fact, not as a claim by the developer.",
|
|
5
|
+
"fixer": "## Your task: correction\n\nYou are the Specrails FIXER. The verification commands (or the review) failed after the developer's pass; your only job is to make them pass with minimal, precise edits to this change.\n\n1. Read the failing output below first: the exact command, its exit code and the reported files and lines. Start from those files, not from the plan.\n2. Decide whether this change caused the failure. It did when the failure points at files in the change set below or at behavior they affect. A failure in code the change never touched (another package or workspace, a pre-existing test), in the environment (missing credentials, environment variables, registry access or tools) or in configuration outside the change is NOT yours to fix: edit nothing for it, and report it under `incomplete` with the exact error. Core then stops and asks the host instead of starting another round.\n3. Patch exactly the files and lines the failure names; touch neighbouring code only when the failure cannot be fixed otherwise. Rewrite a file only when a patch cannot express the change.\n4. Never re-implement a feature, rename or restructure, reformat, add dependencies, or widen the change beyond the failure. Never weaken an assertion, delete a test or change acceptance criteria to make a check pass — if a test is wrong, fix the test to the specification and say so.\n5. A test file the host reports as never executed must be wired into the repository's test command (the test script or runner configuration) and then made to pass.\n6. Confirm with the narrowest command that reproduces the failure (the failing test file or test name). Do not re-run whole suites or other workspaces: Core re-runs the complete verification plan after your turn.\n7. Do not read the repository beyond the files the failure names and their direct dependencies. `proposal.md`, `design.md` and the specs are frozen; in `tasks.md` only tick a task you completed.\n8. Finish with the same JSON summary object as the developer, listing under `incomplete` only failures you could not fix and why.\n\n## Durable implementation progress\n\nWhen loading the official apply skill, read its savedProgress handoff before repeating investigation. Reconcile it with the current diff and tasks; it is advisory history, never accepted verification evidence. Preserve completed work and previously discovered test commands or environment blockers. Recheck a blocker only when its relevant condition changed.\nAfter each completed task, and whenever the next action or a blocker changes, call the scoped workflow action write_progress with progress: {summary, completedTasks, nextTasks, checks: [{command, outcome}], blockers}. Replace the previous handoff with a concise current account (at most 8 KB); include exact focused test commands, original exit outcomes, relevant paths and the next discriminating action. Keep nextTasks concrete enough for a fresh session to continue unfinished work. Do not include full logs, credentials or claims that old checks authorize acceptance. The host persists this record outside the frozen OpenSpec artifacts. Use read_progress to refresh it. If interrupted, the next session receives it from load_skill; do not write runtime files directly."
|
|
6
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.defaultLoopAgents = defaultLoopAgents;
|
|
7
|
+
const loop_agent_defaults_json_1 = __importDefault(require("./loop-agent-defaults.json"));
|
|
8
|
+
function defaultLoopAgents(mode = 'implementation') {
|
|
9
|
+
return {
|
|
10
|
+
schemaVersion: 1,
|
|
11
|
+
agents: { architect: { provider: 'inherit' }, developer: { provider: 'inherit' }, reviewer: { provider: 'inherit' } },
|
|
12
|
+
fixer: { provider: 'inherit' },
|
|
13
|
+
rolePrompts: mode === 'implementation' ? { ...loop_agent_defaults_json_1.default } : {
|
|
14
|
+
architect: 'Explore the repositories and plan the requested work from the supplied goal and acceptance criteria.',
|
|
15
|
+
developer: 'Implement the supplied spec directly in the existing codebase. Explore relevant code, make the requested changes and add meaningful tests. Use the project verification commands. If a human decision is required, finish with LOOP_BLOCKED: followed by the specific question.',
|
|
16
|
+
reviewer: 'Review actual code and verification evidence against the supplied acceptance criteria. Report correctness, security and regression issues.',
|
|
17
|
+
fixer: 'Correct the reported implementation or verification failures with focused changes and tests. Preserve previously completed requirements. If a human decision is required, finish with LOOP_BLOCKED: followed by the specific question.',
|
|
18
|
+
},
|
|
19
|
+
roles: { 'loop-decider': { provider: 'inherit', access: 'read', artifacts: 'none', prompt: 'Inspect actual implementation and verification evidence against every frozen acceptance criterion. Request corrections until the goal is satisfied.' } },
|
|
20
|
+
};
|
|
21
|
+
}
|
|
@@ -165,7 +165,7 @@ exports.LOOP_COMMANDS = [
|
|
|
165
165
|
FROZEN_SPEC_SCOPE,
|
|
166
166
|
'',
|
|
167
167
|
'- Missing or incomplete implementation: implement the missing pieces whether verification reported `VERIFICATION: FAIL` or `VERIFICATION: PASS`. Passing baseline tests do not satisfy missing acceptance criteria. If the main step only prepared the environment, planned, or launched unfinished delegated work, continue the required implementation now; do not merely re-run the unchanged baseline.',
|
|
168
|
-
'- Resume from
|
|
168
|
+
'- Resume from completed work and preserve correct code already on disk. If the selected workflow uses OpenSpec, follow its approved artifacts and remaining obligations without restarting completed phases. In a free-form workflow, implement the frozen spec directly; do not require or fabricate an OpenSpec change. Read the frozen spec and current files rather than assuming the previous agent finished.',
|
|
169
169
|
'- Implementation present but checks fail: repair the reported code/test/type-check/lint/build defects with the smallest relevant change, then run focused checks. Do not weaken tests or add unrelated edits. If implementation gaps and check failures coexist, address both within the authorized scope.',
|
|
170
170
|
'- For a delivery revision, the authorized scope is the requested delta and its frozen briefing. Preserve the delivered feature; do not turn a revision repair into a new implementation of unrelated requirements.',
|
|
171
171
|
'',
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.coreFactoryGraph = coreFactoryGraph;
|
|
4
|
+
const loop_implement_recipe_1 = require("./loop-implement-recipe");
|
|
5
|
+
const loop_agents_1 = require("./loop-agents");
|
|
4
6
|
/** App-owned definition graphs. Core supplies schemas, execution and canonical versions. */
|
|
5
|
-
function coreFactoryGraph(mode) {
|
|
7
|
+
function coreFactoryGraph(mode, independent = false, configurable = false) {
|
|
8
|
+
if (mode === 'implement' && configurable)
|
|
9
|
+
return (0, loop_implement_recipe_1.configurableImplementGraph)();
|
|
6
10
|
const nodes = [{ id: 'start', type: 'start', position: { x: 0, y: 0 } }];
|
|
7
11
|
const edges = [];
|
|
8
12
|
const config = { maxIterations: 12, maxTransitions: 120, timeoutMinutes: 0, aiStepTimeoutMinutes: 0,
|
|
@@ -15,10 +19,22 @@ function coreFactoryGraph(mode) {
|
|
|
15
19
|
const next = (id) => ({ next: id, failed: 'failed' });
|
|
16
20
|
const promptNext = (id) => ({ ...next(id), blocked: 'failed' });
|
|
17
21
|
const verify = (id, passed = 'done', failed = 'failed') => node(id, 'verify', { commands: 'configured' }, { pass: passed, fail: failed, failed: 'failed' });
|
|
22
|
+
if (independent)
|
|
23
|
+
config.agents = (0, loop_agents_1.defaultLoopAgents)(mode === 'freestyle' ? 'free' : 'implementation');
|
|
18
24
|
let entry;
|
|
19
25
|
if (mode === 'implement') {
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
if (independent) {
|
|
27
|
+
entry = 'architect';
|
|
28
|
+
for (const [phase, next] of Object.entries({ architect: 'developer', developer: 'verify', fixer: 'verify', verify: 'reviewer', reviewer: 'archive', archive: 'done' })) {
|
|
29
|
+
node(phase, 'implementation-step', { phase }, { next, incomplete: 'developer', rejected: 'fixer', replan: 'architect', reverify: 'verify', rereview: 'reviewer', failed: 'failed' });
|
|
30
|
+
nodes.at(-1).data.label = phase.charAt(0).toUpperCase() + phase.slice(1);
|
|
31
|
+
nodes.at(-1).position = phase === 'fixer' ? { x: 360, y: 360 } : { x: 0, y: (['architect', 'developer', 'verify', 'reviewer', 'archive'].indexOf(phase) + 1) * 140 };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
entry = 'implement';
|
|
36
|
+
node(entry, 'implementation', {}, { next: 'done', rejected: 'failed', failed: 'failed' });
|
|
37
|
+
}
|
|
22
38
|
}
|
|
23
39
|
else if (mode === 'quick-sdd') {
|
|
24
40
|
entry = 'prepare';
|
|
@@ -4,12 +4,14 @@ exports.compileLoopToDefinition = compileLoopToDefinition;
|
|
|
4
4
|
const loop_command_catalog_1 = require("./loop-command-catalog");
|
|
5
5
|
const loop_constants_1 = require("./loop-constants");
|
|
6
6
|
const loop_graph_1 = require("./loop-graph");
|
|
7
|
-
const AI_KINDS = new Set(['prompt', 'role-turn', 'decider', 'implementation']);
|
|
7
|
+
const AI_KINDS = new Set(['prompt', 'role-turn', 'decider', 'implementation', 'implementation-step']);
|
|
8
8
|
const object = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
9
9
|
/** No I/O, model call, hash implementation or scheduling belongs in this adapter. */
|
|
10
10
|
function compileLoopToDefinition(graph, launch) {
|
|
11
11
|
(0, loop_graph_1.assertDefinitionGraph)(graph);
|
|
12
12
|
const allGraphs = [graph, ...Object.values(graph.components ?? {})];
|
|
13
|
+
if (!graph.config.agents && allGraphs.some(body => body.nodes.some(node => node.data?.kind === 'implementation-step')))
|
|
14
|
+
throw new Error('Independent implementation steps require loop-owned agents');
|
|
13
15
|
for (const body of allGraphs) {
|
|
14
16
|
(0, loop_graph_1.assertDefinitionGraph)(body);
|
|
15
17
|
const validation = (0, loop_graph_1.validateLoopGraph)(body);
|
|
@@ -41,14 +43,25 @@ function compileLoopToDefinition(graph, launch) {
|
|
|
41
43
|
...(typeof node.data?.reason === 'string' ? { reason: node.data.reason } : {}),
|
|
42
44
|
}
|
|
43
45
|
: node.data.params;
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
+
const promptRole = node.id === 'fix' ? 'fixer' : node.id === 'prepare' ? 'architect' : rawParams.access === 'read' ? 'reviewer' : 'developer';
|
|
47
|
+
const agents = graph.config.agents ? launch.loopAgents ?? graph.config.agents : undefined;
|
|
48
|
+
const loopEngine = agents ? (promptRole === 'fixer' ? agents.fixer ?? agents.agents.developer : agents.agents[promptRole]) : undefined;
|
|
49
|
+
const engineInput = { ...(loopEngine ? { provider: loopEngine.provider, ...(loopEngine.model ? { model: loopEngine.model } : {}), ...(loopEngine.effort ? { effort: loopEngine.effort } : {}), ...(loopEngine.thinking ? { thinking: loopEngine.thinking } : {}), ...(loopEngine.maxTurns ? { maxTurns: loopEngine.maxTurns } : {}) } : {}), ...(object(rawParams.engine) ? rawParams.engine : {}) };
|
|
50
|
+
const inherited = engineInput.provider === 'inherit';
|
|
51
|
+
const provider = inherited ? launch.provider : typeof engineInput.provider === 'string' ? engineInput.provider : launch.provider;
|
|
52
|
+
if (inherited) {
|
|
53
|
+
engineInput.provider = provider;
|
|
54
|
+
if (!engineInput.model && launch.model)
|
|
55
|
+
engineInput.model = launch.model;
|
|
56
|
+
if (!engineInput.effort && launch.effort)
|
|
57
|
+
engineInput.effort = launch.effort;
|
|
58
|
+
}
|
|
46
59
|
const params = resolveValue(rawParams, launch, provider);
|
|
47
60
|
if (kind === 'prompt') {
|
|
48
61
|
params.engine = {
|
|
49
62
|
provider,
|
|
50
|
-
...(provider === launch.provider && launch.model ? { model: launch.model } : {}),
|
|
51
|
-
...(provider === launch.provider && launch.effort ? { effort: launch.effort } : {}),
|
|
63
|
+
...(!loopEngine && provider === launch.provider && launch.model ? { model: launch.model } : {}),
|
|
64
|
+
...(!loopEngine && provider === launch.provider && launch.effort ? { effort: launch.effort } : {}),
|
|
52
65
|
...engineInput,
|
|
53
66
|
};
|
|
54
67
|
if (typeof rawParams.text === 'string' &&
|
|
@@ -61,6 +74,15 @@ function compileLoopToDefinition(graph, launch) {
|
|
|
61
74
|
}
|
|
62
75
|
}
|
|
63
76
|
}
|
|
77
|
+
if (kind === 'prompt' && graph.config.agents) {
|
|
78
|
+
const definition = graph.config.agents.rolePrompts?.[promptRole]?.replaceAll('{{', '{{{{');
|
|
79
|
+
if (definition) {
|
|
80
|
+
if (object(params.nativeCommand))
|
|
81
|
+
params.nativeCommand = { ...params.nativeCommand, args: String(params.nativeCommand.args ?? '') + '\n\nLoop agent definition:\n' + definition };
|
|
82
|
+
else if (typeof params.text === 'string')
|
|
83
|
+
params.text = definition + '\n\n' + params.text;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
64
86
|
if (launch.briefing && ['prompt', 'role-turn', 'decider'].includes(kind)) {
|
|
65
87
|
const suffix = '\n\nFrozen launch context:\n' + launch.briefing;
|
|
66
88
|
if (object(params.nativeCommand))
|
|
@@ -70,7 +92,7 @@ function compileLoopToDefinition(graph, launch) {
|
|
|
70
92
|
if (typeof params[field] === 'string')
|
|
71
93
|
params[field] += suffix;
|
|
72
94
|
}
|
|
73
|
-
if (kind === 'implementation')
|
|
95
|
+
if ((kind === 'implementation' || kind === 'implementation-step'))
|
|
74
96
|
for (const role of ['architect', 'developer', 'reviewer'])
|
|
75
97
|
roleIds.add(role);
|
|
76
98
|
if ((kind === 'role-turn' || kind === 'decider') && typeof params.roleId === 'string')
|
|
@@ -102,10 +124,51 @@ function compileLoopToDefinition(graph, launch) {
|
|
|
102
124
|
};
|
|
103
125
|
};
|
|
104
126
|
const body = compileBody(graph);
|
|
127
|
+
// Specialize the Desktop Implement recipe with the frozen launch scope.
|
|
128
|
+
// Its ordinary review/candidate gates remain in place; an addendum cannot
|
|
129
|
+
// disappear behind a generic approved=true response.
|
|
130
|
+
if (launch.id === 'factory:implement' && launch.addendaIds?.length) {
|
|
131
|
+
const reviewer = body.nodes.reviewer;
|
|
132
|
+
const policy = body.nodes['review-policy'];
|
|
133
|
+
if (reviewer?.kind === 'role-turn' && policy?.kind === 'condition' && object(reviewer.params.structuredOutput)) {
|
|
134
|
+
const schema = reviewer.params.structuredOutput;
|
|
135
|
+
const entries = Object.fromEntries(launch.addendaIds.map((id, index) => [`a${index}`, {
|
|
136
|
+
type: 'object', additionalProperties: false, required: ['id', 'verdict', 'files', 'tests'],
|
|
137
|
+
properties: {
|
|
138
|
+
id: { type: 'string', const: id },
|
|
139
|
+
verdict: { enum: ['applied', 'partial', 'blocked'] },
|
|
140
|
+
files: { type: 'array', items: { type: 'string', minLength: 1 }, maxItems: 100 },
|
|
141
|
+
tests: { type: 'array', items: { type: 'string', minLength: 1 }, maxItems: 100 },
|
|
142
|
+
},
|
|
143
|
+
}]));
|
|
144
|
+
reviewer.params.structuredOutput = {
|
|
145
|
+
...schema, properties: { ...(object(schema.properties) ? schema.properties : {}), addenda: { type: 'object', additionalProperties: false, properties: entries, required: Object.keys(entries) } },
|
|
146
|
+
required: [...(Array.isArray(schema.required) ? schema.required : []), 'addenda'],
|
|
147
|
+
};
|
|
148
|
+
reviewer.params.prompt += '\nReport every frozen addendum in addenda (slots in launch order): ' + JSON.stringify(launch.addendaIds) + '. Inspect real code and verification evidence. Use applied only with concrete files and executed tests; otherwise partial or blocked. Also include one line per addendum in summary: - [id] applied — files: paths — tests: commands and results.';
|
|
149
|
+
const checks = launch.addendaIds.map((_, index) => {
|
|
150
|
+
const ref = `$outputs.reviewer.structured.addenda.a${index}`;
|
|
151
|
+
return `${ref}.verdict == "applied" && ${ref}.files.length > 0 && ${ref}.tests.length > 0`;
|
|
152
|
+
});
|
|
153
|
+
// Core bounds expression length/token count. Keep large addendum scopes
|
|
154
|
+
// in small successive gates instead of generating one unbounded expression.
|
|
155
|
+
let next = reviewer.ends.next;
|
|
156
|
+
for (let offset = checks.length - 1; offset >= 0; offset -= 10) {
|
|
157
|
+
const first = Math.max(0, offset - 9);
|
|
158
|
+
let id = `addenda-review-${first}`;
|
|
159
|
+
while (body.nodes[id])
|
|
160
|
+
id += '-gate';
|
|
161
|
+
body.nodes[id] = { kind: 'condition', params: { expr: checks.slice(first, offset + 1).map(expr => `(${expr})`).join(' && ') }, ends: { true: next, false: policy.ends.false } };
|
|
162
|
+
next = id;
|
|
163
|
+
}
|
|
164
|
+
reviewer.ends.next = next;
|
|
165
|
+
body.maxTransitions = Math.min(10000, body.maxTransitions + Math.ceil(checks.length / 10) * graph.config.maxIterations);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
105
168
|
const components = Object.fromEntries(Object.entries(graph.components ?? {})
|
|
106
169
|
.sort(([a], [b]) => a.localeCompare(b, 'en'))
|
|
107
170
|
.map(([id, component]) => [id, compileBody(component)]));
|
|
108
|
-
const implementation = [body, ...Object.values(components)].some((component) => Object.values(component.nodes).some((node) => node.kind === 'implementation'));
|
|
171
|
+
const implementation = [body, ...Object.values(components)].some((component) => Object.values(component.nodes).some((node) => (node.kind === 'implementation' || node.kind === 'implementation-step')));
|
|
109
172
|
const budget = {
|
|
110
173
|
...(graph.config.maxCostUsd && graph.config.maxCostUsd > 0
|
|
111
174
|
? { maxCostUsd: graph.config.maxCostUsd }
|
|
@@ -143,8 +206,8 @@ function pieceWrites(node, roles) {
|
|
|
143
206
|
if (kind === 'prompt')
|
|
144
207
|
return params.access !== 'read';
|
|
145
208
|
if (kind === 'role-turn')
|
|
146
|
-
return roles
|
|
147
|
-
return ['verify', 'shell', 'openspec-archive', 'implementation'].includes(kind ?? '');
|
|
209
|
+
return (roles ?? {})[String(params.roleId)]?.access !== 'read';
|
|
210
|
+
return ['verify', 'shell', 'openspec-archive', 'implementation', 'implementation-step'].includes(kind ?? '');
|
|
148
211
|
}
|
|
149
212
|
/** Expand templates once. Text inserted by spec/constants cannot introduce another token. */
|
|
150
213
|
function resolveValue(value, launch, provider) {
|
|
@@ -153,10 +216,14 @@ function resolveValue(value, launch, provider) {
|
|
|
153
216
|
if (token === '{{{{')
|
|
154
217
|
return token; // Core resolves the literal escape at execution.
|
|
155
218
|
if (/^\{\{\s*spec\./.test(token))
|
|
156
|
-
return (0, loop_graph_1.interpolateSpec)(token, launch.spec);
|
|
219
|
+
return (0, loop_graph_1.interpolateSpec)(token, launch.spec).replaceAll('{{', '{{{{');
|
|
157
220
|
if (/^\{\{\s*const:/.test(token))
|
|
158
|
-
return (0, loop_constants_1.resolveConstants)(token.replace(/\s+/g, ''), launch.constants);
|
|
159
|
-
|
|
221
|
+
return (0, loop_constants_1.resolveConstants)(token.replace(/\s+/g, ''), launch.constants).replaceAll('{{', '{{{{');
|
|
222
|
+
// Registry templates contain launch tokens themselves. Resolve only
|
|
223
|
+
// those trusted tokens, never commands introduced by task data.
|
|
224
|
+
return (0, loop_command_catalog_1.expandCommands)(token, { provider, ticketIds: launch.spec?.ticketIds, specId: launch.spec?.id }).replace(/\{\{\s*(?:spec\.\w+|const:[A-Za-z0-9_.-]+)\s*\}\}/g, nested => (/^\{\{\s*spec\./.test(nested)
|
|
225
|
+
? (0, loop_graph_1.interpolateSpec)(nested, launch.spec)
|
|
226
|
+
: (0, loop_constants_1.resolveConstants)(nested.replace(/\s+/g, ''), launch.constants)).replaceAll('{{', '{{{{'));
|
|
160
227
|
});
|
|
161
228
|
}
|
|
162
229
|
if (Array.isArray(value))
|
|
@@ -11,7 +11,7 @@ function classifyLoopEffect(graph) {
|
|
|
11
11
|
if (n.data?.kind === 'prompt')
|
|
12
12
|
return n.data.params?.access !== 'read';
|
|
13
13
|
// Role access binds at launch; isolation is conservative before that binding.
|
|
14
|
-
return ['role-turn', 'verify', 'shell', 'openspec-archive', 'implementation', 'component', 'map'].includes(String(n.data?.kind));
|
|
14
|
+
return ['role-turn', 'verify', 'shell', 'openspec-archive', 'implementation', 'implementation-step', 'component', 'map'].includes(String(n.data?.kind));
|
|
15
15
|
}) || Object.values(graph.components ?? {}).some(component => classifyLoopEffect(component) === 'mutating');
|
|
16
16
|
return writes ? 'mutating' : 'read-only';
|
|
17
17
|
}
|
|
@@ -185,10 +185,20 @@ function createLoopExecutors(opts = {}) {
|
|
|
185
185
|
const host = (0, agent_runtime_bridge_1.readFrozenRuntimeHost)(contextPath, resolveEnv(), runId);
|
|
186
186
|
await (0, agent_runtime_bridge_1.runAgentRuntimeControl)({ kind: 'cancel', runId, contextPath, requestId, ...host });
|
|
187
187
|
},
|
|
188
|
-
async assertDefinitionSupport() {
|
|
188
|
+
async assertDefinitionSupport(graph) {
|
|
189
189
|
const runtime = await (0, agent_runtime_loader_1.loadCoreAgentRuntime)();
|
|
190
190
|
if (runtime.api?.capabilities?.engineV2 !== 1 || runtime.api?.capabilities?.workflowDefinitions !== 1)
|
|
191
191
|
throw new Error('engine_unsupported: Update Core to run workflow definitions');
|
|
192
|
+
if (graph && [graph, ...Object.values(graph.components ?? {})].some(body => body.nodes.some(node => node.data?.kind === 'artifact-contract'))) {
|
|
193
|
+
if (runtime.api?.capabilities?.workflowAgentSteps !== 1)
|
|
194
|
+
throw new Error('engine_unsupported: Update Core to run configurable workflow agent steps');
|
|
195
|
+
}
|
|
196
|
+
if (graph && [graph, ...Object.values(graph.components ?? {})].some(body => body.nodes.some(node => node.data?.kind === 'implementation-step'))) {
|
|
197
|
+
if (runtime.api?.capabilities?.implementationSteps !== 1)
|
|
198
|
+
throw new Error('engine_unsupported: Update Core to run independent implementation steps');
|
|
199
|
+
if (!graph.config.agents)
|
|
200
|
+
throw new Error('Independent implementation steps require loop-owned agents');
|
|
201
|
+
}
|
|
192
202
|
},
|
|
193
203
|
async assertLegacyEngineSupport() {
|
|
194
204
|
// Legacy traversal predates Core: only a Core that positively lacks engine 1 blocks it.
|
|
@@ -211,17 +221,21 @@ function createLoopExecutors(opts = {}) {
|
|
|
211
221
|
return { ...result, runtimeStatus: result.runtimeStatus ?? 'failed' };
|
|
212
222
|
}
|
|
213
223
|
const seeded = (0, loop_run_manager_1.seedChangeId)({ ...request, runId });
|
|
214
|
-
const
|
|
224
|
+
const delta = seeded && (seeded.source === 'addenda' || seeded.source === 'revision')
|
|
225
|
+
? `DELTA OPENSPEC TARGET: ${seeded.id}. Use ONLY openspec/changes/${seeded.id}/; create it if missing. Inspect the current branch before planning. For delivered work, plan and implement ONLY the attached addenda or delivery change request, preserving existing behavior outside the delta. For work not yet implemented, implement the full spec together with its addenda. Map each requested change to acceptance checks. Do not redo or archive the original proposal. Report each addendum id with concrete files and test evidence.`
|
|
226
|
+
: undefined;
|
|
227
|
+
const briefing = [delta, request.constants?.REVISION_REQUEST, request.followUp?.briefing, request.addenda?.briefing].filter(Boolean).join('\n\n');
|
|
215
228
|
const spec = request.spec && briefing ? { ...request.spec, description: [request.spec.description, briefing].filter(Boolean).join('\n\n'), ...(request.spec.tickets ? { tickets: request.spec.tickets.map(ticket => ({ ...ticket, description: [ticket.description, briefing].filter(Boolean).join('\n\n') })) } : {}) } : request.spec;
|
|
216
229
|
const env = { ...programmaticStepEnv(resolveEnv(), request.repoDir, request.executionManifest), SPECRAILS_GIT_AUTO: 'false' };
|
|
217
|
-
const core = (0, core_execution_1.prepareCoreExecution)({ run: { runId, projectId: request.projectId, repositoryId: request.repositoryId, spec, goal: request.spec?.title ?? request.loopName }, cwd: request.cwd, repoDir: request.repoDir, manifest: request.executionManifest, env, sourcePath: request.executionManifest ? undefined : opts.sourcePath?.() });
|
|
230
|
+
const core = (0, core_execution_1.prepareCoreExecution)({ run: { runId, projectId: request.projectId, repositoryId: request.repositoryId, spec, goal: request.spec?.title ?? request.loopName }, cwd: request.cwd, repoDir: request.repoDir, manifest: request.executionManifest, env, sourcePath: request.executionManifest ? undefined : opts.sourcePath?.(), workspacePaths: request.executionManifest ? undefined : opts.workspacePaths?.(), selectedWorkspacePaths: request.workspacePaths });
|
|
218
231
|
const result = await (0, agent_runtime_bridge_1.runAgentRuntimeInvocation)({
|
|
219
232
|
contextPath: core.contextPath, cwd: request.cwd, env: core.env,
|
|
220
233
|
configPath: runtimeConfigPath(request.cwd), engineVersion: 2,
|
|
234
|
+
loopConfig: request.graph.config.agents,
|
|
221
235
|
change: seeded?.id ?? (0, agent_runtime_bridge_1.runtimeChangeName)(runId),
|
|
222
236
|
resume: input.resume, answer: input.answer, approve: input.approve, interruptId: input.interruptId,
|
|
223
|
-
defaultProvider: request.provider, providerOverride: request.runtimeProviderOverride,
|
|
224
|
-
...(request.graph.config.legacyDeciderRole ? { workflowRoleBindings: {
|
|
237
|
+
defaultProvider: request.provider, providerOverride: request.runtimeProviderOverride ?? (request.graph.config.agents ? { provider: request.provider, model: request.model, effort: request.effort } : undefined),
|
|
238
|
+
...(!request.graph.config.agents && request.graph.config.legacyDeciderRole ? { workflowRoleBindings: {
|
|
225
239
|
[request.graph.config.legacyDeciderRole]: {
|
|
226
240
|
...(request.deciderEngine ?? { provider: request.provider, model: request.model, effort: request.effort }),
|
|
227
241
|
access: 'read', artifacts: 'none',
|
|
@@ -229,9 +243,10 @@ function createLoopExecutors(opts = {}) {
|
|
|
229
243
|
} } : {}),
|
|
230
244
|
...(!input.resume ? { prepareDefinition: (config) => (0, loop_definition_1.compileLoopToDefinition)(request.graph, {
|
|
231
245
|
id: request.loopId, title: request.loopName, spec: request.spec,
|
|
246
|
+
...(request.graph.config.agents ? { loopAgents: config } : {}),
|
|
232
247
|
constants: request.constants ?? {}, provider: request.provider, model: request.model, effort: request.effort,
|
|
233
248
|
roles: { architect: { access: 'read' }, developer: { access: 'write' }, reviewer: { access: 'read' }, ...config.roles }, repositoryCount: request.executionManifest?.repositories.length ?? 1,
|
|
234
|
-
changeId: seeded?.id, briefing,
|
|
249
|
+
changeId: seeded?.id, briefing: [briefing, core.workspaceBriefing].filter(Boolean).join('\n\n'), addendaIds: request.addenda?.ids,
|
|
235
250
|
}) } : {}),
|
|
236
251
|
onPrepared: input.onPrepared, onLine: input.onLine, onRuntimeEvent: input.onRuntimeEvent, onSpawn: input.onSpawn, timeoutMs: input.timeoutMs,
|
|
237
252
|
});
|
|
@@ -297,7 +312,7 @@ function createLoopExecutors(opts = {}) {
|
|
|
297
312
|
const existingContext = coreRun ? runtimeContextPath(cwd, coreRun.runId, baseEnv) : undefined;
|
|
298
313
|
const programmatic = coreRun && existingContext && (coreRun.implementation || (coreRun.verificationStep && existsRuntimeRequest(existingContext)));
|
|
299
314
|
const baseStepEnv = programmatic ? programmaticStepEnv(baseEnv, repoDir, executionManifest) : withProfileEnv(aiStepEnv(baseEnv, repoDir, executionManifest), provider, profileName);
|
|
300
|
-
const core = coreRun ? (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.() }) : undefined;
|
|
315
|
+
const core = coreRun ? (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.(), workspacePaths: executionManifest ? undefined : opts.workspacePaths?.(), selectedWorkspacePaths: coreRun?.workspacePaths }) : undefined;
|
|
301
316
|
const stepEnv = core?.env ?? baseStepEnv;
|
|
302
317
|
if (coreRun?.implementation && core) {
|
|
303
318
|
completionContexts.set(coreRun.runId, { cwd, contextPath: core.contextPath, env: stepEnv, runId: coreRun.runId });
|
|
@@ -536,7 +551,7 @@ function createLoopExecutors(opts = {}) {
|
|
|
536
551
|
if (!adapter.capabilities.persistentStdin)
|
|
537
552
|
return null;
|
|
538
553
|
const baseStepEnv = withProfileEnv(aiStepEnv(resolveEnv(), repoDir, executionManifest), provider, profileName);
|
|
539
|
-
const core = coreRun ? (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.() }) : undefined;
|
|
554
|
+
const core = coreRun ? (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.(), workspacePaths: executionManifest ? undefined : opts.workspacePaths?.(), selectedWorkspacePaths: coreRun?.workspacePaths }) : undefined;
|
|
540
555
|
const stepEnv = core?.env ?? baseStepEnv;
|
|
541
556
|
const extraArgs = aiStepExtraArgs(adapter, cwd, repoDir, executionManifest);
|
|
542
557
|
if (repoDir) {
|
|
@@ -592,7 +607,7 @@ function createLoopExecutors(opts = {}) {
|
|
|
592
607
|
const baseEnv = resolveEnv();
|
|
593
608
|
const programmatic = existsRuntimeRequest(runtimeContextPath(cwd, coreRun.runId, baseEnv));
|
|
594
609
|
const baseStepEnv = programmatic ? programmaticStepEnv(baseEnv, repoDir, executionManifest) : withProfileEnv(aiStepEnv(baseEnv, repoDir, executionManifest), provider, profileName);
|
|
595
|
-
const core = (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.() });
|
|
610
|
+
const core = (0, core_execution_1.prepareCoreExecution)({ run: coreRun, cwd, repoDir, manifest: executionManifest, env: baseStepEnv, sourcePath: executionManifest ? undefined : opts.sourcePath?.(), workspacePaths: executionManifest ? undefined : opts.workspacePaths?.(), selectedWorkspacePaths: coreRun?.workspacePaths });
|
|
596
611
|
if (existsRuntimeRequest(core.contextPath))
|
|
597
612
|
return (0, core_execution_1.checkCoreCompletion)(core.contextPath, cwd, { ...programmaticStepEnv(resolveEnv(), repoDir, executionManifest), SPECRAILS_EXECUTION_CONTEXT: core.contextPath }, coreRun.runId);
|
|
598
613
|
const env = (0, runtime_1.buildProviderEnv)((0, providers_1.getAdapter)(provider), { prompt: '', model, reasoning_effort: effort }, core.env);
|
|
@@ -94,7 +94,7 @@ const FACTORY_BY_ID = new Map([
|
|
|
94
94
|
function factoryLoopsForCapabilities(capabilities) {
|
|
95
95
|
if (capabilities?.engineV2 !== 1 || capabilities.workflowDefinitions !== 1)
|
|
96
96
|
return exports.FACTORY_LOOPS;
|
|
97
|
-
return exports.FACTORY_LOOPS.map(factory => ({ ...factory, graph: (0, loop_core_factory_1.coreFactoryGraph)(factory.id === 'factory:implement' ? 'implement' : factory.id === 'factory:freestyle' ? 'freestyle' : 'quick-sdd') }));
|
|
97
|
+
return exports.FACTORY_LOOPS.map(factory => ({ ...factory, graph: (0, loop_core_factory_1.coreFactoryGraph)(factory.id === 'factory:implement' ? 'implement' : factory.id === 'factory:freestyle' ? 'freestyle' : 'quick-sdd', capabilities.implementationSteps === 1, capabilities.workflowAgentSteps === 1) }));
|
|
98
98
|
}
|
|
99
99
|
function getFactoryLoop(id, capabilities) {
|
|
100
100
|
const legacy = FACTORY_BY_ID.get(id);
|