specrails-desktop 2.46.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-CsHQPpvl.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-rnD-EPMd.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-pWZGYCv3.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-CtmOE4-b.js → LoopBuilderPage-DQE-7Vby.js} +2 -2
- package/client/dist/assets/{LoopPreviewModal-BYeLjQbr.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-DsCRIc4v.js → ProjectSettingsDialog-D6vBCoay.js} +1 -1
- package/client/dist/assets/{RepositoryDeliveries-BQm_3TO8.js → RepositoryDeliveries-C36ipKp_.js} +1 -1
- package/client/dist/assets/{RepositoryScopeSelector-DnAFtjTz.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-BwLgX6n7.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-45GbINfh.js → agents-CA5eT1d0.js} +1 -1
- package/client/dist/assets/{agents-DMKFStHL.js → agents-CAgy-5uS.js} +1 -1
- package/client/dist/assets/{agents-IUOgqT5t.js → agents-CNm6Qzlv.js} +1 -1
- package/client/dist/assets/{agents-ms7TwG16.js → agents-CTwbx-fb.js} +1 -1
- package/client/dist/assets/{agents-DaYcxAr1.js → agents-DEGV1Vao.js} +1 -1
- package/client/dist/assets/{agents-BHp0_FQt.js → agents-HsDF1aQU.js} +1 -1
- package/client/dist/assets/{agents-Bvyi3y6R.js → agents-IQjdA-z9.js} +1 -1
- package/client/dist/assets/{agents-rzPnqoBR.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-CeZv93iR.js → formatDistanceToNow-w_2U5pm0.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-DxAkjvJb.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-BSldQfjd.js → narration--hUhjM5v.js} +1 -1
- package/client/dist/assets/{narration-DKLjXIFI.js → narration-CHI06czm.js} +1 -1
- package/client/dist/assets/{narration-4OSg0b5-.js → narration-DjKB3pQZ.js} +1 -1
- package/client/dist/assets/{narration-BPMOQPFe.js → narration-DtCYvRLM.js} +1 -1
- package/client/dist/assets/{narration-D-e9ssxA.js → narration-DvLQBW9U.js} +1 -1
- package/client/dist/assets/{narration-DnrKC3mF.js → narration-eSx3gR0W.js} +1 -1
- package/client/dist/assets/{narration-Dypjxeel.js → narration-n3hOA7LY.js} +1 -1
- package/client/dist/assets/{narration-CvQdWdR5.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-BvBj9XH9.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-DNM6NAoA.js → spending-hcgxHP4O.js} +1 -1
- package/client/dist/assets/{useDesktop-mxHVQOdx.js → useDesktop-CAisjKWC.js} +1 -1
- package/client/dist/assets/{useSharedWebSocket-DnxHj8jP.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/docs/plans/implementation-efficiency-artifact.json +18 -0
- package/docs/plans/implementation-efficiency-evaluation.json +1817 -0
- package/docs/plans/implementation-efficiency-evaluation.md +41 -0
- package/docs/plans/implementation-efficiency-progress.md +48 -0
- package/docs/plans/implementation-efficiency-release.md +41 -0
- package/docs/plans/implementation-efficiency.md +124 -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 +35 -26
- package/server/dist/agent-runtime-controls-router.js +20 -1
- package/server/dist/agent-runtime-controls.js +94 -12
- package/server/dist/agent-runtime-effective-config.js +53 -0
- package/server/dist/agent-runtime-events.js +46 -0
- package/server/dist/agent-runtime-history.js +47 -0
- package/server/dist/agent-runtime-loader.js +84 -5
- package/server/dist/agent-runtime-metrics.js +77 -1
- package/server/dist/agent-runtime-package.js +205 -0
- package/server/dist/agent-runtime-settings-router.js +96 -5
- package/server/dist/agent-runtime-settings.js +163 -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 +4 -3
- package/server/dist/loop-role-engines.js +129 -0
- package/server/dist/loop-run-manager.js +14 -6
- package/server/dist/mcp/tools/app-settings.js +30 -4
- package/server/dist/mcp/tools/rails.js +2 -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 +4 -3
- package/server/dist/rails-router.js +90 -8
- package/server/dist/result-event.js +17 -0
- package/server/dist/review-packet.js +1 -0
- package/server/dist/schemas/agent-runtime.schema.json +28 -3
- package/server/dist/schemas/fixtures/runtime-efficiency-summary.v1.json +790 -0
- 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-BVcCK7x0.js +0 -1
- package/client/dist/assets/AgentModeAnalyticsPane-C5F3m29G.js +0 -2
- package/client/dist/assets/AgentModeCodePane-C5QuKsu0.js +0 -2
- package/client/dist/assets/AgentsPage-DiyJWZcU.js +0 -87
- package/client/dist/assets/AnalyticsPage-Dkmb0dnd.js +0 -1
- package/client/dist/assets/CodePage-C0dH99-t.js +0 -3
- package/client/dist/assets/DesktopAnalyticsPage-DuTjr2n8.js +0 -1
- package/client/dist/assets/DocsDialog-BVZJZHVR.js +0 -11
- package/client/dist/assets/DocsPage-D6KHTv8V.js +0 -11
- package/client/dist/assets/InteractiveJobComposer-Ck3PQxjN.js +0 -19
- package/client/dist/assets/JobDetailModal-DkoMH3ie.js +0 -1
- package/client/dist/assets/JobDetailPage-CBVivcl2.js +0 -1
- package/client/dist/assets/JobsPage-CzxEeca9.js +0 -1
- package/client/dist/assets/LoopsPage-6iW7g25k.js +0 -1
- package/client/dist/assets/MinimizedChatsContext-B6wVevPq.js +0 -1
- package/client/dist/assets/PluginsPage-Bf1z3WPF.js +0 -2
- package/client/dist/assets/ReviewPacketPage-CaXAwtR7.js +0 -1
- package/client/dist/assets/TemplatePreviewModal-9aRFpJ_J.js +0 -1
- package/client/dist/assets/TicketDetailModalContext-B1X5glzV.js +0 -14
- package/client/dist/assets/agentRuntime-BUagD2I_.js +0 -1
- package/client/dist/assets/agentRuntime-BkL70WtX.js +0 -1
- package/client/dist/assets/agentRuntime-CWXVqgqE.js +0 -1
- package/client/dist/assets/agentRuntime-CvNBdUAe.js +0 -1
- package/client/dist/assets/agentRuntime-D4RmeuNY.js +0 -1
- package/client/dist/assets/agentRuntime-DTidBWKg.js +0 -1
- package/client/dist/assets/agentRuntime-DhKYfyaU.js +0 -1
- package/client/dist/assets/agentRuntime-LL8_GLx9.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-DYoyDWim.js +0 -76
- package/client/dist/assets/jira-api-ChjVGJx_.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
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// Spec: openspec/specs/multi-provider-architecture/spec.md
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.UnknownProviderError = exports.CUSTOM_MODEL_ALIAS_MAX_LENGTH = exports.isSafeCustomModelAlias = exports.isModelAvailableForAdapter = exports.isReasoningEffortValidForModel = exports.defaultReasoningEffortForModel = exports.reasoningEffortsForModel = exports.requireToolPolicy = exports.pureOutputToolPolicy = exports.supportsToolPolicy = exports.parseStreamEvents = exports.buildProviderRepoAccessArgs = exports.formatProviderCommand = exports.buildProviderEnv = exports.kimiAdapter = exports.geminiAdapter = exports.codexAdapter = exports.claudeAdapter = exports.listAdapters = exports.hasAdapter = exports.getAdapter = void 0;
|
|
10
|
+
exports.UnknownProviderError = exports.CUSTOM_MODEL_ALIAS_MAX_LENGTH = exports.isSafeCustomModelAlias = exports.isModelAvailableForAdapter = exports.isReasoningEffortValidForModel = exports.defaultReasoningEffortForModel = exports.reasoningEffortsForModel = exports.requireToolPolicy = exports.pureOutputToolPolicy = exports.supportsToolPolicy = exports.parseStreamEvents = exports.buildProviderRepoAccessArgs = exports.formatProviderCommand = exports.buildProviderEnv = exports.kimiAdapter = exports.geminiAdapter = exports.codexAdapter = exports.claudeAdapter = exports.adapterKind = exports.isLocalAdapterId = exports.unregisterAdapter = exports.listAdapters = exports.hasAdapter = exports.getAdapter = void 0;
|
|
11
11
|
const registry_1 = require("./registry");
|
|
12
12
|
const claude_adapter_1 = require("./claude-adapter");
|
|
13
13
|
Object.defineProperty(exports, "claudeAdapter", { enumerable: true, get: function () { return claude_adapter_1.claudeAdapter; } });
|
|
@@ -25,6 +25,9 @@ var registry_2 = require("./registry");
|
|
|
25
25
|
Object.defineProperty(exports, "getAdapter", { enumerable: true, get: function () { return registry_2.getAdapter; } });
|
|
26
26
|
Object.defineProperty(exports, "hasAdapter", { enumerable: true, get: function () { return registry_2.hasAdapter; } });
|
|
27
27
|
Object.defineProperty(exports, "listAdapters", { enumerable: true, get: function () { return registry_2.listAdapters; } });
|
|
28
|
+
Object.defineProperty(exports, "unregisterAdapter", { enumerable: true, get: function () { return registry_2.unregisterAdapter; } });
|
|
29
|
+
Object.defineProperty(exports, "isLocalAdapterId", { enumerable: true, get: function () { return registry_2.isLocalAdapterId; } });
|
|
30
|
+
Object.defineProperty(exports, "adapterKind", { enumerable: true, get: function () { return registry_2.adapterKind; } });
|
|
28
31
|
var runtime_1 = require("./runtime");
|
|
29
32
|
Object.defineProperty(exports, "buildProviderEnv", { enumerable: true, get: function () { return runtime_1.buildProviderEnv; } });
|
|
30
33
|
Object.defineProperty(exports, "formatProviderCommand", { enumerable: true, get: function () { return runtime_1.formatProviderCommand; } });
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Dynamic registration of local (OpenAI-compatible) adapters — one per
|
|
3
|
+
// connection in `~/.specrails/runtime-providers.json`. Called at boot (after
|
|
4
|
+
// the connections load) and after every successful `PUT /api/runtime-providers`.
|
|
5
|
+
//
|
|
6
|
+
// Kill switch: SPECRAILS_LOCAL_ENGINES=0|false|off ⇒ no registration, and any
|
|
7
|
+
// previously registered local adapter is removed (byte-identical legacy).
|
|
8
|
+
//
|
|
9
|
+
// Spec: openspec/changes/local-ai-engines/specs/local-ai-engines/spec.md
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.isLocalEnginesEnabled = isLocalEnginesEnabled;
|
|
12
|
+
exports.listLocalAdapters = listLocalAdapters;
|
|
13
|
+
exports.listLocalConnections = listLocalConnections;
|
|
14
|
+
exports.syncLocalAdapters = syncLocalAdapters;
|
|
15
|
+
const registry_1 = require("./registry");
|
|
16
|
+
const local_adapter_1 = require("./local-adapter");
|
|
17
|
+
function isLocalEnginesEnabled() {
|
|
18
|
+
const raw = (process.env.SPECRAILS_LOCAL_ENGINES ?? '').trim().toLowerCase();
|
|
19
|
+
return !(raw === '0' || raw === 'false' || raw === 'off');
|
|
20
|
+
}
|
|
21
|
+
/** Every currently registered local adapter, in registry order. */
|
|
22
|
+
function listLocalAdapters() {
|
|
23
|
+
return (0, registry_1.listAdapters)().filter(local_adapter_1.isLocalAdapter);
|
|
24
|
+
}
|
|
25
|
+
/** The connections behind the registered local adapters. */
|
|
26
|
+
function listLocalConnections() {
|
|
27
|
+
return listLocalAdapters().map((a) => a.localConnection);
|
|
28
|
+
}
|
|
29
|
+
function syncLocalAdapters(connections) {
|
|
30
|
+
const result = { registered: [], unregistered: [], skipped: [] };
|
|
31
|
+
const wanted = new Map();
|
|
32
|
+
if (isLocalEnginesEnabled()) {
|
|
33
|
+
for (const conn of connections) {
|
|
34
|
+
if (conn.kind !== 'openai-compatible' || typeof conn.baseUrl !== 'string')
|
|
35
|
+
continue;
|
|
36
|
+
if ((0, registry_1.hasAdapter)(conn.id) && (0, registry_1.adapterKind)(conn.id) !== 'local') {
|
|
37
|
+
result.skipped.push(conn.id);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
wanted.set(conn.id, conn);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
for (const existing of listLocalAdapters()) {
|
|
44
|
+
if (!wanted.has(existing.id)) {
|
|
45
|
+
if ((0, registry_1.unregisterAdapter)(existing.id))
|
|
46
|
+
result.unregistered.push(existing.id);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const [id, conn] of wanted) {
|
|
50
|
+
// Re-register on every sync so a changed baseUrl/rates/label takes effect;
|
|
51
|
+
// in-flight jobs keep the adapter instance they resolved earlier.
|
|
52
|
+
(0, registry_1.register)((0, local_adapter_1.createLocalAdapter)({ ...conn }), 'local');
|
|
53
|
+
result.registered.push(id);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Local (OpenAI-compatible) engine adapter.
|
|
3
|
+
//
|
|
4
|
+
// One adapter instance PER CONNECTION (`~/.specrails/runtime-providers.json`,
|
|
5
|
+
// `kind: 'openai-compatible'`), registered dynamically by
|
|
6
|
+
// `local-adapter-registry.ts`. The engine is the bundled local agent runner
|
|
7
|
+
// (`local-runner/` → `src-tauri/binaries/specrails-local-runner.js`) executed
|
|
8
|
+
// by the bundled Node — spawned exactly like a CLI so every manager's
|
|
9
|
+
// spawn/stream/settle path is reused unchanged (design D1).
|
|
10
|
+
//
|
|
11
|
+
// The runner speaks a claude-shaped `stream-json` dialect (design D2). This
|
|
12
|
+
// adapter owns its OWN frame parser — deliberately NOT an import of the claude
|
|
13
|
+
// parser — so claude-specific heuristics (notification frames, alias pinning,
|
|
14
|
+
// background tasks) never leak in and a claude parser change can't silently
|
|
15
|
+
// break local engines. `total_cost_usd` is never present.
|
|
16
|
+
//
|
|
17
|
+
// Spec: openspec/changes/local-ai-engines/specs/local-ai-engines/spec.md
|
|
18
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
19
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.LOCAL_REASONING_EFFORTS = exports.LOCAL_PROJECT_DIR = void 0;
|
|
23
|
+
exports.isLocalAdapter = isLocalAdapter;
|
|
24
|
+
exports.resolveLocalRunnerScript = resolveLocalRunnerScript;
|
|
25
|
+
exports.resolveLocalRunnerNode = resolveLocalRunnerNode;
|
|
26
|
+
exports.filterLocalExtraArgs = filterLocalExtraArgs;
|
|
27
|
+
exports.parseLocalStreamLine = parseLocalStreamLine;
|
|
28
|
+
exports.extractLocalResult = extractLocalResult;
|
|
29
|
+
exports.createLocalAdapter = createLocalAdapter;
|
|
30
|
+
exports.localConnectionRates = localConnectionRates;
|
|
31
|
+
const fs_1 = __importDefault(require("fs"));
|
|
32
|
+
const path_1 = __importDefault(require("path"));
|
|
33
|
+
const path_resolver_1 = require("../path-resolver");
|
|
34
|
+
const win_spawn_1 = require("../util/win-spawn");
|
|
35
|
+
const local_engine_detection_1 = require("../local-engine-detection");
|
|
36
|
+
function isLocalAdapter(adapter) {
|
|
37
|
+
return typeof adapter.localConnection === 'object'
|
|
38
|
+
&& adapter.localConnection !== null;
|
|
39
|
+
}
|
|
40
|
+
exports.LOCAL_PROJECT_DIR = '.specrails-local';
|
|
41
|
+
exports.LOCAL_REASONING_EFFORTS = ['low', 'medium', 'high'];
|
|
42
|
+
function fileExists(p) {
|
|
43
|
+
try {
|
|
44
|
+
return fs_1.default.statSync(p).isFile();
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const RUNNER_RELS = [
|
|
51
|
+
path_1.default.join('src-tauri', 'binaries', 'specrails-local-runner.js'),
|
|
52
|
+
path_1.default.join('local-runner', 'dist', 'specrails-local-runner.js'),
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* Absolute path to the bundled runner script (mirrors the MCP bridge resolution
|
|
56
|
+
* in agent-mcp-config.ts): `SPECRAILS_BUNDLED_LOCAL_RUNNER_PATH`, then the
|
|
57
|
+
* staged `src-tauri/binaries/` copy, then the `local-runner/dist/` build
|
|
58
|
+
* output. Returns null when none exists.
|
|
59
|
+
*/
|
|
60
|
+
function resolveLocalRunnerScript() {
|
|
61
|
+
const fromEnv = process.env.SPECRAILS_BUNDLED_LOCAL_RUNNER_PATH;
|
|
62
|
+
if (fromEnv) {
|
|
63
|
+
const cleaned = (0, win_spawn_1.stripWindowsVerbatimPrefix)(fromEnv);
|
|
64
|
+
if (fileExists(cleaned))
|
|
65
|
+
return cleaned;
|
|
66
|
+
}
|
|
67
|
+
const roots = [
|
|
68
|
+
path_1.default.resolve(__dirname, '..', '..'),
|
|
69
|
+
path_1.default.resolve(__dirname, '..', '..', '..'),
|
|
70
|
+
process.cwd(),
|
|
71
|
+
];
|
|
72
|
+
for (const root of roots) {
|
|
73
|
+
for (const rel of RUNNER_RELS) {
|
|
74
|
+
const candidate = path_1.default.join(root, rel);
|
|
75
|
+
if (fileExists(candidate))
|
|
76
|
+
return candidate;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
/** Deterministic argv[0] even when the build output is absent (spawn then
|
|
82
|
+
* fails with a clear "no such file" instead of an undefined argument). */
|
|
83
|
+
function runnerScriptOrDefault() {
|
|
84
|
+
return resolveLocalRunnerScript() ?? path_1.default.resolve(__dirname, '..', '..', RUNNER_RELS[0]);
|
|
85
|
+
}
|
|
86
|
+
/** The node executable that runs the runner (bundled in the packaged app, else PATH `node`). */
|
|
87
|
+
function resolveLocalRunnerNode() {
|
|
88
|
+
return (0, path_resolver_1.resolveBundledNodeExe)() ?? 'node';
|
|
89
|
+
}
|
|
90
|
+
// ─── extraArgs allowlist ─────────────────────────────────────────────────────
|
|
91
|
+
//
|
|
92
|
+
// Managers forward provider-shaped extras through `extraArgs` (context-scope
|
|
93
|
+
// tool flags, `--mcp-config`, repo access, codex plugin `-c` pairs, the claude
|
|
94
|
+
// openspec `--plugin-dir`…). The runner fails fast on unknown flags, so only
|
|
95
|
+
// the flags it understands pass; everything else is dropped WITH its value.
|
|
96
|
+
const RUNNER_VALUE_FLAGS = new Set([
|
|
97
|
+
'--tools', '--disallowedTools', '--mcp-config', '--add-dir', '--max-turns',
|
|
98
|
+
'--model', '--resume', '--system-prompt', '--append-system-prompt',
|
|
99
|
+
'--reasoning-effort', '--base-url', '--api-key-env',
|
|
100
|
+
]);
|
|
101
|
+
/** Flags foreign to the runner that carry a value (drop flag + value). */
|
|
102
|
+
const FOREIGN_VALUE_FLAGS = new Set([
|
|
103
|
+
'--setting-sources', '--permission-mode', '--effort', '--plugin-dir', '-c',
|
|
104
|
+
'--allowedTools', '--output-format', '--input-format', '--approval-mode',
|
|
105
|
+
'--sandbox', '-i', '--image', '-m', '-S', '--session',
|
|
106
|
+
]);
|
|
107
|
+
function filterLocalExtraArgs(extra) {
|
|
108
|
+
if (!extra || extra.length === 0)
|
|
109
|
+
return [];
|
|
110
|
+
const out = [];
|
|
111
|
+
for (let i = 0; i < extra.length; i++) {
|
|
112
|
+
const token = extra[i];
|
|
113
|
+
if (RUNNER_VALUE_FLAGS.has(token)) {
|
|
114
|
+
if (i + 1 < extra.length) {
|
|
115
|
+
out.push(token, extra[i + 1]);
|
|
116
|
+
i++;
|
|
117
|
+
}
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (FOREIGN_VALUE_FLAGS.has(token)) {
|
|
121
|
+
if (i + 1 < extra.length && !extra[i + 1].startsWith('-'))
|
|
122
|
+
i++;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
// Any other flag (`--dangerously-skip-permissions`, `--safe-mode`,
|
|
126
|
+
// `--verbose`, `--yolo`, …) or stray positional token is dropped.
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
// ─── argv ────────────────────────────────────────────────────────────────────
|
|
131
|
+
function toolPolicyArgs(policy) {
|
|
132
|
+
if (policy === 'none')
|
|
133
|
+
return ['--tools', '__none__'];
|
|
134
|
+
if (policy === 'read-only')
|
|
135
|
+
return ['--tools', 'Read,Grep,Glob'];
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
function buildLocalArgs(connection, action, opts) {
|
|
139
|
+
const script = runnerScriptOrDefault();
|
|
140
|
+
const args = [script];
|
|
141
|
+
const effectiveOpts = action === 'auto-title' ? { ...opts, toolPolicy: 'none' } : opts;
|
|
142
|
+
const common = () => [
|
|
143
|
+
'--model', effectiveOpts.model || connection.defaultModel || 'default',
|
|
144
|
+
'--base-url', connection.baseUrl,
|
|
145
|
+
...(connection.apiKeyEnv ? ['--api-key-env', connection.apiKeyEnv] : []),
|
|
146
|
+
...toolPolicyArgs(effectiveOpts.toolPolicy),
|
|
147
|
+
...(connection.supportsReasoningEffort && effectiveOpts.reasoning_effort
|
|
148
|
+
? ['--reasoning-effort', effectiveOpts.reasoning_effort]
|
|
149
|
+
: []),
|
|
150
|
+
'--output-format', 'stream-json',
|
|
151
|
+
];
|
|
152
|
+
const extras = filterLocalExtraArgs(effectiveOpts.extraArgs);
|
|
153
|
+
const maxTurns = effectiveOpts.maxTurns != null ? ['--max-turns', String(effectiveOpts.maxTurns)] : [];
|
|
154
|
+
switch (action) {
|
|
155
|
+
case 'chat-turn':
|
|
156
|
+
args.push(...common());
|
|
157
|
+
if (opts.systemPrompt)
|
|
158
|
+
args.push('--system-prompt', opts.systemPrompt);
|
|
159
|
+
args.push(...maxTurns, '-p', opts.prompt, ...extras);
|
|
160
|
+
return args;
|
|
161
|
+
case 'chat-resume':
|
|
162
|
+
if (!opts.sessionId)
|
|
163
|
+
throw new Error('chat-resume requires sessionId');
|
|
164
|
+
args.push(...common());
|
|
165
|
+
if (opts.systemPrompt)
|
|
166
|
+
args.push('--system-prompt', opts.systemPrompt);
|
|
167
|
+
args.push('--resume', opts.sessionId, ...maxTurns, '-p', opts.prompt, ...extras);
|
|
168
|
+
return args;
|
|
169
|
+
case 'chat-stream':
|
|
170
|
+
// Persistent multi-turn: the prompt arrives over stdin, one line per turn.
|
|
171
|
+
// `--max-turns` omitted on purpose (it would end the whole session).
|
|
172
|
+
args.push(...common());
|
|
173
|
+
if (opts.systemPrompt)
|
|
174
|
+
args.push('--system-prompt', opts.systemPrompt);
|
|
175
|
+
if (opts.sessionId)
|
|
176
|
+
args.push('--resume', opts.sessionId);
|
|
177
|
+
args.push('--input-format', 'stream-json', ...extras);
|
|
178
|
+
return args;
|
|
179
|
+
case 'rail-job':
|
|
180
|
+
// The slash command carries its own framing; ADD to it, never overwrite.
|
|
181
|
+
args.push(...common());
|
|
182
|
+
if (opts.systemPrompt)
|
|
183
|
+
args.push('--append-system-prompt', opts.systemPrompt);
|
|
184
|
+
args.push('-p', opts.prompt, ...extras);
|
|
185
|
+
return args;
|
|
186
|
+
case 'spec-gen':
|
|
187
|
+
args.push(...common(), ...maxTurns);
|
|
188
|
+
if (opts.systemPrompt)
|
|
189
|
+
args.push('--system-prompt', opts.systemPrompt);
|
|
190
|
+
args.push('-p', opts.prompt, ...extras);
|
|
191
|
+
return args;
|
|
192
|
+
case 'agent-refine':
|
|
193
|
+
args.push(...common());
|
|
194
|
+
if (opts.sessionId)
|
|
195
|
+
args.push('--resume', opts.sessionId);
|
|
196
|
+
args.push('-p', opts.prompt, ...extras);
|
|
197
|
+
return args;
|
|
198
|
+
case 'setup-enrich':
|
|
199
|
+
args.push('-p', opts.prompt, ...common(), ...extras);
|
|
200
|
+
return args;
|
|
201
|
+
case 'setup-enrich-resume':
|
|
202
|
+
if (!opts.sessionId)
|
|
203
|
+
throw new Error('setup-enrich-resume requires sessionId');
|
|
204
|
+
args.push('--resume', opts.sessionId, ...common(), '-p', opts.prompt, ...extras);
|
|
205
|
+
return args;
|
|
206
|
+
case 'auto-title':
|
|
207
|
+
args.push(...common(), '-p', opts.prompt);
|
|
208
|
+
return args;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function readNumber(value) {
|
|
212
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : 0;
|
|
213
|
+
}
|
|
214
|
+
function withUsage(ev, msg) {
|
|
215
|
+
const usage = msg?.usage;
|
|
216
|
+
if (!usage || typeof usage !== 'object')
|
|
217
|
+
return ev;
|
|
218
|
+
const carrier = ev;
|
|
219
|
+
carrier.usage = usage;
|
|
220
|
+
if (typeof msg?.id === 'string')
|
|
221
|
+
carrier.messageId = msg.id;
|
|
222
|
+
if (typeof msg?.model === 'string')
|
|
223
|
+
carrier.model = msg.model;
|
|
224
|
+
return ev;
|
|
225
|
+
}
|
|
226
|
+
function parseLocalStreamLine(line) {
|
|
227
|
+
if (line.length === 0)
|
|
228
|
+
return null;
|
|
229
|
+
let parsed;
|
|
230
|
+
try {
|
|
231
|
+
parsed = JSON.parse(line);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
const type = parsed.type;
|
|
237
|
+
if (!type)
|
|
238
|
+
return { kind: 'other', type: '<missing>', raw: parsed };
|
|
239
|
+
if (type === 'system' || type === 'init') {
|
|
240
|
+
const sid = parsed.session_id;
|
|
241
|
+
if (sid)
|
|
242
|
+
return { kind: 'session-started', sessionId: sid };
|
|
243
|
+
return { kind: 'other', type, raw: parsed };
|
|
244
|
+
}
|
|
245
|
+
if (type === 'result') {
|
|
246
|
+
const result = { kind: 'result', payload: parsed, ...(parsed.is_error === true ? { isError: true } : {}) };
|
|
247
|
+
// The runner puts the endpoint/loop failure text in `result` when
|
|
248
|
+
// `is_error` is set (HTTP 4xx/5xx body, connection refused, max_turns…).
|
|
249
|
+
// Surface it as an explicit error event too, so every manager reports the
|
|
250
|
+
// real reason instead of a generic "returned no output".
|
|
251
|
+
const message = parsed.is_error === true && typeof parsed.result === 'string' && parsed.result.trim()
|
|
252
|
+
? parsed.result.trim().slice(0, 600)
|
|
253
|
+
: null;
|
|
254
|
+
return message ? [{ kind: 'error', message }, result] : result;
|
|
255
|
+
}
|
|
256
|
+
if (type === 'error') {
|
|
257
|
+
const message = typeof parsed.message === 'string' ? parsed.message
|
|
258
|
+
: typeof parsed.error === 'string' ? parsed.error : 'local runner error';
|
|
259
|
+
return { kind: 'error', message };
|
|
260
|
+
}
|
|
261
|
+
if (type === 'assistant') {
|
|
262
|
+
const msg = parsed.message;
|
|
263
|
+
const blocks = Array.isArray(msg?.content) ? msg.content : [];
|
|
264
|
+
const text = blocks
|
|
265
|
+
.filter((b) => b.type === 'text')
|
|
266
|
+
.map((b) => (typeof b.text === 'string' ? b.text : ''))
|
|
267
|
+
.join('');
|
|
268
|
+
if (text)
|
|
269
|
+
return withUsage({ kind: 'text-delta', text }, msg);
|
|
270
|
+
const tool = blocks.find((b) => b.type === 'tool_use');
|
|
271
|
+
if (tool && typeof tool.name === 'string') {
|
|
272
|
+
const input = JSON.stringify(tool.input ?? {});
|
|
273
|
+
return withUsage({
|
|
274
|
+
kind: 'tool-use',
|
|
275
|
+
name: tool.name,
|
|
276
|
+
inputPreview: input.slice(0, 200),
|
|
277
|
+
...(typeof tool.id === 'string' ? { toolUseId: tool.id } : {}),
|
|
278
|
+
}, msg);
|
|
279
|
+
}
|
|
280
|
+
return withUsage({ kind: 'other', type, raw: parsed }, msg);
|
|
281
|
+
}
|
|
282
|
+
if (type === 'user') {
|
|
283
|
+
const msg = parsed.message;
|
|
284
|
+
const blocks = Array.isArray(msg?.content) ? msg.content : [];
|
|
285
|
+
const resultBlock = blocks.find((b) => b.type === 'tool_result');
|
|
286
|
+
if (resultBlock) {
|
|
287
|
+
const content = resultBlock.content;
|
|
288
|
+
const text = typeof content === 'string'
|
|
289
|
+
? content
|
|
290
|
+
: Array.isArray(content)
|
|
291
|
+
? content
|
|
292
|
+
.filter((c) => c.type === 'text' && typeof c.text === 'string')
|
|
293
|
+
.map((c) => c.text)
|
|
294
|
+
.join('\n')
|
|
295
|
+
: '';
|
|
296
|
+
return {
|
|
297
|
+
kind: 'tool-result',
|
|
298
|
+
outputPreview: text.slice(0, 600),
|
|
299
|
+
...(typeof resultBlock.tool_use_id === 'string' ? { toolUseId: resultBlock.tool_use_id } : {}),
|
|
300
|
+
...(resultBlock.is_error === true ? { isError: true } : {}),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return { kind: 'other', type, raw: parsed };
|
|
305
|
+
}
|
|
306
|
+
function extractLocalResult(events) {
|
|
307
|
+
let resultPayload = null;
|
|
308
|
+
let sessionId;
|
|
309
|
+
for (const ev of events) {
|
|
310
|
+
if (ev.kind === 'result')
|
|
311
|
+
resultPayload = ev.payload;
|
|
312
|
+
else if (ev.kind === 'session-started')
|
|
313
|
+
sessionId = ev.sessionId;
|
|
314
|
+
}
|
|
315
|
+
const usageFromAssistant = () => {
|
|
316
|
+
// No terminal `result` (killed/aborted). Reconstruct per message id,
|
|
317
|
+
// last snapshot wins; each distinct message is a separate API call.
|
|
318
|
+
const perMessage = new Map();
|
|
319
|
+
let model;
|
|
320
|
+
let anonymous = 0;
|
|
321
|
+
for (const ev of events) {
|
|
322
|
+
const carrier = ev;
|
|
323
|
+
if (!carrier.usage)
|
|
324
|
+
continue;
|
|
325
|
+
perMessage.set(carrier.messageId ?? `__anon-${anonymous++}`, carrier.usage);
|
|
326
|
+
if (carrier.model)
|
|
327
|
+
model = carrier.model;
|
|
328
|
+
}
|
|
329
|
+
if (perMessage.size === 0)
|
|
330
|
+
return { session_id: sessionId };
|
|
331
|
+
let tokensIn = 0, tokensOut = 0, cacheRead = 0, cacheCreate = 0;
|
|
332
|
+
for (const u of perMessage.values()) {
|
|
333
|
+
tokensIn += readNumber(u.input_tokens);
|
|
334
|
+
tokensOut += readNumber(u.output_tokens);
|
|
335
|
+
cacheRead += readNumber(u.cache_read_input_tokens);
|
|
336
|
+
cacheCreate += readNumber(u.cache_creation_input_tokens);
|
|
337
|
+
}
|
|
338
|
+
return { tokens_in: tokensIn, tokens_out: tokensOut, tokens_cache_read: cacheRead, tokens_cache_create: cacheCreate, model, session_id: sessionId };
|
|
339
|
+
};
|
|
340
|
+
if (!resultPayload)
|
|
341
|
+
return usageFromAssistant();
|
|
342
|
+
const usage = resultPayload.usage;
|
|
343
|
+
const finalSessionId = (typeof resultPayload.session_id === 'string' ? resultPayload.session_id : undefined) ?? sessionId;
|
|
344
|
+
const tokens = usage && typeof usage === 'object'
|
|
345
|
+
? {
|
|
346
|
+
tokens_in: typeof usage.input_tokens === 'number' ? usage.input_tokens : undefined,
|
|
347
|
+
tokens_out: typeof usage.output_tokens === 'number' ? usage.output_tokens : undefined,
|
|
348
|
+
tokens_cache_read: typeof usage.cache_read_input_tokens === 'number' ? usage.cache_read_input_tokens : undefined,
|
|
349
|
+
tokens_cache_create: typeof usage.cache_creation_input_tokens === 'number' ? usage.cache_creation_input_tokens : undefined,
|
|
350
|
+
}
|
|
351
|
+
: (() => { const r = usageFromAssistant(); return { tokens_in: r.tokens_in, tokens_out: r.tokens_out, tokens_cache_read: r.tokens_cache_read, tokens_cache_create: r.tokens_cache_create }; })();
|
|
352
|
+
return {
|
|
353
|
+
...tokens,
|
|
354
|
+
// NEVER total_cost_usd: local engines report no cost (design D9).
|
|
355
|
+
num_turns: typeof resultPayload.num_turns === 'number' ? resultPayload.num_turns : undefined,
|
|
356
|
+
model: typeof resultPayload.model === 'string' ? resultPayload.model : undefined,
|
|
357
|
+
duration_ms: typeof resultPayload.duration_ms === 'number' ? resultPayload.duration_ms : undefined,
|
|
358
|
+
duration_api_ms: typeof resultPayload.duration_api_ms === 'number' ? resultPayload.duration_api_ms : undefined,
|
|
359
|
+
session_id: finalSessionId,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
// ─── factory ─────────────────────────────────────────────────────────────────
|
|
363
|
+
function createLocalAdapter(connection) {
|
|
364
|
+
const supportsEffort = connection.supportsReasoningEffort === true;
|
|
365
|
+
const displayName = connection.label?.trim() || connection.id;
|
|
366
|
+
const adapter = {
|
|
367
|
+
id: connection.id,
|
|
368
|
+
displayName,
|
|
369
|
+
binary: resolveLocalRunnerNode(),
|
|
370
|
+
minCliVersion: null,
|
|
371
|
+
projectDirName: exports.LOCAL_PROJECT_DIR,
|
|
372
|
+
instructionsFilename: 'AGENTS.md',
|
|
373
|
+
mcpRegistration: 'project-json',
|
|
374
|
+
localConnection: connection,
|
|
375
|
+
capabilities: {
|
|
376
|
+
nativeResume: true,
|
|
377
|
+
nativeStreamJson: true,
|
|
378
|
+
nativeCostUsd: false,
|
|
379
|
+
reportsUsage: true,
|
|
380
|
+
nativeOtelEnv: false,
|
|
381
|
+
profileEnvSupport: false,
|
|
382
|
+
systemPromptArg: true,
|
|
383
|
+
persistentStdin: true,
|
|
384
|
+
supportsReasoningEffort: supportsEffort,
|
|
385
|
+
...(supportsEffort ? { reasoningEfforts: exports.LOCAL_REASONING_EFFORTS } : {}),
|
|
386
|
+
supportsImageInput: false,
|
|
387
|
+
structuredActions: false,
|
|
388
|
+
toolPolicies: ['none', 'read-only'],
|
|
389
|
+
profiles: false,
|
|
390
|
+
customModelAliases: true,
|
|
391
|
+
customRoles: false,
|
|
392
|
+
freestyle: true,
|
|
393
|
+
userMcp: false,
|
|
394
|
+
},
|
|
395
|
+
modelCatalog: () => {
|
|
396
|
+
const cached = (0, local_engine_detection_1.getCachedModels)(connection.id);
|
|
397
|
+
if (cached.length === 0) {
|
|
398
|
+
const value = connection.defaultModel ?? 'default';
|
|
399
|
+
return [{ value, label: value, default: true }];
|
|
400
|
+
}
|
|
401
|
+
const stored = connection.defaultModel && cached.includes(connection.defaultModel) ? connection.defaultModel : cached[0];
|
|
402
|
+
return cached.map((value) => ({ value, label: value, ...(value === stored ? { default: true } : {}) }));
|
|
403
|
+
},
|
|
404
|
+
defaultModel: () => connection.defaultModel ?? (0, local_engine_detection_1.getCachedModels)(connection.id)[0] ?? 'default',
|
|
405
|
+
buildArgs: (action, opts) => buildLocalArgs(connection, action, opts),
|
|
406
|
+
parseStreamLine: parseLocalStreamLine,
|
|
407
|
+
extractResult: extractLocalResult,
|
|
408
|
+
formatCoreCommand: (command) => command,
|
|
409
|
+
buildRepoAccessArgs: (paths) => paths.flatMap((p) => ['--add-dir', p]),
|
|
410
|
+
projectMcpPath: (root) => path_1.default.join(root, exports.LOCAL_PROJECT_DIR, 'mcp.json'),
|
|
411
|
+
baselineAgents: () => ['sr-architect', 'sr-developer', 'sr-reviewer'],
|
|
412
|
+
detectInstalled: async () => {
|
|
413
|
+
const probe = await (0, local_engine_detection_1.probeConnection)({ baseUrl: connection.baseUrl, apiKeyEnv: connection.apiKeyEnv });
|
|
414
|
+
return {
|
|
415
|
+
installed: probe.installed,
|
|
416
|
+
executable: probe.executable,
|
|
417
|
+
...(probe.error ? { error: probe.error } : {}),
|
|
418
|
+
};
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
return adapter;
|
|
422
|
+
}
|
|
423
|
+
/** Rates of a registered local adapter (null for CLI/unknown ids or no rates). */
|
|
424
|
+
function localConnectionRates(adapter) {
|
|
425
|
+
return isLocalAdapter(adapter) ? adapter.localConnection.rates ?? null : null;
|
|
426
|
+
}
|
|
@@ -1,19 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// In-process registry of ProviderAdapter instances.
|
|
3
|
-
// `register(this)` on module load;
|
|
2
|
+
// In-process registry of ProviderAdapter instances. CLI adapter modules call
|
|
3
|
+
// `register(this)` on module load; local (OpenAI-compatible) adapters are
|
|
4
|
+
// registered/unregistered DYNAMICALLY by `syncLocalAdapters` when connections
|
|
5
|
+
// load or save. Managers look up by id via `getAdapter`.
|
|
4
6
|
//
|
|
5
7
|
// Spec: openspec/specs/multi-provider-architecture/spec.md
|
|
6
8
|
// - "Provider registry exposes lookup by id"
|
|
7
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
10
|
exports.register = register;
|
|
11
|
+
exports.unregisterAdapter = unregisterAdapter;
|
|
9
12
|
exports.getAdapter = getAdapter;
|
|
10
13
|
exports.hasAdapter = hasAdapter;
|
|
14
|
+
exports.adapterKind = adapterKind;
|
|
15
|
+
exports.isLocalAdapterId = isLocalAdapterId;
|
|
11
16
|
exports.listAdapters = listAdapters;
|
|
12
17
|
exports._clearForTests = _clearForTests;
|
|
13
18
|
const types_1 = require("./types");
|
|
14
19
|
const _registry = new Map();
|
|
15
|
-
|
|
20
|
+
const _kinds = new Map();
|
|
21
|
+
/** Register an adapter. `kind` defaults to `'cli'` (module-load registrations);
|
|
22
|
+
* local adapters pass `'local'` so they can later be unregistered. */
|
|
23
|
+
function register(adapter, kind = 'cli') {
|
|
16
24
|
_registry.set(adapter.id, adapter);
|
|
25
|
+
_kinds.set(adapter.id, kind);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Remove a LOCAL adapter. CLI adapters (claude/codex/gemini/kimi — anything
|
|
29
|
+
* registered without `kind: 'local'`) are never removed: returns false and
|
|
30
|
+
* leaves them registered.
|
|
31
|
+
*/
|
|
32
|
+
function unregisterAdapter(id) {
|
|
33
|
+
if (_kinds.get(id) !== 'local')
|
|
34
|
+
return false;
|
|
35
|
+
_registry.delete(id);
|
|
36
|
+
_kinds.delete(id);
|
|
37
|
+
return true;
|
|
17
38
|
}
|
|
18
39
|
function getAdapter(id) {
|
|
19
40
|
const adapter = _registry.get(id);
|
|
@@ -25,8 +46,21 @@ function getAdapter(id) {
|
|
|
25
46
|
function hasAdapter(id) {
|
|
26
47
|
return _registry.has(id);
|
|
27
48
|
}
|
|
49
|
+
/** Kind of a registered adapter, or null when unknown. */
|
|
50
|
+
function adapterKind(id) {
|
|
51
|
+
return _kinds.get(id) ?? null;
|
|
52
|
+
}
|
|
53
|
+
/** True when `id` names a registered LOCAL (OpenAI-compatible) adapter. */
|
|
54
|
+
function isLocalAdapterId(id) {
|
|
55
|
+
return !!id && _kinds.get(id) === 'local';
|
|
56
|
+
}
|
|
57
|
+
/** CLI adapters first (registration order), then local adapters. */
|
|
28
58
|
function listAdapters() {
|
|
29
|
-
|
|
59
|
+
const all = Array.from(_registry.values());
|
|
60
|
+
return [
|
|
61
|
+
...all.filter((a) => _kinds.get(a.id) !== 'local'),
|
|
62
|
+
...all.filter((a) => _kinds.get(a.id) === 'local'),
|
|
63
|
+
];
|
|
30
64
|
}
|
|
31
65
|
/**
|
|
32
66
|
* Reset the registry. Test-only — production code MUST NOT call this.
|
|
@@ -34,4 +68,5 @@ function listAdapters() {
|
|
|
34
68
|
*/
|
|
35
69
|
function _clearForTests() {
|
|
36
70
|
_registry.clear();
|
|
71
|
+
_kinds.clear();
|
|
37
72
|
}
|
|
@@ -379,7 +379,7 @@ function branchRecords(results) {
|
|
|
379
379
|
* fallback for these errors; execution stays bound to a verified worktree.
|
|
380
380
|
*/
|
|
381
381
|
async function launchIsolatedRail(input, io = {}) {
|
|
382
|
-
const { ctx, railIndex, ticketIds, loopId, loopName, loopGraph, provider, model, effort } = input;
|
|
382
|
+
const { ctx, railIndex, ticketIds, loopId, loopName, loopGraph, provider, model, effort, deciderEngine } = input;
|
|
383
383
|
let expectedRepositoryBaseSha = input.repositoryExecution?.expectedBaseSha;
|
|
384
384
|
let singleRepositoryId = input.repositoryExecution?.repositoryId;
|
|
385
385
|
if (!input.repositoryExecution) {
|
|
@@ -1187,6 +1187,7 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1187
1187
|
});
|
|
1188
1188
|
const spec = ctx.getTicketSpec(a.ticketId);
|
|
1189
1189
|
const enginePromise = ctx.loopRunManager.run({
|
|
1190
|
+
runtimeProviderOverride: input.runtimeProviderOverride,
|
|
1190
1191
|
runId: a.runId, loopId, loopName, graph: loopGraph, projectId: ctx.project.id,
|
|
1191
1192
|
cwd: a.handle.worktreePath, repoDir: a.handle.worktreePath,
|
|
1192
1193
|
repositoryId: singleRepositoryId,
|
|
@@ -1204,7 +1205,7 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1204
1205
|
},
|
|
1205
1206
|
ticketCompletionStatus: runFinishedOpts.ticketCompletionStatus,
|
|
1206
1207
|
deferTerminalOutcome: true,
|
|
1207
|
-
constants, provider, model, effort,
|
|
1208
|
+
constants, provider, model, effort, ...(deciderEngine ? { deciderEngine } : {}),
|
|
1208
1209
|
profileName: input.profileName,
|
|
1209
1210
|
});
|
|
1210
1211
|
runPromises.push(settleAllocatedRun(a, enginePromise));
|
|
@@ -1610,7 +1611,7 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1610
1611
|
try {
|
|
1611
1612
|
const outcomes = await (0, rail_merge_orchestrator_1.runMergeBack)({
|
|
1612
1613
|
git, executor: (0, loop_executors_1.createLoopExecutors)(), baseDir: baseRepo,
|
|
1613
|
-
provider, model, effort, constants, branches,
|
|
1614
|
+
provider, model, effort, ...(deciderEngine ? { deciderEngine } : {}), constants, branches,
|
|
1614
1615
|
// CRIT-2: record every merge-back AI step (verify/resolve-merge/fix) as a
|
|
1615
1616
|
// `surface='job'` invocation tied to the rail (surface_ref_id
|
|
1616
1617
|
// `${jobId}:merge:${step}`), primary ticket = ticketIds[0].
|