specrails-desktop 2.44.1 → 2.45.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.
Files changed (104) hide show
  1. package/README.md +2 -0
  2. package/client/dist/assets/{ActivityFeedPage-CL1BUXoj.js → ActivityFeedPage-D3SQklu5.js} +1 -1
  3. package/client/dist/assets/AgentBrowserCapture-DNGxE33d.js +1 -0
  4. package/client/dist/assets/AgentModeAnalyticsPane-BUvZNoj0.js +2 -0
  5. package/client/dist/assets/AgentModeCodePane-YW717UvJ.js +2 -0
  6. package/client/dist/assets/{AgentModeJobsPane-C7ELTPC1.js → AgentModeJobsPane-Cxg_eNmd.js} +1 -1
  7. package/client/dist/assets/AgentsPage-B_DCByok.js +87 -0
  8. package/client/dist/assets/{AnalyticsPage-DTsg-N5z.js → AnalyticsPage-BqV6mM2O.js} +1 -1
  9. package/client/dist/assets/CodePage-PyZKCj1B.js +3 -0
  10. package/client/dist/assets/{DesktopAnalyticsPage-Y0Z1ryQr.js → DesktopAnalyticsPage-D6WNax3K.js} +1 -1
  11. package/client/dist/assets/{DocsDialog-KeCOxvGH.js → DocsDialog-68jKUH9f.js} +1 -1
  12. package/client/dist/assets/{DocsPage-Bt1PgYPO.js → DocsPage-BoGJgRpm.js} +1 -1
  13. package/client/dist/assets/{ExportDropdown-DczgU4at.js → ExportDropdown-B2C_XLGp.js} +1 -1
  14. package/client/dist/assets/InteractiveJobComposer-l8q-r5Yy.js +19 -0
  15. package/client/dist/assets/{JobDetailModal-DKA_DUHe.js → JobDetailModal-C-X2GI9j.js} +1 -1
  16. package/client/dist/assets/JobDetailPage-C6Jdm7S9.js +1 -0
  17. package/client/dist/assets/{JobsPage-Bwoe-Fb0.js → JobsPage-DycM6Vgp.js} +1 -1
  18. package/client/dist/assets/{LoopBuilderPage-CqC4hbXb.js → LoopBuilderPage-D21GxFmL.js} +2 -2
  19. package/client/dist/assets/{LoopPreviewModal-T0zuFv6y.js → LoopPreviewModal-Bo3fYFxN.js} +1 -1
  20. package/client/dist/assets/{LoopsPage-DrQGcZv2.js → LoopsPage-BcrBw1cg.js} +1 -1
  21. package/client/dist/assets/{MinimizedChatsContext-C843Y13h.js → MinimizedChatsContext-Dsv2eHf_.js} +1 -1
  22. package/client/dist/assets/PluginsPage-BbyTmSLx.js +2 -0
  23. package/client/dist/assets/ProjectSettingsDialog-B3lEXLv7.js +1 -0
  24. package/client/dist/assets/{RepositoryDeliveries-bgIHE8ep.js → RepositoryDeliveries-ok_Wo65C.js} +1 -1
  25. package/client/dist/assets/{RepositoryScopeSelector-CZ_U_R3U.js → RepositoryScopeSelector-DU6QwNaS.js} +1 -1
  26. package/client/dist/assets/{ReviewPacketPage-DbKO6pT8.js → ReviewPacketPage-BBCeHtgd.js} +1 -1
  27. package/client/dist/assets/TemplatePreviewModal-CfNmxS6O.js +1 -0
  28. package/client/dist/assets/{TicketDetailModalContext-BbRA6cb7.js → TicketDetailModalContext-DW3eQ0I7.js} +1 -1
  29. package/client/dist/assets/{Trans-mjFZpHw1.js → Trans-BHPMWkOw.js} +1 -1
  30. package/client/dist/assets/agentRuntime-14faDjWM.js +1 -0
  31. package/client/dist/assets/agentRuntime-BIokojkJ.js +1 -0
  32. package/client/dist/assets/agentRuntime-Bu4xEcRw.js +1 -0
  33. package/client/dist/assets/agentRuntime-Cz0ASixB.js +1 -0
  34. package/client/dist/assets/agentRuntime-DESdGXLV.js +1 -0
  35. package/client/dist/assets/agentRuntime-DhX5Xy5S.js +1 -0
  36. package/client/dist/assets/agentRuntime-Dpbh_gLp.js +1 -0
  37. package/client/dist/assets/agentRuntime-jS2lplFm.js +1 -0
  38. package/client/dist/assets/{formatDistanceToNow-BIL-HxGy.js → formatDistanceToNow-Bq8iptrG.js} +1 -1
  39. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-Cs9z2sdO.js → getTimezoneOffsetInMilliseconds-BVFky3l_.js} +1 -1
  40. package/client/dist/assets/index-CBLYwPw6.css +2 -0
  41. package/client/dist/assets/index-DOKftX7y.js +76 -0
  42. package/client/dist/assets/{jira-api-B2BBZQTx.js → jira-api-zrRdZFBB.js} +1 -1
  43. package/client/dist/assets/{narration-C1Px0-0j.js → narration-BA8Yw5ZL.js} +1 -1
  44. package/client/dist/assets/{narration-DQ1l_Kq6.js → narration-BuMFUS7Q.js} +1 -1
  45. package/client/dist/assets/{narration-kwzfglQo.js → narration-Bv_iQQG5.js} +1 -1
  46. package/client/dist/assets/{narration-B-ynWTpn.js → narration-C6Ost9nL.js} +1 -1
  47. package/client/dist/assets/{narration-CAXhGbzM.js → narration-CIbjo2pA.js} +1 -1
  48. package/client/dist/assets/{narration-BxHpmMv_.js → narration-CNJ2-2-a.js} +1 -1
  49. package/client/dist/assets/{narration-Bj_laZwg.js → narration-OzsPJCXM.js} +1 -1
  50. package/client/dist/assets/{narration-XISXwqZd.js → narration-zCdgiuSU.js} +1 -1
  51. package/client/dist/assets/{project-repositories-xSqimlaj.js → project-repositories-CFRaB9UB.js} +1 -1
  52. package/client/dist/assets/{settings-BLOsrXEw.js → settings-CNiERE-R.js} +1 -1
  53. package/client/dist/assets/{settings-NSWQjY5B.js → settings-ChOCVjX_.js} +1 -1
  54. package/client/dist/assets/{settings-C6s9PtRD.js → settings-CrtCs5-B.js} +1 -1
  55. package/client/dist/assets/{settings-DPjNiUsS.js → settings-DJ2jl3hJ.js} +1 -1
  56. package/client/dist/assets/{settings-FIqwhTup.js → settings-DVRVybR_.js} +1 -1
  57. package/client/dist/assets/{settings-CCNZN5Q-.js → settings-DzXZCb1h.js} +1 -1
  58. package/client/dist/assets/{settings-2Yx_9yJM.js → settings-OOyDrZQo.js} +1 -1
  59. package/client/dist/assets/{settings-CpKwTew1.js → settings-a7NIODme.js} +1 -1
  60. package/client/dist/assets/{spending-DXOcv1tn.js → spending-lTZ-bm_y.js} +1 -1
  61. package/client/dist/assets/{useDesktop-gVGoWWiY.js → useDesktop-Duv5ddAa.js} +1 -1
  62. package/client/dist/assets/useSharedWebSocket-5mj2SMKn.js +2 -0
  63. package/client/dist/index.html +17 -16
  64. package/docs/README.md +1 -0
  65. package/docs/internals/README.md +2 -0
  66. package/docs/internals/agent-runtime-framework-evaluation.md +208 -0
  67. package/docs/internals/programmatic-agent-runtime-validation.md +72 -0
  68. package/docs/internals/programmatic-agent-runtime.md +129 -0
  69. package/docs/internals/verification-handoff-regression.md +70 -0
  70. package/package.json +1 -1
  71. package/server/dist/agent-runtime-accounting.js +73 -0
  72. package/server/dist/agent-runtime-bridge.js +183 -0
  73. package/server/dist/agent-runtime-controls-router.js +44 -0
  74. package/server/dist/agent-runtime-controls.js +277 -0
  75. package/server/dist/agent-runtime-loader.js +85 -0
  76. package/server/dist/agent-runtime-paths.js +12 -0
  77. package/server/dist/agent-runtime-settings-router.js +59 -0
  78. package/server/dist/agent-runtime-settings.js +135 -0
  79. package/server/dist/agent-runtime-verification-suggestions.js +119 -0
  80. package/server/dist/background-windows-bootstrap.js +6 -3
  81. package/server/dist/core-execution.js +57 -3
  82. package/server/dist/core-node-runtime.js +12 -0
  83. package/server/dist/framework-manager.js +23 -4
  84. package/server/dist/loop-executors.js +47 -1
  85. package/server/dist/loop-factory.js +1 -1
  86. package/server/dist/loop-run-manager.js +76 -27
  87. package/server/dist/project-registry.js +9 -0
  88. package/server/dist/project-router-settings.js +4 -0
  89. package/server/dist/rail-isolated-launch.js +18 -0
  90. package/server/dist/rails-router.js +1 -0
  91. package/server/dist/schemas/agent-runtime.schema.json +55 -0
  92. package/client/dist/assets/AgentBrowserCapture-BQem50z4.js +0 -1
  93. package/client/dist/assets/AgentModeAnalyticsPane-BC_xbT_l.js +0 -2
  94. package/client/dist/assets/AgentModeCodePane-DK-q3K6Y.js +0 -2
  95. package/client/dist/assets/AgentsPage-DbLezyI6.js +0 -87
  96. package/client/dist/assets/CodePage-DwgbeqQ_.js +0 -3
  97. package/client/dist/assets/InteractiveJobComposer-BcR6xlHU.js +0 -19
  98. package/client/dist/assets/JobDetailPage-vaOcE-cI.js +0 -1
  99. package/client/dist/assets/PluginsPage-CTUHX_Lu.js +0 -2
  100. package/client/dist/assets/ProjectSettingsDialog-DeL8md1s.js +0 -1
  101. package/client/dist/assets/TemplatePreviewModal-_CgCCV7L.js +0 -1
  102. package/client/dist/assets/index-Bfj8HvX1.css +0 -2
  103. package/client/dist/assets/index-UnaJTMIy.js +0 -76
  104. package/client/dist/assets/useSharedWebSocket-DbGL_YOt.js +0 -2
@@ -19,6 +19,7 @@ const ai_invocations_1 = require("./ai-invocations");
19
19
  const provider_limit_1 = require("./provider-limit");
20
20
  const ids_1 = require("./ids");
21
21
  const providers_1 = require("./providers");
22
+ const agent_runtime_accounting_1 = require("./agent-runtime-accounting");
22
23
  const runtime_1 = require("./providers/runtime");
23
24
  const result_event_1 = require("./result-event");
24
25
  const rails_store_1 = require("./rails-store");
@@ -137,6 +138,13 @@ function extractChangeId(text) {
137
138
  const m = CHANGE_ID_RE.exec(text);
138
139
  return m ? m[1] : undefined;
139
140
  }
141
+ function programmaticUsageAvailability(db, runId) {
142
+ const row = db.prepare(`SELECT COUNT(*) AS count,
143
+ MAX(total_cost_usd IS NULL) AS cost_unknown,
144
+ MAX(tokens_in IS NULL) AS input_unknown, MAX(tokens_out IS NULL) AS output_unknown
145
+ FROM ai_invocations WHERE surface = 'loop' AND loop_run_id = ? AND provider = 'agent-runtime'`).get(runId);
146
+ return { present: row.count > 0, costUnknown: Boolean(row.cost_unknown), tokensInUnknown: Boolean(row.input_unknown), tokensOutUnknown: Boolean(row.output_unknown) };
147
+ }
140
148
  function insertLoopInvocation(db, payload, result, finishedAt) {
141
149
  const ticketIds = Array.isArray(payload.ticketIds)
142
150
  ? [...new Set(payload.ticketIds)]
@@ -193,14 +201,15 @@ function insertLoopInvocation(db, payload, result, finishedAt) {
193
201
  FROM ai_invocations
194
202
  WHERE surface = 'loop' AND loop_run_id = ?
195
203
  `).get(payload.runId);
196
- const usageTelemetryAvailable = (0, providers_1.getAdapter)(payload.provider).capabilities.reportsUsage !== false;
204
+ const runtimeUsage = programmaticUsageAvailability(db, payload.runId);
205
+ const usageTelemetryAvailable = runtimeUsage.present || (0, providers_1.getAdapter)(payload.provider).capabilities.reportsUsage !== false;
197
206
  db.prepare(`
198
207
  UPDATE jobs
199
208
  SET total_cost_usd = ?, tokens_in = ?, tokens_out = ?,
200
209
  tokens_cache_read = ?, tokens_cache_create = ?,
201
210
  duration_ms = ?, num_turns = ?
202
211
  WHERE id = ? AND owner = 'loop'
203
- `).run(usageTelemetryAvailable ? aggregate.cost : null, usageTelemetryAvailable ? aggregate.tokens_in : null, usageTelemetryAvailable ? aggregate.tokens_out : null, usageTelemetryAvailable ? aggregate.cache_read : null, usageTelemetryAvailable ? aggregate.cache_create : null, aggregate.duration, usageTelemetryAvailable ? aggregate.turns : null, payload.runId);
212
+ `).run(usageTelemetryAvailable && !runtimeUsage.costUnknown ? aggregate.cost : null, usageTelemetryAvailable && !runtimeUsage.tokensInUnknown ? aggregate.tokens_in : null, usageTelemetryAvailable && !runtimeUsage.tokensOutUnknown ? aggregate.tokens_out : null, usageTelemetryAvailable && !runtimeUsage.present ? aggregate.cache_read : null, usageTelemetryAvailable && !runtimeUsage.present ? aggregate.cache_create : null, aggregate.duration, usageTelemetryAvailable && !runtimeUsage.present ? aggregate.turns : null, payload.runId);
204
213
  }
205
214
  /** Recover every AI/decider step that was staged before provider spawn but did
206
215
  * not atomically land its invocation. Completed interactive turns are the jobs
@@ -266,6 +275,23 @@ function recoverOrphanLoopStepAccounting(db, finishedAt = new Date().toISOString
266
275
  WHERE job_id = ? AND seq > ? AND source = 'stdout'
267
276
  ORDER BY seq
268
277
  `).all(payload.runId, payload.completedEventSeq ?? -1);
278
+ const programmatic = (0, agent_runtime_accounting_1.parseProgrammaticUsage)(rawRows.map(row => row.payload)) ?? (payload.provider === 'agent-runtime'
279
+ ? { provider: 'agent-runtime', model: 'per-role', estimated: true, failed: true }
280
+ : undefined);
281
+ if (programmatic) {
282
+ const durationMs = boundedInflightDurationMs(payload, finishedAt);
283
+ const didRecover = (0, loop_runs_store_1.completeLoopStepRecovery)(db, payload.runId, payload.stepKey, stable => {
284
+ insertLoopInvocation(db, stable, { ...programmatic, durationMs }, finishedAt);
285
+ const aggregate = (0, loop_runs_store_1.readLoopJobUsage)(db, payload.runId);
286
+ const duration = (payload.loopDurationBaseline ?? 0) + durationMs;
287
+ db.prepare(`UPDATE loop_runs SET total_cost_usd = ?, total_tokens = ?, total_duration_ms = ?, iteration_count = MAX(iteration_count, COALESCE(?, iteration_count)) WHERE id = ?`)
288
+ .run(aggregate.totalCostUsd, aggregate.tokensIn + aggregate.tokensOut, duration, stable.iterationCount ?? null, payload.runId);
289
+ db.prepare(`UPDATE jobs SET duration_ms = ? WHERE id = ? AND owner = 'loop'`).run(duration, payload.runId);
290
+ });
291
+ if (didRecover)
292
+ recovered += 1;
293
+ continue;
294
+ }
269
295
  const adapter = (0, providers_1.getAdapter)(payload.provider);
270
296
  const usageTelemetryAvailable = adapter.capabilities.reportsUsage !== false;
271
297
  const events = rawRows
@@ -560,7 +586,7 @@ class LoopRunManager {
560
586
  if (req.graph.nodes.some((node) => node.type === 'decider')) {
561
587
  (0, providers_1.requireToolPolicy)(adapter, 'read-only');
562
588
  }
563
- const usageTelemetryAvailable = adapter.capabilities.reportsUsage !== false;
589
+ let usageTelemetryAvailable = adapter.capabilities.reportsUsage !== false;
564
590
  const neverAfterDispose = () => new Promise(() => { });
565
591
  const runId = req.runId ?? (0, ids_1.newId)();
566
592
  const maxIterations = req.graph.config.maxIterations;
@@ -828,20 +854,20 @@ class LoopRunManager {
828
854
  // The REAL turn-start instant, captured before the step/decider await. The
829
855
  // row is bucketed/ordered by started_at, so it must be the start, not finish.
830
856
  startedAt, stepKey) => {
857
+ if (r.provider === 'agent-runtime')
858
+ usageTelemetryAvailable = true;
831
859
  if (usageTelemetryAvailable)
832
860
  totalCost += r.cost ?? 0;
833
- // A cost-bearing step that produced work (tokens) or hard-failed but reports
834
- // no priced cost → its real spend is missing from the total. The common case
835
- // is a claude step killed by timeout before its terminal `result` event:
836
- // tokens streamed, but cost (and tokens) are dropped, so the step bills $0.
837
- // Flag the run total as a lower bound and say so, per occurrence.
861
+ // Missing pricing does not identify the failure cause or prove zero spend:
862
+ // providers can omit billing after a successful call or reject a request
863
+ // before model work starts. Retain known costs and flag the incomplete sum.
838
864
  const costBearingButUnpriced = usageTelemetryAvailable
839
865
  && r.cost == null
840
866
  && (r.failed === true || (r.tokens ?? 0) > 0);
841
867
  if (costBearingButUnpriced) {
842
868
  costUncertain = true;
843
869
  costUnknownWarned = true; // this line already carries the cap caveat below
844
- logLine(`⚠️ Step cost unknown — the process ended before billing (timeout/crash), so it counts as $0. The loop total is a lower bound.${maxCostUsd !== undefined ? ' The cost cap may under-count.' : ''}`, 'stderr');
870
+ logLine(`⚠️ Step cost unknown — this step did not report a priced cost. The reported loop total includes known costs only.${maxCostUsd !== undefined ? ' The cost cap may under-count.' : ''}`, 'stderr');
845
871
  }
846
872
  // Honest cost-cap caveat: a cost-bearing step that reports no figure means
847
873
  // the cap can't be enforced precisely from here on. Warn once (live).
@@ -1058,6 +1084,8 @@ class LoopRunManager {
1058
1084
  // --yes`, codex `$implement #<id> --yes`) — then resolve `{{spec.*}}`
1059
1085
  // data tokens and finally `{{const:*}}` library constants.
1060
1086
  const rawTemplate = String(node.data?.prompt ?? '');
1087
+ const requiresCoreCompletion = node.data?.operation === 'core-implementation' || /\{\{\s*cmd:(?:implement|batch)\s*\}\}/.test(rawTemplate)
1088
+ || /^\s*(?:\/specrails:|\/skill:specrails-|\$)(?:implement|batch-implement)(?:\s|$)/.test(rawTemplate);
1061
1089
  const expanded = (0, loop_constants_1.resolveConstants)(resolveRunVars((0, loop_graph_1.interpolateSpec)((0, loop_command_catalog_1.expandCommands)(rawTemplate, { provider: nodeProvider, ticketIds: req.spec?.ticketIds, specId: req.spec?.id }), req.spec), runVars), constMap);
1062
1090
  const base = [(0, multi_repo_execution_store_1.executionManifestPrompt)(req.executionManifest), withReviewContinuationContext(expanded, rawTemplate, req.spec)].filter(Boolean).join('\n\n');
1063
1091
  // Inject the cross-iteration history only when there's no live session
@@ -1094,7 +1122,7 @@ class LoopRunManager {
1094
1122
  const effectiveTimeoutMs = Number.isFinite(remainingMs)
1095
1123
  ? Math.max(1, Math.min(aiStepTimeoutMs === 0 ? Infinity : (aiStepTimeoutMs ?? 15 * 60_000), remainingMs))
1096
1124
  : aiStepTimeoutMs;
1097
- const coreRun = { runId, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId, verificationStep: node.data?.requireVerificationPass === true || /\{\{\s*cmd:(?:verify|revision-verify|opsx:verify)\s*\}\}/.test(rawTemplate) };
1125
+ const coreRun = { runId, implementation: requiresCoreCompletion, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId, verificationStep: node.data?.requireVerificationPass === true || /\{\{\s*cmd:(?:verify|revision-verify|opsx:verify)\s*\}\}/.test(rawTemplate) };
1098
1126
  const interactivePlan = this.executors.planInteractiveAiStep?.({
1099
1127
  coreRun,
1100
1128
  provider: nodeProvider,
@@ -1220,6 +1248,19 @@ class LoopRunManager {
1220
1248
  settled = true;
1221
1249
  break;
1222
1250
  }
1251
+ if (!res.failed && !zeroWork && !this._cancelled.has(runId)
1252
+ && requiresCoreCompletion
1253
+ && this.executors.validateCoreCompletion) {
1254
+ const completion = this.executors.validateCoreCompletion({
1255
+ coreRun: { runId, spec: req.spec, goal: req.spec ? undefined : JSON.stringify({ loop: req.loopName, graph: req.graph }), repositoryId: req.repositoryId },
1256
+ provider: nodeProvider, model: nodeModel, effort: nodeEffort, profileName: req.profileName,
1257
+ cwd: req.cwd, repoDir: req.repoDir, executionManifest: req.executionManifest,
1258
+ });
1259
+ if (!completion.valid) {
1260
+ res = { ...res, failed: true, errorText: completion.reason ?? 'Core implementation is incomplete' };
1261
+ logLine(`✖ ${res.errorText}`, 'stderr');
1262
+ }
1263
+ }
1223
1264
  const blockedReason = aiStepBlockedReason(res.text);
1224
1265
  const requiresVerification = node.data?.requireVerificationPass === true
1225
1266
  || /\{\{cmd:(?:verify|revision-verify|opsx:verify)\}\}/.test(rawTemplate);
@@ -1233,9 +1274,7 @@ class LoopRunManager {
1233
1274
  ? `blocked — ${blockedReason}`
1234
1275
  : verificationFailed
1235
1276
  ? 'verification did not finish with VERIFICATION: PASS'
1236
- : undefined);
1237
- if (verificationFailed && !blockedReason)
1238
- logLine(`✖ ${stepErrorText}`, 'stderr');
1277
+ : res.failed ? 'provider invocation failed without a reported reason' : undefined);
1239
1278
  // Make the failure reason land visibly INSIDE the step's log
1240
1279
  // segment (the interactive session already surfaced its own note at
1241
1280
  // settle; the Template/Command flat-log lines are gone, so without
@@ -1243,6 +1282,14 @@ class LoopRunManager {
1243
1282
  if (oneShotZeroWork) {
1244
1283
  logLine(`✖ Zero work performed — the command never ran${res.text.trim() ? `: ${res.text.trim()}` : ''}`, 'stderr');
1245
1284
  }
1285
+ else if (stepFailed && stepErrorText) {
1286
+ // Executors can return a structured failure without streaming any
1287
+ // text (configuration rejection, missing CLI, provider startup).
1288
+ // Persist its reason before the step closes, including early stops.
1289
+ logLine(`✖ ${stepErrorText}`, 'stderr');
1290
+ }
1291
+ record(`loop:${runId}`, { ...res, failed: stepFailed }, aiStepStart, aiRecoveryKey);
1292
+ this._activeStepRecovery.delete(runId);
1246
1293
  // Step tail marker — after the step's last streamed output (both the
1247
1294
  // one-shot and interactive paths have fully persisted their frames by
1248
1295
  // here: the session resolves inside onSettle, past its final writes).
@@ -1266,8 +1313,6 @@ class LoopRunManager {
1266
1313
  else if (stepFailed)
1267
1314
  aiSessionId = undefined;
1268
1315
  history.push(`AI Step${stepFailed ? ` FAILED (${stepErrorText ?? 'provider invocation failed'})` : ''}: ${truncate(res.text)}`);
1269
- record(`loop:${runId}`, { ...res, failed: stepFailed }, aiStepStart, aiRecoveryKey);
1270
- this._activeStepRecovery.delete(runId);
1271
1316
  if (blockedReason) {
1272
1317
  const decision = await awaitHumanDecision(blockedReason);
1273
1318
  if (decision.action === 'stop') {
@@ -1564,6 +1609,7 @@ class LoopRunManager {
1564
1609
  // The ONLY way a persisted stream ends with an open step is a mid-flight
1565
1610
  // dispose (shutdown/project removal), where this code never runs.
1566
1611
  emitStepEnd({ status: 'failed' });
1612
+ let runtimeUsage = { present: false, costUnknown: false, tokensInUnknown: false, tokensOutUnknown: false };
1567
1613
  // A traversal exception can bypass record(). Reconcile the staged step
1568
1614
  // BEFORE writing aggregate job totals, while its jobs baseline is still
1569
1615
  // valid. If reconciliation itself fails, finishLoopRunAndJob detects the
@@ -1571,6 +1617,8 @@ class LoopRunManager {
1571
1617
  try {
1572
1618
  const recoveredSteps = recoverOrphanLoopStepAccounting(this.db, new Date(this.now()).toISOString(), runId);
1573
1619
  this._activeStepRecovery.delete(runId);
1620
+ runtimeUsage = programmaticUsageAvailability(this.db, runId);
1621
+ usageTelemetryAvailable ||= runtimeUsage.present;
1574
1622
  const aggregate = this.db.prepare(`
1575
1623
  SELECT COALESCE(SUM(total_cost_usd), 0) AS cost,
1576
1624
  COALESCE(SUM(tokens_in), 0) AS tokens_in,
@@ -1602,19 +1650,17 @@ class LoopRunManager {
1602
1650
  }
1603
1651
  if (usageTelemetryAvailable) {
1604
1652
  finalJobUsage = {
1605
- tokensIn: aggregate.tokens_in,
1606
- tokensOut: aggregate.tokens_out,
1607
- tokensCacheRead: aggregate.cache_read,
1608
- tokensCacheCreate: aggregate.cache_create,
1609
- numTurns: aggregate.turns,
1653
+ tokensIn: runtimeUsage.tokensInUnknown ? null : aggregate.tokens_in,
1654
+ tokensOut: runtimeUsage.tokensOutUnknown ? null : aggregate.tokens_out,
1655
+ tokensCacheRead: runtimeUsage.present ? null : aggregate.cache_read,
1656
+ tokensCacheCreate: runtimeUsage.present ? null : aggregate.cache_create,
1657
+ numTurns: runtimeUsage.present ? null : aggregate.turns,
1610
1658
  };
1611
1659
  }
1612
1660
  }
1613
1661
  catch (err) {
1614
1662
  console.error(`[loop] staged accounting reconciliation failed for ${runId}:`, err);
1615
1663
  }
1616
- console.log(`[loop] settle run=${runId} outcome=${outcome} iterations=${iteration} ` +
1617
- (usageTelemetryAvailable ? `cost=$${totalCost.toFixed(4)}` : 'usage=unavailable'));
1618
1664
  // Execution and acceptance are separate facts. Persist the runtime's own
1619
1665
  // terminal snapshot alongside counters so history does not depend on prose.
1620
1666
  let coreCompletion = null;
@@ -1625,6 +1671,9 @@ class LoopRunManager {
1625
1671
  const executionOutcome = outcome;
1626
1672
  if (outcome === 'success' && coreCompletion && (coreCompletion.completion.implementation !== 'complete' || !['verified', 'with-exceptions'].includes(coreCompletion.completion.validation)))
1627
1673
  outcome = 'blocked';
1674
+ const costAvailable = usageTelemetryAvailable && !runtimeUsage.costUnknown;
1675
+ console.log(`[loop] settle run=${runId} outcome=${outcome} iterations=${iteration} ` +
1676
+ (costAvailable ? `cost=$${totalCost.toFixed(4)}` : 'cost=unavailable'));
1628
1677
  emitRunEvent('loop_completion', {
1629
1678
  version: 1, execution: executionOutcome, steps: stepNum, deciderEvaluations: iteration,
1630
1679
  turns: finalJobUsage.numTurns, costUsd: usageTelemetryAvailable ? totalCost : null,
@@ -1642,8 +1691,8 @@ class LoopRunManager {
1642
1691
  // `≥` when any cost-bearing step ended unpriced (timeout/crash) — the figure
1643
1692
  // is a lower bound, not exact. Providers without usage telemetry get an
1644
1693
  // explicit unavailable marker, never a fabricated "$0.0000".
1645
- logLine(`\n■ Loop execution finished: ${executionOutcome} — ${stepNum} step${stepNum === 1 ? '' : 's'}, ${iteration} decider evaluation${iteration === 1 ? '' : 's'}, ${finalJobUsage.numTurns ?? 'unknown'} agent turns, ` +
1646
- (usageTelemetryAvailable
1694
+ logLine(`\n■ Loop execution finished: ${outcome} — ${stepNum} step${stepNum === 1 ? '' : 's'}, ${iteration} decider evaluation${iteration === 1 ? '' : 's'}, ${finalJobUsage.numTurns ?? 'unknown'} agent turns, ` +
1695
+ (costAvailable
1647
1696
  ? `${costUncertain ? '≥ ' : ''}$${totalCost.toFixed(4)}`
1648
1697
  : 'usage/cost unavailable'));
1649
1698
  if (coreCompletion)
@@ -1663,7 +1712,7 @@ class LoopRunManager {
1663
1712
  job: {
1664
1713
  exitCode: outcome === 'success' ? 0 : 1,
1665
1714
  status: jobStatus,
1666
- totalCostUsd: usageTelemetryAvailable ? totalCost : null,
1715
+ totalCostUsd: costAvailable ? totalCost : null,
1667
1716
  tokensIn: finalJobUsage.tokensIn,
1668
1717
  tokensOut: finalJobUsage.tokensOut,
1669
1718
  tokensCacheRead: finalJobUsage.tokensCacheRead,
@@ -1684,7 +1733,7 @@ class LoopRunManager {
1684
1733
  tokens_out: finalJobUsage.tokensOut,
1685
1734
  tokens_cache_read: finalJobUsage.tokensCacheRead,
1686
1735
  tokens_cache_create: finalJobUsage.tokensCacheCreate,
1687
- total_cost_usd: usageTelemetryAvailable ? totalCost : null,
1736
+ total_cost_usd: costAvailable ? totalCost : null,
1688
1737
  num_turns: finalJobUsage.numTurns,
1689
1738
  },
1690
1739
  timestamp: new Date(this.now()).toISOString(),
@@ -1701,7 +1750,7 @@ class LoopRunManager {
1701
1750
  runId,
1702
1751
  outcome,
1703
1752
  iterations: iteration,
1704
- totalCostUsd: usageTelemetryAvailable ? totalCost : null,
1753
+ totalCostUsd: costAvailable ? totalCost : null,
1705
1754
  ...(outcome === 'stalled' && stallReason ? { stallReason } : {}),
1706
1755
  ...(providerLimitHit ? { providerLimit: providerLimitHit } : {}),
1707
1756
  };
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ProjectRegistry = void 0;
7
7
  exports.emitRecoveredPrDelivery = emitRecoveredPrDelivery;
8
8
  exports.reprojectActivePrDeliveries = reprojectActivePrDeliveries;
9
+ const agent_runtime_controls_router_1 = require("./agent-runtime-controls-router");
9
10
  const path_1 = __importDefault(require("path"));
10
11
  const fs_1 = __importDefault(require("fs"));
11
12
  const os_1 = __importDefault(require("os"));
@@ -305,6 +306,10 @@ class ProjectRegistry {
305
306
  // Loop engine teardown: dispose any resident interactive step sessions
306
307
  // (SIGTERM, no settle) + kill in-flight one-shot loop children — BEFORE
307
308
  // db.close() so a late close handler can't write to the closed handle.
309
+ try {
310
+ (0, agent_runtime_controls_router_1.shutdownAgentRuntimeControls)(ctx);
311
+ }
312
+ catch { /* ignore */ }
308
313
  try {
309
314
  ctx.loopRunManager.shutdown();
310
315
  }
@@ -540,6 +545,10 @@ class ProjectRegistry {
540
545
  catch { /* ignore */ }
541
546
  // Loop engine: dispose resident interactive step sessions + kill in-flight
542
547
  // one-shot loop children so a quit mid-run doesn't orphan claude processes.
548
+ try {
549
+ (0, agent_runtime_controls_router_1.shutdownAgentRuntimeControls)(ctx);
550
+ }
551
+ catch { /* ignore */ }
543
552
  try {
544
553
  ctx.loopRunManager.shutdown();
545
554
  }
@@ -19,7 +19,11 @@ const terminal_settings_1 = require("./terminal-settings");
19
19
  const terminal_marks_store_1 = require("./terminal-marks-store");
20
20
  const project_router_helpers_1 = require("./project-router-helpers");
21
21
  const install_config_path_1 = require("./install-config-path");
22
+ const agent_runtime_settings_router_1 = require("./agent-runtime-settings-router");
23
+ const agent_runtime_controls_router_1 = require("./agent-runtime-controls-router");
22
24
  function registerSettingsRoutes(deps) {
25
+ (0, agent_runtime_settings_router_1.registerAgentRuntimeSettingsRoutes)(deps);
26
+ (0, agent_runtime_controls_router_1.registerAgentRuntimeControlRoutes)(deps);
23
27
  const { router, registry, ctx, ticketPath } = deps;
24
28
  // ─── Project settings (pipeline telemetry) ───────────────────────────────────
25
29
  router.get('/:projectId/settings', (req, res) => {
@@ -67,6 +67,7 @@ const artifact_registry_1 = require("./artifact-registry");
67
67
  const ids_1 = require("./ids");
68
68
  const loop_constants_1 = require("./loop-constants");
69
69
  const worktree_manager_1 = require("./worktree-manager");
70
+ const agent_runtime_paths_1 = require("./agent-runtime-paths");
70
71
  const rail_worktrees_store_1 = require("./rail-worktrees-store");
71
72
  const pr_naming_1 = require("./pr-naming");
72
73
  const jira_db_1 = require("./jira/jira-db");
@@ -1015,6 +1016,19 @@ async function launchIsolatedRail(input, io = {}) {
1015
1016
  callbackFailure = errorDetail(err);
1016
1017
  console.error(`[rail-isolated] terminal callback failed for ${a.runId}: ${callbackFailure}`);
1017
1018
  }
1019
+ // A paused/interrupted programmatic workflow owns unfinished changes and
1020
+ // verification evidence in this exact mount. Keep them available for
1021
+ // explicit Core recovery, including a currently clean worktree.
1022
+ if (implementationOutcome === 'failed' && (0, agent_runtime_paths_1.hasAgentRuntimeRequest)(ctx.project, a.runId)) {
1023
+ markWorktree(a, 'needs-review');
1024
+ return {
1025
+ run: a, implementationOutcome, deliveryOutcome: 'blocked',
1026
+ initialSha: a.initialSha, finalSha: await readHeadSha(git, a.handle.worktreePath),
1027
+ failureCode: 'settlement_interrupted',
1028
+ failureDetail: 'Programmatic workflow needs attention. Resume it from Agent Runtime settings in its original worktree.',
1029
+ safeToRelease: false,
1030
+ };
1031
+ }
1018
1032
  // The engine may have edited a copied overlay file. Re-authenticate only
1019
1033
  // automatic-cleanup authority. Allocation-time paths remain conservative
1020
1034
  // NEVER-COMMIT exclusions, while a modified copy is preserved in the
@@ -1828,6 +1842,10 @@ async function reconcileRailWorktrees(db, repoDir, io = {}) {
1828
1842
  const run = row.run_id
1829
1843
  ? db.prepare('SELECT status, final_outcome FROM loop_runs WHERE id = ?').get(row.run_id)
1830
1844
  : undefined;
1845
+ if (row.run_id && run?.final_outcome !== 'success' && (0, agent_runtime_paths_1.hasAgentRuntimeRequest)({ path: io.primaryRepoDir ?? repoDir }, row.run_id)) {
1846
+ (0, rail_worktrees_store_1.updateRailWorktreeState)(db, row.id, 'needs-review');
1847
+ continue;
1848
+ }
1831
1849
  const inspection = await inspectRecoveryWorktree(git, repoDir, row);
1832
1850
  if (run?.status === 'completed' && run.final_outcome === 'success') {
1833
1851
  if (inspection.safe && inspection.sha) {
@@ -1020,6 +1020,7 @@ function createRailsRouter() {
1020
1020
  loopName,
1021
1021
  graph: loopGraph,
1022
1022
  projectId: c.project.id,
1023
+ repositoryId: primaryRepository.id,
1023
1024
  cwd: loopExec.cwd,
1024
1025
  repoDir: loopExec.relocated ? loopExec.repoDir : undefined,
1025
1026
  railIndex,
@@ -0,0 +1,55 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://specrails.dev/schemas/agent-runtime.schema.json",
4
+ "title": "Specrails programmatic agent runtime",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schemaVersion", "enabled", "providers", "agents", "verification"],
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "enabled": { "type": "boolean" },
11
+ "providers": { "type": "array", "items": { "oneOf": [
12
+ { "type": "object", "additionalProperties": false, "required": ["id", "kind", "cli"], "properties": {
13
+ "id": { "$ref": "#/definitions/id" }, "kind": { "const": "cli" }, "cli": { "enum": ["claude", "codex", "gemini", "kimi"] }
14
+ } },
15
+ { "type": "object", "additionalProperties": false, "required": ["id", "kind", "baseUrl"], "properties": {
16
+ "id": { "$ref": "#/definitions/id" }, "kind": { "const": "openai-compatible" },
17
+ "baseUrl": { "type": "string", "pattern": "^https?://[^/?#@]+(?:/[^?#]*)?$" },
18
+ "apiKeyEnv": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }
19
+ } }
20
+ ] } },
21
+ "agents": { "type": "object", "additionalProperties": false, "required": ["architect", "developer", "reviewer"], "properties": {
22
+ "architect": { "$ref": "#/definitions/agent" }, "developer": { "$ref": "#/definitions/agent" }, "reviewer": { "$ref": "#/definitions/agent" }
23
+ } },
24
+ "limits": { "type": "object", "additionalProperties": false, "properties": {
25
+ "maxAttempts": { "$ref": "#/definitions/positiveInteger" }, "maxTokens": { "$ref": "#/definitions/positiveInteger" },
26
+ "maxCostUsd": { "type": "number", "exclusiveMinimum": 0 }, "timeoutMs": { "$ref": "#/definitions/positiveInteger" }
27
+ } },
28
+ "verification": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["repositoryId", "command", "args"], "properties": {
29
+ "repositoryId": { "$ref": "#/definitions/id" }, "command": { "type": "string", "minLength": 1 },
30
+ "args": { "type": "array", "items": { "type": "string" } }, "cwd": { "type": "string", "minLength": 1 },
31
+ "env": { "type": "object", "propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, "additionalProperties": { "type": "string" } },
32
+ "timeoutMs": { "$ref": "#/definitions/positiveInteger" }
33
+ } } },
34
+ "approvalBeforeArchive": { "type": "boolean" },
35
+ "review": { "type": "object", "additionalProperties": false, "description": "Review gate thresholds; unset fields keep Core's defaults (score 70, security 75, other aspects 60).", "properties": {
36
+ "minScore": { "$ref": "#/definitions/score" },
37
+ "aspects": { "type": "object", "additionalProperties": false, "properties": {
38
+ "type_correctness": { "$ref": "#/definitions/score" }, "pattern_adherence": { "$ref": "#/definitions/score" }, "test_coverage": { "$ref": "#/definitions/score" },
39
+ "security": { "$ref": "#/definitions/score" }, "architectural_alignment": { "$ref": "#/definitions/score" }
40
+ } }
41
+ } },
42
+ "architect": { "type": "object", "additionalProperties": false, "properties": {
43
+ "onLowConfidence": { "enum": ["ask", "proceed"], "description": "After one autonomous investigation pass, a still-low design either pauses with its question (ask, default) or proceeds on stated assumptions (proceed)." }
44
+ } }
45
+ },
46
+ "definitions": {
47
+ "id": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$" },
48
+ "positiveInteger": { "type": "integer", "minimum": 1 },
49
+ "score": { "type": "number", "minimum": 0, "maximum": 100 },
50
+ "agent": { "type": "object", "additionalProperties": false, "required": ["provider"], "properties": {
51
+ "provider": { "$ref": "#/definitions/id" }, "model": { "type": "string", "minLength": 1, "maxLength": 256, "pattern": "^[^-\\r\\n\\u0000][^\\r\\n\\u0000]*$" },
52
+ "maxTurns": { "$ref": "#/definitions/positiveInteger" }
53
+ } }
54
+ }
55
+ }
@@ -1 +0,0 @@
1
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{d as r,f as i,m as a,u as o}from"./TicketDetailModalContext-BbRA6cb7.js";import{n as s}from"./createLucideIcon-Bjbbp_KS.js";import{s as c}from"./useDesktop-gVGoWWiY.js";import{$ as l,Bt as u,Ht as d,P as f,W as p,X as m,v as h}from"./index-UnaJTMIy.js";var g=e(t(),1),_=n();function v({projectId:e,conversationId:t}){let{t:n}=s(`agent`),{error:v}=d(),y=l(t??`__new-mission__`),{closeBrowser:b,queueCapture:x,browserOwnerId:S,browserUrl:C,setBrowserUrl:w}=p(),{materializeDraftConversation:T}=f(),[E,D]=(0,g.useState)(!1),[O,k]=(0,g.useState)(!1),[A,j]=(0,g.useState)(`probing`);(0,g.useEffect)(()=>{if(t&&(E||O||A===`screencast`))return m(t)},[t,E,O,A]);let M=(0,g.useMemo)(()=>{if(C)return C;try{return i(localStorage.getItem(`specrails-desktop:agent-browser-url:${e}`)??``)??`about:blank`}catch{return`about:blank`}},[e]);(0,g.useEffect)(()=>{let e=!0;return r().then(t=>{e&&j(t?`native`:`screencast`)}),()=>{e=!1}},[]);let N=(0,g.useMemo)(()=>`agent-${t??`home`}-${Math.round(performance.now())}`,[t]),P=async e=>{k(!0);try{let r=await u(t??(await T()).id,a(e.screenshotDataUrl,`capture-${Date.now()}.png`));x(r),c.success(n(`workspace.browserCaptured`)),b()}catch(e){throw c.error(n(`workspace.uploadFailed`),{description:e instanceof Error?e.message:void 0}),e}finally{k(!1)}};return A===`probing`?null:A===`native`?(0,_.jsx)(o,{ownerId:S??void 0,leaseRevision:y,onBusyChange:D,transferError:v,url:M,onClose:b,onFallback:()=>j(`screencast`),onUrlChange:t=>{if(w(t),i(t)&&t!==`about:blank`)try{localStorage.setItem(`specrails-desktop:agent-browser-url:${e}`,t)}catch{}},confirmLabel:n(`workspace.browserConfirm`),selectLabel:n(`workspace.browserSelectStart`),onCaptured:P}):(0,_.jsx)(h,{open:!0,onClose:b,projectId:e,pendingSpecId:N,confirmLabel:n(`workspace.browserConfirm`),selectLabel:n(`workspace.browserSelectStart`),onCaptured:P})}export{v as AgentBrowserCapture};
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/AnalyticsPage-DTsg-N5z.js","assets/rolldown-runtime-CNC7AqOf.js","assets/jsx-runtime-zu2_FqZY.js","assets/chunk-KS7C4IRE-DEKu087k.js","assets/preload-helper-Czpn1I53.js","assets/useSharedWebSocket-DbGL_YOt.js","assets/activity-DiyjsgQV.js","assets/addspec-D2YIQ0GU.js","assets/agent-Bh9K7iEt.js","assets/agents-ms7TwG16.js","assets/agentstudio-CN2pokQu.js","assets/aiedit-iIjz2_Qk.js","assets/analytics-BMzCvrCU.js","assets/attachments-B53PG733.js","assets/browser-CG0PE1j_.js","assets/builder-BTiGnPT0.js","assets/chat-BGD7MXAy.js","assets/code-DaZ-ZwCT.js","assets/commands-BAWSYHjQ.js","assets/common-BrlwEnL7.js","assets/dashboard-BfBir_6o.js","assets/explore-DgBACn39.js","assets/integrations-mB-7zoyT.js","assets/jira-CKJg3HZd.js","assets/jobs-DMPU64ms.js","assets/loops-B7YpGizO.js","assets/mcp-CUAN6ORK.js","assets/narration-B-ynWTpn.js","assets/nav-Ch-zHBG5.js","assets/packet-Cx-zRuWn.js","assets/settings-CCNZN5Q-.js","assets/setup-Ba11l2JA.js","assets/specs-DII0C8gw.js","assets/terminal-BAzpHtpY.js","assets/tickets-W6pBmbrX.js","assets/createLucideIcon-Bjbbp_KS.js","assets/Trans-mjFZpHw1.js","assets/useDesktop-gVGoWWiY.js","assets/react-dom-DiWzBDP9.js","assets/spending-DXOcv1tn.js","assets/clsx-CjueKrWZ.js","assets/formatDistanceToNow-BIL-HxGy.js","assets/getTimezoneOffsetInMilliseconds-Cs9z2sdO.js","assets/provider-capabilities-B-wOHfLP.js","assets/index-UnaJTMIy.js","assets/TicketDetailModalContext-BbRA6cb7.js","assets/save-B6wGWwBR.js","assets/MinimizedChatsContext-C843Y13h.js","assets/sparkles-DrY8hb0X.js","assets/x-CBMI9ejn.js","assets/file-text-BcLCfcWZ.js","assets/circle-x-CMYsN4ZH.js","assets/download-CFHyyHcw.js","assets/git-branch-BRM0F5GN.js","assets/loader-circle-DcygOQpC.js","assets/pencil-C9RkrIPZ.js","assets/square-Ci-PFcFe.js","assets/plus-CsnjfkmX.js","assets/search-Cq_gE3Ik.js","assets/trash-2-BOU3WdZZ.js","assets/triangle-alert-H9T4jAmh.js","assets/jira-api-B2BBZQTx.js","assets/zap-CHo3aCD1.js","assets/dialog-xXB2q5HJ.js","assets/utils-BC4DHlz2.js","assets/lib-CGdyxIWg.js","assets/button-Ccc9Dc-D.js","assets/RepositoryScopeSelector-CZ_U_R3U.js","assets/project-repositories-xSqimlaj.js","assets/core-B3mKBIh5.js","assets/RepositoryDeliveries-bgIHE8ep.js","assets/event-Bt7f3wXe.js","assets/index-Bfj8HvX1.css","assets/ExportDropdown-DczgU4at.js","assets/BarChart-nBOMNfiy.js","assets/value-s-gzBeyF.js"])))=>i.map(i=>d[i]);
2
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{t as r}from"./preload-helper-Czpn1I53.js";import{n as i}from"./createLucideIcon-Bjbbp_KS.js";import{t as a}from"./loader-circle-DcygOQpC.js";import{t as o}from"./x-CBMI9ejn.js";import{t as s}from"./utils-BC4DHlz2.js";import{W as c,_t as l,gt as u}from"./index-UnaJTMIy.js";var d=e(t(),1),f=n(),p=(0,d.lazy)(()=>r(()=>import(`./AnalyticsPage-DTsg-N5z.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75]))),m=420,h=560;function g(){let{t:e}=i(`agent`),{closeAnalyticsPane:t}=c(),[n,r]=(0,d.useState)(h),[g,_]=(0,d.useState)(!1),v=(0,d.useCallback)(e=>{let t=e.clientX,i=n,a=window.innerWidth-m;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}let o=e=>{let n=Math.min(Math.max(i+(t-e.clientX),m),a);r(n)},s=()=>{window.removeEventListener(`pointermove`,o),window.removeEventListener(`pointerup`,s),window.removeEventListener(`pointercancel`,s)};window.addEventListener(`pointermove`,o),window.addEventListener(`pointerup`,s),window.addEventListener(`pointercancel`,s)},[n]);return(0,f.jsxs)(`div`,{className:s(`flex h-full flex-col overflow-hidden border-l border-border bg-background`,g&&`absolute inset-0 z-30 border-l-0`),style:g?void 0:{width:n,flexShrink:0},"data-testid":`agent-analytics-pane`,children:[(0,f.jsx)(`div`,{className:`relative flex items-center`,children:!g&&(0,f.jsx)(`div`,{role:`separator`,"aria-orientation":`vertical`,onPointerDown:v,className:`absolute left-0 top-0 h-full w-1.5 -translate-x-full cursor-col-resize select-none touch-none hover:bg-accent-primary/20`,title:e(`workspace.resizeAnalytics`)})}),(0,f.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-border/60 bg-surface/40 px-3 py-1.5`,children:[(0,f.jsx)(`span`,{className:`text-xs font-medium text-foreground/70`,children:e(`workspace.analytics`)}),(0,f.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[(0,f.jsx)(`button`,{type:`button`,onClick:()=>_(e=>!e),"aria-label":e(g?`restore`:`maximize`),title:e(g?`restore`:`maximize`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:g?(0,f.jsx)(u,{className:`h-4 w-4`}):(0,f.jsx)(l,{className:`h-4 w-4`})}),(0,f.jsx)(`button`,{type:`button`,onClick:t,"aria-label":e(`workspace.closeAnalytics`),title:e(`workspace.closeAnalytics`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:(0,f.jsx)(o,{className:`h-4 w-4`})})]})]}),(0,f.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto`,children:(0,f.jsx)(d.Suspense,{fallback:(0,f.jsx)(`div`,{className:`flex h-24 items-center justify-center text-muted-foreground`,children:(0,f.jsx)(a,{className:`h-4 w-4 animate-spin`})}),children:(0,f.jsx)(p,{})})})]})}export{g as AgentModeAnalyticsPane};
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/CodePage-DwgbeqQ_.js","assets/rolldown-runtime-CNC7AqOf.js","assets/jsx-runtime-zu2_FqZY.js","assets/react-dom-DiWzBDP9.js","assets/preload-helper-Czpn1I53.js","assets/chunk-KS7C4IRE-DEKu087k.js","assets/TicketDetailModalContext-BbRA6cb7.js","assets/useSharedWebSocket-DbGL_YOt.js","assets/activity-DiyjsgQV.js","assets/addspec-D2YIQ0GU.js","assets/agent-Bh9K7iEt.js","assets/agents-ms7TwG16.js","assets/agentstudio-CN2pokQu.js","assets/aiedit-iIjz2_Qk.js","assets/analytics-BMzCvrCU.js","assets/attachments-B53PG733.js","assets/browser-CG0PE1j_.js","assets/builder-BTiGnPT0.js","assets/chat-BGD7MXAy.js","assets/code-DaZ-ZwCT.js","assets/commands-BAWSYHjQ.js","assets/common-BrlwEnL7.js","assets/dashboard-BfBir_6o.js","assets/explore-DgBACn39.js","assets/integrations-mB-7zoyT.js","assets/jira-CKJg3HZd.js","assets/jobs-DMPU64ms.js","assets/loops-B7YpGizO.js","assets/mcp-CUAN6ORK.js","assets/narration-B-ynWTpn.js","assets/nav-Ch-zHBG5.js","assets/packet-Cx-zRuWn.js","assets/settings-CCNZN5Q-.js","assets/setup-Ba11l2JA.js","assets/specs-DII0C8gw.js","assets/terminal-BAzpHtpY.js","assets/tickets-W6pBmbrX.js","assets/createLucideIcon-Bjbbp_KS.js","assets/Trans-mjFZpHw1.js","assets/useDesktop-gVGoWWiY.js","assets/save-B6wGWwBR.js","assets/spending-DXOcv1tn.js","assets/MinimizedChatsContext-C843Y13h.js","assets/sparkles-DrY8hb0X.js","assets/x-CBMI9ejn.js","assets/file-text-BcLCfcWZ.js","assets/circle-x-CMYsN4ZH.js","assets/download-CFHyyHcw.js","assets/git-branch-BRM0F5GN.js","assets/loader-circle-DcygOQpC.js","assets/pencil-C9RkrIPZ.js","assets/square-Ci-PFcFe.js","assets/plus-CsnjfkmX.js","assets/search-Cq_gE3Ik.js","assets/trash-2-BOU3WdZZ.js","assets/triangle-alert-H9T4jAmh.js","assets/jira-api-B2BBZQTx.js","assets/zap-CHo3aCD1.js","assets/dialog-xXB2q5HJ.js","assets/utils-BC4DHlz2.js","assets/clsx-CjueKrWZ.js","assets/lib-CGdyxIWg.js","assets/formatDistanceToNow-BIL-HxGy.js","assets/getTimezoneOffsetInMilliseconds-Cs9z2sdO.js","assets/button-Ccc9Dc-D.js","assets/RepositoryScopeSelector-CZ_U_R3U.js","assets/project-repositories-xSqimlaj.js","assets/provider-capabilities-B-wOHfLP.js","assets/index-UnaJTMIy.js","assets/core-B3mKBIh5.js","assets/RepositoryDeliveries-bgIHE8ep.js","assets/event-Bt7f3wXe.js","assets/index-Bfj8HvX1.css"])))=>i.map(i=>d[i]);
2
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{t as r}from"./preload-helper-Czpn1I53.js";import{n as i}from"./createLucideIcon-Bjbbp_KS.js";import{t as a}from"./x-CBMI9ejn.js";import{Q as o,W as s,Z as c,_t as l,gt as u}from"./index-UnaJTMIy.js";var d=e(t(),1),f=n(),p=(0,d.lazy)(()=>r(()=>import(`./CodePage-DwgbeqQ_.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72]))),m=420,h=560;function g({projectId:e,conversationId:t}){let{t:n}=i(`agent`),{closeCodePane:r}=s(),[g,_]=(0,d.useState)(h),[v,y]=(0,d.useState)(()=>window.innerWidth),[b,x]=(0,d.useState)(!1),S=(0,d.useRef)(null),C=(0,d.useRef)(null),w=Math.max(0,v-Math.min(m,v/2)),T=Math.min(m,w),E=Math.min(w,Math.max(T,g)),D=(0,d.useCallback)(e=>Math.min(w,Math.max(T,e)),[w,T]),O=`${e}:${t}`,k=(0,d.useCallback)(n=>{o(e,t,{path:n})},[e,t]);(0,d.useLayoutEffect)(()=>{let e=S.current,t=e?.parentElement;if(!e||!t)return;let n=()=>{let n=t.getBoundingClientRect().width||t.clientWidth||window.innerWidth,r=0;for(let n of t.children){if(n===e||!(n instanceof HTMLElement))continue;let t=getComputedStyle(n);t.display===`none`||t.position===`absolute`||t.position===`fixed`||Number(t.flexGrow)>0||(r+=n.getBoundingClientRect().width)}y(Math.max(0,n-r))},r=new ResizeObserver(n),i=()=>{r.disconnect(),r.observe(t);for(let n of t.children)n!==e&&r.observe(n);n()},a=new MutationObserver(i);return a.observe(t,{childList:!0}),i(),window.addEventListener(`resize`,n),()=>{r.disconnect(),a.disconnect(),window.removeEventListener(`resize`,n)}},[O]),(0,d.useEffect)(()=>()=>{C.current?.()},[O,b,w]);let A=(0,d.useCallback)(e=>{if(e.button!==0)return;e.preventDefault(),C.current?.();let t=e.clientX,n=E,r=e.pointerId,i=e.currentTarget;try{i.setPointerCapture(r)}catch{}let a=e=>{e.pointerId===r&&_(D(n+t-e.clientX))},o=()=>{window.removeEventListener(`pointermove`,a),window.removeEventListener(`pointerup`,s),window.removeEventListener(`pointercancel`,s),window.removeEventListener(`blur`,o),i.removeEventListener(`lostpointercapture`,o);try{i.hasPointerCapture(r)&&i.releasePointerCapture(r)}catch{}C.current=null},s=e=>{e.pointerId===r&&o()};C.current=o,window.addEventListener(`pointermove`,a),window.addEventListener(`pointerup`,s),window.addEventListener(`pointercancel`,s),window.addEventListener(`blur`,o),i.addEventListener(`lostpointercapture`,o)},[E,D]);return(0,f.jsxs)(`div`,{ref:S,className:`flex h-full min-w-0 flex-col overflow-hidden border-l border-border bg-background${b?` absolute inset-0 z-30 border-l-0`:` relative`}`,style:b?void 0:{width:E,maxWidth:`100%`,flexShrink:0},"data-testid":`agent-code-pane`,children:[!b&&(0,f.jsx)(`div`,{role:`separator`,"aria-orientation":`vertical`,onPointerDown:A,tabIndex:0,"aria-label":n(`workspace.resizeCode`),"aria-valuemin":Math.round(T),"aria-valuemax":Math.round(w),"aria-valuenow":Math.round(E),onKeyDown:e=>{let t=e.key===`ArrowLeft`?E+32:e.key===`ArrowRight`?E-32:e.key===`Home`?T:e.key===`End`?w:null;t!==null&&(e.preventDefault(),_(D(t)))},className:`absolute inset-y-0 left-0 z-20 w-1.5 cursor-col-resize select-none touch-none hover:bg-accent-primary/20 focus-visible:bg-accent-primary/30 focus-visible:outline-none`,title:n(`workspace.resizeCode`)}),(0,f.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-border/60 bg-surface/40 px-3 py-1.5`,children:[(0,f.jsx)(`span`,{className:`text-xs font-medium text-foreground/70`,children:n(`workspace.files`)}),(0,f.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[(0,f.jsx)(`button`,{type:`button`,onClick:()=>{C.current?.(),x(e=>!e)},"aria-label":n(b?`restore`:`maximize`),title:n(b?`restore`:`maximize`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:b?(0,f.jsx)(u,{className:`h-4 w-4`}):(0,f.jsx)(l,{className:`h-4 w-4`})}),(0,f.jsx)(`button`,{type:`button`,onClick:()=>{C.current?.(),r()},"aria-label":n(`workspace.closeFiles`),title:n(`workspace.closeFiles`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:(0,f.jsx)(a,{className:`h-4 w-4`})})]})]}),(0,f.jsx)(`div`,{className:`min-h-0 flex-1 overflow-hidden`,children:(0,f.jsx)(d.Suspense,{fallback:(0,f.jsx)(`div`,{className:`flex h-full items-center justify-center text-sm text-muted-foreground`,children:`…`}),children:(0,f.jsx)(p,{embedded:!0,initialRepositoryId:c(e,t).repositoryId,onRepositoryChange:n=>o(e,t,{repositoryId:n}),initialPath:c(e,t).path,onSelectedPathChange:k},O)})})]},O)}export{g as AgentModeCodePane};