specrails-desktop 2.58.1 → 2.59.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -5
- package/client/dist/assets/{ActivityFeedPage-BQGppIR0.js → ActivityFeedPage-vAY9C71W.js} +1 -1
- package/client/dist/assets/AgentBrowserCapture-CpwjGXQm.js +1 -0
- package/client/dist/assets/AgentModeAnalyticsPane-BHeoJVxQ.js +2 -0
- package/client/dist/assets/AgentModeCodePane-B0Nf3YkN.js +2 -0
- package/client/dist/assets/{AgentModeJobsPane-DR_06oqH.js → AgentModeJobsPane-CpbIItY_.js} +1 -1
- package/client/dist/assets/{AgentsPage-BKIKjm_1.js → AgentsPage-BVJAFXn3.js} +3 -3
- package/client/dist/assets/{AnalyticsPage-069npRVo.js → AnalyticsPage-DDWees-K.js} +1 -1
- package/client/dist/assets/CodePage-CzV5tkvS.js +3 -0
- package/client/dist/assets/{DesktopAnalyticsPage-qtWAPM47.js → DesktopAnalyticsPage-DfRHNK7i.js} +1 -1
- package/client/dist/assets/DocsDialog-BxduBYbh.js +11 -0
- package/client/dist/assets/DocsPage-DOVeRJn_.js +11 -0
- package/client/dist/assets/{ExportDropdown-BcMpvV89.js → ExportDropdown-BnKVV8UK.js} +1 -1
- package/client/dist/assets/InteractiveJobComposer-Da9dvM-0.js +19 -0
- package/client/dist/assets/JobDetailModal-DhwKbZGe.js +1 -0
- package/client/dist/assets/JobDetailPage-MtPE-E_1.js +1 -0
- package/client/dist/assets/JobsPage-0vdlbNik.js +1 -0
- package/client/dist/assets/LoopBuilderPage-DCUHozXh.js +1 -0
- package/client/dist/assets/{LoopPreviewModal-je5pSCf-.js → LoopPreviewModal-Df-S8NGf.js} +1 -1
- package/client/dist/assets/LoopWindowSurface-sdb7QbSo.js +1 -0
- package/client/dist/assets/LoopsPage-CDfCC9Px.js +2 -0
- package/client/dist/assets/MinimizedChatsContext-DKlPf54Y.js +1 -0
- package/client/dist/assets/PluginsPage-iL0G3rQQ.js +2 -0
- package/client/dist/assets/ProjectSettingsDialog-DOkjT7xv.js +1 -0
- package/client/dist/assets/{RepositoryDeliveries-BiwJuS8r.js → RepositoryDeliveries-CJVpOUpZ.js} +1 -1
- package/client/dist/assets/RepositoryScopeSelector-BeM-2gIZ.js +2 -0
- package/client/dist/assets/ReviewPacketPage-EBCim2w_.js +4 -0
- package/client/dist/assets/TemplatePreviewModal-1-92UGwN.js +1 -0
- package/client/dist/assets/TicketDetailModalContext-CkVXuHxU.js +16 -0
- package/client/dist/assets/{Trans-D3AfDylf.js → Trans-CzhPjnjB.js} +1 -1
- package/client/dist/assets/agent--8QAfoXL.js +1 -0
- package/client/dist/assets/agent-3HxXkIWX.js +1 -0
- package/client/dist/assets/agent-BjttZLIl.js +1 -0
- package/client/dist/assets/agent-CpFCz32T.js +1 -0
- package/client/dist/assets/agent-DVnw6ybO.js +1 -0
- package/client/dist/assets/agent-Z5s_GTP0.js +1 -0
- package/client/dist/assets/agent-hv57-zyG.js +1 -0
- package/client/dist/assets/agent-xGrZ_aN1.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-DlQaXc7c.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-DOGj1Mfz.js → getTimezoneOffsetInMilliseconds-DxUFGcVq.js} +1 -1
- package/client/dist/assets/index-Bfunvi6w.css +2 -0
- package/client/dist/assets/index-Czov72g7.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-CbJvbRYx.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-BkBD3rxx.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-CYotH-9N.js} +1 -1
- package/client/dist/assets/subscriptionUsage-BH73RO86.js +1 -0
- package/client/dist/assets/subscriptionUsage-BWTeWlHx.js +1 -0
- package/client/dist/assets/subscriptionUsage-CBh82zVa.js +1 -0
- package/client/dist/assets/subscriptionUsage-CHcQ7lLe.js +1 -0
- package/client/dist/assets/subscriptionUsage-CdlE5Mg1.js +1 -0
- package/client/dist/assets/subscriptionUsage-DUPBmSQi.js +1 -0
- package/client/dist/assets/subscriptionUsage-DrlwrJ17.js +1 -0
- package/client/dist/assets/subscriptionUsage-EFu0mIK3.js +1 -0
- package/client/dist/assets/tauri-shell-DE6LfGPw.js +2 -0
- package/client/dist/assets/{useDesktop-0unHDBJJ.js → useDesktop-DpxVGfUC.js} +1 -1
- package/client/dist/assets/{useRuntimeRuns-BN1rhyEm.js → useRuntimeRuns-CfGRbhr9.js} +1 -1
- package/client/dist/assets/useSharedWebSocket-C17TGqT8.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/api-reference.md +1 -0
- package/docs/internals/core-runtime-updates.md +9 -2
- package/docs/internals/desktop-owned-workflows.md +144 -0
- package/docs/internals/mission-rail-cards.md +15 -0
- package/docs/internals/operations-runbook.md +26 -0
- package/docs/internals/programmatic-agent-runtime.md +34 -28
- package/docs/internals/project-builder.md +34 -11
- package/docs/internals/safe-pr-review-flow.md +25 -7
- package/docs/internals/source-map.md +53 -3
- package/docs/internals/spec-addenda.md +11 -5
- package/docs/internals/visual-design-audit.md +5 -0
- 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/core-update-manager.js +1 -1
- package/server/dist/db/migrations.js +9 -0
- 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 +169 -17
- 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 +10 -5
- package/server/dist/modules/delivery/runtime/rail-isolated-launch.js +66 -30
- package/server/dist/modules/delivery/runtime/rail-launch-parser.js +8 -0
- package/server/dist/modules/delivery/runtime/rail-pr-store.js +3 -3
- package/server/dist/modules/delivery/runtime/rail-relaunch.js +154 -0
- package/server/dist/modules/delivery/runtime/rails-router.js +157 -36
- package/server/dist/modules/delivery/runtime/review-packet.js +1 -1
- package/server/dist/modules/execution/runtime/revision-seed.js +6 -2
- 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-events.js +1 -1
- 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 +53 -0
- package/server/dist/modules/loops/runtime/loop-run-manager.js +30 -20
- 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 +123 -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 +12 -0
- package/server/dist/modules/subscription-usage/domain.js +104 -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/server/dist/worktree-manager.js +114 -6
- package/server/dist/worktree-overlay.js +4 -4
- 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
|
@@ -129,7 +129,7 @@ function createDefinitionEventProjection(input) {
|
|
|
129
129
|
persist('loop_step', { index: nextCount, kind: 'core', title: workflow.nodePath, iteration: workflow.visit, startedAtMs: Date.parse(at), ...correlation });
|
|
130
130
|
}
|
|
131
131
|
else if (opening && ['step_succeeded', 'step_failed', 'step_blocked', 'step_interrupted', 'step_retrying', 'step_paused'].includes(String(workflow.type))) {
|
|
132
|
-
persist('loop_step_end', { index: opening.index, status: workflow.type === 'step_succeeded' ? 'ok' : workflow.type === 'step_paused' ? 'paused' : workflow.type === 'step_interrupted' ? 'interrupted' : 'failed',
|
|
132
|
+
persist('loop_step_end', { index: opening.index, status: workflow.type === 'step_succeeded' ? workflow.outcome === 'fail' ? 'failed' : 'ok' : workflow.type === 'step_paused' ? 'paused' : workflow.type === 'step_interrupted' ? 'interrupted' : 'failed',
|
|
133
133
|
runtimeStatus: String(workflow.type).slice(5), outcome: workflow.outcome, exitCode: null,
|
|
134
134
|
durationMs: Math.max(0, Date.parse(at) - Number(opening.startedAtMs)), ...correlation });
|
|
135
135
|
}
|
|
@@ -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);
|
|
@@ -1,14 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Loop graph model + validation — the pure core shared by the canvas builder
|
|
4
|
-
* (`loop-builder-canvas`) and the publish gate (`loops-store.publishLoop`).
|
|
5
|
-
*
|
|
6
|
-
* A loop is a directed graph of typed nodes connected by edges. Cycles are
|
|
7
|
-
* allowed (a loop routes back to an earlier node); the run is bounded by the
|
|
8
|
-
* graph's `config.maxIterations` + `timeoutMinutes`. This module has NO I/O and
|
|
9
|
-
* NO provider knowledge — it only describes and validates the shape, so it is
|
|
10
|
-
* trivially unit-testable and reused verbatim by both the store and the engine.
|
|
11
|
-
*/
|
|
12
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
3
|
exports.CORE_NODE_KINDS = void 0;
|
|
14
4
|
exports.isDefinitionReviewerPath = isDefinitionReviewerPath;
|
|
@@ -24,8 +14,8 @@ exports.successors = successors;
|
|
|
24
14
|
exports.loopNeedsTicket = loopNeedsTicket;
|
|
25
15
|
exports.interpolateSpec = interpolateSpec;
|
|
26
16
|
exports.CORE_NODE_KINDS = ['prompt', 'role-turn', 'decider', 'condition', 'assign', 'verify', 'shell',
|
|
27
|
-
'openspec-validate', 'openspec-archive', 'approval', 'question', 'gate', 'map', 'join',
|
|
28
|
-
'component', 'implementation', 'end'];
|
|
17
|
+
'artifact-contract', 'openspec-validate', 'openspec-archive', 'approval', 'question', 'gate', 'map', 'join',
|
|
18
|
+
'component', 'implementation', 'implementation-step', 'end'];
|
|
29
19
|
function isDefinitionReviewerPath(graph, value) {
|
|
30
20
|
if (typeof value !== 'string' || !value || value.length > 2048)
|
|
31
21
|
return false;
|
|
@@ -38,7 +28,7 @@ function isDefinitionReviewerPath(graph, value) {
|
|
|
38
28
|
if (!node || node.type !== 'core')
|
|
39
29
|
return false;
|
|
40
30
|
if (index === segments.length - 1)
|
|
41
|
-
return node.data?.kind === 'role-turn';
|
|
31
|
+
return node.data?.kind === 'role-turn' || node.data?.kind === 'implementation-step' && node.data.params?.phase === 'reviewer';
|
|
42
32
|
const ref = node.data?.kind === 'component' ? node.data.params?.ref : node.data?.kind === 'map' ? node.data.params?.body : undefined;
|
|
43
33
|
body = typeof ref === 'string' ? graph.components?.[ref] : undefined;
|
|
44
34
|
}
|
|
@@ -176,6 +166,7 @@ function validateLoopGraph(graph, catalog) {
|
|
|
176
166
|
(cfg.maxCostUsd !== undefined && !Number.isFinite(cfg.maxCostUsd)) ||
|
|
177
167
|
(cfg.maxTransitions !== undefined && (!Number.isInteger(cfg.maxTransitions) || cfg.maxTransitions < 1 || cfg.maxTransitions > 10000)) ||
|
|
178
168
|
(cfg.maxTokens !== undefined && (!Number.isInteger(cfg.maxTokens) || cfg.maxTokens < 1)) ||
|
|
169
|
+
(cfg.ticketScope !== undefined && !['all', 'per-ticket'].includes(cfg.ticketScope)) ||
|
|
179
170
|
(cfg.reviewerStepId !== undefined && !isDefinitionReviewerPath(graph, cfg.reviewerStepId))) {
|
|
180
171
|
errors.push({
|
|
181
172
|
code: 'INVALID_CONFIG',
|
|
@@ -300,7 +291,7 @@ function loopNeedsTicket(graph) {
|
|
|
300
291
|
if (!graph)
|
|
301
292
|
return false;
|
|
302
293
|
for (const node of graph.nodes) {
|
|
303
|
-
if (node.type === 'core' && node.data?.kind
|
|
294
|
+
if (node.type === 'core' && ['implementation', 'implementation-step'].includes(node.data?.kind ?? ''))
|
|
304
295
|
return true;
|
|
305
296
|
const text = [node.data?.prompt, node.data?.command, node.data?.goal,
|
|
306
297
|
node.type === 'core' ? JSON.stringify(node.data?.params) : undefined]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.configurableImplementGraph = configurableImplementGraph;
|
|
4
|
+
const loop_agents_1 = require("./loop-agents");
|
|
5
|
+
const objectSchema = (properties, required = Object.keys(properties)) => ({ type: 'object', properties, required, additionalProperties: false });
|
|
6
|
+
const string = { type: 'string' };
|
|
7
|
+
const strings = { type: 'array', items: string, maxItems: 100 };
|
|
8
|
+
const score = { type: 'number', minimum: 0, maximum: 100 };
|
|
9
|
+
const aspects = { type_correctness: 60, pattern_adherence: 60, test_coverage: 60, security: 75, architectural_alignment: 60 };
|
|
10
|
+
/** The recipe, roles, schemas and decisions are Desktop data, never Core phases. */
|
|
11
|
+
function configurableImplementGraph() {
|
|
12
|
+
const agents = (0, loop_agents_1.defaultLoopAgents)();
|
|
13
|
+
agents.roles = {
|
|
14
|
+
plan: { ...agents.agents.architect, access: 'read', artifacts: 'all', openspecSkill: 'openspec-ff-change', prompt: agents.rolePrompts?.architect },
|
|
15
|
+
build: { ...agents.agents.developer, access: 'write', artifacts: 'tasks-checkboxes', openspecSkill: 'openspec-apply-change', prompt: agents.rolePrompts?.developer },
|
|
16
|
+
assess: { ...agents.agents.reviewer, access: 'read', artifacts: 'none', openspecSkill: 'openspec-verify-change', prompt: agents.rolePrompts?.reviewer },
|
|
17
|
+
correct: { ...agents.fixer, access: 'write', artifacts: 'tasks-checkboxes', openspecSkill: 'openspec-apply-change', prompt: agents.rolePrompts?.fixer },
|
|
18
|
+
};
|
|
19
|
+
const nodes = [{ id: 'start', type: 'start', position: { x: 0, y: 0 } }], edges = [];
|
|
20
|
+
const node = (id, label, kind, params, outcomes) => {
|
|
21
|
+
nodes.push({ id, type: 'core', position: { x: 0, y: nodes.length * 140 }, data: { kind, label, params } });
|
|
22
|
+
for (const [outcome, target] of Object.entries(outcomes))
|
|
23
|
+
edges.push({ id: `e-${id}-${outcome}`, source: id, target, label: outcome });
|
|
24
|
+
};
|
|
25
|
+
const turn = (id, label, roleId, prompt, schema, next) => node(id, label, 'role-turn', { roleId, prompt, structuredOutput: schema, sessionContinuity: 'none' }, { next, invalid: 'failed', failed: 'failed' });
|
|
26
|
+
const change = '{{run.changeId}}';
|
|
27
|
+
turn('architect', 'Architect', 'plan', `Plan the frozen ticket requirements for OpenSpec change ${change}. Use the bound official fast-forward skill. Author complete proposal, design, delta specs and actionable tasks. Propose existing repository verification commands for repositories with no configured checks. Return confidence, question (empty when none), and verification [{repositoryId,command,args,cwd?}]. When revisiting low confidence, investigate the missing repository evidence before asking again. Context and human answers: {{history.text}}`, objectSchema({ confidence: { enum: ['high', 'medium', 'low'] }, question: string, verification: { type: 'array', maxItems: 20, items: objectSchema({ repositoryId: string, command: string, args: strings, cwd: string }, ['repositoryId', 'command', 'args']) } }), 'confidence');
|
|
28
|
+
node('confidence', 'Planning confidence', 'condition', { expr: '$outputs.architect.structured.confidence != "low"' }, { true: 'validate', false: 'investigate' });
|
|
29
|
+
node('investigate', 'Investigate low confidence once', 'condition', { expr: '!exists($vars.planInvestigated) || $vars.planInvestigated == false' }, { true: 'mark-investigated', false: 'clarify' });
|
|
30
|
+
node('mark-investigated', 'Mark confidence investigation', 'assign', { set: { planInvestigated: true } }, { next: 'architect', failed: 'failed' });
|
|
31
|
+
node('clarify', 'Clarify plan', 'question', { text: '{{outputs.architect.structured.question}}' }, { next: 'reset-confidence' });
|
|
32
|
+
node('reset-confidence', 'Reset confidence after answer', 'assign', { set: { planInvestigated: false } }, { next: 'architect', failed: 'failed' });
|
|
33
|
+
node('validate', 'Validate OpenSpec', 'openspec-validate', { change }, { pass: 'freeze', fail: 'architect', failed: 'failed' });
|
|
34
|
+
node('freeze', 'Freeze approved artifacts', 'artifact-contract', { change, contractId: 'plan', action: 'freeze' }, { pass: 'developer', fail: 'architect', failed: 'failed' });
|
|
35
|
+
const implementation = objectSchema({ summary: string, incomplete: strings });
|
|
36
|
+
turn('developer', 'Developer', 'build', `Implement every approved task for change ${change} using the bound official apply skill. Preserve proposal, design and specs; only tick tasks you completed. Report summary and incomplete task descriptions. Prior execution evidence and human answers: {{history.text}}`, implementation, 'tasks');
|
|
37
|
+
node('tasks', 'Check artifacts and completed tasks', 'artifact-contract', { change, contractId: 'plan', action: 'check', requireCompletedTasks: true }, { pass: 'verify', fail: 'developer', failed: 'failed' });
|
|
38
|
+
node('verify', 'Verify candidate', 'verify', { commands: 'configured', additionalCommandsFrom: 'architect' }, { pass: 'reviewer', fail: 'correction-context', failed: 'failed' });
|
|
39
|
+
turn('reviewer', 'Reviewer', 'assess', `Review change ${change} read-only against all approved artifacts and frozen ticket acceptance criteria. Inspect real code and the host verification evidence: {{outputs.verify}}. Return approved, summary, issues, score (0-100), and aspects. Prior execution: {{history.text}}`, objectSchema({ approved: { type: 'boolean' }, summary: string, issues: strings, score, aspects: objectSchema(Object.fromEntries(Object.keys(aspects).map(aspect => [aspect, score]))) }), 'review-policy');
|
|
40
|
+
node('review-policy', 'Review acceptance policy', 'condition', { expr: ['$outputs.reviewer.structured.approved == true', '$outputs.reviewer.structured.issues.length == 0', '$outputs.reviewer.structured.score >= 70', ...Object.entries(aspects).map(([aspect, threshold]) => `$outputs.reviewer.structured.aspects.${aspect} >= ${threshold}`)].join(' && ') }, { true: 'approve', false: 'correction-context' });
|
|
41
|
+
node('correction-context', 'Select current review findings', 'condition', { expr: '$outputs.verify.valid == true && exists($outputs.reviewer.candidateHash) && $outputs.reviewer.candidateHash == $outputs.verify.candidateHash' }, { true: 'capture-review', false: 'clear-review' });
|
|
42
|
+
node('capture-review', 'Capture current review findings', 'assign', { set: { latestReview: '{{outputs.reviewer}}' } }, { next: 'fixer', failed: 'failed' });
|
|
43
|
+
node('clear-review', 'Clear outdated review findings', 'assign', { set: { latestReview: 'No reviewer findings for the current verified candidate. Diagnose the current host verification failure first.' } }, { next: 'fixer', failed: 'failed' });
|
|
44
|
+
turn('fixer', 'Fixer', 'correct', `Correct the reported implementation, test or review failure for change ${change} using the bound apply skill and preserving approved artifacts. Read failureSummary, command, cwd, exitCode and evidenceId first; use read_verification_evidence for truncated evidence. Reproduce the exact focused failure, inspect the assertion and tested code, then apply the smallest repair that preserves the required behavior. An unchanged filename does not prove an unrelated failure. A mandatory test inside the admitted workspace may receive a proven formatting/compatibility repair while retaining every required guard. Report diagnosis, focused commands with original exit codes, repair and unresolved failures in summary and incomplete. If the cause cannot be repaired within scope, explain the evidence and required action; an unchanged candidate with failed verification stops this run. Host verification: {{outputs.verify}}. Latest review findings for this candidate: {{run.latestReview}}. Prior execution: {{history.text}}`, implementation, 'correction-progress');
|
|
45
|
+
node('correction-progress', 'Check correction progress', 'condition', { expr: '$outputs.verify.valid == true || (exists($outputs.fixer.candidateHash) && $outputs.fixer.candidateHash != null && $outputs.fixer.candidateHash != $outputs.verify.candidateHash)' }, { true: 'tasks', false: 'correction-stalled' });
|
|
46
|
+
node('approve', 'Approve archive', 'approval', { reason: `Approve archiving the reviewed and verified candidate for change ${change}.`, bindCandidate: true, enabled: agents.approvalBeforeArchive === true }, { next: 'approval-policy' });
|
|
47
|
+
node('approval-policy', 'Approval decision', 'condition', { expr: '$outputs.approve.response.approved == true' }, { true: 'archive', false: 'failed' });
|
|
48
|
+
node('archive', 'Archive OpenSpec', 'openspec-archive', { change, allowArchived: true, requiresVerified: true, reviewedCandidate: '{{outputs.reviewer.candidateHash}}', approvedCandidate: '{{outputs.approve.candidateHash}}' }, { next: 'verify-archive', failed: 'verify' });
|
|
49
|
+
node('verify-archive', 'Verify archived candidate', 'verify', { commands: 'configured', additionalCommandsFrom: 'architect' }, { pass: 'done', fail: 'failed', failed: 'failed' });
|
|
50
|
+
nodes.push({ id: 'done', type: 'end', position: { x: 0, y: nodes.length * 140 }, data: { outcome: 'success', requiresVerified: true } }, { id: 'failed', type: 'end', position: { x: 360, y: nodes.length * 140 }, data: { outcome: 'failure' } }, { id: 'correction-stalled', type: 'end', position: { x: 720, y: nodes.length * 140 }, data: { outcome: 'failure', reason: 'Verification remains failed and the correction made no candidate changes. {{outputs.fixer.structured.summary}}' } });
|
|
51
|
+
edges.unshift({ id: 'e-start', source: 'start', target: 'architect' });
|
|
52
|
+
return { nodes, edges, config: { maxIterations: 12, maxTransitions: 200, timeoutMinutes: 0, aiStepTimeoutMinutes: 0, change: 'new', journal: 'ledger-only', ticketScope: 'all', agents, reviewerStepId: 'reviewer' } };
|
|
53
|
+
}
|
|
@@ -145,29 +145,37 @@ const CHANGE_ID_RE = /openspec[\\/]+changes[\\/]+(?!archive(?:[\\/]|$))([A-Za-z0
|
|
|
145
145
|
function resolveRunVars(text, vars) {
|
|
146
146
|
return text.replace(RUN_TOKEN_RE, (_m, key) => vars[key] ?? '');
|
|
147
147
|
}
|
|
148
|
-
const CHANGE_NAME_RE = /^[a-z0-9][a-z0-9
|
|
148
|
+
const CHANGE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
149
149
|
/** The authoritative OpenSpec target for a launch.
|
|
150
|
-
* Addenda/delivery deltas
|
|
150
|
+
* Addenda/delivery deltas use durable content/lineage identity; ordinary launches use the
|
|
151
151
|
* follow-up's declared name, else the spec's `openspecChangeName`. */
|
|
152
152
|
function seedChangeId(req) {
|
|
153
|
-
|
|
154
|
-
// A new generation never picks an old/archived proposal, even when the
|
|
155
|
-
// ticket metadata still names it. Resuming the same run keeps its target.
|
|
156
|
-
const hash = (0, crypto_1.createHash)('sha256').update(JSON.stringify([req.runId, req.addenda.ids, req.addenda.briefing])).digest('hex').slice(0, 20);
|
|
157
|
-
return { id: `spec-addenda-${hash}`, source: 'addenda' };
|
|
158
|
-
}
|
|
159
|
-
if (req.constants?.REVISION_REQUEST) {
|
|
160
|
-
const hash = (0, crypto_1.createHash)('sha256').update(JSON.stringify([req.runId, req.constants.REVISION_REQUEST])).digest('hex').slice(0, 20);
|
|
161
|
-
return { id: `delivery-change-${hash}`, source: 'revision' };
|
|
162
|
-
}
|
|
153
|
+
const valid = (value) => value.length <= 64 && CHANGE_NAME_RE.test(value);
|
|
163
154
|
const fromFollowUp = typeof req.followUp?.openspecChangeName === 'string' ? req.followUp.openspecChangeName.trim() : '';
|
|
164
|
-
if (fromFollowUp && CHANGE_NAME_RE.test(fromFollowUp))
|
|
165
|
-
return { id: fromFollowUp, source: 'followUp' };
|
|
166
155
|
const direct = typeof req.spec?.openspecChangeName === 'string' ? req.spec.openspecChangeName.trim() : '';
|
|
167
156
|
const fromMetadata = typeof req.spec?.metadata?.openspecChangeName === 'string' ? req.spec.metadata.openspecChangeName.trim() : '';
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
157
|
+
const declared = valid(fromFollowUp) ? fromFollowUp : valid(direct || fromMetadata) ? direct || fromMetadata : '';
|
|
158
|
+
const source = declared === fromFollowUp ? 'followUp' : 'spec';
|
|
159
|
+
if (req.addenda?.ids.length || req.constants?.REVISION_REQUEST) {
|
|
160
|
+
const state = declared ? openspecChangeState(req.cwd, declared) : 'missing';
|
|
161
|
+
// A pending implementation's addenda refine the same change. Only work
|
|
162
|
+
// already delivered/archived needs a separate delta proposal.
|
|
163
|
+
if (declared && (state === 'active' || (state === 'missing' && !req.constants?.REVISION_REQUEST && req.spec?.status !== 'on_review')))
|
|
164
|
+
return { id: declared, source };
|
|
165
|
+
const hash = (0, crypto_1.createHash)('sha256').update(JSON.stringify([
|
|
166
|
+
req.constants?.REVISION_OF_DELIVERY_ID ?? null,
|
|
167
|
+
req.constants?.REVISION_NUMBER ?? null,
|
|
168
|
+
req.constants?.REVISION_REQUEST ?? null,
|
|
169
|
+
req.addenda ? [...req.addenda.ids].sort() : [], req.addenda?.briefing ?? null,
|
|
170
|
+
])).digest('hex').slice(0, 12);
|
|
171
|
+
const revisionNumber = req.constants?.REVISION_NUMBER;
|
|
172
|
+
const kind = req.addenda?.ids.length ? 'addenda' : 'revision';
|
|
173
|
+
const suffix = req.constants?.REVISION_REQUEST ? `rev${revisionNumber && /^[1-9]\d{0,3}$/.test(revisionNumber) ? revisionNumber : '1'}-${hash}` : `addenda-${hash}`;
|
|
174
|
+
const stem = declared || (req.ticketId && req.ticketId > 0 ? `ticket-${req.ticketId}` : 'spec');
|
|
175
|
+
return { id: `${stem.slice(0, 63 - suffix.length).replace(/-+$/, '')}-${suffix}`, source: kind };
|
|
176
|
+
}
|
|
177
|
+
if (declared)
|
|
178
|
+
return { id: declared, source };
|
|
171
179
|
return undefined;
|
|
172
180
|
}
|
|
173
181
|
/** Where an OpenSpec change currently lives under `cwd`: an ACTIVE
|
|
@@ -498,7 +506,7 @@ class LoopRunManager {
|
|
|
498
506
|
if ((0, loop_graph_1.isDefinitionGraph)(graph)) {
|
|
499
507
|
if (!this.executors.runDefinition)
|
|
500
508
|
throw new Error('engine_unsupported: Core workflow execution is unavailable');
|
|
501
|
-
await this.executors.assertDefinitionSupport?.();
|
|
509
|
+
await this.executors.assertDefinitionSupport?.(graph);
|
|
502
510
|
}
|
|
503
511
|
else
|
|
504
512
|
await this.executors.assertLegacyEngineSupport?.();
|
|
@@ -854,6 +862,8 @@ class LoopRunManager {
|
|
|
854
862
|
});
|
|
855
863
|
if (definitionEngine)
|
|
856
864
|
(0, loop_runs_store_1.saveDefinitionRun)(this.db, runId, { request: req, source: 'definition' });
|
|
865
|
+
if (req.launchConfig)
|
|
866
|
+
this.db.prepare('UPDATE loop_runs SET launch_config_json = ? WHERE id = ?').run(JSON.stringify(req.launchConfig), runId);
|
|
857
867
|
if (req.executionManifest)
|
|
858
868
|
this.db.prepare('UPDATE loop_runs SET execution_manifest = ? WHERE id = ?').run(JSON.stringify(req.executionManifest), runId);
|
|
859
869
|
(0, db_1.createJob)(this.db, {
|
|
@@ -1422,7 +1432,7 @@ class LoopRunManager {
|
|
|
1422
1432
|
const effectiveTimeoutMs = Number.isFinite(remainingMs)
|
|
1423
1433
|
? Math.max(1, Math.min(aiStepTimeoutMs === 0 ? Infinity : (aiStepTimeoutMs ?? 15 * 60_000), remainingMs))
|
|
1424
1434
|
: aiStepTimeoutMs;
|
|
1425
|
-
const coreRun = { runId, implementation: requiresCoreCompletion, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId, verificationStep: node.data?.requireVerificationPass === true || /\{\{\s*cmd:(?:verify|revision-verify|opsx:verify)\s*\}\}/.test(rawTemplate) };
|
|
1435
|
+
const coreRun = { runId, implementation: requiresCoreCompletion, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId, workspacePaths: req.workspacePaths, verificationStep: node.data?.requireVerificationPass === true || /\{\{\s*cmd:(?:verify|revision-verify|opsx:verify)\s*\}\}/.test(rawTemplate) };
|
|
1426
1436
|
const interactivePlan = this.executors.planInteractiveAiStep?.({
|
|
1427
1437
|
coreRun,
|
|
1428
1438
|
provider: nodeProvider,
|
|
@@ -1552,7 +1562,7 @@ class LoopRunManager {
|
|
|
1552
1562
|
&& requiresCoreCompletion
|
|
1553
1563
|
&& this.executors.validateCoreCompletion) {
|
|
1554
1564
|
const completion = this.executors.validateCoreCompletion({
|
|
1555
|
-
coreRun: { runId, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId },
|
|
1565
|
+
coreRun: { runId, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId, workspacePaths: req.workspacePaths },
|
|
1556
1566
|
provider: nodeProvider, model: nodeModel, effort: nodeEffort, profileName: req.profileName,
|
|
1557
1567
|
cwd: req.cwd, repoDir: req.repoDir, executionManifest: req.executionManifest,
|
|
1558
1568
|
});
|
|
@@ -8,6 +8,8 @@ exports.coreStarterGraph = coreStarterGraph;
|
|
|
8
8
|
exports.supportsCoreTemplates = supportsCoreTemplates;
|
|
9
9
|
exports.loopTemplatesForCapabilities = loopTemplatesForCapabilities;
|
|
10
10
|
exports.getLoopTemplate = getLoopTemplate;
|
|
11
|
+
const loop_core_factory_1 = require("./loop-core-factory");
|
|
12
|
+
const loop_agents_1 = require("./loop-agents");
|
|
11
13
|
/** Closed taxonomy a template's `category` must belong to. Single source of truth
|
|
12
14
|
* for the gallery's category chips (the client derives its chip list from the
|
|
13
15
|
* categories actually present in the served catalog). */
|
|
@@ -303,7 +305,8 @@ exports.LEGACY_LOOP_TEMPLATES = exports.LOOP_TEMPLATES;
|
|
|
303
305
|
// - every mutating starter runs the real `verify` piece over the project's
|
|
304
306
|
// configured checks and ends with `requiresVerified: true` — a passing sentinel
|
|
305
307
|
// alone never reaches a success end;
|
|
306
|
-
// - ship-and-green
|
|
308
|
+
// - ship-and-green expands implementation operations when supported, then
|
|
309
|
+
// verify → decider → prompt(fix); old Core retains the compatibility wrapper;
|
|
307
310
|
// - prompts, goals and iteration caps come from STARTER_TEXT (legacy parity).
|
|
308
311
|
/** Fix step shared by every mutating Core starter. Reuses the tuned `{{cmd:fix}}`
|
|
309
312
|
* contract and covers the verify→fix edge, where no Decider verdict precedes it. */
|
|
@@ -324,7 +327,7 @@ function watcherVerdict(goal) {
|
|
|
324
327
|
const CORE_GOAL_EVIDENCE = 'The configured host verification already passed on the current candidate before this decision. Judge only from concrete evidence in the history (commands run, their output, files changed); green baseline checks, setup or planning alone do not prove the goal.';
|
|
325
328
|
const SHIP_AND_GREEN_GOAL = 'Stop only when the history proves the spec is implemented: every acceptance criterion maps to real code and behavioral evidence across every selected ticket and repository, and the configured host verification passed on the current candidate. Setup, planning, a launched subagent or green baseline checks alone are insufficient; continue when work is missing or incomplete.';
|
|
326
329
|
/** Wall-clock cap for starters (legacy default). ship-and-green is untimed like the
|
|
327
|
-
* Implement factory:
|
|
330
|
+
* Implement factory: implementation operations retain their own execution limits. */
|
|
328
331
|
const STARTER_TIMEOUT_MIN = 30;
|
|
329
332
|
const CORE_STARTER_SHAPES = {
|
|
330
333
|
'ship-and-green': 'ship',
|
|
@@ -338,8 +341,10 @@ const CORE_STARTER_SHAPES = {
|
|
|
338
341
|
};
|
|
339
342
|
/** Core definition graph for one of the eight starters. Deterministic: same id ⇒
|
|
340
343
|
* structurally identical graph (positions included). */
|
|
341
|
-
function coreStarterGraph(id) {
|
|
344
|
+
function coreStarterGraph(id, independent = false, configurable = false) {
|
|
342
345
|
const shape = CORE_STARTER_SHAPES[id];
|
|
346
|
+
if (shape === 'ship' && configurable)
|
|
347
|
+
return (0, loop_core_factory_1.coreFactoryGraph)('implement', true, true);
|
|
343
348
|
const { prompt, goal, maxIterations } = STARTER_TEXT[id];
|
|
344
349
|
const nodes = [{ id: 'start', type: 'start', position: { x: COL_X, y: 0 } }];
|
|
345
350
|
const edges = [];
|
|
@@ -381,10 +386,29 @@ function coreStarterGraph(id) {
|
|
|
381
386
|
}
|
|
382
387
|
edges.unshift({ id: 'e-start', source: 'start', target: entry });
|
|
383
388
|
nodes.push({ id: 'done', type: 'end', position: { x: COL_X, y: ROW_GAP * nodes.length }, data: successEnd }, { id: 'failed', type: 'end', position: { x: COL_RIGHT_X, y: ROW_GAP * nodes.length }, data: { outcome: 'failure' } });
|
|
389
|
+
if (independent) {
|
|
390
|
+
config.agents = (0, loop_agents_1.defaultLoopAgents)(shape === 'ship' ? 'implementation' : 'free');
|
|
391
|
+
if (shape === 'ship') {
|
|
392
|
+
const operations = (0, loop_core_factory_1.coreFactoryGraph)('implement', true);
|
|
393
|
+
const path = (id) => id === 'done' ? 'verify' : id === 'failed' ? 'failed' : `implementation-${id}`;
|
|
394
|
+
nodes.splice(nodes.findIndex(node => node.id === 'implement'), 1);
|
|
395
|
+
const remove = new Set(edges.filter(edge => edge.source === 'implement').map(edge => edge.id));
|
|
396
|
+
for (let i = edges.length - 1; i >= 0; i--)
|
|
397
|
+
if (remove.has(edges[i].id))
|
|
398
|
+
edges.splice(i, 1);
|
|
399
|
+
for (const edge of edges)
|
|
400
|
+
if (edge.target === 'implement')
|
|
401
|
+
edge.target = path('architect');
|
|
402
|
+
for (const node of operations.nodes.filter(node => node.type === 'core'))
|
|
403
|
+
nodes.push({ ...node, id: path(node.id) });
|
|
404
|
+
for (const edge of operations.edges.filter(edge => edge.source !== 'start'))
|
|
405
|
+
edges.push({ ...edge, id: 'implementation-' + edge.id, source: path(edge.source), target: path(edge.target) });
|
|
406
|
+
}
|
|
407
|
+
}
|
|
384
408
|
return { nodes, edges, config };
|
|
385
409
|
}
|
|
386
410
|
const CORE_STARTER_DESCRIPTIONS = {
|
|
387
|
-
'ship-and-green': 'Fully autonomous: run
|
|
411
|
+
'ship-and-green': 'Fully autonomous: run the loop\'s implementation steps for the spec, then verify with the project\'s configured checks and refine (fix) on failure — verify → decide → fix until green. Success requires a verified candidate; no human intervention.',
|
|
388
412
|
'verify-pass': 'Autonomous verify-and-fix: run the project\'s configured build/lint/tests through Core\'s verify piece, then refine on failure — verify → fix → verify until green.',
|
|
389
413
|
'ci-watch': 'Poll CI checks on the open PR (the agent uses the repo\'s CI tooling) until every check is green. Read-only: the watcher never edits the repository.',
|
|
390
414
|
'lint-and-fix': 'The agent detects and runs the project\'s linter and fixes every issue, iterating until the codebase is clean. Every pass is confirmed by the project\'s configured verification before the loop can succeed.',
|
|
@@ -414,7 +438,7 @@ function loopTemplatesForCapabilities(capabilities) {
|
|
|
414
438
|
...template,
|
|
415
439
|
description: CORE_STARTER_DESCRIPTIONS[template.id],
|
|
416
440
|
...(readOnly ? { readOnly: true } : {}),
|
|
417
|
-
graph: coreStarterGraph(template.id),
|
|
441
|
+
graph: coreStarterGraph(template.id, capabilities?.implementationSteps === 1, capabilities?.workflowAgentSteps === 1),
|
|
418
442
|
};
|
|
419
443
|
});
|
|
420
444
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerLoopsRoutes = registerLoopsRoutes;
|
|
4
|
+
const loop_agents_1 = require("./loop-agents");
|
|
5
|
+
const agent_runtime_settings_1 = require("../../agent-runtime/runtime/agent-runtime-settings");
|
|
4
6
|
const ids_1 = require("../../../ids");
|
|
5
7
|
const feature_flags_1 = require("../../../feature-flags");
|
|
6
8
|
const loops_store_1 = require("./loops-store");
|
|
@@ -57,6 +59,12 @@ function registerLoopsRoutes(router, deps) {
|
|
|
57
59
|
}
|
|
58
60
|
return true;
|
|
59
61
|
}
|
|
62
|
+
router.get('/loop-agent-defaults', async (_req, res) => {
|
|
63
|
+
if (!guard(res))
|
|
64
|
+
return;
|
|
65
|
+
const runtime = await (0, agent_runtime_loader_1.loadCoreAgentRuntime)().catch(() => null);
|
|
66
|
+
res.json({ agents: (0, loop_agents_1.defaultLoopAgents)(), guardrails: { supported: runtime?.api?.capabilities?.configurableGuardrails === 1, catalog: runtime?.api?.guardrails ?? [] } });
|
|
67
|
+
});
|
|
60
68
|
// ── Templates ──────────────────────────────────────────────────────────────
|
|
61
69
|
router.get('/loop-templates', async (_req, res) => {
|
|
62
70
|
if (!guard(res))
|
|
@@ -410,6 +418,15 @@ function registerLoopsRoutes(router, deps) {
|
|
|
410
418
|
res.status(409).json({ error: 'engine_unsupported', message: 'Update Core to publish executable workflows.' });
|
|
411
419
|
return;
|
|
412
420
|
}
|
|
421
|
+
if (current.graph.config.agents) {
|
|
422
|
+
try {
|
|
423
|
+
(0, agent_runtime_settings_1.validateLoopRuntimeSettings)(current.graph.config.agents);
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
res.status(422).json({ error: 'invalid_loop_agents', message: error instanceof Error ? error.message : 'Invalid loop agent settings' });
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
413
430
|
let draft;
|
|
414
431
|
try {
|
|
415
432
|
draft = (0, loop_definition_1.compileLoopToDefinition)(current.graph, { id: current.id, title: current.name, constants: (0, loop_constants_1.loadConstantMap)(db), provider: 'claude', spec: { id: 1, title: 'Sample spec', description: 'Publication preview' } });
|