specrails-desktop 2.45.0 → 2.47.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/client/dist/assets/{ActivityFeedPage-D3SQklu5.js → ActivityFeedPage-DSCpSOGV.js} +1 -1
- package/client/dist/assets/{AgentBrowserCapture-DNGxE33d.js → AgentBrowserCapture-mc1c5pgS.js} +1 -1
- package/client/dist/assets/AgentModeAnalyticsPane--UkamBTm.js +2 -0
- package/client/dist/assets/AgentModeCodePane-DJCswsvU.js +2 -0
- package/client/dist/assets/AgentModeJobsPane-Dw3DNFkh.js +1 -0
- package/client/dist/assets/AgentsPage-ChElPUL9.js +87 -0
- package/client/dist/assets/{AnalyticsPage-BqV6mM2O.js → AnalyticsPage-BGtkFHBp.js} +1 -1
- package/client/dist/assets/CodePage-BBnw3VGn.js +3 -0
- package/client/dist/assets/{DesktopAnalyticsPage-D6WNax3K.js → DesktopAnalyticsPage-B85tNScc.js} +1 -1
- package/client/dist/assets/{DocsDialog-68jKUH9f.js → DocsDialog-D4cXrOMl.js} +1 -1
- package/client/dist/assets/{DocsPage-BoGJgRpm.js → DocsPage-BCAbC6qy.js} +1 -1
- package/client/dist/assets/{ExportDropdown-B2C_XLGp.js → ExportDropdown-BwuvqS2Y.js} +1 -1
- package/client/dist/assets/InteractiveJobComposer-Coq-Zhk0.js +19 -0
- package/client/dist/assets/JobDetailModal--Mjam0Nc.js +1 -0
- package/client/dist/assets/JobDetailPage-B6ShWSBh.js +1 -0
- package/client/dist/assets/JobsPage-DHU_ep_B.js +1 -0
- package/client/dist/assets/{LoopBuilderPage-D21GxFmL.js → LoopBuilderPage-Byvq8Oqx.js} +2 -2
- package/client/dist/assets/{LoopPreviewModal-Bo3fYFxN.js → LoopPreviewModal-BMC9Mgj1.js} +1 -1
- package/client/dist/assets/{LoopsPage-BcrBw1cg.js → LoopsPage-CFGl-Nw8.js} +1 -1
- package/client/dist/assets/{MinimizedChatsContext-Dsv2eHf_.js → MinimizedChatsContext-DHjHBYqa.js} +1 -1
- package/client/dist/assets/{PluginsPage-BbyTmSLx.js → PluginsPage-BHygRnW6.js} +2 -2
- package/client/dist/assets/{ProjectSettingsDialog-B3lEXLv7.js → ProjectSettingsDialog-nspqxH-M.js} +1 -1
- package/client/dist/assets/{RepositoryDeliveries-ok_Wo65C.js → RepositoryDeliveries-Dq9NJOWi.js} +1 -1
- package/client/dist/assets/{RepositoryScopeSelector-DU6QwNaS.js → RepositoryScopeSelector-C3jKG2z0.js} +1 -1
- package/client/dist/assets/{ReviewPacketPage-BBCeHtgd.js → ReviewPacketPage-C9mqhi65.js} +1 -1
- package/client/dist/assets/{TemplatePreviewModal-CfNmxS6O.js → TemplatePreviewModal-BmwoAjOr.js} +1 -1
- package/client/dist/assets/{TicketDetailModalContext-DW3eQ0I7.js → TicketDetailModalContext-lYmoONrd.js} +1 -1
- package/client/dist/assets/{Trans-BHPMWkOw.js → Trans-C0KyPo2O.js} +1 -1
- package/client/dist/assets/agentRuntime-62GWdc26.js +1 -0
- package/client/dist/assets/agentRuntime-BN1mKAuY.js +1 -0
- package/client/dist/assets/agentRuntime-BNBCzQLM.js +1 -0
- package/client/dist/assets/agentRuntime-BQE_K1GG.js +1 -0
- package/client/dist/assets/agentRuntime-CCU3HCtS.js +1 -0
- package/client/dist/assets/agentRuntime-DT72Bpdv.js +1 -0
- package/client/dist/assets/agentRuntime-ZfMPeeiP.js +1 -0
- package/client/dist/assets/agentRuntime-g3W8Ry4d.js +1 -0
- package/client/dist/assets/{agents-IUOgqT5t.js → agents-BIT1UdTy.js} +1 -1
- package/client/dist/assets/{agents-DaYcxAr1.js → agents-C7fKfvm8.js} +1 -1
- package/client/dist/assets/{agents-ms7TwG16.js → agents-CST-lpYX.js} +1 -1
- package/client/dist/assets/{agents-Bvyi3y6R.js → agents-Cm2JYrAa.js} +1 -1
- package/client/dist/assets/{agents-BHp0_FQt.js → agents-DsgGMGwU.js} +1 -1
- package/client/dist/assets/{agents-45GbINfh.js → agents-DyI4rbHa.js} +1 -1
- package/client/dist/assets/{agents-rzPnqoBR.js → agents-H6tVQUNP.js} +1 -1
- package/client/dist/assets/{agents-DMKFStHL.js → agents-_5mJMkAE.js} +1 -1
- package/client/dist/assets/format-duration-NJVPOPBG.js +1 -0
- package/client/dist/assets/{formatDistanceToNow-Bq8iptrG.js → formatDistanceToNow-D44phX1J.js} +1 -1
- package/client/dist/assets/{getTimezoneOffsetInMilliseconds-BVFky3l_.js → getTimezoneOffsetInMilliseconds-EAh9LFSx.js} +1 -1
- package/client/dist/assets/index-Cfh9aaep.css +2 -0
- package/client/dist/assets/index-DgMBighL.js +76 -0
- package/client/dist/assets/{jira-api-zrRdZFBB.js → jira-api-BQR6G0YY.js} +1 -1
- package/client/dist/assets/narration-B1uGfofH.js +1 -0
- package/client/dist/assets/narration-BKcL4Srl.js +1 -0
- package/client/dist/assets/narration-BtjKyHVM.js +1 -0
- package/client/dist/assets/narration-CqNvgRi9.js +1 -0
- package/client/dist/assets/narration-DiPyj06T.js +1 -0
- package/client/dist/assets/narration-DrBAf8uv.js +1 -0
- package/client/dist/assets/narration-OyujFouL.js +1 -0
- package/client/dist/assets/narration-UWi_xIZ-.js +1 -0
- package/client/dist/assets/{project-repositories-CFRaB9UB.js → project-repositories-GRYdjlKB.js} +1 -1
- package/client/dist/assets/{provider-capabilities-B-wOHfLP.js → provider-capabilities-CXipwrXJ.js} +1 -1
- package/client/dist/assets/{settings-OOyDrZQo.js → settings-B3rWEwOx.js} +1 -1
- package/client/dist/assets/{settings-CNiERE-R.js → settings-BcVoiMyw.js} +1 -1
- package/client/dist/assets/settings-BlYvaQ7w.js +1 -0
- package/client/dist/assets/{settings-DJ2jl3hJ.js → settings-CD5uEBiL.js} +1 -1
- package/client/dist/assets/{settings-a7NIODme.js → settings-CyhS0DLJ.js} +1 -1
- package/client/dist/assets/{settings-DVRVybR_.js → settings-DCMFUGDs.js} +1 -1
- package/client/dist/assets/{settings-CrtCs5-B.js → settings-DEgcA0jd.js} +1 -1
- package/client/dist/assets/{settings-ChOCVjX_.js → settings-DmbYKyMJ.js} +1 -1
- package/client/dist/assets/{spending-lTZ-bm_y.js → spending-CN9PHM65.js} +1 -1
- package/client/dist/assets/{useDesktop-Duv5ddAa.js → useDesktop-iius_s5d.js} +1 -1
- package/client/dist/assets/{useSharedWebSocket-5mj2SMKn.js → useSharedWebSocket-D7hkZ3Ka.js} +2 -2
- package/client/dist/index.html +20 -19
- package/docs/internals/programmatic-agent-runtime.md +19 -8
- 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/package.json +1 -1
- package/server/dist/agent-mcp-config.js +5 -0
- package/server/dist/agent-operator-prompt.js +2 -2
- package/server/dist/agent-runtime-bridge.js +69 -17
- package/server/dist/agent-runtime-controls-router.js +50 -0
- package/server/dist/agent-runtime-controls.js +123 -12
- package/server/dist/agent-runtime-effective-config.js +32 -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 +90 -5
- package/server/dist/agent-runtime-metrics.js +122 -0
- package/server/dist/agent-runtime-package.js +205 -0
- package/server/dist/agent-runtime-repositories.js +37 -0
- package/server/dist/agent-runtime-settings-router.js +29 -6
- package/server/dist/agent-runtime-settings.js +151 -21
- package/server/dist/agent-runtime-settlement.js +133 -0
- package/server/dist/contract-refine-runner.js +7 -7
- package/server/dist/core-completion.js +8 -5
- package/server/dist/desktop-router.js +26 -0
- package/server/dist/job-listing.js +7 -3
- package/server/dist/loop-command-catalog.js +2 -2
- package/server/dist/loop-executors.js +6 -4
- package/server/dist/loop-run-manager.js +12 -4
- package/server/dist/mcp/tools/rails.js +1 -0
- package/server/dist/mcp/tools/specs.js +7 -4
- package/server/dist/multi-repo-execution.js +1 -0
- package/server/dist/project-router-jobs.js +8 -2
- package/server/dist/project-router-tickets.js +4 -4
- package/server/dist/providers/runtime.js +6 -0
- package/server/dist/rail-isolated-launch.js +2 -1
- package/server/dist/rails-router.js +29 -1
- package/server/dist/runtime-role-prompts-router.js +25 -0
- package/server/dist/schemas/agent-runtime.schema.json +25 -3
- package/server/dist/schemas/fixtures/runtime-efficiency-summary.v1.json +790 -0
- package/server/dist/worktree-overlay.js +32 -1
- package/client/dist/assets/AgentModeAnalyticsPane-BUvZNoj0.js +0 -2
- package/client/dist/assets/AgentModeCodePane-YW717UvJ.js +0 -2
- package/client/dist/assets/AgentModeJobsPane-Cxg_eNmd.js +0 -1
- package/client/dist/assets/AgentsPage-B_DCByok.js +0 -87
- package/client/dist/assets/CodePage-PyZKCj1B.js +0 -3
- package/client/dist/assets/InteractiveJobComposer-l8q-r5Yy.js +0 -19
- package/client/dist/assets/JobDetailModal-C-X2GI9j.js +0 -1
- package/client/dist/assets/JobDetailPage-C6Jdm7S9.js +0 -1
- package/client/dist/assets/JobsPage-DycM6Vgp.js +0 -1
- package/client/dist/assets/agentRuntime-14faDjWM.js +0 -1
- package/client/dist/assets/agentRuntime-BIokojkJ.js +0 -1
- package/client/dist/assets/agentRuntime-Bu4xEcRw.js +0 -1
- package/client/dist/assets/agentRuntime-Cz0ASixB.js +0 -1
- package/client/dist/assets/agentRuntime-DESdGXLV.js +0 -1
- package/client/dist/assets/agentRuntime-DhX5Xy5S.js +0 -1
- package/client/dist/assets/agentRuntime-Dpbh_gLp.js +0 -1
- package/client/dist/assets/agentRuntime-jS2lplFm.js +0 -1
- package/client/dist/assets/index-CBLYwPw6.css +0 -2
- package/client/dist/assets/index-DOKftX7y.js +0 -76
- package/client/dist/assets/narration-BA8Yw5ZL.js +0 -1
- package/client/dist/assets/narration-BuMFUS7Q.js +0 -1
- package/client/dist/assets/narration-Bv_iQQG5.js +0 -1
- package/client/dist/assets/narration-C6Ost9nL.js +0 -1
- package/client/dist/assets/narration-CIbjo2pA.js +0 -1
- package/client/dist/assets/narration-CNJ2-2-a.js +0 -1
- package/client/dist/assets/narration-OzsPJCXM.js +0 -1
- package/client/dist/assets/narration-zCdgiuSU.js +0 -1
- package/client/dist/assets/settings-DzXZCb1h.js +0 -1
|
@@ -5,6 +5,8 @@ exports.truncate = truncate;
|
|
|
5
5
|
exports.resolveRunVars = resolveRunVars;
|
|
6
6
|
exports.extractChangeId = extractChangeId;
|
|
7
7
|
exports.recoverOrphanLoopStepAccounting = recoverOrphanLoopStepAccounting;
|
|
8
|
+
const agent_runtime_events_1 = require("./agent-runtime-events");
|
|
9
|
+
const agent_runtime_settings_1 = require("./agent-runtime-settings");
|
|
8
10
|
const multi_repo_execution_store_1 = require("./multi-repo-execution-store");
|
|
9
11
|
const win_spawn_1 = require("./util/win-spawn");
|
|
10
12
|
const db_1 = require("./db");
|
|
@@ -561,7 +563,7 @@ class LoopRunManager {
|
|
|
561
563
|
async run(req) {
|
|
562
564
|
// Later fresh steps must verify the admitted spec, even if its caller or
|
|
563
565
|
// backlog changes while the implementation is running.
|
|
564
|
-
req = { ...req, graph: structuredClone(req.graph) };
|
|
566
|
+
req = { ...req, runtimeProviderOverride: (0, agent_runtime_settings_1.validateRuntimeProviderOverride)(req.runtimeProviderOverride), graph: structuredClone(req.graph) };
|
|
565
567
|
if (req.spec)
|
|
566
568
|
req = { ...req, spec: structuredClone(req.spec) };
|
|
567
569
|
if (req.executionManifest)
|
|
@@ -752,6 +754,12 @@ class LoopRunManager {
|
|
|
752
754
|
logLine(line);
|
|
753
755
|
return;
|
|
754
756
|
}
|
|
757
|
+
if (eventType === 'runtime-efficiency-event') {
|
|
758
|
+
const validated = (0, agent_runtime_events_1.runtimeEfficiencyEventLine)(line, runId);
|
|
759
|
+
if (!validated || (0, agent_runtime_events_1.isRecordedRuntimeEfficiencyEvent)(this.db, runId, validated))
|
|
760
|
+
return;
|
|
761
|
+
line = validated;
|
|
762
|
+
}
|
|
755
763
|
const s = takeSeq();
|
|
756
764
|
try {
|
|
757
765
|
(0, db_1.appendEvent)(this.db, runId, s, { event_type: eventType, source: 'stdout', payload: line });
|
|
@@ -1099,7 +1107,7 @@ class LoopRunManager {
|
|
|
1099
1107
|
// were noise once the step explorer landed; the log now opens on real
|
|
1100
1108
|
// output). `template` is omitted when rendering changed nothing (a
|
|
1101
1109
|
// plain free-text prompt would just duplicate `command`).
|
|
1102
|
-
const stepTitle = `🤖 ${nodeLabel || 'AI Step'} (${nodeProvider}/${nodeModel}${nodeEffort ? `, effort: ${nodeEffort}` : ''})`;
|
|
1110
|
+
const stepTitle = requiresCoreCompletion ? '🤖 Implementation (agent runtime)' : `🤖 ${nodeLabel || 'AI Step'} (${nodeProvider}/${nodeModel}${nodeEffort ? `, effort: ${nodeEffort}` : ''})`;
|
|
1103
1111
|
const stepDetail = {
|
|
1104
1112
|
...(rawTemplate && rawTemplate !== base ? { template: capText(rawTemplate, STEP_TEMPLATE_CAP) } : {}),
|
|
1105
1113
|
...(base ? { command: capText(base, STEP_COMMAND_CAP) } : {}),
|
|
@@ -1145,7 +1153,7 @@ class LoopRunManager {
|
|
|
1145
1153
|
nextEventSeq: takeSeq,
|
|
1146
1154
|
recoveryStepKey: aiRecoveryKey,
|
|
1147
1155
|
})
|
|
1148
|
-
: this.executors.runAiStep({ coreRun, prompt, sessionId, provider: nodeProvider, model: nodeModel, effort: nodeEffort, profileName: req.profileName, cwd: req.cwd, repoDir: req.repoDir, executionManifest: req.executionManifest, onLine: logLine, onRawLine, onSpawn: (c) => this._activeChild.set(runId, c), aiStepTimeoutMs: effectiveTimeoutMs, idleTimeoutMs });
|
|
1156
|
+
: this.executors.runAiStep({ runtimeProviderOverride: req.runtimeProviderOverride, coreRun, prompt, sessionId, provider: nodeProvider, model: nodeModel, effort: nodeEffort, profileName: req.profileName, cwd: req.cwd, repoDir: req.repoDir, executionManifest: req.executionManifest, onLine: logLine, onRawLine, onSpawn: (c) => this._activeChild.set(runId, c), aiStepTimeoutMs: effectiveTimeoutMs, idleTimeoutMs });
|
|
1149
1157
|
};
|
|
1150
1158
|
let res = await runAttempt(aiSessionId);
|
|
1151
1159
|
if (this._disposed)
|
|
@@ -1691,7 +1699,7 @@ class LoopRunManager {
|
|
|
1691
1699
|
// `≥` when any cost-bearing step ended unpriced (timeout/crash) — the figure
|
|
1692
1700
|
// is a lower bound, not exact. Providers without usage telemetry get an
|
|
1693
1701
|
// explicit unavailable marker, never a fabricated "$0.0000".
|
|
1694
|
-
logLine(`\n■ Loop execution finished: ${outcome} —
|
|
1702
|
+
logLine(`\n■ Loop execution finished: ${outcome} — ` +
|
|
1695
1703
|
(costAvailable
|
|
1696
1704
|
? `${costUncertain ? '≥ ' : ''}$${totalCost.toFixed(4)}`
|
|
1697
1705
|
: 'usage/cost unavailable'));
|
|
@@ -47,6 +47,7 @@ function railsTools() {
|
|
|
47
47
|
'stop (destructive — kills all active jobs and loop runs for the rail). ' +
|
|
48
48
|
'For on_review tickets with an already-open GitHub PR (including a published pr_ready delivery), launch automatically tries to continue that PR head branch; Jira-linked in_progress tickets can do the same when the PR match is explicit; fresh tickets still start from the project integration branch. ' +
|
|
49
49
|
'When launched from the in-app agent chat without an explicit aiEngine, the engine defaults to your conversation\'s provider. On that same provider, an omitted model inherits the conversation model; an explicit model wins (pass aiEngine to override; launch_all always uses each rail\'s stored engine). ' +
|
|
50
|
+
'For Implement and Batch Implement, the selected launch provider applies to ALL runtime roles (architect, developer, reviewer); an explicit launch model/effort also applies to each role. Resuming an existing execution keeps its original frozen role configuration; start a new launch to change providers. ' +
|
|
50
51
|
'User-facing naming: call the free-form autonomous mode "Freestyle"; use "freestyle" as the canonical API enum value for that same capability. ' +
|
|
51
52
|
'For small OpenSpec-governed work, recommend "SDD Quick (OpenSpec)" and launch with mode "loop" plus loopId "factory:sdd-quick-openspec"; keep Freestyle for ticket-local implementation-only work. ' +
|
|
52
53
|
'NAMING: railIndex is the 0-BASED internal identity; the dashboard shows rails 1-based ("Rail N" = railIndex N-1). When talking to the user, ALWAYS say "Rail <railIndex + 1>" (or the rail\'s custom name) — results include railLabel with the correct user-facing label.',
|
|
@@ -59,7 +59,8 @@ function specsTools() {
|
|
|
59
59
|
'— pass contractRefine: false to skip it, e.g. when the user declined it), ' +
|
|
60
60
|
'generate (same as create but takes a pre-formed idea string; both accept contextScope/attachments/createLocal), ' +
|
|
61
61
|
'ai_edit (ai-spawn, AI-edit a ticket title+description), ' +
|
|
62
|
-
'cancel_ai_edit (abort an in-flight ai-edit), contract_refine (ai-spawn, append/re-fire a Contract Layer — Claude
|
|
62
|
+
'cancel_ai_edit (abort an in-flight ai-edit), contract_refine (ai-spawn, append/re-fire a Contract Layer — Claude or Codex; ' +
|
|
63
|
+
'pass numeric id, not ticketId; optional aiEngine selects the provider for origin-less specs, while Explore specs retain their origin provider; ' +
|
|
63
64
|
'works on Explore-origin AND agent-authored/Quick specs), ' +
|
|
64
65
|
'smash (ai-spawn, decompose a spec into N child sub-specs under an epic — Claude-only), ' +
|
|
65
66
|
'smash_undo (destructive, reverse a prior SMASH), delete_epic_children (destructive, delete all children of an epic), ' +
|
|
@@ -150,7 +151,7 @@ function specsTools() {
|
|
|
150
151
|
aiEngine: zod_1.z
|
|
151
152
|
.string()
|
|
152
153
|
.optional()
|
|
153
|
-
.describe('create/generate: provider/engine override (must be
|
|
154
|
+
.describe('create/generate/contract_refine: provider/engine override (must be installed; contract_refine cannot switch an Explore-origin provider)'),
|
|
154
155
|
contextScope: zod_1.z
|
|
155
156
|
.record(zod_1.z.unknown())
|
|
156
157
|
.optional()
|
|
@@ -158,7 +159,7 @@ function specsTools() {
|
|
|
158
159
|
contractRefine: zod_1.z
|
|
159
160
|
.boolean()
|
|
160
161
|
.optional()
|
|
161
|
-
.describe('create/generate/commit_draft: enrich the spec with a Contract Layer post-persist (Claude
|
|
162
|
+
.describe('create/generate/commit_draft: enrich the spec with a Contract Layer post-persist (Claude or Codex). DEFAULTS TO TRUE — pass false to opt out (e.g. the user asked for no contract layer)'),
|
|
162
163
|
attachmentIds: zod_1.z.array(zod_1.z.string()).optional().describe('create/generate/ai_edit: attachment ids (create/generate require pendingSpecId)'),
|
|
163
164
|
// ── ai_edit ──────────────────────────────────────────────────────
|
|
164
165
|
instructions: zod_1.z.string().optional().describe('ai_edit: natural-language editing instructions (required)'),
|
|
@@ -428,7 +429,9 @@ function specsTools() {
|
|
|
428
429
|
return { ...r, hint: WATCH_HINT };
|
|
429
430
|
}
|
|
430
431
|
case 'contract_refine': {
|
|
431
|
-
const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/${requireId()}/contract-refine
|
|
432
|
+
const r = await (0, types_1.apiCall)(ctx, 'POST', `${base}/tickets/${requireId()}/contract-refine`, {
|
|
433
|
+
aiEngine: args.aiEngine,
|
|
434
|
+
});
|
|
432
435
|
return { ...r, hint: CONTRACT_REFINE_HINT };
|
|
433
436
|
}
|
|
434
437
|
case 'smash': {
|
|
@@ -353,6 +353,7 @@ async function launchMultiRepositoryRail(input, io = {}) {
|
|
|
353
353
|
};
|
|
354
354
|
await (0, rail_isolated_launch_1.launchIsolatedRail)({
|
|
355
355
|
...input, ctx: childCtx, scope: effectiveScope, loopGraph: graph,
|
|
356
|
+
runtimeStateProject: ctx.project,
|
|
356
357
|
revision: input.revision && previous ? { ...input.revision, ofDeliveryId: previous.id, decision: previous.decision } : undefined,
|
|
357
358
|
requiredPrContinuation: continuation,
|
|
358
359
|
explicitPrTarget: continuation?.prNumber ? { prNumber: continuation.prNumber } : input.explicitPrTarget,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerJobsRoutes = registerJobsRoutes;
|
|
4
|
+
const agent_runtime_controls_router_1 = require("./agent-runtime-controls-router");
|
|
4
5
|
const ids_1 = require("./ids");
|
|
5
6
|
const db_1 = require("./db");
|
|
6
7
|
const queue_manager_1 = require("./queue-manager");
|
|
@@ -229,6 +230,10 @@ function registerJobsRoutes(deps) {
|
|
|
229
230
|
router.post('/:projectId/jobs/:id/cancel', (req, res) => {
|
|
230
231
|
const id = req.params.id;
|
|
231
232
|
const c = ctx(req);
|
|
233
|
+
if ((0, agent_runtime_controls_router_1.cancelRuntimeContinuation)(c, id)) {
|
|
234
|
+
res.json({ ok: true, status: 'canceling' });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
232
237
|
// A loop run is backed by a job row but is NOT a QueueManager job — it is
|
|
233
238
|
// driven by the LoopRunManager. Cancel it through the engine (which settles
|
|
234
239
|
// it 'stopped' → finishJob('canceled') + releases the rail's tickets).
|
|
@@ -409,12 +414,13 @@ function registerJobsRoutes(deps) {
|
|
|
409
414
|
const from = req.query.from;
|
|
410
415
|
const to = req.query.to;
|
|
411
416
|
const { db } = ctx(req);
|
|
412
|
-
const result = (0, job_listing_1.listUnifiedJobs)(db, { limit, offset, status, from, to });
|
|
417
|
+
const result = (0, job_listing_1.listUnifiedJobs)(db, { limit, offset, status, from, to, activeRuntimeIds: (0, agent_runtime_controls_router_1.activeRuntimeContinuationIds)(ctx(req)) });
|
|
413
418
|
// Annotate each job with hasTelemetry so the client can show the
|
|
414
419
|
// Export diagnostic button without an extra round trip.
|
|
415
420
|
const jobsWithTelemetry = (0, db_1.getJobsWithTelemetry)(db);
|
|
416
421
|
const annotatedJobs = result.jobs.map((j) => ({
|
|
417
422
|
...j,
|
|
423
|
+
...((0, agent_runtime_controls_router_1.isRuntimeContinuationActive)(ctx(req), j.id) ? { status: 'running', finished_at: null } : {}),
|
|
418
424
|
hasTelemetry: jobsWithTelemetry.has(j.id),
|
|
419
425
|
}));
|
|
420
426
|
res.json({ jobs: annotatedJobs, total: result.total });
|
|
@@ -593,7 +599,7 @@ function registerJobsRoutes(deps) {
|
|
|
593
599
|
interactiveAcceptingTurns = qmMode !== null || loopStepActive || loopPaused;
|
|
594
600
|
interactiveSettleMode = qmMode ?? ((loopStepActive || loopPaused || loopRun) ? 'auto' : null);
|
|
595
601
|
}
|
|
596
|
-
const annotated = { ...job, hasTelemetry: (0, db_1.hasJobTelemetry)(db, jobId), tickets, interactiveSettleMode, interactiveAcceptingTurns, loopPaused, loopPauseReason };
|
|
602
|
+
const annotated = { ...job, ...((0, agent_runtime_controls_router_1.isRuntimeContinuationActive)(c, jobId) ? { status: 'running', finished_at: null } : {}), hasTelemetry: (0, db_1.hasJobTelemetry)(db, jobId), tickets, interactiveSettleMode, interactiveAcceptingTurns, loopPaused, loopPauseReason };
|
|
597
603
|
res.json({ job: annotated, events, phaseDefinitions });
|
|
598
604
|
});
|
|
599
605
|
router.delete('/:projectId/jobs', (req, res) => {
|
|
@@ -271,7 +271,7 @@ function registerTicketsRoutes(deps) {
|
|
|
271
271
|
const rawScope = req.body?.contextScope;
|
|
272
272
|
// Contract Layer is available only when the selected adapter advertises a
|
|
273
273
|
// safe structured-action implementation.
|
|
274
|
-
const quickContractRefine = (0, providers_1.getAdapter)(provider)
|
|
274
|
+
const quickContractRefine = !(0, runtime_1.supportsContractRefine)((0, providers_1.getAdapter)(provider))
|
|
275
275
|
? false
|
|
276
276
|
: typeof req.body?.contractRefine === 'boolean'
|
|
277
277
|
? req.body.contractRefine
|
|
@@ -1057,7 +1057,7 @@ function registerTicketsRoutes(deps) {
|
|
|
1057
1057
|
if (conversationId &&
|
|
1058
1058
|
created &&
|
|
1059
1059
|
convoProvider &&
|
|
1060
|
-
(0, providers_1.getAdapter)(convoProvider)
|
|
1060
|
+
(0, runtime_1.supportsContractRefine)((0, providers_1.getAdapter)(convoProvider))) {
|
|
1061
1061
|
const createdTicketId = created.id;
|
|
1062
1062
|
const convoId = conversationId;
|
|
1063
1063
|
console.log(`[project-router] from-draft hook: scheduling refine ticket=${createdTicketId} conv=${convoId}`);
|
|
@@ -1093,7 +1093,7 @@ function registerTicketsRoutes(deps) {
|
|
|
1093
1093
|
const requestedAgentProvider = typeof body.agentProvider === 'string' ? body.agentProvider : null;
|
|
1094
1094
|
const providerCheck = (0, provider_selection_1.validateRequestedProvider)(project, requestedAgentProvider);
|
|
1095
1095
|
const refineProvider = providerCheck.ok ? providerCheck.provider : null;
|
|
1096
|
-
if (refineProvider && (0, providers_1.getAdapter)(refineProvider)
|
|
1096
|
+
if (refineProvider && (0, runtime_1.supportsContractRefine)((0, providers_1.getAdapter)(refineProvider))) {
|
|
1097
1097
|
const refineTicketId = created.id;
|
|
1098
1098
|
const refineTitle = created.title;
|
|
1099
1099
|
const refineDescription = created.description;
|
|
@@ -1262,7 +1262,7 @@ function registerTicketsRoutes(deps) {
|
|
|
1262
1262
|
return;
|
|
1263
1263
|
}
|
|
1264
1264
|
const refineProvider = refineProviderCheck.provider;
|
|
1265
|
-
if ((0, providers_1.getAdapter)(refineProvider)
|
|
1265
|
+
if (!(0, runtime_1.supportsContractRefine)((0, providers_1.getAdapter)(refineProvider))) {
|
|
1266
1266
|
res.status(409).json({ error: `contract_refine_unsupported_for_${refineProvider}` });
|
|
1267
1267
|
return;
|
|
1268
1268
|
}
|
|
@@ -5,6 +5,7 @@ exports.isSafeCustomModelAlias = isSafeCustomModelAlias;
|
|
|
5
5
|
exports.isModelAvailableForAdapter = isModelAvailableForAdapter;
|
|
6
6
|
exports.supportsToolPolicy = supportsToolPolicy;
|
|
7
7
|
exports.pureOutputToolPolicy = pureOutputToolPolicy;
|
|
8
|
+
exports.supportsContractRefine = supportsContractRefine;
|
|
8
9
|
exports.requireToolPolicy = requireToolPolicy;
|
|
9
10
|
exports.reasoningEffortsForModel = reasoningEffortsForModel;
|
|
10
11
|
exports.defaultReasoningEffortForModel = defaultReasoningEffortForModel;
|
|
@@ -61,6 +62,11 @@ function pureOutputToolPolicy(adapter) {
|
|
|
61
62
|
return 'read-only';
|
|
62
63
|
return null;
|
|
63
64
|
}
|
|
65
|
+
/** Contract extraction also supports Codex's enforced read-only transform. */
|
|
66
|
+
function supportsContractRefine(adapter) {
|
|
67
|
+
return adapter.capabilities.structuredActions === true
|
|
68
|
+
|| (adapter.id === 'codex' && pureOutputToolPolicy(adapter) !== null);
|
|
69
|
+
}
|
|
64
70
|
/** Throw a stable, provider-identifying error for an unsupported boundary. */
|
|
65
71
|
function requireToolPolicy(adapter, policy) {
|
|
66
72
|
if (!supportsToolPolicy(adapter, policy)) {
|
|
@@ -1019,7 +1019,7 @@ async function launchIsolatedRail(input, io = {}) {
|
|
|
1019
1019
|
// A paused/interrupted programmatic workflow owns unfinished changes and
|
|
1020
1020
|
// verification evidence in this exact mount. Keep them available for
|
|
1021
1021
|
// explicit Core recovery, including a currently clean worktree.
|
|
1022
|
-
if (implementationOutcome === 'failed' && (0, agent_runtime_paths_1.hasAgentRuntimeRequest)(ctx.project, a.runId)) {
|
|
1022
|
+
if (implementationOutcome === 'failed' && (0, agent_runtime_paths_1.hasAgentRuntimeRequest)(input.runtimeStateProject ?? ctx.project, a.runId)) {
|
|
1023
1023
|
markWorktree(a, 'needs-review');
|
|
1024
1024
|
return {
|
|
1025
1025
|
run: a, implementationOutcome, deliveryOutcome: 'blocked',
|
|
@@ -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,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.prDeliveryRevisionAllowed = prDeliveryRevisionAllowed;
|
|
4
4
|
exports.createRailsRouter = createRailsRouter;
|
|
5
|
+
const agent_runtime_settings_1 = require("./agent-runtime-settings");
|
|
5
6
|
const express_1 = require("express");
|
|
6
7
|
const rails_store_1 = require("./rails-store");
|
|
7
8
|
const queue_manager_1 = require("./queue-manager");
|
|
@@ -432,6 +433,14 @@ function createRailsRouter() {
|
|
|
432
433
|
res.status(400).json({ error: 'Invalid rail index' });
|
|
433
434
|
return;
|
|
434
435
|
}
|
|
436
|
+
let runtimeProviderOverride;
|
|
437
|
+
try {
|
|
438
|
+
runtimeProviderOverride = (0, agent_runtime_settings_1.validateRuntimeProviderOverride)(req.body?.runtimeProviderOverride);
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
res.status(400).json({ error: 'invalid_runtime_provider_override' });
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
435
444
|
let { mode = 'implement' } = req.body ?? {};
|
|
436
445
|
const { repositoryIds: rawRepositoryIds, baseDeliveryIds, profileName, aiEngine, model, loopId: rawLoopId, reasoning_effort, originConversationId, originSurface, targetPrNumber, revisionOfDeliveryId, revisionNote, baseBranch: rawBaseBranch } = req.body ?? {};
|
|
437
446
|
// Revision launch (nontech-review-experience Wave 3): the user asked for a
|
|
@@ -635,6 +644,21 @@ function createRailsRouter() {
|
|
|
635
644
|
// Only pass a provider override when one was actually requested (keeps
|
|
636
645
|
// single-provider rails on the legacy code path).
|
|
637
646
|
const railProvider = requestedEngine ? engineCheck.provider : undefined;
|
|
647
|
+
// Mission/MCP and Launch-all submit aiEngine or use the saved rail engine.
|
|
648
|
+
// Resolve this at the shared boundary, not only in the Dashboard payload.
|
|
649
|
+
if (!runtimeProviderOverride && requestedEngine) {
|
|
650
|
+
try {
|
|
651
|
+
runtimeProviderOverride = (0, agent_runtime_settings_1.validateRuntimeProviderOverride)({ provider: engineCheck.provider, ...(model ? { model } : {}), ...(reasoning_effort ? { effort: reasoning_effort } : {}) });
|
|
652
|
+
}
|
|
653
|
+
catch {
|
|
654
|
+
res.status(400).json({ error: 'invalid_runtime_provider_override' });
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (runtimeProviderOverride && runtimeProviderOverride.provider !== engineCheck.provider) {
|
|
659
|
+
res.status(400).json({ error: 'runtime_provider_mismatch' });
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
638
662
|
// Freestyle bypasses the OpenSpec pipeline and hands the raw spec to a
|
|
639
663
|
// provider that explicitly advertises autonomous freestyle support.
|
|
640
664
|
if (mode === 'freestyle' && (0, providers_1.getAdapter)(engineCheck.provider).capabilities.freestyle !== true) {
|
|
@@ -907,6 +931,7 @@ function createRailsRouter() {
|
|
|
907
931
|
}
|
|
908
932
|
try {
|
|
909
933
|
const ids = await (0, rail_isolated_launch_1.launchIsolatedRail)({
|
|
934
|
+
runtimeProviderOverride,
|
|
910
935
|
ctx: c, railIndex, ticketIds: [...rail.ticketIds], repositoryIds, ...repositoryBases, loopId, loopName, loopGraph,
|
|
911
936
|
provider: loopProvider, model: loopModel, effort, scope,
|
|
912
937
|
profileName: resolvedProfile,
|
|
@@ -1015,6 +1040,7 @@ function createRailsRouter() {
|
|
|
1015
1040
|
};
|
|
1016
1041
|
const runPromise = c.loopRunManager
|
|
1017
1042
|
.run({
|
|
1043
|
+
runtimeProviderOverride,
|
|
1018
1044
|
runId,
|
|
1019
1045
|
loopId,
|
|
1020
1046
|
loopName,
|
|
@@ -1211,7 +1237,8 @@ function createRailsRouter() {
|
|
|
1211
1237
|
// (so we deliberately do NOT delete railLoopRuns here).
|
|
1212
1238
|
for (const runId of targetLoopRunIds) {
|
|
1213
1239
|
try {
|
|
1214
|
-
|
|
1240
|
+
if (!(0, agent_runtime_controls_router_1.cancelRuntimeContinuation)(c, runId))
|
|
1241
|
+
c.loopRunManager.cancel(runId);
|
|
1215
1242
|
canceledCount++;
|
|
1216
1243
|
}
|
|
1217
1244
|
catch (err) {
|
|
@@ -1534,3 +1561,4 @@ function createRailsRouter() {
|
|
|
1534
1561
|
});
|
|
1535
1562
|
return router;
|
|
1536
1563
|
}
|
|
1564
|
+
const agent_runtime_controls_router_1 = require("./agent-runtime-controls-router");
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerRuntimeRolePromptRoutes = registerRuntimeRolePromptRoutes;
|
|
4
|
+
const agent_runtime_settings_1 = require("./agent-runtime-settings");
|
|
5
|
+
const agent_runtime_loader_1 = require("./agent-runtime-loader");
|
|
6
|
+
function registerRuntimeRolePromptRoutes(router) {
|
|
7
|
+
router.get('/runtime-role-prompts', async (_req, res) => {
|
|
8
|
+
try {
|
|
9
|
+
const defaults = (await (0, agent_runtime_loader_1.loadCoreAgentRuntime)()).rolePromptDefaults();
|
|
10
|
+
res.json({ defaults, overrides: (0, agent_runtime_settings_1.loadRuntimeRolePrompts)() });
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
res.status(422).json({ message: error.message });
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
router.put('/runtime-role-prompts', async (req, res) => {
|
|
17
|
+
try {
|
|
18
|
+
const defaults = (await (0, agent_runtime_loader_1.loadCoreAgentRuntime)()).rolePromptDefaults();
|
|
19
|
+
res.json({ defaults, overrides: (0, agent_runtime_settings_1.saveRuntimeRolePrompts)(req.body.overrides) });
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
res.status(error instanceof agent_runtime_settings_1.AgentRuntimeConfigError ? 400 : 500).json({ message: error.message });
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -4,10 +4,20 @@
|
|
|
4
4
|
"title": "Specrails programmatic agent runtime",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": false,
|
|
7
|
-
"required": ["schemaVersion", "
|
|
7
|
+
"required": ["schemaVersion", "providers", "agents", "verification"],
|
|
8
8
|
"properties": {
|
|
9
9
|
"schemaVersion": { "const": 1 },
|
|
10
|
-
"
|
|
10
|
+
"efficiency": { "type": "object", "additionalProperties": false, "required": ["schemaVersion"], "properties": {
|
|
11
|
+
"schemaVersion": { "const": 1 }, "contextMode": { "enum": ["full", "incremental"] }, "reviewMode": { "enum": ["full", "incremental"] },
|
|
12
|
+
"planning": { "enum": ["full", "proportional"] }, "acceptDeveloperChecks": { "type": "boolean" },
|
|
13
|
+
"verification": { "type": "object", "additionalProperties": false, "properties": { "maxConcurrency": { "type": "integer", "minimum": 1, "maximum": 4 } } }
|
|
14
|
+
} },
|
|
15
|
+
"rolePrompts": { "type": "object", "additionalProperties": false, "properties": {
|
|
16
|
+
"architect": { "type": "string", "minLength": 1, "maxLength": 20000, "pattern": "\\S" },
|
|
17
|
+
"developer": { "type": "string", "minLength": 1, "maxLength": 20000, "pattern": "\\S" },
|
|
18
|
+
"reviewer": { "type": "string", "minLength": 1, "maxLength": 20000, "pattern": "\\S" }
|
|
19
|
+
} },
|
|
20
|
+
"enabled": { "type": "boolean", "deprecated": true, "description": "Accepted for saved configuration compatibility; implementation always uses the agent runtime." },
|
|
11
21
|
"providers": { "type": "array", "items": { "oneOf": [
|
|
12
22
|
{ "type": "object", "additionalProperties": false, "required": ["id", "kind", "cli"], "properties": {
|
|
13
23
|
"id": { "$ref": "#/definitions/id" }, "kind": { "const": "cli" }, "cli": { "enum": ["claude", "codex", "gemini", "kimi"] }
|
|
@@ -26,6 +36,7 @@
|
|
|
26
36
|
"maxCostUsd": { "type": "number", "exclusiveMinimum": 0 }, "timeoutMs": { "$ref": "#/definitions/positiveInteger" }
|
|
27
37
|
} },
|
|
28
38
|
"verification": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["repositoryId", "command", "args"], "properties": {
|
|
39
|
+
"key": { "$ref": "#/definitions/id" }, "label": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "\\S" }, "policy": { "$ref": "#/definitions/checkPolicy" },
|
|
29
40
|
"repositoryId": { "$ref": "#/definitions/id" }, "command": { "type": "string", "minLength": 1 },
|
|
30
41
|
"args": { "type": "array", "items": { "type": "string" } }, "cwd": { "type": "string", "minLength": 1 },
|
|
31
42
|
"env": { "type": "object", "propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, "additionalProperties": { "type": "string" } },
|
|
@@ -44,10 +55,21 @@
|
|
|
44
55
|
} }
|
|
45
56
|
},
|
|
46
57
|
"definitions": {
|
|
58
|
+
"effort": { "type": "string", "pattern": "^[a-z][a-z0-9_-]{0,31}$" },
|
|
59
|
+
"inputList": { "type": "array", "maxItems": 256, "items": { "type": "string", "minLength": 1, "maxLength": 4096, "pattern": "\\S" } },
|
|
60
|
+
"checkPolicy": { "type": "object", "additionalProperties": false, "properties": {
|
|
61
|
+
"reuse": { "enum": ["never", "snapshot-local"] }, "inputs": { "$ref": "#/definitions/inputList" },
|
|
62
|
+
"toolchainInputs": { "$ref": "#/definitions/inputList" }, "resources": { "$ref": "#/definitions/inputList" },
|
|
63
|
+
"deterministic": { "type": "boolean" }, "readOnly": { "type": "boolean" }, "independentGroup": { "$ref": "#/definitions/id" }
|
|
64
|
+
} },
|
|
47
65
|
"id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$" },
|
|
48
66
|
"positiveInteger": { "type": "integer", "minimum": 1 },
|
|
49
67
|
"score": { "type": "number", "minimum": 0, "maximum": 100 },
|
|
50
|
-
"agent": { "type": "object", "additionalProperties": false, "required": ["provider"], "properties": {
|
|
68
|
+
"agent": { "type": "object", "additionalProperties": false, "required": ["provider"], "dependencies": { "escalation": ["model"] }, "properties": {
|
|
69
|
+
"effort": { "$ref": "#/definitions/effort" },
|
|
70
|
+
"escalation": { "type": "object", "additionalProperties": false, "required": ["model"], "properties": {
|
|
71
|
+
"model": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "^[^-\\r\\n\\u0000][^\\r\\n\\u0000]*$" }, "effort": { "$ref": "#/definitions/effort" }
|
|
72
|
+
} },
|
|
51
73
|
"provider": { "$ref": "#/definitions/id" }, "model": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "^[^-\\r\\n\\u0000][^\\r\\n\\u0000]*$" },
|
|
52
74
|
"maxTurns": { "$ref": "#/definitions/positiveInteger" }
|
|
53
75
|
} }
|