specrails-desktop 2.47.0 → 2.48.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 -0
- package/client/dist/assets/{ActivityFeedPage-DSCpSOGV.js → ActivityFeedPage-CGq6QhP5.js} +1 -1
- package/client/dist/assets/AgentBrowserCapture-C4GZ-kdf.js +1 -0
- package/client/dist/assets/AgentModeAnalyticsPane-_IPmL0ZS.js +2 -0
- package/client/dist/assets/AgentModeCodePane-B5WG5DOP.js +2 -0
- package/client/dist/assets/{AgentModeJobsPane-Dw3DNFkh.js → AgentModeJobsPane-w1N_HIXh.js} +1 -1
- package/client/dist/assets/AgentsPage-CmwnnT46.js +87 -0
- package/client/dist/assets/AnalyticsPage-BUMsKtsJ.js +1 -0
- package/client/dist/assets/CodePage-PjgywJPS.js +3 -0
- package/client/dist/assets/DesktopAnalyticsPage-BXY1pH4y.js +1 -0
- package/client/dist/assets/DocsDialog-ccQETHKB.js +11 -0
- package/client/dist/assets/DocsPage-DzEgplB3.js +11 -0
- package/client/dist/assets/{ExportDropdown-BwuvqS2Y.js → ExportDropdown-B8bBGopr.js} +1 -1
- package/client/dist/assets/InteractiveJobComposer-CEwGkJ8F.js +19 -0
- package/client/dist/assets/JobDetailModal-BYXmuTDe.js +1 -0
- package/client/dist/assets/JobDetailPage-CWp6MwJj.js +1 -0
- package/client/dist/assets/JobsPage-D1O1uyG8.js +1 -0
- package/client/dist/assets/{LoopBuilderPage-Byvq8Oqx.js → LoopBuilderPage-DQE-7Vby.js} +2 -2
- package/client/dist/assets/{LoopPreviewModal-BMC9Mgj1.js → LoopPreviewModal-Bk7zIXD5.js} +1 -1
- package/client/dist/assets/LoopsPage-CqG7mx56.js +1 -0
- package/client/dist/assets/MinimizedChatsContext-UpnqccPx.js +1 -0
- package/client/dist/assets/PluginsPage-CjgVvmpW.js +2 -0
- package/client/dist/assets/{ProjectSettingsDialog-nspqxH-M.js → ProjectSettingsDialog-D6vBCoay.js} +1 -1
- package/client/dist/assets/{RepositoryDeliveries-Dq9NJOWi.js → RepositoryDeliveries-C36ipKp_.js} +1 -1
- package/client/dist/assets/{RepositoryScopeSelector-C3jKG2z0.js → RepositoryScopeSelector-0Gz_fR1E.js} +1 -1
- package/client/dist/assets/ReviewPacketPage-Dr9RYs7q.js +1 -0
- package/client/dist/assets/TemplatePreviewModal-sCMHYFJo.js +1 -0
- package/client/dist/assets/TicketDetailModalContext-C5GgJlEt.js +14 -0
- package/client/dist/assets/{Trans-C0KyPo2O.js → Trans-9pHTy2fH.js} +1 -1
- package/client/dist/assets/{addspec-D2YIQ0GU.js → addspec-BOvgmcgk.js} +1 -1
- package/client/dist/assets/{addspec-B_TPJt-R.js → addspec-Clmyfn_R.js} +1 -1
- package/client/dist/assets/{addspec-A1lDQa4M.js → addspec-CsNaBpHp.js} +1 -1
- package/client/dist/assets/{addspec-CQBjzt24.js → addspec-D2tvWmmV.js} +1 -1
- package/client/dist/assets/{addspec-CLYLvaVP.js → addspec-D3nFwvD0.js} +1 -1
- package/client/dist/assets/{addspec-BPpZ_olp.js → addspec-DVLEhK3w.js} +1 -1
- package/client/dist/assets/{addspec-iFdQ6D99.js → addspec-DXbK-tb0.js} +1 -1
- package/client/dist/assets/{addspec-BHZd0N5F.js → addspec-OHG8DFEn.js} +1 -1
- package/client/dist/assets/agentRuntime-BUimiX__.js +1 -0
- package/client/dist/assets/agentRuntime-BcN97sAB.js +1 -0
- package/client/dist/assets/agentRuntime-Ckyg-ahT.js +1 -0
- package/client/dist/assets/agentRuntime-DJCwyX4t.js +1 -0
- package/client/dist/assets/agentRuntime-DVzPJOl0.js +1 -0
- package/client/dist/assets/agentRuntime-V9AZTG3c.js +1 -0
- package/client/dist/assets/agentRuntime-bAAjw1vw.js +1 -0
- package/client/dist/assets/agentRuntime-v4qKAYH-.js +1 -0
- package/client/dist/assets/{agents-DyI4rbHa.js → agents-CA5eT1d0.js} +1 -1
- package/client/dist/assets/{agents-_5mJMkAE.js → agents-CAgy-5uS.js} +1 -1
- package/client/dist/assets/{agents-BIT1UdTy.js → agents-CNm6Qzlv.js} +1 -1
- package/client/dist/assets/{agents-CST-lpYX.js → agents-CTwbx-fb.js} +1 -1
- package/client/dist/assets/{agents-C7fKfvm8.js → agents-DEGV1Vao.js} +1 -1
- package/client/dist/assets/{agents-DsgGMGwU.js → agents-HsDF1aQU.js} +1 -1
- package/client/dist/assets/{agents-Cm2JYrAa.js → agents-IQjdA-z9.js} +1 -1
- package/client/dist/assets/{agents-H6tVQUNP.js → agents-RkGoteXh.js} +1 -1
- package/client/dist/assets/analytics-BNgZ1Nj7.js +1 -0
- package/client/dist/assets/analytics-BlypLLR6.js +1 -0
- package/client/dist/assets/analytics-CoEzWPx4.js +1 -0
- package/client/dist/assets/analytics-DFh5lSOl.js +1 -0
- package/client/dist/assets/analytics-DZp-DS2U.js +1 -0
- package/client/dist/assets/analytics-DjLlE7jS.js +1 -0
- package/client/dist/assets/analytics-jzCkOec3.js +1 -0
- package/client/dist/assets/analytics-nZIjjtCc.js +1 -0
- package/client/dist/assets/{dashboard-BK_mez-z.js → dashboard-C29Qwa-L.js} +1 -1
- package/client/dist/assets/{dashboard-BwkjxI6H.js → dashboard-CSJzf5OT.js} +1 -1
- package/client/dist/assets/{dashboard-C3bDKf7s.js → dashboard-CTfEtkqX.js} +1 -1
- package/client/dist/assets/{dashboard-Bt0cCzeG.js → dashboard-Cr4D8oMn.js} +1 -1
- package/client/dist/assets/{dashboard-fRrP0Kv0.js → dashboard-Csf1yGSN.js} +1 -1
- package/client/dist/assets/{dashboard-CdvjGfog.js → dashboard-D40HP2ks.js} +1 -1
- package/client/dist/assets/dashboard-DBAP2_SQ.js +1 -0
- package/client/dist/assets/{dashboard-B14uYXkG.js → dashboard-DMklepmz.js} +1 -1
- package/client/dist/assets/file-text-CFT-xXKN.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-D44phX1J.js → formatDistanceToNow-w_2U5pm0.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-EAh9LFSx.js → getTimezoneOffsetInMilliseconds-BRymg1Kk.js} +1 -1
- package/client/dist/assets/index-D0ScFZvR.css +2 -0
- package/client/dist/assets/index-DORCrgbI.js +77 -0
- package/client/dist/assets/jira-api-BqHbSdxK.js +1 -0
- package/client/dist/assets/jobs-C6qmvUAi.js +1 -0
- package/client/dist/assets/jobs-C9j2PuwW.js +1 -0
- package/client/dist/assets/jobs-DLHdDkHc.js +1 -0
- package/client/dist/assets/jobs-DSp9DoD_.js +1 -0
- package/client/dist/assets/jobs-DTQO08nJ.js +1 -0
- package/client/dist/assets/jobs-DoFaEl9g.js +1 -0
- package/client/dist/assets/jobs-Dpw6AEOP.js +1 -0
- package/client/dist/assets/jobs-DrF8Do8s.js +1 -0
- package/client/dist/assets/{narration-UWi_xIZ-.js → narration--hUhjM5v.js} +1 -1
- package/client/dist/assets/{narration-DiPyj06T.js → narration-CHI06czm.js} +1 -1
- package/client/dist/assets/{narration-DrBAf8uv.js → narration-DjKB3pQZ.js} +1 -1
- package/client/dist/assets/{narration-CqNvgRi9.js → narration-DtCYvRLM.js} +1 -1
- package/client/dist/assets/{narration-B1uGfofH.js → narration-DvLQBW9U.js} +1 -1
- package/client/dist/assets/{narration-BKcL4Srl.js → narration-eSx3gR0W.js} +1 -1
- package/client/dist/assets/{narration-OyujFouL.js → narration-n3hOA7LY.js} +1 -1
- package/client/dist/assets/{narration-BtjKyHVM.js → narration-thJOeT2-.js} +1 -1
- package/client/dist/assets/packet-BNW8ODQw.js +1 -0
- package/client/dist/assets/packet-CXYvfdjy.js +1 -0
- package/client/dist/assets/packet-CtIlB8YE.js +1 -0
- package/client/dist/assets/packet-DA1SJkMH.js +1 -0
- package/client/dist/assets/packet-DYcWfBip.js +1 -0
- package/client/dist/assets/packet-DseX9yKC.js +1 -0
- package/client/dist/assets/packet-_m5aOy_d.js +1 -0
- package/client/dist/assets/packet-qOemUAm3.js +1 -0
- package/client/dist/assets/{project-repositories-GRYdjlKB.js → project-repositories-BsCs1PPA.js} +1 -1
- package/client/dist/assets/provider-capabilities-Vi1ZGvAK.js +1 -0
- package/client/dist/assets/settings-BsehKpty.js +1 -0
- package/client/dist/assets/settings-CQWob3SB.js +1 -0
- package/client/dist/assets/settings-CgCl0hzy.js +1 -0
- package/client/dist/assets/settings-DLRLd22L.js +1 -0
- package/client/dist/assets/settings-Dn5prAFl.js +1 -0
- package/client/dist/assets/settings-HZSu-K4l.js +1 -0
- package/client/dist/assets/{settings-DEgcA0jd.js → settings-p4OVIyRh.js} +1 -1
- package/client/dist/assets/settings-rddaldUp.js +1 -0
- package/client/dist/assets/setup-B5FHuxvB.js +1 -0
- package/client/dist/assets/setup-BihzXadx.js +1 -0
- package/client/dist/assets/setup-BtC6Hg4K.js +1 -0
- package/client/dist/assets/setup-C9Fy9PKQ.js +1 -0
- package/client/dist/assets/setup-CROyqD5N.js +1 -0
- package/client/dist/assets/setup-DpqzaEEA.js +1 -0
- package/client/dist/assets/setup-Dwzt5nO1.js +1 -0
- package/client/dist/assets/setup-zLEDnG7N.js +1 -0
- package/client/dist/assets/{spending-CN9PHM65.js → spending-hcgxHP4O.js} +1 -1
- package/client/dist/assets/{useDesktop-iius_s5d.js → useDesktop-CAisjKWC.js} +1 -1
- package/client/dist/assets/{useSharedWebSocket-D7hkZ3Ka.js → useSharedWebSocket-BHIfW4QT.js} +2 -2
- package/client/dist/assets/wrench-BfmNO9Vw.js +1 -0
- package/client/dist/index.html +27 -26
- package/docs/guide/de/integrations/7-local-engines.md +37 -0
- package/docs/guide/en/integrations/7-local-engines.md +37 -0
- package/docs/guide/es/integrations/7-local-engines.md +37 -0
- package/docs/guide/fr/integrations/7-local-engines.md +37 -0
- package/docs/guide/it/integrations/7-local-engines.md +37 -0
- package/docs/guide/ja/integrations/7-local-engines.md +37 -0
- package/docs/guide/pt/integrations/7-local-engines.md +37 -0
- package/docs/guide/zh/integrations/7-local-engines.md +37 -0
- package/docs/internals/adding-a-provider.md +12 -0
- package/docs/internals/local-agent-runner.md +153 -0
- package/docs/internals/review-packet.md +8 -0
- package/docs/local-providers.md +215 -0
- package/local-runner/dist/specrails-local-runner.js +15815 -0
- package/package.json +7 -5
- package/server/dist/agent-chat-manager.js +8 -1
- package/server/dist/agent-chat-router.js +5 -2
- package/server/dist/agent-fence-promotion.js +53 -0
- package/server/dist/agent-mcp-config.js +4 -1
- package/server/dist/agent-runtime-bridge.js +7 -2
- package/server/dist/agent-runtime-controls-router.js +12 -1
- package/server/dist/agent-runtime-controls.js +37 -2
- package/server/dist/agent-runtime-effective-config.js +28 -7
- package/server/dist/agent-runtime-metrics.js +1 -1
- package/server/dist/agent-runtime-settings-router.js +76 -6
- package/server/dist/agent-runtime-settings.js +137 -7
- package/server/dist/agent-spec-framing.js +10 -4
- package/server/dist/binary-probe.js +12 -0
- package/server/dist/blueprint-router.js +3 -2
- package/server/dist/chat-manager.js +5 -1
- package/server/dist/desktop-router.js +87 -4
- package/server/dist/index.js +13 -0
- package/server/dist/interactive-job-session.js +3 -1
- package/server/dist/local-engine-detection.js +118 -0
- package/server/dist/loop-executors.js +2 -1
- package/server/dist/loop-role-engines.js +129 -0
- package/server/dist/loop-run-manager.js +4 -4
- package/server/dist/mcp/tools/app-settings.js +30 -4
- package/server/dist/mcp/tools/rails.js +1 -1
- package/server/dist/pr-body.js +11 -2
- package/server/dist/pricing.js +15 -0
- package/server/dist/provider-detection.js +63 -1
- package/server/dist/provider-selection.js +26 -1
- package/server/dist/providers/index.js +4 -1
- package/server/dist/providers/local-adapter-registry.js +56 -0
- package/server/dist/providers/local-adapter.js +426 -0
- package/server/dist/providers/registry.js +39 -4
- package/server/dist/rail-isolated-launch.js +3 -3
- package/server/dist/rails-router.js +66 -10
- package/server/dist/result-event.js +17 -0
- package/server/dist/review-packet.js +1 -0
- package/server/dist/schemas/agent-runtime.schema.json +10 -2
- package/server/dist/setup-manager.js +5 -1
- package/server/dist/spec-models.js +14 -2
- package/server/dist/worktree-node-modules.js +97 -65
- package/client/dist/assets/AgentBrowserCapture-mc1c5pgS.js +0 -1
- package/client/dist/assets/AgentModeAnalyticsPane--UkamBTm.js +0 -2
- package/client/dist/assets/AgentModeCodePane-DJCswsvU.js +0 -2
- package/client/dist/assets/AgentsPage-ChElPUL9.js +0 -87
- package/client/dist/assets/AnalyticsPage-BGtkFHBp.js +0 -1
- package/client/dist/assets/CodePage-BBnw3VGn.js +0 -3
- package/client/dist/assets/DesktopAnalyticsPage-B85tNScc.js +0 -1
- package/client/dist/assets/DocsDialog-D4cXrOMl.js +0 -11
- package/client/dist/assets/DocsPage-BCAbC6qy.js +0 -11
- package/client/dist/assets/InteractiveJobComposer-Coq-Zhk0.js +0 -19
- package/client/dist/assets/JobDetailModal--Mjam0Nc.js +0 -1
- package/client/dist/assets/JobDetailPage-B6ShWSBh.js +0 -1
- package/client/dist/assets/JobsPage-DHU_ep_B.js +0 -1
- package/client/dist/assets/LoopsPage-CFGl-Nw8.js +0 -1
- package/client/dist/assets/MinimizedChatsContext-DHjHBYqa.js +0 -1
- package/client/dist/assets/PluginsPage-BHygRnW6.js +0 -2
- package/client/dist/assets/ReviewPacketPage-C9mqhi65.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-BmwoAjOr.js +0 -1
- package/client/dist/assets/TicketDetailModalContext-lYmoONrd.js +0 -14
- package/client/dist/assets/agentRuntime-62GWdc26.js +0 -1
- package/client/dist/assets/agentRuntime-BN1mKAuY.js +0 -1
- package/client/dist/assets/agentRuntime-BNBCzQLM.js +0 -1
- package/client/dist/assets/agentRuntime-BQE_K1GG.js +0 -1
- package/client/dist/assets/agentRuntime-CCU3HCtS.js +0 -1
- package/client/dist/assets/agentRuntime-DT72Bpdv.js +0 -1
- package/client/dist/assets/agentRuntime-ZfMPeeiP.js +0 -1
- package/client/dist/assets/agentRuntime-g3W8Ry4d.js +0 -1
- package/client/dist/assets/analytics-47pYygtV.js +0 -1
- package/client/dist/assets/analytics-B0vt1g5U.js +0 -1
- package/client/dist/assets/analytics-BMzCvrCU.js +0 -1
- package/client/dist/assets/analytics-BlFN3Zwx.js +0 -1
- package/client/dist/assets/analytics-CZYzL-nR.js +0 -1
- package/client/dist/assets/analytics-DAsb9MJ8.js +0 -1
- package/client/dist/assets/analytics-FXQlQ2Yt.js +0 -1
- package/client/dist/assets/analytics-RnFCNkah.js +0 -1
- package/client/dist/assets/dashboard-BfBir_6o.js +0 -1
- package/client/dist/assets/file-text-BcLCfcWZ.js +0 -1
- package/client/dist/assets/index-Cfh9aaep.css +0 -2
- package/client/dist/assets/index-DgMBighL.js +0 -76
- package/client/dist/assets/jira-api-BQR6G0YY.js +0 -1
- package/client/dist/assets/jobs-5PQJbdOf.js +0 -1
- package/client/dist/assets/jobs-B2B08Oy9.js +0 -1
- package/client/dist/assets/jobs-BJZtBbwb.js +0 -1
- package/client/dist/assets/jobs-BlWEsQb7.js +0 -1
- package/client/dist/assets/jobs-ConfD8xD.js +0 -1
- package/client/dist/assets/jobs-DMPU64ms.js +0 -1
- package/client/dist/assets/jobs-DojASiTp.js +0 -1
- package/client/dist/assets/jobs-kVHkNpB-.js +0 -1
- package/client/dist/assets/packet-BQ2tXfJI.js +0 -1
- package/client/dist/assets/packet-BdpOJ1Cy.js +0 -1
- package/client/dist/assets/packet-C3Ievb9R.js +0 -1
- package/client/dist/assets/packet-C4zJIG0Y.js +0 -1
- package/client/dist/assets/packet-Cx-zRuWn.js +0 -1
- package/client/dist/assets/packet-DUTGX52M.js +0 -1
- package/client/dist/assets/packet-DhsKtUPB.js +0 -1
- package/client/dist/assets/packet-z3LJ8Zyy.js +0 -1
- package/client/dist/assets/provider-capabilities-CXipwrXJ.js +0 -1
- package/client/dist/assets/settings-B3rWEwOx.js +0 -1
- package/client/dist/assets/settings-BcVoiMyw.js +0 -1
- package/client/dist/assets/settings-BlYvaQ7w.js +0 -1
- package/client/dist/assets/settings-CD5uEBiL.js +0 -1
- package/client/dist/assets/settings-CyhS0DLJ.js +0 -1
- package/client/dist/assets/settings-DCMFUGDs.js +0 -1
- package/client/dist/assets/settings-DmbYKyMJ.js +0 -1
- package/client/dist/assets/setup-2xGgu4Sk.js +0 -1
- package/client/dist/assets/setup-Ba11l2JA.js +0 -1
- package/client/dist/assets/setup-BzW4kMok.js +0 -1
- package/client/dist/assets/setup-COzwYhkJ.js +0 -1
- package/client/dist/assets/setup-CRVULhtF.js +0 -1
- package/client/dist/assets/setup-DgvlxwpE.js +0 -1
- package/client/dist/assets/setup-Dw_wYeEY.js +0 -1
- package/client/dist/assets/setup-ZPtEeJPW.js +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specrails-desktop",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.48.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"server/dist",
|
|
18
18
|
"client/dist",
|
|
19
19
|
"mcp-bridge/dist",
|
|
20
|
+
"local-runner/dist",
|
|
20
21
|
"docs",
|
|
21
22
|
".claude/commands/specrails"
|
|
22
23
|
],
|
|
@@ -25,20 +26,21 @@
|
|
|
25
26
|
"dev:server": "tsx watch server/index.ts",
|
|
26
27
|
"dev:client": "cd client && npm run dev",
|
|
27
28
|
"dev:desktop": "tauri dev",
|
|
28
|
-
"dev:desktop:prepare": "npm run build:sidecar && npm run build:mcp-bridge && npm run dev:client",
|
|
29
|
+
"dev:desktop:prepare": "npm run build:sidecar && npm run build:mcp-bridge && npm run build:local-runner && npm run dev:client",
|
|
29
30
|
"build:server": "node -e \"require('fs').rmSync('server/dist',{recursive:true,force:true})\" && tsc --project tsconfig.build.json --noCheck && node scripts/copy-server-assets.mjs",
|
|
30
31
|
"build:cli": "tsc --project tsconfig.cli.json",
|
|
31
|
-
"build": "npm run build:server && npm run build --prefix client && npm run build:cli && npm run build:mcp-bridge",
|
|
32
|
+
"build": "npm run build:server && npm run build --prefix client && npm run build:cli && npm run build:mcp-bridge && npm run build:local-runner",
|
|
32
33
|
"prepack": "npm run build",
|
|
33
34
|
"test:scripts": "node scripts/test-scripts.mjs",
|
|
34
35
|
"check:package": "node scripts/check-package.mjs",
|
|
35
36
|
"build:sidecar": "node scripts/build-sidecar.mjs",
|
|
36
37
|
"build:mcp-bridge": "node scripts/build-mcp-bridge.mjs",
|
|
37
|
-
"build:
|
|
38
|
+
"build:local-runner": "node scripts/build-local-runner.mjs",
|
|
39
|
+
"build:desktop": "npm run build:server && cd client && npm run build && cd .. && npm run build:sidecar && npm run build:mcp-bridge && npm run build:local-runner && tauri build",
|
|
38
40
|
"assemble-runtimes": "node scripts/assemble-runtimes-local.mjs",
|
|
39
41
|
"build:desktop:local": "npm run assemble-runtimes && npm run build:desktop",
|
|
40
42
|
"generate-icons": "tauri icon src-tauri/icons/icon.svg",
|
|
41
|
-
"typecheck": "tsc --noEmit && tsc -p tsconfig.cli.json --noEmit && tsc -p mcp-bridge/tsconfig.json --noEmit && cd client && tsc --noEmit",
|
|
43
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.cli.json --noEmit && tsc -p mcp-bridge/tsconfig.json --noEmit && tsc -p local-runner/tsconfig.json --noEmit && cd client && tsc --noEmit",
|
|
42
44
|
"check-core-compat": "tsx scripts/check-core-compat.ts",
|
|
43
45
|
"test": "vitest run && tsx scripts/check-core-compat.ts",
|
|
44
46
|
"test:watch": "vitest",
|
|
@@ -13,6 +13,7 @@ const promises_1 = require("node:fs/promises");
|
|
|
13
13
|
const win_spawn_1 = require("./util/win-spawn");
|
|
14
14
|
const providers_1 = require("./providers");
|
|
15
15
|
const runtime_1 = require("./providers/runtime");
|
|
16
|
+
const registry_1 = require("./providers/registry");
|
|
16
17
|
const spawn_lifecycle_1 = require("./spawn-lifecycle");
|
|
17
18
|
const live_session_1 = require("./providers/live-session");
|
|
18
19
|
const live_session_types_1 = require("./providers/live-session-types");
|
|
@@ -415,7 +416,13 @@ class AgentChatManager {
|
|
|
415
416
|
// Per-turn dynamic context (pinned project, permission level, provider) rides
|
|
416
417
|
// the USER turn, never the system prompt — byte-stability contract (see
|
|
417
418
|
// agent-operator-prompt.ts).
|
|
418
|
-
|
|
419
|
+
// Local (OpenAI-compatible) engines are usually small models that drop the
|
|
420
|
+
// exact fence tags and ask the user for the #noframe waiver — restate the
|
|
421
|
+
// two rules they break most, on the USER turn (system prompt stays byte-stable).
|
|
422
|
+
const localProtocolNote = (0, registry_1.isLocalAdapterId)(adapter.id)
|
|
423
|
+
? ' | Protocol: fenced protocol blocks MUST be tagged exactly problem-frame, spec-draft or options (never json); never ask the user to type #noframe'
|
|
424
|
+
: '';
|
|
425
|
+
const turnSettings = `Permission level: ${tierLevel} (${['observe', 'edit', 'operate', 'autonomous'][tierLevel]}) | Provider: ${adapter.id} | Model: ${model}${reasoningEffort ? ` | Reasoning effort: ${reasoningEffort}` : ''}${localProtocolNote}`;
|
|
419
426
|
const contextPrefix = conversation.pinned_project_id
|
|
420
427
|
? `[Active project: projectId="${conversation.pinned_project_id}" | ${turnSettings}. Use this project for project-scoped tools; changing the execution target requires changing the conversation pin.]`
|
|
421
428
|
: `[No project pinned (Home) | ${turnSettings}]`;
|
|
@@ -9,6 +9,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
9
9
|
const multer_1 = __importDefault(require("multer"));
|
|
10
10
|
const express_1 = require("express");
|
|
11
11
|
const providers_1 = require("./providers");
|
|
12
|
+
const provider_selection_1 = require("./provider-selection");
|
|
12
13
|
const agent_tier_1 = require("./agent-tier");
|
|
13
14
|
const attachment_manager_1 = require("./attachment-manager");
|
|
14
15
|
const agent_store_1 = require("./agent-store");
|
|
@@ -121,7 +122,7 @@ function createAgentChatRouter(deps) {
|
|
|
121
122
|
});
|
|
122
123
|
// Per-provider model catalog for the header model selector.
|
|
123
124
|
router.get('/models', (req, res) => {
|
|
124
|
-
const provider = validProvider(req.query.provider) ??
|
|
125
|
+
const provider = validProvider(req.query.provider) ?? (0, provider_selection_1.defaultMachineProvider)();
|
|
125
126
|
const adapter = (0, providers_1.getAdapter)(provider);
|
|
126
127
|
const requestedModel = (0, providers_1.isModelAvailableForAdapter)(adapter, req.query.model)
|
|
127
128
|
? req.query.model
|
|
@@ -171,7 +172,9 @@ function createAgentChatRouter(deps) {
|
|
|
171
172
|
});
|
|
172
173
|
router.post('/conversations', (req, res) => {
|
|
173
174
|
const body = (req.body ?? {});
|
|
174
|
-
|
|
175
|
+
// No explicit provider ⇒ the machine's primary (a local-only machine lands
|
|
176
|
+
// on its local engine, never on an uninstalled claude).
|
|
177
|
+
let provider = (0, provider_selection_1.defaultMachineProvider)();
|
|
175
178
|
if (body.provider !== undefined) {
|
|
176
179
|
const v = validProvider(body.provider);
|
|
177
180
|
if (!v) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ─── Protocol fence promotion (small-model tolerance) ─────────────────────────
|
|
3
|
+
// The operator protocol asks for fenced blocks tagged EXACTLY `options`,
|
|
4
|
+
// `problem-frame` and `spec-draft`. Small / local models routinely emit the
|
|
5
|
+
// right JSON under a generic ```json (or bare ```) fence instead, which the
|
|
6
|
+
// strict parsers ignore — so no chips, no framing card, and commit_draft then
|
|
7
|
+
// refuses because no frame was ever registered. This pass re-tags a CLOSED
|
|
8
|
+
// generic fence whose body unambiguously has one protocol shape. Anything
|
|
9
|
+
// ambiguous is left untouched; fences already carrying a protocol tag are
|
|
10
|
+
// never rewritten. Mirror: server/agent-fence-promotion.ts (keep in sync).
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.detectProtocolShape = detectProtocolShape;
|
|
13
|
+
exports.promoteAgentProtocolFences = promoteAgentProtocolFences;
|
|
14
|
+
const GENERIC_FENCE_RE = /```(?:json|jsonc|JSON)?[^\S\n]*\n([\s\S]*?)\n[^\S\n]*```/g;
|
|
15
|
+
function isRecord(v) {
|
|
16
|
+
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
17
|
+
}
|
|
18
|
+
/** Detect which protocol block a parsed JSON body is, or null. */
|
|
19
|
+
function detectProtocolShape(value) {
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
if (value.length < 2 || value.length > 8)
|
|
22
|
+
return null;
|
|
23
|
+
return value.every((s) => typeof s === 'string' && s.trim() && s.trim().length <= 80) ? 'options' : null;
|
|
24
|
+
}
|
|
25
|
+
if (!isRecord(value))
|
|
26
|
+
return null;
|
|
27
|
+
if (isRecord(value.restated) && isRecord(value.alternative) && typeof value.discriminator === 'string')
|
|
28
|
+
return 'problem-frame';
|
|
29
|
+
if (typeof value.title === 'string' && typeof value.description === 'string'
|
|
30
|
+
&& ('acceptanceCriteria' in value || 'labels' in value || 'priority' in value))
|
|
31
|
+
return 'spec-draft';
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/** Re-tag generic fences that carry a protocol-shaped JSON body. */
|
|
35
|
+
function promoteAgentProtocolFences(text) {
|
|
36
|
+
if (!text || !text.includes('```'))
|
|
37
|
+
return text ?? '';
|
|
38
|
+
GENERIC_FENCE_RE.lastIndex = 0;
|
|
39
|
+
return text.replace(GENERIC_FENCE_RE, (whole, body, offset) => {
|
|
40
|
+
// A fence preceded by another backtick is a quoted/long fence — never touch.
|
|
41
|
+
if (offset > 0 && text[offset - 1] === '`')
|
|
42
|
+
return whole;
|
|
43
|
+
let parsed;
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(body);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return whole;
|
|
49
|
+
}
|
|
50
|
+
const shape = detectProtocolShape(parsed);
|
|
51
|
+
return shape ? '```' + shape + '\n' + body + '\n```' : whole;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -306,7 +306,10 @@ function codexExternalOverrides(server) {
|
|
|
306
306
|
function prepareAgentMcp(opts) {
|
|
307
307
|
const adapter = (0, providers_1.getAdapter)(opts.adapterId);
|
|
308
308
|
const external = (opts.external ?? []).filter((s) => s.name !== 'specrails');
|
|
309
|
-
|
|
309
|
+
// claude AND local (OpenAI-compatible) runners take the per-conversation
|
|
310
|
+
// `--mcp-config <file>` (design D8): the bundled runner starts the same
|
|
311
|
+
// stdio servers and exposes their tools as `mcp__<server>__<tool>`.
|
|
312
|
+
if (adapter.id === 'claude' || (0, providers_1.isLocalAdapterId)(adapter.id)) {
|
|
310
313
|
const extraArgs = buildAgentMcpArgs(opts);
|
|
311
314
|
if (!extraArgs.length)
|
|
312
315
|
throw new Error('The bundled Specrails MCP bridge is unavailable.');
|
|
@@ -62,8 +62,13 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
62
62
|
repositoryIds: admittedContext.repositories.map(repo => repo.id), source, providerOverride: options.providerOverride,
|
|
63
63
|
});
|
|
64
64
|
config.rolePrompts = { ...(await (0, agent_runtime_loader_1.loadCoreAgentRuntime)()).rolePromptDefaults(), ...(0, agent_runtime_settings_1.loadRuntimeRolePrompts)() };
|
|
65
|
-
|
|
65
|
+
// Core rejects unknown connection keys: drop the desktop-only local-engine
|
|
66
|
+
// fields (label/defaultModel/rates/supportsReasoningEffort) before Core sees it.
|
|
66
67
|
const runtime = await (0, agent_runtime_loader_1.loadCoreAgentRuntime)();
|
|
68
|
+
config.providers = (0, agent_runtime_settings_1.stripDesktopConnectionFields)(config.providers, (0, agent_runtime_settings_1.coreConnectionFieldGates)(runtime.api?.capabilities));
|
|
69
|
+
if (runtime.api?.capabilities?.configurableGuardrails !== 1)
|
|
70
|
+
delete config.guardrails;
|
|
71
|
+
const override = options.providerOverride;
|
|
67
72
|
runtime.validateRuntimeConfig(JSON.parse(JSON.stringify(config)));
|
|
68
73
|
(0, agent_runtime_loader_1.validateRequestedRoleEfforts)(runtime, config);
|
|
69
74
|
cli = (0, agent_runtime_package_1.retainAgentRuntime)(cli, options.contextPath);
|
|
@@ -94,7 +99,7 @@ async function runAgentRuntimeInvocation(options) {
|
|
|
94
99
|
const frozenPath = (0, node_path_1.join)((0, node_path_1.dirname)(options.contextPath), 'desktop-runtime-config.json');
|
|
95
100
|
if ((0, node_fs_1.existsSync)(frozenPath)) {
|
|
96
101
|
const frozen = JSON.parse((0, node_fs_1.readFileSync)(frozenPath, 'utf8'));
|
|
97
|
-
for (const [role, assignment] of Object.entries(frozen.agents)) {
|
|
102
|
+
for (const [role, assignment] of [...Object.entries(frozen.agents), ...(frozen.fixer ? [['fixer', frozen.fixer]] : [])]) {
|
|
98
103
|
try {
|
|
99
104
|
options.onLine?.(`[runtime] ${role}: ${assignment.provider}/${assignment.model ?? 'provider default'}\n`);
|
|
100
105
|
}
|
|
@@ -38,7 +38,9 @@ function registerAgentRuntimeControlRoutes({ router, ctx }) {
|
|
|
38
38
|
}
|
|
39
39
|
const context = ctx(req);
|
|
40
40
|
const latest = context.db.prepare('SELECT id FROM loop_runs WHERE rail_index = ? ORDER BY started_at DESC, rowid DESC LIMIT 1').get(railIndex);
|
|
41
|
-
|
|
41
|
+
// A dismissed continuation — or a green one with nothing left to decide — leaves the rail card (the run stays in the history list below).
|
|
42
|
+
const summary = latest && (0, agent_runtime_paths_1.hasAgentRuntimeRequest)(context.project, latest.id) ? await controls(req).summary(latest.id) : undefined;
|
|
43
|
+
res.json({ runs: summary && (0, agent_runtime_controls_1.pinsRailCard)(summary) ? [summary] : [] });
|
|
42
44
|
return;
|
|
43
45
|
}
|
|
44
46
|
res.json({ runs: await controls(req).list() });
|
|
@@ -82,6 +84,15 @@ function registerAgentRuntimeControlRoutes({ router, ctx }) {
|
|
|
82
84
|
res.status(error instanceof agent_runtime_controls_1.RuntimeControlError ? error.statusCode : 500).json({ message: error instanceof agent_runtime_controls_1.RuntimeControlError ? error.message : 'Could not prepare delivery' });
|
|
83
85
|
}
|
|
84
86
|
});
|
|
87
|
+
router.post('/:projectId/agent-runtime/runs/:runId/dismiss', (req, res) => {
|
|
88
|
+
try {
|
|
89
|
+
controls(req).dismiss(String(req.params.runId));
|
|
90
|
+
res.json({ dismissed: true });
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
res.status(error instanceof agent_runtime_controls_1.RuntimeControlError ? error.statusCode : 500).json({ error: error instanceof agent_runtime_controls_1.RuntimeControlError ? error.code : 'runtime_dismiss_failed', message: error instanceof Error ? error.message : 'Could not dismiss the execution' });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
85
96
|
router.post('/:projectId/agent-runtime/runs/:runId/cancel', (req, res) => {
|
|
86
97
|
try {
|
|
87
98
|
controls(req).cancel(String(req.params.runId));
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.AgentRuntimeControls = exports.RuntimeControlError = void 0;
|
|
7
|
+
exports.pinsRailCard = pinsRailCard;
|
|
7
8
|
exports.validateRuntimeResumeInput = validateRuntimeResumeInput;
|
|
8
9
|
exports.readAgentRuntimeStatus = readAgentRuntimeStatus;
|
|
9
10
|
const agent_runtime_events_1 = require("./agent-runtime-events");
|
|
@@ -25,8 +26,23 @@ const multi_repo_execution_store_1 = require("./multi-repo-execution-store");
|
|
|
25
26
|
const db_1 = require("./db");
|
|
26
27
|
const loop_run_manager_1 = require("./loop-run-manager");
|
|
27
28
|
const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
28
|
-
const STEP_IDS = ['architect', 'developer', 'verify', 'reviewer', 'archive'];
|
|
29
|
+
const STEP_IDS = ['architect', 'developer', 'fixer', 'verify', 'reviewer', 'archive'];
|
|
29
30
|
const ANSWER_LIMIT = 20_000;
|
|
31
|
+
/**
|
|
32
|
+
* Whether a continuation still deserves the rail card. Anything that needs a
|
|
33
|
+
* human (blocked, failed, interrupted, a pending question or approval, a
|
|
34
|
+
* settle still owed) pins the rail; a run that SUCCEEDED and offers no action
|
|
35
|
+
* does not — its evidence lives in the job log and the Settings history, and
|
|
36
|
+
* the delivery decision (when one exists) has its own strip. Observed: a rail
|
|
37
|
+
* kept a "Completed" card with only Dismiss on it after every green run.
|
|
38
|
+
*/
|
|
39
|
+
function pinsRailCard(summary) {
|
|
40
|
+
if (summary.dismissed)
|
|
41
|
+
return false;
|
|
42
|
+
if (summary.active || summary.canResume || summary.canSettle || summary.canCancel || summary.pendingQuestion || summary.pendingApproval || summary.recoverableSteps.length)
|
|
43
|
+
return true;
|
|
44
|
+
return summary.status !== 'succeeded';
|
|
45
|
+
}
|
|
30
46
|
class RuntimeControlError extends Error {
|
|
31
47
|
statusCode;
|
|
32
48
|
code;
|
|
@@ -189,7 +205,8 @@ class AgentRuntimeControls {
|
|
|
189
205
|
traceId: state.traceId, pendingQuestion: openQuestion(state),
|
|
190
206
|
metrics: (0, agent_runtime_metrics_1.readRuntimeEfficiency)(superseding ? superseding.metrics : state.metrics), efficiencySummary: (0, agent_runtime_metrics_1.readRuntimeEfficiencySummary)(superseding ? superseding.efficiencySummary : state.efficiencySummary),
|
|
191
207
|
canSettle: !superseding && !active && state.status === 'succeeded' && this.ctx.db.prepare('SELECT status FROM jobs WHERE id = ?').get(runId)?.status !== 'completed',
|
|
192
|
-
recoverableSteps, active, canCancel: this.active.has(runId), canResume: !active && parent?.status === 'completed' && state.status !== 'succeeded'
|
|
208
|
+
recoverableSteps, active, canCancel: this.active.has(runId), canResume: !active && parent?.status === 'completed' && state.status !== 'succeeded',
|
|
209
|
+
canDismiss: !active, dismissed: this.isDismissed(runId) };
|
|
193
210
|
}
|
|
194
211
|
catch (error) {
|
|
195
212
|
try {
|
|
@@ -362,6 +379,24 @@ class AgentRuntimeControls {
|
|
|
362
379
|
this.ctx.broadcast?.({ type: 'runtime.continuation', projectId: this.ctx.project.id, jobId: runId, railIndex: (0, loop_runs_store_1.getLoopRun)(this.ctx.db, runId)?.rail_index ?? null, active: false });
|
|
363
380
|
}
|
|
364
381
|
}
|
|
382
|
+
/** Marker next to the frozen context: the rail card stops showing this run; history/log stay intact. */
|
|
383
|
+
dismissMarker(runId) { return node_path_1.default.join(node_path_1.default.dirname(this.context(runId).file), 'desktop-runtime-dismissed'); }
|
|
384
|
+
isDismissed(runId) { try {
|
|
385
|
+
return node_fs_1.default.existsSync(this.dismissMarker(runId));
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
return false;
|
|
389
|
+
} }
|
|
390
|
+
dismiss(runId) {
|
|
391
|
+
if (this.active.has(runId))
|
|
392
|
+
throw new RuntimeControlError(409, 'runtime_run_active', 'Stop the execution before dismissing it');
|
|
393
|
+
const parent = (0, loop_runs_store_1.getLoopRun)(this.ctx.db, runId);
|
|
394
|
+
if (parent && (parent.status === 'running' || parent.status === 'paused'))
|
|
395
|
+
throw new RuntimeControlError(409, 'runtime_run_active', 'Stop the rail run before dismissing it');
|
|
396
|
+
node_fs_1.default.writeFileSync(this.dismissMarker(runId), new Date().toISOString() + '\n', { mode: 0o600 });
|
|
397
|
+
this.statusCache.delete(runId);
|
|
398
|
+
this.ctx.broadcast?.({ type: 'runtime.continuation', projectId: this.ctx.project.id, jobId: runId, railIndex: parent?.rail_index ?? null, active: false });
|
|
399
|
+
}
|
|
365
400
|
cancel(runId) {
|
|
366
401
|
const active = this.active.get(runId);
|
|
367
402
|
if (!active)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveEffectiveRuntimeConfig = resolveEffectiveRuntimeConfig;
|
|
4
|
+
exports.fillDefaultRoleModels = fillDefaultRoleModels;
|
|
4
5
|
const providers_1 = require("./providers");
|
|
5
6
|
/** A pure admission resolver; only deliberate launch intent overrides a project role. */
|
|
6
7
|
function resolveEffectiveRuntimeConfig(input, options) {
|
|
@@ -10,23 +11,43 @@ function resolveEffectiveRuntimeConfig(input, options) {
|
|
|
10
11
|
const provider = config.providers.find(entry => entry.id === override.provider);
|
|
11
12
|
if (!provider)
|
|
12
13
|
throw new Error(`Selected runtime provider is not configured: ${override.provider}`);
|
|
13
|
-
for (const role of ['architect', 'developer', 'reviewer']) {
|
|
14
|
-
const current = config.agents[role];
|
|
14
|
+
for (const role of ['architect', 'developer', 'reviewer', 'fixer']) {
|
|
15
|
+
const current = role === 'fixer' ? config.fixer : config.agents[role];
|
|
16
|
+
if (!current)
|
|
17
|
+
continue;
|
|
15
18
|
const changedProvider = current.provider !== provider.id;
|
|
16
19
|
const changedModel = override.model !== undefined && override.model !== current.model;
|
|
17
|
-
|
|
20
|
+
const next = {
|
|
18
21
|
...current, provider: provider.id,
|
|
19
22
|
model: override.model ?? (changedProvider ? undefined : current.model),
|
|
20
23
|
effort: override.effort ?? (changedProvider || changedModel ? undefined : current.effort),
|
|
21
24
|
escalation: changedProvider || changedModel ? undefined : current.escalation,
|
|
22
25
|
};
|
|
26
|
+
if (role === 'fixer')
|
|
27
|
+
config.fixer = next;
|
|
28
|
+
else
|
|
29
|
+
config.agents[role] = next;
|
|
23
30
|
}
|
|
24
31
|
}
|
|
25
|
-
|
|
32
|
+
fillDefaultRoleModels(config);
|
|
33
|
+
config.verification = config.verification.filter(check => options.repositoryIds.includes(check.repositoryId));
|
|
34
|
+
return { config, origins: { architect: override ? 'explicit-launch-override' : options.source, developer: override ? 'explicit-launch-override' : options.source, reviewer: override ? 'explicit-launch-override' : options.source } };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Every role gets a concrete model: CLI roles the adapter default, local
|
|
38
|
+
* (OpenAI-compatible) roles the connection's default (stored `defaultModel`,
|
|
39
|
+
* else the first discovered model). Core requires a model on API providers, so
|
|
40
|
+
* this runs before launch AND before the compatibility check — otherwise
|
|
41
|
+
* picking a local engine with "provider default" failed the check with
|
|
42
|
+
* "Role architect requires a model for its API provider".
|
|
43
|
+
*/
|
|
44
|
+
function fillDefaultRoleModels(config, options = {}) {
|
|
45
|
+
for (const agent of [...Object.values(config.agents), ...(config.fixer ? [config.fixer] : [])]) {
|
|
26
46
|
const provider = config.providers.find(entry => entry.id === agent.provider);
|
|
27
|
-
if (!agent.model && provider?.kind === 'cli')
|
|
47
|
+
if (!agent.model && provider?.kind === 'cli' && !options.localOnly)
|
|
28
48
|
agent.model = (0, providers_1.getAdapter)(provider.cli).defaultModel();
|
|
49
|
+
if (!agent.model && provider?.kind === 'openai-compatible' && (0, providers_1.hasAdapter)(provider.id) && (0, providers_1.isLocalAdapterId)(provider.id))
|
|
50
|
+
agent.model = (0, providers_1.getAdapter)(provider.id).defaultModel();
|
|
29
51
|
}
|
|
30
|
-
|
|
31
|
-
return { config, origins: { architect: override ? 'explicit-launch-override' : options.source, developer: override ? 'explicit-launch-override' : options.source, reviewer: override ? 'explicit-launch-override' : options.source } };
|
|
52
|
+
return config;
|
|
32
53
|
}
|
|
@@ -5,7 +5,7 @@ exports.readRuntimeEfficiencySummary = readRuntimeEfficiencySummary;
|
|
|
5
5
|
exports.applyRuntimeSelectionOrigins = applyRuntimeSelectionOrigins;
|
|
6
6
|
const COUNTS = ['attempts', 'measuredAttempts', 'providerCalls', 'toolCalls', 'inputTokens', 'outputTokens', 'uncachedInputTokens', 'cacheReadInputTokens', 'cacheWriteInputTokens'];
|
|
7
7
|
const KEYS = [...COUNTS, 'durationMs', 'agentDurationMs', 'costUsd'];
|
|
8
|
-
const PHASES = ['architect', 'developer', 'verify', 'reviewer', 'archive'];
|
|
8
|
+
const PHASES = ['architect', 'developer', 'fixer', 'verify', 'reviewer', 'archive'];
|
|
9
9
|
function object(value) { return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : null; }
|
|
10
10
|
function counters(value) {
|
|
11
11
|
const input = object(value);
|
|
@@ -4,9 +4,59 @@ exports.registerAgentRuntimeSettingsRoutes = registerAgentRuntimeSettingsRoutes;
|
|
|
4
4
|
const node_fs_1 = require("node:fs");
|
|
5
5
|
const agent_runtime_loader_1 = require("./agent-runtime-loader");
|
|
6
6
|
const agent_runtime_settings_1 = require("./agent-runtime-settings");
|
|
7
|
+
const agent_runtime_settings_2 = require("./agent-runtime-settings");
|
|
7
8
|
const agent_runtime_verification_suggestions_1 = require("./agent-runtime-verification-suggestions");
|
|
8
9
|
const project_repositories_1 = require("./project-repositories");
|
|
10
|
+
const agent_runtime_effective_config_1 = require("./agent-runtime-effective-config");
|
|
11
|
+
/** Raw request body → same body with every local role's missing model filled (validation then passes). */
|
|
12
|
+
function withDefaultRoleModels(body) {
|
|
13
|
+
const agents = body.agents;
|
|
14
|
+
const providers = body.providers;
|
|
15
|
+
if (!agents || typeof agents !== 'object' || !Array.isArray(providers))
|
|
16
|
+
return body;
|
|
17
|
+
// Only LOCAL roles: a CLI role saved without a model keeps meaning "provider default" (never frozen into the file).
|
|
18
|
+
const fixer = body.fixer && typeof body.fixer === 'object' ? structuredClone(body.fixer) : undefined;
|
|
19
|
+
const filled = (0, agent_runtime_effective_config_1.fillDefaultRoleModels)({ agents: structuredClone(agents), providers: providers, ...(fixer ? { fixer: fixer } : {}) }, { localOnly: true });
|
|
20
|
+
return { ...body, agents: filled.agents, ...(filled.fixer ? { fixer: filled.fixer } : {}) };
|
|
21
|
+
}
|
|
22
|
+
const loop_role_engines_1 = require("./loop-role-engines");
|
|
9
23
|
function registerAgentRuntimeSettingsRoutes({ router, ctx }) {
|
|
24
|
+
/** Guardrail catalog of the loaded Core (configurable-guardrails): ids + phase; the UI owns labels. `supported:false` on older cores. */
|
|
25
|
+
router.get('/:projectId/agent-runtime/guardrails', async (_req, res) => {
|
|
26
|
+
try {
|
|
27
|
+
const runtime = await (0, agent_runtime_loader_1.loadCoreAgentRuntime)();
|
|
28
|
+
const supported = runtime.api?.capabilities?.configurableGuardrails === 1;
|
|
29
|
+
const catalog = supported && Array.isArray(runtime.api?.guardrails) ? runtime.api.guardrails.filter((item) => item && typeof item.id === 'string' && typeof item.phase === 'string') : [];
|
|
30
|
+
res.json({ supported, catalog });
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
res.json({ supported: false, catalog: [] });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
/** Loop-side role engines (verifier / decider) for `roles` rail launches — hybrid-role-engines. */
|
|
37
|
+
router.get('/:projectId/agent-runtime/loop-roles', (req, res) => {
|
|
38
|
+
try {
|
|
39
|
+
res.json({ roles: (0, loop_role_engines_1.loadLoopRoleEngines)(ctx(req).project) });
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
res.status(500).json({ error: 'loop_roles_read_failed' });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
router.put('/:projectId/agent-runtime/loop-roles', (req, res) => {
|
|
46
|
+
try {
|
|
47
|
+
const project = ctx(req).project;
|
|
48
|
+
const roles = (0, loop_role_engines_1.validateLoopRoleEngines)(req.body?.roles ?? req.body ?? {}, project);
|
|
49
|
+
(0, loop_role_engines_1.saveLoopRoleEngines)(project, roles);
|
|
50
|
+
res.json({ roles });
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof loop_role_engines_1.LoopRoleEnginesError) {
|
|
54
|
+
res.status(400).json({ error: 'invalid_loop_roles', message: error.message });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
res.status(500).json({ error: 'loop_roles_write_failed' });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
10
60
|
router.get('/:projectId/agent-runtime/config', async (_req, res) => {
|
|
11
61
|
try {
|
|
12
62
|
const project = ctx(_req).project;
|
|
@@ -39,19 +89,38 @@ function registerAgentRuntimeSettingsRoutes({ router, ctx }) {
|
|
|
39
89
|
});
|
|
40
90
|
router.post('/:projectId/agent-runtime/capabilities', async (req, res) => {
|
|
41
91
|
try {
|
|
42
|
-
const config = (0, agent_runtime_settings_1.validateAgentRuntimeConfig)({ ...req.body, providers: (0, agent_runtime_settings_1.loadRuntimeProviders)(), enabled: true });
|
|
92
|
+
const config = (0, agent_runtime_settings_1.validateAgentRuntimeConfig)(withDefaultRoleModels({ ...req.body, providers: (0, agent_runtime_settings_1.loadRuntimeProviders)(), enabled: true }));
|
|
43
93
|
const runtime = await (0, agent_runtime_loader_1.loadCoreAgentRuntime)();
|
|
44
94
|
if (!runtime.capabilities)
|
|
45
95
|
throw new Error('Installed Core does not support capability introspection');
|
|
46
|
-
|
|
96
|
+
// Core's schema is additionalProperties:false — strip the desktop-only
|
|
97
|
+
// connection fields (label/defaultModel/rates/supportsReasoningEffort) first.
|
|
98
|
+
res.json(await runtime.capabilities({ ...(0, agent_runtime_settings_2.forCoreRuntime)(config, runtime.api?.capabilities), providers: (0, agent_runtime_settings_2.stripDesktopConnectionFields)(config.providers, (0, agent_runtime_settings_1.coreConnectionFieldGates)(runtime.api?.capabilities)) }));
|
|
47
99
|
}
|
|
48
100
|
catch (error) {
|
|
49
|
-
|
|
101
|
+
// Core prints its verdict (`{"type":"runtime-result","status":"failed","error":…}`) before exiting non-zero: show that, not the generic "update Core" line.
|
|
102
|
+
const coreMessage = (() => { const out = error.stdout; if (typeof out !== 'string')
|
|
103
|
+
return undefined; try {
|
|
104
|
+
const parsed = JSON.parse(out.trim().split('\n').pop() ?? '');
|
|
105
|
+
return typeof parsed.error === 'string' ? parsed.error : undefined;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return undefined;
|
|
109
|
+
} })();
|
|
110
|
+
res.status(error instanceof agent_runtime_settings_1.AgentRuntimeConfigError || coreMessage ? 400 : 503).json({ error: 'runtime_capabilities_unavailable', message: error instanceof agent_runtime_settings_1.AgentRuntimeConfigError ? error.message : coreMessage ?? 'Update the paired Core runtime to inspect model capabilities' });
|
|
50
111
|
}
|
|
51
112
|
});
|
|
52
113
|
router.put('/:projectId/agent-runtime/config', async (req, res) => {
|
|
53
114
|
try {
|
|
54
|
-
|
|
115
|
+
// A local role saved as "provider default" carries no model; fill the
|
|
116
|
+
// connection default (the launch rule) before the model-required check.
|
|
117
|
+
// The raw body is validated first (its own providers included — a
|
|
118
|
+
// credential in a submitted URL must be refused), then the stored
|
|
119
|
+
// connections replace them and the missing local-role models are filled
|
|
120
|
+
// from each connection's default before the model-required check.
|
|
121
|
+
// Saved WITHOUT filling local-role models: "provider default" stays a
|
|
122
|
+
// live reference to the connection's default (resolved at launch).
|
|
123
|
+
(0, agent_runtime_settings_1.validateAgentRuntimeConfig)({ ...req.body, providers: Array.isArray(req.body?.providers) ? req.body.providers : (0, agent_runtime_settings_1.loadRuntimeProviders)() });
|
|
55
124
|
const config = (0, agent_runtime_settings_1.validateAgentRuntimeConfig)({ ...req.body, enabled: true, providers: (0, agent_runtime_settings_1.loadRuntimeProviders)() });
|
|
56
125
|
const runtimeAvailable = (0, agent_runtime_loader_1.findCoreAgentRuntimeEntry)() !== null;
|
|
57
126
|
if (config.enabled && !runtimeAvailable) {
|
|
@@ -62,8 +131,9 @@ function registerAgentRuntimeSettingsRoutes({ router, ctx }) {
|
|
|
62
131
|
if (config.enabled && runtimeAvailable) {
|
|
63
132
|
try {
|
|
64
133
|
const runtime = await (0, agent_runtime_loader_1.loadCoreAgentRuntime)();
|
|
65
|
-
runtime.
|
|
66
|
-
|
|
134
|
+
const coreConfig = { ...(0, agent_runtime_settings_2.forCoreRuntime)((0, agent_runtime_effective_config_1.fillDefaultRoleModels)(structuredClone(config), { localOnly: true }), runtime.api?.capabilities), providers: (0, agent_runtime_settings_2.stripDesktopConnectionFields)(config.providers, (0, agent_runtime_settings_1.coreConnectionFieldGates)(runtime.api?.capabilities)) };
|
|
135
|
+
runtime.validateRuntimeConfig(coreConfig);
|
|
136
|
+
(0, agent_runtime_loader_1.validateRequestedRoleEfforts)(runtime, coreConfig);
|
|
67
137
|
efficiencyAvailable = runtime.api?.capabilities?.efficientRoleExecution === 1;
|
|
68
138
|
}
|
|
69
139
|
catch {
|