@gobing-ai/spur 0.4.30 → 0.4.31
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/.claude-plugin/marketplace.json +1 -1
- package/config/workflows/history-anatomy.yaml +13 -2
- package/config/workflows/task-pipeline.yaml +5 -0
- package/package.json +1 -1
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/skills/history-anatomy/references/operations.md +11 -0
- package/plugins/sp/skills/history-anatomy/references/report-contract.md +28 -0
- package/plugins/sp/skills/spur-cli/references/tasks.md +39 -1
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +26 -0
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +5 -0
- package/spur.js +1005 -149
package/spur.js
CHANGED
|
@@ -43367,6 +43367,56 @@ var require_extra_typings = __commonJS(function(exports, module) {
|
|
|
43367
43367
|
exports.createArgument = (name, description) => new commander.Argument(name, description);
|
|
43368
43368
|
});
|
|
43369
43369
|
|
|
43370
|
+
// ../../packages/domain/src/analytics/models.ts
|
|
43371
|
+
var init_models = () => {};
|
|
43372
|
+
|
|
43373
|
+
// ../../packages/domain/src/analytics/costs.ts
|
|
43374
|
+
function cacheHitRatio(totals) {
|
|
43375
|
+
if (totals.recordsWithUsage === 0)
|
|
43376
|
+
return null;
|
|
43377
|
+
if (totals.inputTokens === 0)
|
|
43378
|
+
return null;
|
|
43379
|
+
return totals.cacheReadTokens / totals.inputTokens;
|
|
43380
|
+
}
|
|
43381
|
+
function formatSummary(summary) {
|
|
43382
|
+
const lines = [];
|
|
43383
|
+
const totalCost = summary.totals.costUsd.toFixed(2);
|
|
43384
|
+
const totalInput = (summary.totals.inputTokens / 1e6).toFixed(1);
|
|
43385
|
+
const totalOutput = (summary.totals.outputTokens / 1e6).toFixed(1);
|
|
43386
|
+
lines.push(`Total: ${totalInput}M input / ${totalOutput}M output tokens \xB7 $${totalCost} \xB7 ${summary.totals.records} records`);
|
|
43387
|
+
lines.push(`Cache hit: ${formatRatio(cacheHitRatio(summary.totals))} of input tokens served from cache`);
|
|
43388
|
+
lines.push("");
|
|
43389
|
+
lines.push("By source:");
|
|
43390
|
+
const sourceEntries = Object.entries(summary.bySource).sort(byCostDesc);
|
|
43391
|
+
for (const entry of sourceEntries) {
|
|
43392
|
+
const [source, stats] = entry;
|
|
43393
|
+
lines.push(` ${source.padEnd(12)} $${stats.costUsd.toFixed(2).padStart(8)} ${(stats.inputTokens / 1e6).toFixed(1).padStart(6)}M in ${(stats.outputTokens / 1e6).toFixed(1).padStart(6)}M out ${String(stats.records).padStart(6)} rec`);
|
|
43394
|
+
}
|
|
43395
|
+
lines.push("");
|
|
43396
|
+
lines.push("By model:");
|
|
43397
|
+
const modelEntries = Object.entries(summary.byModel).sort(byCostDesc);
|
|
43398
|
+
for (const entry of modelEntries) {
|
|
43399
|
+
const [model, stats] = entry;
|
|
43400
|
+
lines.push(` ${model.slice(0, 28).padEnd(28)} $${stats.costUsd.toFixed(2).padStart(8)} ${(stats.inputTokens / 1e6).toFixed(1).padStart(6)}M in ${(stats.outputTokens / 1e6).toFixed(1).padStart(6)}M out ${String(stats.records).padStart(6)} rec`);
|
|
43401
|
+
}
|
|
43402
|
+
return lines.join(`
|
|
43403
|
+
`);
|
|
43404
|
+
}
|
|
43405
|
+
function formatRatio(ratio) {
|
|
43406
|
+
return ratio === null ? "n/a" : `${(ratio * 100).toFixed(1)}%`;
|
|
43407
|
+
}
|
|
43408
|
+
function byCostDesc([, a], [, b]) {
|
|
43409
|
+
return b.costUsd - a.costUsd;
|
|
43410
|
+
}
|
|
43411
|
+
var init_costs = __esm(() => {
|
|
43412
|
+
init_models();
|
|
43413
|
+
});
|
|
43414
|
+
|
|
43415
|
+
// ../../packages/domain/src/analytics/antigravity-etl.ts
|
|
43416
|
+
var init_antigravity_etl = __esm(() => {
|
|
43417
|
+
init_costs();
|
|
43418
|
+
});
|
|
43419
|
+
|
|
43370
43420
|
// ../../packages/domain/src/analytics/artifact.ts
|
|
43371
43421
|
import { createHash } from "crypto";
|
|
43372
43422
|
function selectorDigest(selector) {
|
|
@@ -43434,51 +43484,6 @@ var init_artifact_digest = __esm(() => {
|
|
|
43434
43484
|
RANKED_ARTIFACT_KEYS = new Set(Object.entries(ARTIFACT_ARRAY_CLASSIFICATION).filter(([, kind]) => kind === "ranked").map(([key]) => key));
|
|
43435
43485
|
});
|
|
43436
43486
|
|
|
43437
|
-
// ../../packages/domain/src/analytics/models.ts
|
|
43438
|
-
var init_models = () => {};
|
|
43439
|
-
|
|
43440
|
-
// ../../packages/domain/src/analytics/costs.ts
|
|
43441
|
-
function cacheHitRatio(totals) {
|
|
43442
|
-
if (totals.recordsWithUsage === 0)
|
|
43443
|
-
return null;
|
|
43444
|
-
if (totals.inputTokens === 0)
|
|
43445
|
-
return null;
|
|
43446
|
-
return totals.cacheReadTokens / totals.inputTokens;
|
|
43447
|
-
}
|
|
43448
|
-
function formatSummary(summary) {
|
|
43449
|
-
const lines = [];
|
|
43450
|
-
const totalCost = summary.totals.costUsd.toFixed(2);
|
|
43451
|
-
const totalInput = (summary.totals.inputTokens / 1e6).toFixed(1);
|
|
43452
|
-
const totalOutput = (summary.totals.outputTokens / 1e6).toFixed(1);
|
|
43453
|
-
lines.push(`Total: ${totalInput}M input / ${totalOutput}M output tokens \xB7 $${totalCost} \xB7 ${summary.totals.records} records`);
|
|
43454
|
-
lines.push(`Cache hit: ${formatRatio(cacheHitRatio(summary.totals))} of input tokens served from cache`);
|
|
43455
|
-
lines.push("");
|
|
43456
|
-
lines.push("By source:");
|
|
43457
|
-
const sourceEntries = Object.entries(summary.bySource).sort(byCostDesc);
|
|
43458
|
-
for (const entry of sourceEntries) {
|
|
43459
|
-
const [source, stats] = entry;
|
|
43460
|
-
lines.push(` ${source.padEnd(12)} $${stats.costUsd.toFixed(2).padStart(8)} ${(stats.inputTokens / 1e6).toFixed(1).padStart(6)}M in ${(stats.outputTokens / 1e6).toFixed(1).padStart(6)}M out ${String(stats.records).padStart(6)} rec`);
|
|
43461
|
-
}
|
|
43462
|
-
lines.push("");
|
|
43463
|
-
lines.push("By model:");
|
|
43464
|
-
const modelEntries = Object.entries(summary.byModel).sort(byCostDesc);
|
|
43465
|
-
for (const entry of modelEntries) {
|
|
43466
|
-
const [model, stats] = entry;
|
|
43467
|
-
lines.push(` ${model.slice(0, 28).padEnd(28)} $${stats.costUsd.toFixed(2).padStart(8)} ${(stats.inputTokens / 1e6).toFixed(1).padStart(6)}M in ${(stats.outputTokens / 1e6).toFixed(1).padStart(6)}M out ${String(stats.records).padStart(6)} rec`);
|
|
43468
|
-
}
|
|
43469
|
-
return lines.join(`
|
|
43470
|
-
`);
|
|
43471
|
-
}
|
|
43472
|
-
function formatRatio(ratio) {
|
|
43473
|
-
return ratio === null ? "n/a" : `${(ratio * 100).toFixed(1)}%`;
|
|
43474
|
-
}
|
|
43475
|
-
function byCostDesc([, a], [, b]) {
|
|
43476
|
-
return b.costUsd - a.costUsd;
|
|
43477
|
-
}
|
|
43478
|
-
var init_costs = __esm(() => {
|
|
43479
|
-
init_models();
|
|
43480
|
-
});
|
|
43481
|
-
|
|
43482
43487
|
// ../../packages/domain/src/analytics/derived.ts
|
|
43483
43488
|
class MetricRegistry {
|
|
43484
43489
|
fns = [];
|
|
@@ -43719,6 +43724,81 @@ async function deriveMissingAssistantDurations(db, ceilingMs) {
|
|
|
43719
43724
|
`);
|
|
43720
43725
|
}
|
|
43721
43726
|
|
|
43727
|
+
// ../../packages/domain/src/analytics/executor-latency.ts
|
|
43728
|
+
async function readExecutorLatency(db, keys) {
|
|
43729
|
+
const wanted = [...new Set(keys)].filter((key) => key.length > 0);
|
|
43730
|
+
if (wanted.length === 0)
|
|
43731
|
+
return new Map;
|
|
43732
|
+
try {
|
|
43733
|
+
const placeholders = wanted.map(() => "?").join(", ");
|
|
43734
|
+
const rows = await db.queryAll(`SELECT model, assistant_duration_ms, assistant_duration_samples, errors, tool_calls
|
|
43735
|
+
FROM history_board_model_stats WHERE model IN (${placeholders})`, ...wanted);
|
|
43736
|
+
const stats = new Map;
|
|
43737
|
+
for (const row of rows) {
|
|
43738
|
+
const samples = row.assistant_duration_samples;
|
|
43739
|
+
if (samples <= 0)
|
|
43740
|
+
continue;
|
|
43741
|
+
const successRate = row.tool_calls > 0 ? Math.min(1, Math.max(0, 1 - row.errors / row.tool_calls)) : 1;
|
|
43742
|
+
stats.set(row.model, {
|
|
43743
|
+
meanMs: row.assistant_duration_ms / samples,
|
|
43744
|
+
samples,
|
|
43745
|
+
successRate
|
|
43746
|
+
});
|
|
43747
|
+
}
|
|
43748
|
+
return stats;
|
|
43749
|
+
} catch {
|
|
43750
|
+
return new Map;
|
|
43751
|
+
}
|
|
43752
|
+
}
|
|
43753
|
+
|
|
43754
|
+
// ../../packages/domain/src/analytics/selector-time-range.ts
|
|
43755
|
+
function instantPrefilterBound(bound, slackMs) {
|
|
43756
|
+
const at = Date.parse(bound);
|
|
43757
|
+
return Number.isFinite(at) ? new Date(at + slackMs).toISOString() : undefined;
|
|
43758
|
+
}
|
|
43759
|
+
function buildTimeRangeClauses(range, target) {
|
|
43760
|
+
if (!COLUMN.test(target.column))
|
|
43761
|
+
throw new Error(`invalid time-range column: ${target.column}`);
|
|
43762
|
+
const bound = BOUND_SQL[target.grain];
|
|
43763
|
+
const clauses = [];
|
|
43764
|
+
const params = [];
|
|
43765
|
+
const column = COLUMN_SQL[target.grain](target.column);
|
|
43766
|
+
const prefilter = target.grain === "instant";
|
|
43767
|
+
if (range.since !== null) {
|
|
43768
|
+
const low = prefilter ? instantPrefilterBound(range.since, -INSTANT_PREFILTER_SLACK_MS) : undefined;
|
|
43769
|
+
if (low !== undefined) {
|
|
43770
|
+
clauses.push(`${target.column} >= ?`);
|
|
43771
|
+
params.push(low);
|
|
43772
|
+
}
|
|
43773
|
+
clauses.push(`${column} >= ${bound}`);
|
|
43774
|
+
params.push(range.since);
|
|
43775
|
+
}
|
|
43776
|
+
if (range.until !== null) {
|
|
43777
|
+
const high = prefilter ? instantPrefilterBound(range.until, INSTANT_PREFILTER_SLACK_MS) : undefined;
|
|
43778
|
+
if (high !== undefined) {
|
|
43779
|
+
clauses.push(`${target.column} <= ?`);
|
|
43780
|
+
params.push(high);
|
|
43781
|
+
}
|
|
43782
|
+
clauses.push(`${column} <= ${bound}`);
|
|
43783
|
+
params.push(range.until);
|
|
43784
|
+
}
|
|
43785
|
+
return { clauses, params };
|
|
43786
|
+
}
|
|
43787
|
+
var BOUND_SQL, COLUMN_SQL, COLUMN, INSTANT_PREFILTER_SLACK_MS = 1000;
|
|
43788
|
+
var init_selector_time_range = __esm(() => {
|
|
43789
|
+
BOUND_SQL = {
|
|
43790
|
+
day: "DATE(?)",
|
|
43791
|
+
timestamp: "strftime('%Y-%m-%dT%H:%M:%SZ', ?)",
|
|
43792
|
+
instant: "julianday(?)"
|
|
43793
|
+
};
|
|
43794
|
+
COLUMN_SQL = {
|
|
43795
|
+
day: (column) => column,
|
|
43796
|
+
timestamp: (column) => column,
|
|
43797
|
+
instant: (column) => `julianday(${column})`
|
|
43798
|
+
};
|
|
43799
|
+
COLUMN = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/;
|
|
43800
|
+
});
|
|
43801
|
+
|
|
43722
43802
|
// ../../packages/domain/src/analytics/tool-name-sql.ts
|
|
43723
43803
|
var EFFECTIVE_TOOL_NAME_SQL = `CASE
|
|
43724
43804
|
WHEN tc.tool_name IS NOT NULL AND TRIM(tc.tool_name) != '' AND tc.tool_name != 'unknown'
|
|
@@ -43809,13 +43889,10 @@ function escapeLike(value) {
|
|
|
43809
43889
|
function buildMessageWhereClauses(sel, alias) {
|
|
43810
43890
|
const clauses = [];
|
|
43811
43891
|
const params = [];
|
|
43812
|
-
if (sel.since != null) {
|
|
43813
|
-
|
|
43814
|
-
|
|
43815
|
-
|
|
43816
|
-
if (sel.until != null) {
|
|
43817
|
-
clauses.push(`${alias}.ts <= ?`);
|
|
43818
|
-
params.push(sel.until);
|
|
43892
|
+
if (sel.since != null || sel.until != null) {
|
|
43893
|
+
const range = buildTimeRangeClauses({ since: sel.since ?? null, until: sel.until ?? null }, { column: `${alias}.ts`, grain: "instant" });
|
|
43894
|
+
clauses.push(...range.clauses);
|
|
43895
|
+
params.push(...range.params);
|
|
43819
43896
|
}
|
|
43820
43897
|
if (sel.sources != null && sel.sources.length > 0) {
|
|
43821
43898
|
clauses.push(`${alias}.source IN (${sel.sources.map(() => "?").join(", ")})`);
|
|
@@ -43887,6 +43964,7 @@ function buildMessageWhereClauses(sel, alias) {
|
|
|
43887
43964
|
return { clauses, params };
|
|
43888
43965
|
}
|
|
43889
43966
|
var init_message_where = __esm(() => {
|
|
43967
|
+
init_selector_time_range();
|
|
43890
43968
|
init_tool_alias();
|
|
43891
43969
|
});
|
|
43892
43970
|
|
|
@@ -44132,8 +44210,8 @@ async function bySession(db, sel, top, opts) {
|
|
|
44132
44210
|
SUM(COALESCE(cache_read_tokens, 0)) AS cache_read_tokens,
|
|
44133
44211
|
SUM(COALESCE(output_tokens, 0)) AS output_tokens,
|
|
44134
44212
|
SUM(cost_usd) AS cost_usd,
|
|
44135
|
-
SUM(CASE WHEN role = 'assistant' THEN COALESCE(duration_ms, 0) ELSE 0 END) AS assistant_duration_ms,
|
|
44136
|
-
SUM(CASE WHEN role = 'assistant' AND duration_ms IS NULL THEN 1 ELSE 0 END) AS assistant_duration_unmeasured
|
|
44213
|
+
SUM(CASE WHEN role = 'assistant' AND disposition <> 'poll-duplicate' THEN COALESCE(duration_ms, 0) ELSE 0 END) AS assistant_duration_ms,
|
|
44214
|
+
SUM(CASE WHEN role = 'assistant' AND duration_ms IS NULL AND disposition <> 'poll-duplicate' THEN 1 ELSE 0 END) AS assistant_duration_unmeasured
|
|
44137
44215
|
FROM selected m
|
|
44138
44216
|
GROUP BY source, session_id
|
|
44139
44217
|
), last_messages AS (
|
|
@@ -44141,7 +44219,7 @@ async function bySession(db, sel, top, opts) {
|
|
|
44141
44219
|
ROW_NUMBER() OVER (
|
|
44142
44220
|
PARTITION BY source, session_id ORDER BY seq DESC, source_rowid DESC
|
|
44143
44221
|
) AS rank
|
|
44144
|
-
FROM selected WHERE disposition
|
|
44222
|
+
FROM selected WHERE disposition NOT IN ('meta', 'poll-duplicate') -- 1324 R3: latency denominator excludes collapsed polls
|
|
44145
44223
|
)
|
|
44146
44224
|
SELECT ms.session_id AS sessionId, ms.source AS source, ms.model AS model,
|
|
44147
44225
|
ms.started_at AS startedAt, ms.ended_at AS endedAt, ms.messages AS messages,
|
|
@@ -44311,7 +44389,7 @@ async function bySessionPage(db, sel, input, opts) {
|
|
|
44311
44389
|
last_messages AS (
|
|
44312
44390
|
SELECT source, session_id, record_hash, role,
|
|
44313
44391
|
ROW_NUMBER() OVER (PARTITION BY source, session_id ORDER BY seq DESC, source_rowid DESC) AS rank
|
|
44314
|
-
FROM selected WHERE disposition
|
|
44392
|
+
FROM selected WHERE disposition NOT IN ('meta', 'poll-duplicate') -- 1324 R3: latency denominator excludes collapsed polls
|
|
44315
44393
|
)
|
|
44316
44394
|
SELECT ms.session_id AS sessionId, ms.source AS source, ms.model AS model,
|
|
44317
44395
|
ms.started_at AS startedAt, ms.ended_at AS endedAt, ms.messages AS messages,
|
|
@@ -44387,8 +44465,10 @@ async function loops(db, sel, opts) {
|
|
|
44387
44465
|
FROM history_tool_call tc
|
|
44388
44466
|
JOIN history_message m ON m.record_hash = tc.message_hash
|
|
44389
44467
|
WHERE tc.session_id IN (${placeholders})
|
|
44390
|
-
AND tc.args_digest IS NOT NULL
|
|
44468
|
+
AND tc.args_digest IS NOT NULL AND tc.args_digest <> ''
|
|
44391
44469
|
AND tc.session_id NOT IN ('', 'unknown', 'session')
|
|
44470
|
+
-- 1324 R3: tool calls hanging off a collapsed poll duplicate are loop noise.
|
|
44471
|
+
AND COALESCE(m.disposition, '') <> 'poll-duplicate'
|
|
44392
44472
|
GROUP BY tc.source, tc.session_id, tc.tool_name, tc.args_digest
|
|
44393
44473
|
HAVING COUNT(*) >= 3
|
|
44394
44474
|
ORDER BY repeats DESC`, ...sessionScope);
|
|
@@ -44396,7 +44476,7 @@ async function loops(db, sel, opts) {
|
|
|
44396
44476
|
const { where, params } = buildMessageWhere(sel);
|
|
44397
44477
|
const wm = applyWatermarkToWhere(where, opts?.watermark);
|
|
44398
44478
|
return db.queryAll(`WITH filtered_messages AS (
|
|
44399
|
-
SELECT m.record_hash, m.source, m.model, m.ts
|
|
44479
|
+
SELECT m.record_hash, m.source, m.model, m.ts, m.disposition
|
|
44400
44480
|
FROM history_message m
|
|
44401
44481
|
${wm.where}
|
|
44402
44482
|
)
|
|
@@ -44409,7 +44489,8 @@ async function loops(db, sel, opts) {
|
|
|
44409
44489
|
MAX(tc.seq) AS lastSeq
|
|
44410
44490
|
FROM filtered_messages fm
|
|
44411
44491
|
CROSS JOIN history_tool_call tc ON tc.message_hash = fm.record_hash
|
|
44412
|
-
WHERE tc.args_digest IS NOT NULL AND tc.session_id NOT IN ('', 'unknown', 'session')
|
|
44492
|
+
WHERE tc.args_digest IS NOT NULL AND tc.args_digest <> '' AND tc.session_id NOT IN ('', 'unknown', 'session')
|
|
44493
|
+
AND COALESCE(fm.disposition, '') <> 'poll-duplicate' -- 1324 R3: collapsed poll noise
|
|
44413
44494
|
GROUP BY fm.source, tc.session_id, tc.tool_name, tc.args_digest
|
|
44414
44495
|
HAVING COUNT(*) >= 3
|
|
44415
44496
|
ORDER BY repeats DESC`, ...params, ...wm.params);
|
|
@@ -45392,32 +45473,6 @@ var init_forensic_query = __esm(() => {
|
|
|
45392
45473
|
END`;
|
|
45393
45474
|
});
|
|
45394
45475
|
|
|
45395
|
-
// ../../packages/domain/src/analytics/selector-time-range.ts
|
|
45396
|
-
function buildTimeRangeClauses(range, target) {
|
|
45397
|
-
if (!COLUMN.test(target.column))
|
|
45398
|
-
throw new Error(`invalid time-range column: ${target.column}`);
|
|
45399
|
-
const bound = BOUND_SQL[target.grain];
|
|
45400
|
-
const clauses = [];
|
|
45401
|
-
const params = [];
|
|
45402
|
-
if (range.since !== null) {
|
|
45403
|
-
clauses.push(`${target.column} >= ${bound}`);
|
|
45404
|
-
params.push(range.since);
|
|
45405
|
-
}
|
|
45406
|
-
if (range.until !== null) {
|
|
45407
|
-
clauses.push(`${target.column} <= ${bound}`);
|
|
45408
|
-
params.push(range.until);
|
|
45409
|
-
}
|
|
45410
|
-
return { clauses, params };
|
|
45411
|
-
}
|
|
45412
|
-
var BOUND_SQL, COLUMN;
|
|
45413
|
-
var init_selector_time_range = __esm(() => {
|
|
45414
|
-
BOUND_SQL = {
|
|
45415
|
-
day: "DATE(?)",
|
|
45416
|
-
timestamp: "strftime('%Y-%m-%dT%H:%M:%SZ', ?)"
|
|
45417
|
-
};
|
|
45418
|
-
COLUMN = /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?$/;
|
|
45419
|
-
});
|
|
45420
|
-
|
|
45421
45476
|
// ../../packages/domain/src/analytics/history-board-marts.ts
|
|
45422
45477
|
function resolveSummaryReadPath(input) {
|
|
45423
45478
|
if (!input.fresh)
|
|
@@ -53813,18 +53868,12 @@ async function loadFolds(db, spec) {
|
|
|
53813
53868
|
}
|
|
53814
53869
|
}
|
|
53815
53870
|
function eventWindow(spec) {
|
|
53816
|
-
const clauses =
|
|
53817
|
-
const params = [];
|
|
53818
|
-
if (spec.since !== undefined) {
|
|
53819
|
-
clauses.push("occurred_at >= ?");
|
|
53820
|
-
params.push(spec.since);
|
|
53821
|
-
}
|
|
53822
|
-
if (spec.until !== undefined) {
|
|
53823
|
-
clauses.push("occurred_at <= ?");
|
|
53824
|
-
params.push(spec.until);
|
|
53825
|
-
}
|
|
53871
|
+
const { clauses, params } = buildTimeRangeClauses({ since: spec.since ?? null, until: spec.until ?? null }, { column: "occurred_at", grain: "instant" });
|
|
53826
53872
|
return { where: clauses.length > 0 ? `AND ${clauses.join(" AND ")}` : "", params };
|
|
53827
53873
|
}
|
|
53874
|
+
var init_pairings = __esm(() => {
|
|
53875
|
+
init_selector_time_range();
|
|
53876
|
+
});
|
|
53828
53877
|
// ../../packages/domain/src/analytics/render-report.ts
|
|
53829
53878
|
function assertArtifactVersion(actualVersion, artifactPath, expectedVersion = HISTORY_ARTIFACT_SCHEMA_VERSION) {
|
|
53830
53879
|
if (actualVersion !== expectedVersion) {
|
|
@@ -55645,6 +55694,7 @@ var init_workflow_measurements = __esm(() => {
|
|
|
55645
55694
|
|
|
55646
55695
|
// ../../packages/domain/src/analytics/index.ts
|
|
55647
55696
|
var init_analytics = __esm(() => {
|
|
55697
|
+
init_antigravity_etl();
|
|
55648
55698
|
init_artifact();
|
|
55649
55699
|
init_artifact_digest();
|
|
55650
55700
|
init_costs();
|
|
@@ -55655,6 +55705,7 @@ var init_analytics = __esm(() => {
|
|
|
55655
55705
|
init_importer_schema_version();
|
|
55656
55706
|
init_models();
|
|
55657
55707
|
init_narrow_artifact();
|
|
55708
|
+
init_pairings();
|
|
55658
55709
|
init_render_forensics();
|
|
55659
55710
|
init_render_pairings();
|
|
55660
55711
|
init_render_report();
|
|
@@ -58153,7 +58204,7 @@ var init_run_dao = __esm(() => {
|
|
|
58153
58204
|
FROM runs
|
|
58154
58205
|
WHERE (?1 IS NULL OR workflow_name = ?1)
|
|
58155
58206
|
AND (?2 IS NULL OR status = ?2)
|
|
58156
|
-
AND (?3 IS NULL OR started_at >= ?3)
|
|
58207
|
+
AND (?3 IS NULL OR julianday(started_at) >= julianday(?3))
|
|
58157
58208
|
AND (
|
|
58158
58209
|
?4 IS NULL
|
|
58159
58210
|
OR started_at < ?4
|
|
@@ -58165,6 +58216,9 @@ var init_run_dao = __esm(() => {
|
|
|
58165
58216
|
traceRowById(runId) {
|
|
58166
58217
|
return this.adapter.queryFirst("SELECT id, workflow_name, mode, status, agent, started_at, completed_at, updated_at, terminal_reason, metadata_json, pid FROM runs WHERE id = ?", runId);
|
|
58167
58218
|
}
|
|
58219
|
+
traceRowsByIdPrefix(prefix) {
|
|
58220
|
+
return this.adapter.queryAll("SELECT id FROM runs WHERE substr(id, 1, length(?1)) = ?1 ORDER BY id LIMIT 21", prefix);
|
|
58221
|
+
}
|
|
58168
58222
|
mergeMetadata(runId, patch) {
|
|
58169
58223
|
return this.adapter.run(`UPDATE runs
|
|
58170
58224
|
SET metadata_json = json_patch(
|
|
@@ -58321,6 +58375,19 @@ var init_run_storage_reference_dao = __esm(() => {
|
|
|
58321
58375
|
});
|
|
58322
58376
|
|
|
58323
58377
|
// ../../packages/domain/src/dao/run-transfer.ts
|
|
58378
|
+
function stampWorktreeOrigin(row) {
|
|
58379
|
+
let meta = {};
|
|
58380
|
+
try {
|
|
58381
|
+
const parsed = JSON.parse(String(row.metadata_json ?? "{}"));
|
|
58382
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
58383
|
+
meta = parsed;
|
|
58384
|
+
}
|
|
58385
|
+
} catch {
|
|
58386
|
+
meta = {};
|
|
58387
|
+
}
|
|
58388
|
+
meta.worktree_origin = true;
|
|
58389
|
+
return { ...row, metadata_json: JSON.stringify(meta) };
|
|
58390
|
+
}
|
|
58324
58391
|
function insertIfAbsentOps(table, rows, targetColumns) {
|
|
58325
58392
|
return rows.map((row) => {
|
|
58326
58393
|
const columns = Object.keys(row).filter((column) => targetColumns.has(column));
|
|
@@ -58349,6 +58416,29 @@ async function ledgerOps(from, to, runId) {
|
|
|
58349
58416
|
}
|
|
58350
58417
|
return ops;
|
|
58351
58418
|
}
|
|
58419
|
+
async function worktreeRunSessionOps(from, to, runId) {
|
|
58420
|
+
const present = await from.queryFirst("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?", "history_run_session");
|
|
58421
|
+
if (present === undefined)
|
|
58422
|
+
return [];
|
|
58423
|
+
const rows = await from.queryAll("SELECT * FROM history_run_session WHERE run_id = ?", runId);
|
|
58424
|
+
if (rows.length === 0)
|
|
58425
|
+
return [];
|
|
58426
|
+
const targetColumns = await tableColumns(to, "history_run_session");
|
|
58427
|
+
const ops = [];
|
|
58428
|
+
for (const row of rows) {
|
|
58429
|
+
const exists = await to.queryFirst(`SELECT run_id FROM history_run_session
|
|
58430
|
+
WHERE run_id = ? AND source IS ? AND session_id IS ? AND exactness IS ? AND resolved_at IS ?
|
|
58431
|
+
LIMIT 1`, row.run_id, row.source ?? null, row.session_id ?? null, row.exactness ?? null, row.resolved_at ?? null);
|
|
58432
|
+
if (exists !== undefined)
|
|
58433
|
+
continue;
|
|
58434
|
+
const columns = Object.keys(row).filter((column) => targetColumns.has(column));
|
|
58435
|
+
ops.push({
|
|
58436
|
+
sql: `INSERT INTO history_run_session (${columns.join(", ")}) ` + `VALUES (${columns.map(() => "?").join(", ")})`,
|
|
58437
|
+
params: columns.map((column) => column === "mechanism" ? "worktree-merge" : row[column])
|
|
58438
|
+
});
|
|
58439
|
+
}
|
|
58440
|
+
return ops;
|
|
58441
|
+
}
|
|
58352
58442
|
async function tableColumns(db, table) {
|
|
58353
58443
|
const rows = await db.queryAll(`PRAGMA table_info(${table})`);
|
|
58354
58444
|
return new Set(rows.map((row) => row.name));
|
|
@@ -58387,7 +58477,8 @@ async function transferRunTables(from, to) {
|
|
|
58387
58477
|
continue;
|
|
58388
58478
|
}
|
|
58389
58479
|
}
|
|
58390
|
-
const ops = insertIfAbsentOps("runs", [row], targetRunsColumns);
|
|
58480
|
+
const ops = insertIfAbsentOps("runs", [stampWorktreeOrigin(row)], targetRunsColumns);
|
|
58481
|
+
ops.push(...await worktreeRunSessionOps(from, to, id));
|
|
58391
58482
|
for (const table of CHILD_TABLES) {
|
|
58392
58483
|
ops.push(...insertIfAbsentOps(table, await from.queryAll(`SELECT * FROM ${table} WHERE run_id = ?`, id), await tableColumns(to, table)));
|
|
58393
58484
|
}
|
|
@@ -75695,6 +75786,13 @@ class MarkdownDocument {
|
|
|
75695
75786
|
get sectionNames() {
|
|
75696
75787
|
return this._sections.map((s) => s.name);
|
|
75697
75788
|
}
|
|
75789
|
+
sectionContent(name) {
|
|
75790
|
+
const section = this.findSection(name);
|
|
75791
|
+
if (section === undefined)
|
|
75792
|
+
return null;
|
|
75793
|
+
const text = section.modifiedText ?? section.originalText;
|
|
75794
|
+
return { headingLine: section.headingLine, text };
|
|
75795
|
+
}
|
|
75698
75796
|
get demotedHeadings() {
|
|
75699
75797
|
return this._demotedHeadings;
|
|
75700
75798
|
}
|
|
@@ -99550,6 +99648,12 @@ function bounded3(value, maxLength = MAX_FIELD_LENGTH) {
|
|
|
99550
99648
|
const redacted = value.replace(SECRET_PATTERN2, "[REDACTED]");
|
|
99551
99649
|
return redacted.length <= maxLength ? redacted : `${redacted.slice(0, maxLength)}\u2026`;
|
|
99552
99650
|
}
|
|
99651
|
+
function positiveMs(value) {
|
|
99652
|
+
const n = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
|
|
99653
|
+
if (typeof n !== "number" || !Number.isFinite(n) || n <= 0)
|
|
99654
|
+
return;
|
|
99655
|
+
return Math.trunc(n);
|
|
99656
|
+
}
|
|
99553
99657
|
function projectActionMetadata(kind, options) {
|
|
99554
99658
|
if (options === undefined)
|
|
99555
99659
|
return;
|
|
@@ -99561,9 +99665,9 @@ function projectActionMetadata(kind, options) {
|
|
|
99561
99665
|
if (kind === "agent.run" && typeof options.role === "string" && options.role !== "") {
|
|
99562
99666
|
metadata.role = bounded3(options.role);
|
|
99563
99667
|
}
|
|
99564
|
-
|
|
99565
|
-
|
|
99566
|
-
|
|
99668
|
+
const timeoutMs = positiveMs(options.timeoutMs);
|
|
99669
|
+
if (timeoutMs !== undefined)
|
|
99670
|
+
metadata.timeoutMs = timeoutMs;
|
|
99567
99671
|
if (typeof options.maxTokens === "number" && Number.isFinite(options.maxTokens)) {
|
|
99568
99672
|
metadata.maxTokens = Math.max(0, options.maxTokens);
|
|
99569
99673
|
}
|
|
@@ -102629,6 +102733,13 @@ function classifyDispatch(result) {
|
|
|
102629
102733
|
}
|
|
102630
102734
|
return;
|
|
102631
102735
|
}
|
|
102736
|
+
function executorUnavailabilityEvidence(result) {
|
|
102737
|
+
if (result.exitCode === 2)
|
|
102738
|
+
return `dispatch-level exit 2 (executor unusable for this run)`;
|
|
102739
|
+
if (result.signal === "unavailable")
|
|
102740
|
+
return `terminal signal ${result.signal}`;
|
|
102741
|
+
return;
|
|
102742
|
+
}
|
|
102632
102743
|
var FAILURE_RULES, PERMISSION_PATTERNS, HTTP_STATUS_PATTERN;
|
|
102633
102744
|
var init_failure_classification = __esm(() => {
|
|
102634
102745
|
init_agent_execution();
|
|
@@ -105481,6 +105592,8 @@ ${traced.stdout ?? ""}`);
|
|
|
105481
105592
|
const resumedDispatch = ok && affinityOn && !freshSession && resumeSupported && storedSessionId !== undefined;
|
|
105482
105593
|
if (!ok && traced.signal !== undefined)
|
|
105483
105594
|
resultData.signal = traced.signal;
|
|
105595
|
+
if (traced.ladder !== undefined && traced.ladder.length > 0)
|
|
105596
|
+
resultData.ladder = traced.ladder;
|
|
105484
105597
|
resultData.session = resumedDispatch ? "reused" : "fresh";
|
|
105485
105598
|
resultData.sessionSource = sessionDeclared ? "declared" : "default";
|
|
105486
105599
|
if (escalated) {
|
|
@@ -107544,6 +107657,72 @@ var init_agent_invocation_sanitize = __esm(() => {
|
|
|
107544
107657
|
SENSITIVE_INLINE_FLAG = /^(--?(?:api[-_]?key|authorization|credential|password|secret|token)=).+$/i;
|
|
107545
107658
|
});
|
|
107546
107659
|
|
|
107660
|
+
// ../../packages/app/src/services/adaptive-timeout.ts
|
|
107661
|
+
function derivedBudgetMs(latency) {
|
|
107662
|
+
const base = latency.p95Ms ?? latency.meanMs;
|
|
107663
|
+
const headroom = latency.p95Ms !== undefined ? P95_HEADROOM : MEAN_HEADROOM;
|
|
107664
|
+
return Math.max(DERIVED_FLOOR_MS, Math.ceil(base * headroom));
|
|
107665
|
+
}
|
|
107666
|
+
function resolveAdaptiveTimeoutMs(staticMs, latency) {
|
|
107667
|
+
if (latency === undefined || latency.samples <= 0) {
|
|
107668
|
+
return {
|
|
107669
|
+
source: "static",
|
|
107670
|
+
...staticMs !== undefined ? { staticMs, appliedMs: staticMs } : {},
|
|
107671
|
+
fits: true
|
|
107672
|
+
};
|
|
107673
|
+
}
|
|
107674
|
+
const derivedMs = derivedBudgetMs(latency);
|
|
107675
|
+
if (staticMs === undefined) {
|
|
107676
|
+
return {
|
|
107677
|
+
source: "derived-latency",
|
|
107678
|
+
appliedMs: derivedMs,
|
|
107679
|
+
derivedMs,
|
|
107680
|
+
meanMs: latency.meanMs,
|
|
107681
|
+
...latency.p95Ms !== undefined ? { p95Ms: latency.p95Ms } : {},
|
|
107682
|
+
samples: latency.samples,
|
|
107683
|
+
successRate: latency.successRate,
|
|
107684
|
+
fits: true
|
|
107685
|
+
};
|
|
107686
|
+
}
|
|
107687
|
+
const fits = derivedMs <= staticMs;
|
|
107688
|
+
return {
|
|
107689
|
+
source: "static",
|
|
107690
|
+
staticMs,
|
|
107691
|
+
appliedMs: staticMs,
|
|
107692
|
+
derivedMs,
|
|
107693
|
+
meanMs: latency.meanMs,
|
|
107694
|
+
...latency.p95Ms !== undefined ? { p95Ms: latency.p95Ms } : {},
|
|
107695
|
+
samples: latency.samples,
|
|
107696
|
+
successRate: latency.successRate,
|
|
107697
|
+
fits
|
|
107698
|
+
};
|
|
107699
|
+
}
|
|
107700
|
+
function ladderScore(latency) {
|
|
107701
|
+
const base = latency.p95Ms ?? latency.meanMs;
|
|
107702
|
+
const successRate = Math.min(1, Math.max(0.1, latency.successRate));
|
|
107703
|
+
return base / successRate;
|
|
107704
|
+
}
|
|
107705
|
+
async function loadExecutorLatency(source, keys) {
|
|
107706
|
+
if (source.historyLatency !== undefined) {
|
|
107707
|
+
try {
|
|
107708
|
+
return new Map(Object.entries(await source.historyLatency([...keys])));
|
|
107709
|
+
} catch {
|
|
107710
|
+
return new Map;
|
|
107711
|
+
}
|
|
107712
|
+
}
|
|
107713
|
+
if (source.getDb === undefined)
|
|
107714
|
+
return new Map;
|
|
107715
|
+
try {
|
|
107716
|
+
return await readExecutorLatency(await source.getDb(), keys);
|
|
107717
|
+
} catch {
|
|
107718
|
+
return new Map;
|
|
107719
|
+
}
|
|
107720
|
+
}
|
|
107721
|
+
var P95_HEADROOM = 1.5, MEAN_HEADROOM = 3, DERIVED_FLOOR_MS = 30000;
|
|
107722
|
+
var init_adaptive_timeout = __esm(() => {
|
|
107723
|
+
init_src2();
|
|
107724
|
+
});
|
|
107725
|
+
|
|
107547
107726
|
// ../../packages/app/src/services/agent-run-flags.ts
|
|
107548
107727
|
function stringFlag(flags, name, fallback) {
|
|
107549
107728
|
const value = flags[name];
|
|
@@ -107743,10 +107922,12 @@ class AgentResolver {
|
|
|
107743
107922
|
const failedTier = getExecutorTier(failed);
|
|
107744
107923
|
const failedCanonical = resolveAgentName2(failed.agent);
|
|
107745
107924
|
const exhaustedAgents = new Set(executors.filter((executor) => executor.name === fromExecutor || (exclude?.has(executor.name) ?? false)).map((executor) => resolveAgentName2(executor.agent)).filter((agent) => agent !== undefined));
|
|
107746
|
-
|
|
107925
|
+
let sideways = executors.filter((e) => {
|
|
107747
107926
|
const canonical = resolveAgentName2(e.agent);
|
|
107748
107927
|
return !executorDisabled(e) && e.name !== fromExecutor && getExecutorTier(e) === failedTier && canonical !== failedCanonical && (canonical === undefined || !exhaustedAgents.has(canonical)) && !(exclude?.has(e.name) ?? false);
|
|
107749
107928
|
});
|
|
107929
|
+
const sidewaysOrder = await this.orderEligibleByMeasuredLatency(sideways, signal !== undefined);
|
|
107930
|
+
sideways = sidewaysOrder.ordered;
|
|
107750
107931
|
for (const executor of sideways) {
|
|
107751
107932
|
const canonical = resolveAgentName2(executor.agent);
|
|
107752
107933
|
if (canonical === undefined) {
|
|
@@ -107772,6 +107953,7 @@ class AgentResolver {
|
|
|
107772
107953
|
model: executor.model,
|
|
107773
107954
|
source: "stage",
|
|
107774
107955
|
executor: executor.name,
|
|
107956
|
+
...sidewaysOrder.ordering !== undefined ? { ordering: sidewaysOrder.ordering } : {},
|
|
107775
107957
|
stage: {
|
|
107776
107958
|
stageId: stageRecord.id,
|
|
107777
107959
|
policy,
|
|
@@ -107810,7 +107992,9 @@ class AgentResolver {
|
|
|
107810
107992
|
if (eligible.length === 0) {
|
|
107811
107993
|
return;
|
|
107812
107994
|
}
|
|
107813
|
-
|
|
107995
|
+
const measuredOrder = await this.orderEligibleByMeasuredLatency(eligible, signal !== undefined);
|
|
107996
|
+
const orderedEligible = measuredOrder.ordered;
|
|
107997
|
+
for (const executor of orderedEligible) {
|
|
107814
107998
|
const canonical = resolveAgentName2(executor.agent);
|
|
107815
107999
|
if (canonical === undefined) {
|
|
107816
108000
|
return {
|
|
@@ -107834,6 +108018,7 @@ class AgentResolver {
|
|
|
107834
108018
|
model: executor.model,
|
|
107835
108019
|
source: "stage",
|
|
107836
108020
|
executor: executor.name,
|
|
108021
|
+
...measuredOrder.ordering !== undefined ? { ordering: measuredOrder.ordering } : {},
|
|
107837
108022
|
stage: {
|
|
107838
108023
|
stageId: stageRecord.id,
|
|
107839
108024
|
policy,
|
|
@@ -107845,6 +108030,36 @@ class AgentResolver {
|
|
|
107845
108030
|
}
|
|
107846
108031
|
return;
|
|
107847
108032
|
}
|
|
108033
|
+
async orderEligibleByMeasuredLatency(eligible, ordered) {
|
|
108034
|
+
if (!ordered || eligible.length <= 1)
|
|
108035
|
+
return { ordered: [...eligible] };
|
|
108036
|
+
const stats = await loadExecutorLatency(this.ctx, eligible.map((e) => e.model ?? e.name));
|
|
108037
|
+
const scored = eligible.map((executor, index) => {
|
|
108038
|
+
const hit = stats.get(executor.model ?? executor.name);
|
|
108039
|
+
return {
|
|
108040
|
+
executor,
|
|
108041
|
+
index,
|
|
108042
|
+
...hit !== undefined && hit.samples > 0 ? { score: ladderScore(hit), samples: hit.samples } : {}
|
|
108043
|
+
};
|
|
108044
|
+
});
|
|
108045
|
+
const known = scored.filter((entry) => entry.score !== undefined);
|
|
108046
|
+
if (known.length === 0)
|
|
108047
|
+
return { ordered: [...eligible] };
|
|
108048
|
+
const sorted = [...scored].sort((a, b) => {
|
|
108049
|
+
const aKnown = a.score !== undefined ? 1 : 0;
|
|
108050
|
+
const bKnown = b.score !== undefined ? 1 : 0;
|
|
108051
|
+
if (aKnown !== bKnown)
|
|
108052
|
+
return bKnown - aKnown;
|
|
108053
|
+
if (aKnown === 1)
|
|
108054
|
+
return (a.score ?? 0) - (b.score ?? 0) || a.index - b.index;
|
|
108055
|
+
return a.index - b.index;
|
|
108056
|
+
});
|
|
108057
|
+
const ordering = sorted.map((entry) => ({
|
|
108058
|
+
executor: entry.executor.name,
|
|
108059
|
+
...entry.score !== undefined ? { scoreMs: entry.score, samples: entry.samples } : {}
|
|
108060
|
+
}));
|
|
108061
|
+
return { ordered: sorted.map((entry) => entry.executor), ordering };
|
|
108062
|
+
}
|
|
107848
108063
|
async resolveAgentPriority(doctorRunner) {
|
|
107849
108064
|
const results = await doctorRunner.runAll();
|
|
107850
108065
|
for (const name of TIER1_PRIORITY2) {
|
|
@@ -108076,6 +108291,7 @@ var warnedBareBinary;
|
|
|
108076
108291
|
var init_agent_resolver = __esm(() => {
|
|
108077
108292
|
init_src2();
|
|
108078
108293
|
init_dist29();
|
|
108294
|
+
init_adaptive_timeout();
|
|
108079
108295
|
init_capability_attestation();
|
|
108080
108296
|
init_executor_tier();
|
|
108081
108297
|
warnedBareBinary = new Set;
|
|
@@ -109066,6 +109282,36 @@ class AgentRunExecutor {
|
|
|
109066
109282
|
this.resolver = resolver;
|
|
109067
109283
|
}
|
|
109068
109284
|
sessionRootRegistry = { active: new Map, overlapped: new Set };
|
|
109285
|
+
runBreakers = new Map;
|
|
109286
|
+
static BREAKER_CAP = 256;
|
|
109287
|
+
breakerFor(runId) {
|
|
109288
|
+
let breaker = this.runBreakers.get(runId);
|
|
109289
|
+
if (breaker === undefined) {
|
|
109290
|
+
breaker = { executors: new Set, touchedAt: Date.now() };
|
|
109291
|
+
this.runBreakers.set(runId, breaker);
|
|
109292
|
+
if (this.runBreakers.size > AgentRunExecutor.BREAKER_CAP) {
|
|
109293
|
+
const oldest = [...this.runBreakers.entries()].sort((a, b) => a[1].touchedAt - b[1].touchedAt);
|
|
109294
|
+
for (const [key] of oldest.slice(0, this.runBreakers.size - AgentRunExecutor.BREAKER_CAP)) {
|
|
109295
|
+
this.runBreakers.delete(key);
|
|
109296
|
+
}
|
|
109297
|
+
}
|
|
109298
|
+
}
|
|
109299
|
+
breaker.touchedAt = Date.now();
|
|
109300
|
+
return breaker;
|
|
109301
|
+
}
|
|
109302
|
+
recordLadderDecision(decisions, identity, events, decision) {
|
|
109303
|
+
const row = { ...decision, at: new Date().toISOString() };
|
|
109304
|
+
decisions.push(row);
|
|
109305
|
+
if (events !== undefined) {
|
|
109306
|
+
events.emit("agent.invoke.ladder", {
|
|
109307
|
+
runId: identity.runId,
|
|
109308
|
+
executionId: identity.executionId,
|
|
109309
|
+
...identity.actionId !== undefined ? { actionId: identity.actionId } : {},
|
|
109310
|
+
...row,
|
|
109311
|
+
severity: "warning"
|
|
109312
|
+
});
|
|
109313
|
+
}
|
|
109314
|
+
}
|
|
109069
109315
|
async executeRun(prompt, flags, deps, options) {
|
|
109070
109316
|
const silent = options.silent;
|
|
109071
109317
|
const nonInteractive = options.nonInteractive === true;
|
|
@@ -109137,6 +109383,9 @@ class AgentRunExecutor {
|
|
|
109137
109383
|
if (quotaEventBridge !== undefined)
|
|
109138
109384
|
quotaEventBridge.on("agent.quota.exhausted", onQuotaExhausted);
|
|
109139
109385
|
const tiersAttempted = new Set(currentStage ? [currentStage.executorTier] : []);
|
|
109386
|
+
const breaker = this.breakerFor(stringFlag(flags, "run-id", runCorrelation.runId));
|
|
109387
|
+
const ladderDecisions = [];
|
|
109388
|
+
const latencyCache = new Map;
|
|
109140
109389
|
if (!jsonOutput && TIER2_AGENTS2.has(currentAgent)) {
|
|
109141
109390
|
this.ctx.output.error(`Warning: ${currentAgent} is a Tier-2 agent (TUI/gateway only)`);
|
|
109142
109391
|
}
|
|
@@ -109227,10 +109476,85 @@ class AgentRunExecutor {
|
|
|
109227
109476
|
await sessionObserver.watermark(currentAgent, sessionDir);
|
|
109228
109477
|
}
|
|
109229
109478
|
}
|
|
109479
|
+
if (currentStage !== undefined && breaker.executors.has(currentStage.executorName)) {
|
|
109480
|
+
const skippedExecutor = currentStage.executorName;
|
|
109481
|
+
runFlags.signal = "resource-exhaustion";
|
|
109482
|
+
runFlags["from-executor"] = skippedExecutor;
|
|
109483
|
+
const rerun = await this.resolver.resolveAgent(runFlags, doctorRunner, new Set([...attemptedExecutors, ...breaker.executors]));
|
|
109484
|
+
if (!rerun.ok || rerun.stage === undefined || breaker.executors.has(rerun.stage.executorName)) {
|
|
109485
|
+
const message = `executor circuit breaker: every remaining executor for stage '${skippedExecutor}' is ` + `unavailable for this run (tripped: ${[...breaker.executors].join(", ")})`;
|
|
109486
|
+
this.recordLadderDecision(ladderDecisions, lifecycle.identity, this.ctx.events, {
|
|
109487
|
+
kind: "chain-exhausted",
|
|
109488
|
+
executor: skippedExecutor,
|
|
109489
|
+
reason: message
|
|
109490
|
+
});
|
|
109491
|
+
await sessionObserver?.resolve();
|
|
109492
|
+
return { ok: false, exitCode: 2, message };
|
|
109493
|
+
}
|
|
109494
|
+
currentAgent = rerun.agent;
|
|
109495
|
+
currentModel = rerun.model;
|
|
109496
|
+
currentSource = rerun.source;
|
|
109497
|
+
currentStage = rerun.stage;
|
|
109498
|
+
currentRole = rerun.role;
|
|
109499
|
+
currentRoleOrigin = rerun.roleOrigin;
|
|
109500
|
+
dispatchExecutor.setRoleEnv(currentRole);
|
|
109501
|
+
currentTier = rerun.tier;
|
|
109502
|
+
currentExecutor = rerun.executor;
|
|
109503
|
+
routing = buildRoutingAttribution(rerun);
|
|
109504
|
+
attemptedExecutors.add(rerun.stage.executorName);
|
|
109505
|
+
tiersAttempted.add(rerun.stage.executorTier);
|
|
109506
|
+
this.recordLadderDecision(ladderDecisions, lifecycle.identity, this.ctx.events, {
|
|
109507
|
+
kind: "breaker-skip",
|
|
109508
|
+
executor: skippedExecutor,
|
|
109509
|
+
toExecutor: rerun.stage.executorName,
|
|
109510
|
+
reason: "run-scoped circuit breaker: skipped an executor proven unavailable earlier in this run"
|
|
109511
|
+
});
|
|
109512
|
+
}
|
|
109230
109513
|
try {
|
|
109231
109514
|
for (let attempt = 0;; attempt++) {
|
|
109232
109515
|
const agent = currentAgent;
|
|
109233
109516
|
const model = (attempt === 0 ? explicitModel : undefined) ?? currentModel;
|
|
109517
|
+
const attemptExecutorEntry = this.ctx.agentConfig?.executors?.find((entry) => entry.name === (currentExecutor ?? ""));
|
|
109518
|
+
const latencyKey = attemptExecutorEntry?.model ?? currentExecutor ?? agent;
|
|
109519
|
+
if (!latencyCache.has(latencyKey)) {
|
|
109520
|
+
const stats = await loadExecutorLatency(this.ctx, [latencyKey]);
|
|
109521
|
+
latencyCache.set(latencyKey, stats.get(latencyKey));
|
|
109522
|
+
}
|
|
109523
|
+
const timeoutDecision = resolveAdaptiveTimeoutMs(timeoutMs, latencyCache.get(latencyKey));
|
|
109524
|
+
const effectiveTimeoutMs = timeoutDecision.appliedMs ?? timeoutMs;
|
|
109525
|
+
if (timeoutMs !== undefined && !timeoutDecision.fits && currentStage !== undefined && breaker.executors.size + attemptedExecutors.size < 64) {
|
|
109526
|
+
const exhaustedExecutor = currentStage.executorName;
|
|
109527
|
+
runFlags.signal = "timeout";
|
|
109528
|
+
runFlags["from-executor"] = exhaustedExecutor;
|
|
109529
|
+
const rerun = await this.resolver.resolveAgent(runFlags, doctorRunner, new Set([...attemptedExecutors, ...breaker.executors]));
|
|
109530
|
+
if (rerun.ok && rerun.stage !== undefined && rerun.stage.executorName !== exhaustedExecutor && !attemptedExecutors.has(rerun.stage.executorName) && !breaker.executors.has(rerun.stage.executorName)) {
|
|
109531
|
+
currentAgent = rerun.agent;
|
|
109532
|
+
currentModel = rerun.model;
|
|
109533
|
+
currentSource = rerun.source;
|
|
109534
|
+
currentStage = rerun.stage;
|
|
109535
|
+
currentRole = rerun.role;
|
|
109536
|
+
currentRoleOrigin = rerun.roleOrigin;
|
|
109537
|
+
dispatchExecutor.setRoleEnv(currentRole);
|
|
109538
|
+
currentTier = rerun.tier;
|
|
109539
|
+
currentExecutor = rerun.executor;
|
|
109540
|
+
routing = buildRoutingAttribution(rerun);
|
|
109541
|
+
attemptedExecutors.add(rerun.stage.executorName);
|
|
109542
|
+
tiersAttempted.add(rerun.stage.executorTier);
|
|
109543
|
+
this.recordLadderDecision(ladderDecisions, lifecycle.identity, this.ctx.events, {
|
|
109544
|
+
kind: "timeout-exhausted-skip",
|
|
109545
|
+
executor: exhaustedExecutor,
|
|
109546
|
+
toExecutor: rerun.stage.executorName,
|
|
109547
|
+
reason: `measured latency exceeds the static budget; failed over without burning ` + `the ${timeoutMs}ms timeout`,
|
|
109548
|
+
detail: JSON.stringify({
|
|
109549
|
+
derivedMs: timeoutDecision.derivedMs,
|
|
109550
|
+
staticMs: timeoutMs,
|
|
109551
|
+
meanMs: timeoutDecision.meanMs,
|
|
109552
|
+
samples: timeoutDecision.samples
|
|
109553
|
+
})
|
|
109554
|
+
});
|
|
109555
|
+
continue;
|
|
109556
|
+
}
|
|
109557
|
+
}
|
|
109234
109558
|
if (requiresCapabilities !== undefined && Object.keys(requiresCapabilities).length > 0) {
|
|
109235
109559
|
const executorEntry = this.ctx.agentConfig?.executors?.find((entry) => entry.name === (currentExecutor ?? ""));
|
|
109236
109560
|
const evaluation = evaluateCapabilities(requiresCapabilities, executorEntry);
|
|
@@ -109302,7 +109626,8 @@ class AgentRunExecutor {
|
|
|
109302
109626
|
...cwd !== "" ? { cwd } : {},
|
|
109303
109627
|
mode,
|
|
109304
109628
|
outputMode,
|
|
109305
|
-
...
|
|
109629
|
+
...effectiveTimeoutMs !== undefined ? { timeoutMs: effectiveTimeoutMs } : {},
|
|
109630
|
+
...timeoutDecision.staticMs !== undefined || timeoutDecision.derivedMs !== undefined ? { timeoutBudget: timeoutDecision } : {},
|
|
109306
109631
|
continue: !freshRole && continueFlag,
|
|
109307
109632
|
stdinInteractive: false,
|
|
109308
109633
|
...model !== undefined ? { model } : {},
|
|
@@ -109314,7 +109639,7 @@ class AgentRunExecutor {
|
|
|
109314
109639
|
agent,
|
|
109315
109640
|
...model !== undefined ? { model } : {},
|
|
109316
109641
|
invocation: `${attemptInvocation.command} ${attemptInvocation.argv.join(" ")}`,
|
|
109317
|
-
...
|
|
109642
|
+
...effectiveTimeoutMs !== undefined ? { timeoutMs: effectiveTimeoutMs } : {},
|
|
109318
109643
|
...routing !== undefined ? { routing } : {}
|
|
109319
109644
|
});
|
|
109320
109645
|
dispatchStartedAt = Date.now();
|
|
@@ -109359,7 +109684,7 @@ class AgentRunExecutor {
|
|
|
109359
109684
|
try {
|
|
109360
109685
|
result = warmOutcome !== undefined ? warmResult(warmOutcome, dispatchStartedAt) : await runner.runPromptCommand(agent, promptOptions, {
|
|
109361
109686
|
cwd: cwd || undefined,
|
|
109362
|
-
...
|
|
109687
|
+
...effectiveTimeoutMs !== undefined ? { timeout: effectiveTimeoutMs } : {},
|
|
109363
109688
|
signal: controller.signal,
|
|
109364
109689
|
correlation: lifecycle.identity,
|
|
109365
109690
|
...currentExecutor !== undefined ? {
|
|
@@ -109399,8 +109724,19 @@ class AgentRunExecutor {
|
|
|
109399
109724
|
if (result.exitCode === 0)
|
|
109400
109725
|
break;
|
|
109401
109726
|
const quotaConfirmed = currentExecutor !== undefined && runQuotaExhaustedExecutors.has(currentExecutor);
|
|
109727
|
+
const unavailability = executorUnavailabilityEvidence(result);
|
|
109728
|
+
let breakerTripped = false;
|
|
109729
|
+
if (unavailability !== undefined && currentStage !== undefined) {
|
|
109730
|
+
breaker.executors.add(currentStage.executorName);
|
|
109731
|
+
breakerTripped = true;
|
|
109732
|
+
this.recordLadderDecision(ladderDecisions, lifecycle.identity, this.ctx.events, {
|
|
109733
|
+
kind: "breaker-trip",
|
|
109734
|
+
executor: currentStage.executorName,
|
|
109735
|
+
reason: `circuit breaker: ${unavailability}`
|
|
109736
|
+
});
|
|
109737
|
+
}
|
|
109402
109738
|
const classifiedSignal = classifyObjectiveFailure(result);
|
|
109403
|
-
let escalationSignal = quotaConfirmed ? "resource-exhaustion" : classifiedSignal;
|
|
109739
|
+
let escalationSignal = quotaConfirmed ? "resource-exhaustion" : classifiedSignal ?? (breakerTripped ? "resource-exhaustion" : undefined);
|
|
109404
109740
|
if (escalationSignal === undefined && result.signal === undefined && this.ctx.classifyUnmatchedFailure !== undefined) {
|
|
109405
109741
|
const text = `${result.stderr ?? ""}
|
|
109406
109742
|
${result.stdout ?? ""}`;
|
|
@@ -109415,15 +109751,21 @@ ${result.stdout ?? ""}`;
|
|
|
109415
109751
|
}
|
|
109416
109752
|
const matchingFallbacks = currentStage.policy.fallback.filter((fallback) => fallback.trigger === escalationSignal);
|
|
109417
109753
|
if (matchingFallbacks.length === 0 || escalationSignal !== "resource-exhaustion" && attempt >= matchingFallbacks.length) {
|
|
109418
|
-
|
|
109754
|
+
if (!breakerTripped)
|
|
109755
|
+
break;
|
|
109419
109756
|
}
|
|
109420
109757
|
runFlags.signal = escalationSignal;
|
|
109421
109758
|
runFlags["from-executor"] = currentStage.executorName;
|
|
109422
|
-
const nextResolved = await this.resolver.resolveAgent(runFlags, doctorRunner, attemptedExecutors);
|
|
109423
|
-
if (!nextResolved.ok || nextResolved.stage === undefined || attemptedExecutors.has(nextResolved.stage.executorName)) {
|
|
109759
|
+
const nextResolved = await this.resolver.resolveAgent(runFlags, doctorRunner, new Set([...attemptedExecutors, ...breaker.executors]));
|
|
109760
|
+
if (!nextResolved.ok || nextResolved.stage === undefined || attemptedExecutors.has(nextResolved.stage.executorName) || breaker.executors.has(nextResolved.stage.executorName)) {
|
|
109424
109761
|
if (!jsonOutput) {
|
|
109425
109762
|
this.ctx.output.error(`Escalation chain exhausted after ${attempt + 1} attempt(s); stage=${currentStage.stageId}; tiers attempted: ${[...tiersAttempted].join(", ")}; executors tried: ${[...attemptedExecutors].join(", ")}`);
|
|
109426
109763
|
}
|
|
109764
|
+
this.recordLadderDecision(ladderDecisions, lifecycle.identity, this.ctx.events, {
|
|
109765
|
+
kind: "chain-exhausted",
|
|
109766
|
+
executor: currentStage.executorName,
|
|
109767
|
+
reason: `escalation chain exhausted; executors tried: ${[...attemptedExecutors].join(", ")}`
|
|
109768
|
+
});
|
|
109427
109769
|
if (this.ctx.events !== undefined) {
|
|
109428
109770
|
this.ctx.events.emit("agent.invoke.exhausted", {
|
|
109429
109771
|
runId: lifecycle.identity.runId,
|
|
@@ -109438,9 +109780,12 @@ ${result.stdout ?? ""}`;
|
|
|
109438
109780
|
}
|
|
109439
109781
|
break;
|
|
109440
109782
|
}
|
|
109783
|
+
const escalationLine = `Escalating: ${currentStage.executorName} (tier ${currentStage.executorTier}) failed with ${escalationSignal}; retrying on ${nextResolved.stage.executorName} (tier ${nextResolved.stage.executorTier})`;
|
|
109784
|
+
const failedExecutor = currentStage.executorName;
|
|
109441
109785
|
if (!jsonOutput) {
|
|
109442
|
-
this.ctx.output.error(
|
|
109786
|
+
this.ctx.output.error(escalationLine);
|
|
109443
109787
|
}
|
|
109788
|
+
agentLog.append("stderr", escalationLine);
|
|
109444
109789
|
if (this.ctx.events !== undefined) {
|
|
109445
109790
|
this.ctx.events.emit("agent.invoke.escalated", {
|
|
109446
109791
|
runId: lifecycle.identity.runId,
|
|
@@ -109466,6 +109811,12 @@ ${result.stdout ?? ""}`;
|
|
|
109466
109811
|
routing = buildRoutingAttribution(nextResolved);
|
|
109467
109812
|
attemptedExecutors.add(nextResolved.stage.executorName);
|
|
109468
109813
|
tiersAttempted.add(nextResolved.stage.executorTier);
|
|
109814
|
+
this.recordLadderDecision(ladderDecisions, lifecycle.identity, this.ctx.events, {
|
|
109815
|
+
kind: "escalation",
|
|
109816
|
+
executor: failedExecutor,
|
|
109817
|
+
toExecutor: nextResolved.stage.executorName,
|
|
109818
|
+
reason: `escalated on ${escalationSignal}`
|
|
109819
|
+
});
|
|
109469
109820
|
}
|
|
109470
109821
|
} finally {
|
|
109471
109822
|
if (quotaEventBridge !== undefined)
|
|
@@ -109515,6 +109866,7 @@ ${result.stdout ?? ""}`;
|
|
|
109515
109866
|
ok: true,
|
|
109516
109867
|
result,
|
|
109517
109868
|
invocation,
|
|
109869
|
+
...ladderDecisions.length > 0 ? { ladder: ladderDecisions } : {},
|
|
109518
109870
|
...occupantRef !== undefined ? { coordination: { occupant: occupantRef } } : {}
|
|
109519
109871
|
};
|
|
109520
109872
|
}
|
|
@@ -109540,6 +109892,7 @@ var init_agent_run_executor = __esm(() => {
|
|
|
109540
109892
|
init_agent_run_log();
|
|
109541
109893
|
init_fleet_dispatch();
|
|
109542
109894
|
init_run_record();
|
|
109895
|
+
init_adaptive_timeout();
|
|
109543
109896
|
init_agent_invocation_sanitize();
|
|
109544
109897
|
init_agent_routing_attribution();
|
|
109545
109898
|
init_agent_run_preflight();
|
|
@@ -109712,6 +110065,7 @@ class AgentService {
|
|
|
109712
110065
|
return {
|
|
109713
110066
|
exitCode,
|
|
109714
110067
|
invocation: outcome.invocation,
|
|
110068
|
+
...outcome.ladder !== undefined ? { ladder: outcome.ladder } : {},
|
|
109715
110069
|
stdout: result.stdout,
|
|
109716
110070
|
stderr: result.stderr,
|
|
109717
110071
|
durationMs: result.durationMs,
|
|
@@ -119085,7 +119439,10 @@ async function deriveVerifiedOutcome(deps, window2 = {}) {
|
|
|
119085
119439
|
const linkDao = new TaskRunLinkDao(deps.db);
|
|
119086
119440
|
linkRows = [];
|
|
119087
119441
|
for (const run of runs) {
|
|
119088
|
-
|
|
119442
|
+
const startedAt = Date.parse(run.started_at ?? "");
|
|
119443
|
+
if (!Number.isFinite(startedAt))
|
|
119444
|
+
continue;
|
|
119445
|
+
if (window2.until !== null && window2.until !== undefined && startedAt > Date.parse(window2.until))
|
|
119089
119446
|
continue;
|
|
119090
119447
|
for (const link of await linkDao.listByRun(run.id, 100)) {
|
|
119091
119448
|
linkRows.push({
|
|
@@ -128183,10 +128540,19 @@ class TaskFiles {
|
|
|
128183
128540
|
slugify(title) {
|
|
128184
128541
|
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
|
|
128185
128542
|
}
|
|
128543
|
+
async sizeBudgetWarnings(filePath) {
|
|
128544
|
+
try {
|
|
128545
|
+
const doc = MarkdownDocument.parse(await this.ctx.fs.readFile(filePath), "task");
|
|
128546
|
+
return evaluateTaskSize(doc.bodyWithoutFrontmatter).reasons;
|
|
128547
|
+
} catch {
|
|
128548
|
+
return [];
|
|
128549
|
+
}
|
|
128550
|
+
}
|
|
128186
128551
|
}
|
|
128187
128552
|
var init_task_files = __esm(() => {
|
|
128188
128553
|
init_src2();
|
|
128189
128554
|
init_task_service_errors();
|
|
128555
|
+
init_task_size_precheck();
|
|
128190
128556
|
});
|
|
128191
128557
|
|
|
128192
128558
|
// ../../packages/app/src/services/task-section-bodies.ts
|
|
@@ -128524,12 +128890,15 @@ ${templateBackground}`;
|
|
|
128524
128890
|
const ref = { kind: "task", id: wbs, filePath, folder };
|
|
128525
128891
|
return { ref, content };
|
|
128526
128892
|
});
|
|
128527
|
-
|
|
128893
|
+
const sizeWarnings = await this.files.sizeBudgetWarnings(created.ref.filePath);
|
|
128894
|
+
if (featureReopened === undefined && shape.normalized === undefined && sizeWarnings.length === 0) {
|
|
128528
128895
|
return created;
|
|
128896
|
+
}
|
|
128529
128897
|
return {
|
|
128530
128898
|
...created,
|
|
128531
128899
|
...featureReopened !== undefined ? { featureReopened } : {},
|
|
128532
|
-
...shape.normalized !== undefined ? { normalized: shape.normalized } : {}
|
|
128900
|
+
...shape.normalized !== undefined ? { normalized: shape.normalized } : {},
|
|
128901
|
+
...sizeWarnings.length > 0 ? { warnings: [...created.warnings ?? [], ...sizeWarnings] } : {}
|
|
128533
128902
|
};
|
|
128534
128903
|
}
|
|
128535
128904
|
async batchCreate(jsonPath) {
|
|
@@ -128658,7 +129027,14 @@ ${issues}`);
|
|
|
128658
129027
|
const ref = { kind: "task", id: wbs, filePath, folder };
|
|
128659
129028
|
return { ref, content };
|
|
128660
129029
|
});
|
|
128661
|
-
|
|
129030
|
+
const sizeWarnings = await this.files.sizeBudgetWarnings(created.ref.filePath);
|
|
129031
|
+
if (shape.normalized === undefined && sizeWarnings.length === 0)
|
|
129032
|
+
return created;
|
|
129033
|
+
return {
|
|
129034
|
+
...created,
|
|
129035
|
+
...shape.normalized !== undefined ? { normalized: shape.normalized } : {},
|
|
129036
|
+
...sizeWarnings.length > 0 ? { warnings: [...created.warnings ?? [], ...sizeWarnings] } : {}
|
|
129037
|
+
};
|
|
128662
129038
|
}
|
|
128663
129039
|
async deriveBackground(featureId) {
|
|
128664
129040
|
const featuresDir = this.featureLink.deriveFeaturesDir();
|
|
@@ -128725,23 +129101,62 @@ class TaskQueryOps {
|
|
|
128725
129101
|
this.locator = locator;
|
|
128726
129102
|
this.files = files;
|
|
128727
129103
|
}
|
|
128728
|
-
async
|
|
129104
|
+
async readTaskSummary(wbs) {
|
|
128729
129105
|
const filePath = await this.files.resolveTaskFile(wbs);
|
|
128730
129106
|
const raw = await this.ctx.fs.readFile(filePath);
|
|
128731
129107
|
const doc = MarkdownDocument.parse(raw, "task");
|
|
128732
129108
|
const fm = doc.frontmatterData ?? {};
|
|
128733
129109
|
const { status, featureId } = taskMetadata(fm);
|
|
128734
129110
|
return {
|
|
128735
|
-
|
|
128736
|
-
|
|
128737
|
-
|
|
128738
|
-
|
|
128739
|
-
|
|
128740
|
-
|
|
128741
|
-
|
|
128742
|
-
|
|
129111
|
+
summary: {
|
|
129112
|
+
wbs,
|
|
129113
|
+
name: fm.name ?? "",
|
|
129114
|
+
status,
|
|
129115
|
+
filePath,
|
|
129116
|
+
dependencies: Array.isArray(fm.dependencies) ? fm.dependencies.filter((dep) => typeof dep === "string") : [],
|
|
129117
|
+
feature_id: featureId,
|
|
129118
|
+
frontmatter: fm
|
|
129119
|
+
},
|
|
129120
|
+
doc,
|
|
129121
|
+
raw
|
|
128743
129122
|
};
|
|
128744
129123
|
}
|
|
129124
|
+
async show(wbs) {
|
|
129125
|
+
const { summary, doc } = await this.readTaskSummary(wbs);
|
|
129126
|
+
return { ...summary, content: doc.bodyWithoutFrontmatter };
|
|
129127
|
+
}
|
|
129128
|
+
async showSections(wbs, requested) {
|
|
129129
|
+
const { summary, doc, raw } = await this.readTaskSummary(wbs);
|
|
129130
|
+
const present = doc.sectionNames;
|
|
129131
|
+
const unknown = requested.filter((name) => !present.includes(name));
|
|
129132
|
+
if (unknown.length > 0) {
|
|
129133
|
+
const err = new Error(`Unknown section(s): ${unknown.map((n) => `"${n}"`).join(", ")}. ` + `Sections in task ${wbs}: ${present.join(", ") || "(none)"}`);
|
|
129134
|
+
err.cliCode = "VALIDATION_FAILED";
|
|
129135
|
+
throw err;
|
|
129136
|
+
}
|
|
129137
|
+
const sections = present.filter((name) => requested.includes(name)).map((name) => {
|
|
129138
|
+
const content = doc.sectionContent(name);
|
|
129139
|
+
return {
|
|
129140
|
+
name,
|
|
129141
|
+
headingLine: content?.headingLine ?? "",
|
|
129142
|
+
byteCount: Buffer.byteLength(content?.text ?? ""),
|
|
129143
|
+
content: content?.text ?? ""
|
|
129144
|
+
};
|
|
129145
|
+
});
|
|
129146
|
+
return { ...summary, sections, totalBytes: Buffer.byteLength(raw) };
|
|
129147
|
+
}
|
|
129148
|
+
async showSummary(wbs) {
|
|
129149
|
+
const { summary, doc, raw } = await this.readTaskSummary(wbs);
|
|
129150
|
+
const sections = doc.sectionNames.map((name) => {
|
|
129151
|
+
const content = doc.sectionContent(name);
|
|
129152
|
+
return {
|
|
129153
|
+
name,
|
|
129154
|
+
headingLine: content?.headingLine ?? "",
|
|
129155
|
+
byteCount: Buffer.byteLength(content?.text ?? "")
|
|
129156
|
+
};
|
|
129157
|
+
});
|
|
129158
|
+
return { ...summary, sections, totalBytes: Buffer.byteLength(raw) };
|
|
129159
|
+
}
|
|
128745
129160
|
async refresh() {
|
|
128746
129161
|
const folders = this.files.allFolderDirs();
|
|
128747
129162
|
let totalTasks = 0;
|
|
@@ -130327,7 +130742,9 @@ class TaskUpdateOps {
|
|
|
130327
130742
|
async updateBody(wbs, body, _actor) {
|
|
130328
130743
|
const filePath = await this.files.resolveTaskFile(wbs);
|
|
130329
130744
|
const ref = { kind: "task", id: wbs, filePath, folder: this.ctx.tasksDir };
|
|
130330
|
-
|
|
130745
|
+
const result = await this.writeService.updateBody(ref, body);
|
|
130746
|
+
const sizeWarnings = await this.files.sizeBudgetWarnings(filePath);
|
|
130747
|
+
return sizeWarnings.length > 0 ? { ...result, warnings: [...result.warnings ?? [], ...sizeWarnings] } : result;
|
|
130331
130748
|
}
|
|
130332
130749
|
async updateSection(wbs, sectionName, sourceFile, append = false) {
|
|
130333
130750
|
const filePath = await this.files.resolveTaskFile(wbs);
|
|
@@ -130467,6 +130884,12 @@ class TaskService {
|
|
|
130467
130884
|
async show(wbs) {
|
|
130468
130885
|
return this.queryOps.show(wbs);
|
|
130469
130886
|
}
|
|
130887
|
+
async showSections(wbs, requested) {
|
|
130888
|
+
return this.queryOps.showSections(wbs, requested);
|
|
130889
|
+
}
|
|
130890
|
+
async showSummary(wbs) {
|
|
130891
|
+
return this.queryOps.showSummary(wbs);
|
|
130892
|
+
}
|
|
130470
130893
|
async updateStatus(wbs, toStatus, actor) {
|
|
130471
130894
|
return this.updateOps.updateStatus(wbs, toStatus, actor);
|
|
130472
130895
|
}
|
|
@@ -131829,6 +132252,20 @@ class CommandGateActionRunner {
|
|
|
131829
132252
|
error: `resultFile ${error.message}`
|
|
131830
132253
|
};
|
|
131831
132254
|
}
|
|
132255
|
+
let logNormalized;
|
|
132256
|
+
if (options.logFile !== undefined && options.logFile !== null && options.logFile !== "") {
|
|
132257
|
+
if (typeof options.logFile !== "string") {
|
|
132258
|
+
return { ok: false, error: 'Action option "logFile" must be a string resolving under .spur/run/' };
|
|
132259
|
+
}
|
|
132260
|
+
try {
|
|
132261
|
+
logNormalized = await resolveRunArtifactPath(this.fileSystem, workdir, options.logFile);
|
|
132262
|
+
} catch (error) {
|
|
132263
|
+
return {
|
|
132264
|
+
ok: false,
|
|
132265
|
+
error: `logFile ${error.message}`
|
|
132266
|
+
};
|
|
132267
|
+
}
|
|
132268
|
+
}
|
|
131832
132269
|
const split = splitLaunchCommand(executable, 'command.gate "executable"');
|
|
131833
132270
|
if ("error" in split) {
|
|
131834
132271
|
return { ok: false, error: split.error };
|
|
@@ -131853,6 +132290,7 @@ class CommandGateActionRunner {
|
|
|
131853
132290
|
let lastExitCode = -1;
|
|
131854
132291
|
let attemptsRun = 0;
|
|
131855
132292
|
let timedOut = false;
|
|
132293
|
+
let capturedLog = "";
|
|
131856
132294
|
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
131857
132295
|
attemptsRun = attempt;
|
|
131858
132296
|
const res = await this.processExecutor.run({
|
|
@@ -131868,7 +132306,14 @@ class CommandGateActionRunner {
|
|
|
131868
132306
|
lastStderr = res.stderr;
|
|
131869
132307
|
lastExitCode = res.exitCode ?? -1;
|
|
131870
132308
|
timedOut = res.outcome === "timeout";
|
|
132309
|
+
capturedLog += `--- attempt ${attempt} exit ${lastExitCode}${timedOut ? " (timeout)" : ""}
|
|
132310
|
+
${lastStdout}${lastStderr}`;
|
|
131871
132311
|
if (res.exitCode === 0) {
|
|
132312
|
+
if (logNormalized !== undefined) {
|
|
132313
|
+
await this.fileSystem.ensureDir(normalize3(join59(logNormalized, "..")));
|
|
132314
|
+
await this.fileSystem.writeFile(logNormalized, `${capturedLog}gate outcome: PASS (exit 0)
|
|
132315
|
+
`);
|
|
132316
|
+
}
|
|
131872
132317
|
await this.fileSystem.writeFile(normalized, `PASS
|
|
131873
132318
|
`);
|
|
131874
132319
|
return {
|
|
@@ -131879,7 +132324,8 @@ class CommandGateActionRunner {
|
|
|
131879
132324
|
attempts: attemptsRun,
|
|
131880
132325
|
stdout: lastStdout,
|
|
131881
132326
|
stderr: lastStderr,
|
|
131882
|
-
resultFile: normalized
|
|
132327
|
+
resultFile: normalized,
|
|
132328
|
+
...logNormalized !== undefined ? { logFile: logNormalized } : {}
|
|
131883
132329
|
}
|
|
131884
132330
|
};
|
|
131885
132331
|
}
|
|
@@ -131894,6 +132340,11 @@ ${lastStderr}`;
|
|
|
131894
132340
|
}
|
|
131895
132341
|
break;
|
|
131896
132342
|
}
|
|
132343
|
+
if (logNormalized !== undefined) {
|
|
132344
|
+
await this.fileSystem.ensureDir(normalize3(join59(logNormalized, "..")));
|
|
132345
|
+
await this.fileSystem.writeFile(logNormalized, `${capturedLog}gate outcome: ${commandGateOutcome(lastExitCode)} (exit ${lastExitCode})
|
|
132346
|
+
`);
|
|
132347
|
+
}
|
|
131897
132348
|
await this.fileSystem.writeFile(normalized, `${commandGateOutcome(lastExitCode)}
|
|
131898
132349
|
`);
|
|
131899
132350
|
const failureData = {
|
|
@@ -131903,6 +132354,7 @@ ${lastStderr}`;
|
|
|
131903
132354
|
stdout: lastStdout,
|
|
131904
132355
|
stderr: lastStderr,
|
|
131905
132356
|
resultFile: normalized,
|
|
132357
|
+
...logNormalized !== undefined ? { logFile: logNormalized } : {},
|
|
131906
132358
|
...timedOut ? { timedOut: true, timeoutMs } : {}
|
|
131907
132359
|
};
|
|
131908
132360
|
if (softFail) {
|
|
@@ -135927,6 +136379,18 @@ class WorkflowInspectOps {
|
|
|
135927
136379
|
const entries = await Promise.all(rows.map(async (row) => rowToTraceEntry(row, this.ctx.cwd, Date.now(), row.status === "paused" || row.status === "interrupted" ? pausedNodeBudgetMs(await actionDao.actionRowsByRunId(row.id)) : undefined)));
|
|
135928
136380
|
return { entries, total: rows.length };
|
|
135929
136381
|
}
|
|
136382
|
+
async resolveRunId(db, input) {
|
|
136383
|
+
const runDao = new RunDao(db);
|
|
136384
|
+
if (await runDao.traceRowById(input))
|
|
136385
|
+
return input;
|
|
136386
|
+
const rows = await runDao.traceRowsByIdPrefix(input);
|
|
136387
|
+
if (rows.length === 0)
|
|
136388
|
+
throw new Error(`Run not found: ${input}`);
|
|
136389
|
+
if (rows.length === 1)
|
|
136390
|
+
return rows[0]?.id ?? input;
|
|
136391
|
+
const candidates = rows.slice(0, 20).map((r) => r.id);
|
|
136392
|
+
throw new Error(`Ambiguous run id prefix: ${input}; candidates: ${candidates.join(", ")}`);
|
|
136393
|
+
}
|
|
135930
136394
|
async traceRun(db, runId, preloadedRow) {
|
|
135931
136395
|
const runDao = new RunDao(db);
|
|
135932
136396
|
const row = preloadedRow ?? await runDao.traceRowById(runId);
|
|
@@ -137207,10 +137671,18 @@ class WorkflowAppService {
|
|
|
137207
137671
|
const db = await this.ctx.getDb();
|
|
137208
137672
|
if (typeof runIdOrFilter === "string") {
|
|
137209
137673
|
const probe = await reapIfWorkerLost(db, this.ctx.cwd, runIdOrFilter);
|
|
137210
|
-
|
|
137674
|
+
if (probe.row !== undefined) {
|
|
137675
|
+
return this.inspectOps.traceRun(db, runIdOrFilter, probe.reaped ? undefined : probe.row);
|
|
137676
|
+
}
|
|
137677
|
+
const resolved = await this.inspectOps.resolveRunId(db, runIdOrFilter);
|
|
137678
|
+
const resolvedProbe = await reapIfWorkerLost(db, this.ctx.cwd, resolved);
|
|
137679
|
+
return this.inspectOps.traceRun(db, resolved, resolvedProbe.reaped ? undefined : resolvedProbe.row);
|
|
137211
137680
|
}
|
|
137212
137681
|
return this.inspectOps.traceList(db, runIdOrFilter ?? {});
|
|
137213
137682
|
}
|
|
137683
|
+
async resolveRunId(runId) {
|
|
137684
|
+
return this.inspectOps.resolveRunId(await this.ctx.getDb(), runId);
|
|
137685
|
+
}
|
|
137214
137686
|
inspectRunRecord(runId) {
|
|
137215
137687
|
return this.inspectOps.inspectRunRecord(runId);
|
|
137216
137688
|
}
|
|
@@ -146170,6 +146642,83 @@ function resolveReceiptReuse(receiptPath, wbs, capture) {
|
|
|
146170
146642
|
const receiptStatus = readReceiptStatus(receiptPath, capture.tree, wbs);
|
|
146171
146643
|
return { reuse: receiptStatus.reuse, receiptStatus };
|
|
146172
146644
|
}
|
|
146645
|
+
function workspaceGraphFrom(workspaces) {
|
|
146646
|
+
const byName = new Map;
|
|
146647
|
+
for (const workspace of workspaces) {
|
|
146648
|
+
if (workspace.name.length > 0)
|
|
146649
|
+
byName.set(workspace.name, workspace.dir);
|
|
146650
|
+
}
|
|
146651
|
+
const dependents = new Map;
|
|
146652
|
+
for (const workspace of workspaces) {
|
|
146653
|
+
for (const dependency of new Set(workspace.dependencies)) {
|
|
146654
|
+
if (dependency === workspace.name || !byName.has(dependency))
|
|
146655
|
+
continue;
|
|
146656
|
+
const dirs = dependents.get(dependency) ?? [];
|
|
146657
|
+
if (!dirs.includes(workspace.dir))
|
|
146658
|
+
dirs.push(workspace.dir);
|
|
146659
|
+
dependents.set(dependency, dirs);
|
|
146660
|
+
}
|
|
146661
|
+
}
|
|
146662
|
+
return { byName, dependents };
|
|
146663
|
+
}
|
|
146664
|
+
function dependentsOf(graph, dir) {
|
|
146665
|
+
for (const [name, owner] of graph.byName) {
|
|
146666
|
+
if (owner !== dir)
|
|
146667
|
+
continue;
|
|
146668
|
+
return graph.dependents.get(name) ?? [];
|
|
146669
|
+
}
|
|
146670
|
+
return [];
|
|
146671
|
+
}
|
|
146672
|
+
function workspaceDependenciesOf(abs, dir) {
|
|
146673
|
+
let name = "";
|
|
146674
|
+
const dependencies = [];
|
|
146675
|
+
try {
|
|
146676
|
+
const pkg = JSON.parse(readFileSync21(abs(`${dir}/package.json`), "utf8"));
|
|
146677
|
+
if (typeof pkg.name === "string")
|
|
146678
|
+
name = pkg.name;
|
|
146679
|
+
for (const section of [pkg.dependencies, pkg.devDependencies, pkg.peerDependencies]) {
|
|
146680
|
+
if (section !== null && typeof section === "object")
|
|
146681
|
+
dependencies.push(...Object.keys(section));
|
|
146682
|
+
}
|
|
146683
|
+
} catch {}
|
|
146684
|
+
return { name, dependencies };
|
|
146685
|
+
}
|
|
146686
|
+
function readWorkspaceGraph(cwd) {
|
|
146687
|
+
const abs = (p) => cwd ? join78(cwd, p) : p;
|
|
146688
|
+
const info = [];
|
|
146689
|
+
let globs = [];
|
|
146690
|
+
try {
|
|
146691
|
+
const root = JSON.parse(readFileSync21(abs("package.json"), "utf8"));
|
|
146692
|
+
const declared = Array.isArray(root.workspaces) ? root.workspaces : root.workspaces?.packages;
|
|
146693
|
+
if (Array.isArray(declared))
|
|
146694
|
+
globs = declared.filter((g) => typeof g === "string");
|
|
146695
|
+
} catch {
|
|
146696
|
+
return { byName: new Map, dependents: new Map };
|
|
146697
|
+
}
|
|
146698
|
+
const dirs = new Set;
|
|
146699
|
+
for (const glob of globs) {
|
|
146700
|
+
if (glob.endsWith("/*")) {
|
|
146701
|
+
const parent = glob.slice(0, -2);
|
|
146702
|
+
let entries = [];
|
|
146703
|
+
try {
|
|
146704
|
+
entries = readdirSync11(abs(parent));
|
|
146705
|
+
} catch {
|
|
146706
|
+
continue;
|
|
146707
|
+
}
|
|
146708
|
+
for (const entry of entries) {
|
|
146709
|
+
if (existsSync23(abs(`${parent}/${entry}/package.json`)))
|
|
146710
|
+
dirs.add(`${parent}/${entry}`);
|
|
146711
|
+
}
|
|
146712
|
+
} else if (existsSync23(abs(`${glob}/package.json`))) {
|
|
146713
|
+
dirs.add(glob);
|
|
146714
|
+
}
|
|
146715
|
+
}
|
|
146716
|
+
for (const dir of [...dirs].sort()) {
|
|
146717
|
+
const { name, dependencies } = workspaceDependenciesOf(abs, dir);
|
|
146718
|
+
info.push({ dir, name, dependencies });
|
|
146719
|
+
}
|
|
146720
|
+
return workspaceGraphFrom(info);
|
|
146721
|
+
}
|
|
146173
146722
|
function workspaceOf(file, exists) {
|
|
146174
146723
|
const segments = file.split("/");
|
|
146175
146724
|
for (let depth = 1;depth < segments.length; depth++) {
|
|
@@ -146179,7 +146728,7 @@ function workspaceOf(file, exists) {
|
|
|
146179
146728
|
}
|
|
146180
146729
|
return null;
|
|
146181
146730
|
}
|
|
146182
|
-
function lightScope(changedFiles, exists = existsSync23) {
|
|
146731
|
+
function lightScope(changedFiles, exists = existsSync23, graph) {
|
|
146183
146732
|
const files = [];
|
|
146184
146733
|
const workspaces = new Set;
|
|
146185
146734
|
const tests = new Set;
|
|
@@ -146229,7 +146778,29 @@ function lightScope(changedFiles, exists = existsSync23) {
|
|
|
146229
146778
|
tests.add(file);
|
|
146230
146779
|
}
|
|
146231
146780
|
}
|
|
146232
|
-
|
|
146781
|
+
let fallback;
|
|
146782
|
+
if (graph !== undefined) {
|
|
146783
|
+
for (const file of changedFiles) {
|
|
146784
|
+
if (file.startsWith("plugins/sp/"))
|
|
146785
|
+
continue;
|
|
146786
|
+
const owner = workspaceOf(file, exists);
|
|
146787
|
+
if (owner === null) {
|
|
146788
|
+
fallback = `unmapped path ${file} (no owning workspace package.json) \u2014 full suite required`;
|
|
146789
|
+
break;
|
|
146790
|
+
}
|
|
146791
|
+
const dependents = dependentsOf(graph, owner);
|
|
146792
|
+
if (dependents.length > 0) {
|
|
146793
|
+
fallback = `shared package ${owner} (depended on by ${dependents.join(", ")}) \u2014 full suite required`;
|
|
146794
|
+
break;
|
|
146795
|
+
}
|
|
146796
|
+
}
|
|
146797
|
+
}
|
|
146798
|
+
return {
|
|
146799
|
+
files,
|
|
146800
|
+
workspaces: [...workspaces].sort(),
|
|
146801
|
+
tests: [...tests].sort(),
|
|
146802
|
+
...fallback !== undefined ? { fallback } : {}
|
|
146803
|
+
};
|
|
146233
146804
|
}
|
|
146234
146805
|
function workspaceHasTypecheck(workspace) {
|
|
146235
146806
|
try {
|
|
@@ -146272,10 +146843,21 @@ function planLightChecks(scope, hasTypecheck = workspaceHasTypecheck, builder) {
|
|
|
146272
146843
|
}
|
|
146273
146844
|
return plans;
|
|
146274
146845
|
}
|
|
146275
|
-
function
|
|
146846
|
+
function readBaseSha(path) {
|
|
146847
|
+
if (path === undefined)
|
|
146848
|
+
return null;
|
|
146849
|
+
try {
|
|
146850
|
+
const line = readFileSync21(path, "utf8").trim();
|
|
146851
|
+
return /^[0-9a-f]{7,64}$/i.test(line) ? line : null;
|
|
146852
|
+
} catch {
|
|
146853
|
+
return null;
|
|
146854
|
+
}
|
|
146855
|
+
}
|
|
146856
|
+
function gitChangedFiles(cwd, timeoutMs, baseShaPath) {
|
|
146276
146857
|
const abs = (p) => cwd ? join78(cwd, p) : p;
|
|
146277
146858
|
const changed = new Set;
|
|
146278
|
-
|
|
146859
|
+
const base = readBaseSha(baseShaPath) ?? "HEAD";
|
|
146860
|
+
for (const cmd of [`git diff --name-only ${base}`, "git ls-files --others --exclude-standard"]) {
|
|
146279
146861
|
const result = runShellCommand(cmd, cwd, undefined, timeoutMs);
|
|
146280
146862
|
if (result.timedOut)
|
|
146281
146863
|
return null;
|
|
@@ -146302,6 +146884,7 @@ function runDeferredGate(env, options = {}) {
|
|
|
146302
146884
|
writeFileSync14(abs(statusFile), `${status}
|
|
146303
146885
|
`);
|
|
146304
146886
|
const note = status === "DEFERRED" ? `deferred gate: light tier PASS \u2014 the integrated full gate is owed on the batch base ref (1111 R3)
|
|
146887
|
+
` : light.blocked !== undefined ? `deferred gate: light tier BLOCKED (${light.blocked}) \u2014 no scoped tier could decide; the integrated full gate stays owed (1111 R3; 1322 R3)
|
|
146305
146888
|
` : `deferred gate: light tier FAIL \u2014 the task's bounded fix lane runs; the integrated full gate stays owed (1111 R3)
|
|
146306
146889
|
`;
|
|
146307
146890
|
appendFileSync7(abs(light.logFile), note);
|
|
@@ -146318,7 +146901,7 @@ function runLightGate(env, options = {}) {
|
|
|
146318
146901
|
const receiptFile = join78(runDir, `${env.wbs}-check-receipt.json`);
|
|
146319
146902
|
writeFileSync14(abs(logFile), "");
|
|
146320
146903
|
const timeoutMs = gateTimeoutMsFor(env, options);
|
|
146321
|
-
const changed = gitChangedFiles(cwd, timeoutMs);
|
|
146904
|
+
const changed = gitChangedFiles(cwd, timeoutMs, abs(join78(runDir, `${env.wbs}-base.sha`)));
|
|
146322
146905
|
if (changed === null) {
|
|
146323
146906
|
const line = `--- light gate BLOCKED: changed-path git read timed out after ${timeoutMs}ms
|
|
146324
146907
|
`;
|
|
@@ -146339,7 +146922,29 @@ function runLightGate(env, options = {}) {
|
|
|
146339
146922
|
appendFileSync7(abs(logFile), `--- light receipt: no git tree identity \u2014 ${capture.error}
|
|
146340
146923
|
`);
|
|
146341
146924
|
}
|
|
146342
|
-
const scope = lightScope(changed, (p) => existsSync23(abs(p)));
|
|
146925
|
+
const scope = lightScope(changed, (p) => existsSync23(abs(p)), readWorkspaceGraph(cwd));
|
|
146926
|
+
const scopeHeader = scope.fallback !== undefined ? `--- light scope: full-suite-fallback \u2014 ${scope.fallback}
|
|
146927
|
+
--- light scope files (${changed.length}): ${changed.join(" ")}
|
|
146928
|
+
` : `--- light scope: selected \u2014 workspaces: ${scope.workspaces.join(" ") || "(none)"}; related tests: ${scope.tests.length}; files: ${changed.length}
|
|
146929
|
+
--- light scope files (${changed.length}): ${changed.join(" ")}
|
|
146930
|
+
`;
|
|
146931
|
+
appendFileSync7(abs(logFile), scopeHeader);
|
|
146932
|
+
if (scope.fallback !== undefined) {
|
|
146933
|
+
const line = `--- light gate BLOCKED: affected-scope fallback \u2014 full gate owns this tree
|
|
146934
|
+
`;
|
|
146935
|
+
appendFileSync7(abs(logFile), line);
|
|
146936
|
+
process.stdout.write(`light gate BLOCKED \u2014 ${scope.fallback}
|
|
146937
|
+
`);
|
|
146938
|
+
return {
|
|
146939
|
+
status: "FAIL",
|
|
146940
|
+
scope,
|
|
146941
|
+
checks: [],
|
|
146942
|
+
logFile,
|
|
146943
|
+
receiptFile,
|
|
146944
|
+
blocked: scope.fallback,
|
|
146945
|
+
fallback: scope.fallback
|
|
146946
|
+
};
|
|
146947
|
+
}
|
|
146343
146948
|
const plans = planLightChecks(scope, (workspace) => workspaceHasTypecheck(abs(workspace)), options.builder);
|
|
146344
146949
|
const reusable = new Map;
|
|
146345
146950
|
let preserveFullReceipt = false;
|
|
@@ -146401,6 +147006,11 @@ function runLightGate(env, options = {}) {
|
|
|
146401
147006
|
appendFileSync7(abs(logFile), `--- light receipt: not written \u2014 the full-tier receipt at the same git tree is preserved
|
|
146402
147007
|
`);
|
|
146403
147008
|
process.stdout.write(`light gate ${receipt.status} (${scope.files.length} changed files; checks: ${checks.length},` + ` skipped: ${skipped}, unbound: ${unbound}; receipt: ${receiptFile} preserved (full tier); log: ${logFile})
|
|
147009
|
+
`);
|
|
147010
|
+
} else if (!capture.ok) {
|
|
147011
|
+
appendFileSync7(abs(logFile), `--- light receipt: not written \u2014 git tree capture failed (${capture.error})
|
|
147012
|
+
`);
|
|
147013
|
+
process.stdout.write(`light gate ${receipt.status} (${scope.files.length} changed files; checks: ${checks.length},` + ` skipped: ${skipped}, unbound: ${unbound}; receipt: none (git tree capture failed); log: ${logFile})
|
|
146404
147014
|
`);
|
|
146405
147015
|
} else {
|
|
146406
147016
|
writeFileSync14(abs(receiptFile), `${JSON.stringify(receipt, null, 2)}
|
|
@@ -146709,6 +147319,37 @@ function runQualityGate(mode, env, options = {}) {
|
|
|
146709
147319
|
appendFileSync7(abs(logFile), line);
|
|
146710
147320
|
gateRc = 1;
|
|
146711
147321
|
}
|
|
147322
|
+
if (mode === "recheck" && commandPresent && gateRc === 0) {
|
|
147323
|
+
const light = runLightGate(env, options);
|
|
147324
|
+
appendFileSync7(abs(logFile), `--- gate visit ${mode}: affected-scope light tier (1322) \u2014 ${light.status}
|
|
147325
|
+
${readFileSync21(abs(light.logFile), "utf8")}`);
|
|
147326
|
+
if (light.fallback === undefined) {
|
|
147327
|
+
if (light.status === "FAIL") {
|
|
147328
|
+
writeFileSync14(abs(findingsFile), extractFindings(readFileSync21(abs(logFile), "utf8")));
|
|
147329
|
+
writeFileSync14(abs(statusFile), `FAIL
|
|
147330
|
+
`);
|
|
147331
|
+
appendFileSync7(abs(logFile), `git-tree: ${tree.ok ? tree.tree : "unbound"}
|
|
147332
|
+
`);
|
|
147333
|
+
process.stdout.write(`quality gate FAIL (light tier) \u2014 last 40 lines follow (full log: ${logFile})
|
|
147334
|
+
`);
|
|
147335
|
+
process.stdout.write(tailLines(readFileSync21(abs(logFile), "utf8"), 40));
|
|
147336
|
+
return {
|
|
147337
|
+
status: "FAIL",
|
|
147338
|
+
attempts: 0,
|
|
147339
|
+
logFile,
|
|
147340
|
+
findingsFile,
|
|
147341
|
+
statusFile,
|
|
147342
|
+
attemptFile,
|
|
147343
|
+
receiptFile: light.receiptFile
|
|
147344
|
+
};
|
|
147345
|
+
}
|
|
147346
|
+
} else {
|
|
147347
|
+
const line = `check.scope-fallback \u2014 ${light.fallback}; probe + full gate follow
|
|
147348
|
+
`;
|
|
147349
|
+
process.stdout.write(line);
|
|
147350
|
+
appendFileSync7(abs(logFile), line);
|
|
147351
|
+
}
|
|
147352
|
+
}
|
|
146712
147353
|
if (mode === "recheck" && gateRc === 0 && (env.gateProbeCmd ?? "").length > 0) {
|
|
146713
147354
|
const probe = runShellCommand(env.gateProbeCmd ?? "", cwd, undefined, timeoutMs);
|
|
146714
147355
|
writeFileSync14(abs(`${logFile}.probe`), probe.output);
|
|
@@ -147279,6 +147920,15 @@ function parseProvenance(reportMarkdown) {
|
|
|
147279
147920
|
schemaVersion: obj.schemaVersion == null ? undefined : Number(obj.schemaVersion),
|
|
147280
147921
|
executor: optionalString(obj.executor),
|
|
147281
147922
|
model: optionalString(obj.model),
|
|
147923
|
+
executorProfile: optionalString(obj.executorProfile),
|
|
147924
|
+
dispatches: Array.isArray(obj.dispatches) ? obj.dispatches.map((d) => ({
|
|
147925
|
+
node: String(d.node ?? ""),
|
|
147926
|
+
actionId: String(d.actionId ?? ""),
|
|
147927
|
+
agent: String(d.agent ?? ""),
|
|
147928
|
+
executor: String(d.executor ?? ""),
|
|
147929
|
+
model: String(d.model ?? ""),
|
|
147930
|
+
timeoutMs: d.timeoutMs === "unbounded" ? "unbounded" : Number(d.timeoutMs ?? 0)
|
|
147931
|
+
})) : undefined,
|
|
147282
147932
|
cacheDisposition: DISPOSITIONS.includes(obj.cacheDisposition) ? obj.cacheDisposition : undefined
|
|
147283
147933
|
};
|
|
147284
147934
|
} catch {
|
|
@@ -147322,6 +147972,88 @@ function decideCache(cached, current, opts) {
|
|
|
147322
147972
|
reasons.push("window-closed");
|
|
147323
147973
|
return { disposition: reasons.length === 0 ? "hit" : "miss", reasons };
|
|
147324
147974
|
}
|
|
147975
|
+
function evaluateCalcAssertion(expr) {
|
|
147976
|
+
const text = expr.trim().replace(/^`?calc`?\s*:\s*/i, "").trim();
|
|
147977
|
+
const cmp = text.match(new RegExp(`^(${NUM})\\s*(<=|>=|<|>)\\s*(${NUM})$`));
|
|
147978
|
+
if (cmp !== null) {
|
|
147979
|
+
const left = Number(cmp[1]);
|
|
147980
|
+
const right = Number(cmp[3]);
|
|
147981
|
+
switch (cmp[2]) {
|
|
147982
|
+
case "<":
|
|
147983
|
+
return left < right;
|
|
147984
|
+
case "<=":
|
|
147985
|
+
return left <= right;
|
|
147986
|
+
case ">":
|
|
147987
|
+
return left > right;
|
|
147988
|
+
default:
|
|
147989
|
+
return left >= right;
|
|
147990
|
+
}
|
|
147991
|
+
}
|
|
147992
|
+
const eq = text.match(new RegExp(`^(${NUM}(?:\\s*[-+*/]\\s*${NUM})+)\\s*=\\s*(${NUM})$`));
|
|
147993
|
+
if (eq === null)
|
|
147994
|
+
return;
|
|
147995
|
+
const tokens = (eq[1] ?? "").split(/\s*([-+*/])\s*/);
|
|
147996
|
+
let acc = Number(tokens[0]);
|
|
147997
|
+
let exact = Number.isInteger(acc);
|
|
147998
|
+
let divided = false;
|
|
147999
|
+
for (let i = 1;i < tokens.length; i += 2) {
|
|
148000
|
+
const op = tokens[i];
|
|
148001
|
+
const operand = Number(tokens[i + 1]);
|
|
148002
|
+
if (op === "+")
|
|
148003
|
+
acc += operand;
|
|
148004
|
+
else if (op === "-")
|
|
148005
|
+
acc -= operand;
|
|
148006
|
+
else if (op === "*")
|
|
148007
|
+
acc *= operand;
|
|
148008
|
+
else {
|
|
148009
|
+
if (operand === 0)
|
|
148010
|
+
return false;
|
|
148011
|
+
acc /= operand;
|
|
148012
|
+
divided = true;
|
|
148013
|
+
}
|
|
148014
|
+
exact = exact && Number.isInteger(operand);
|
|
148015
|
+
}
|
|
148016
|
+
const expected = Number(eq[2]);
|
|
148017
|
+
if (!divided && exact && Number.isInteger(expected))
|
|
148018
|
+
return acc === expected;
|
|
148019
|
+
const scale = Math.max(Math.abs(acc), Math.abs(expected), 1);
|
|
148020
|
+
return Math.abs(acc - expected) <= 0.000000001 * scale;
|
|
148021
|
+
}
|
|
148022
|
+
function auditArithmetic(findingsBody, problems) {
|
|
148023
|
+
const blockList = findingsBody.split(/^###\s+/m).slice(1);
|
|
148024
|
+
for (const block of blockList) {
|
|
148025
|
+
const lines = block.split(`
|
|
148026
|
+
`);
|
|
148027
|
+
const calcLines = lines.filter((l) => CALC_LINE.test(l));
|
|
148028
|
+
for (const line of calcLines) {
|
|
148029
|
+
const expr = line.slice(line.search(/calc/i));
|
|
148030
|
+
const verdict = evaluateCalcAssertion(expr);
|
|
148031
|
+
const shown = expr.replace(/^`?calc`?\s*:\s*/i, "").trim();
|
|
148032
|
+
if (verdict === undefined)
|
|
148033
|
+
problems.push(`arithmetic-invalid-assertion:${shown}`);
|
|
148034
|
+
else if (!verdict)
|
|
148035
|
+
problems.push(`arithmetic-contradiction:${shown}`);
|
|
148036
|
+
}
|
|
148037
|
+
const fieldNames = ["observation", "inference"];
|
|
148038
|
+
for (const field of fieldNames) {
|
|
148039
|
+
const start = lines.findIndex((l) => new RegExp(`^[-*\\s]*\\*{0,2}\`?${field}\`?\\*{0,2}\\s*:`).test(l));
|
|
148040
|
+
if (start === -1)
|
|
148041
|
+
continue;
|
|
148042
|
+
let end = lines.length;
|
|
148043
|
+
for (let i = start + 1;i < lines.length; i++) {
|
|
148044
|
+
if (/^[-*]\s*`?[a-zA-Z]+`?\s*:/.test(lines[i] ?? "")) {
|
|
148045
|
+
end = i;
|
|
148046
|
+
break;
|
|
148047
|
+
}
|
|
148048
|
+
}
|
|
148049
|
+
const span = lines.slice(start, end).join(`
|
|
148050
|
+
`);
|
|
148051
|
+
if (DERIVED_SIGNAL.test(span) && !CALC_LINE.test(span)) {
|
|
148052
|
+
problems.push(`arithmetic-evidence-missing:${field}`);
|
|
148053
|
+
}
|
|
148054
|
+
}
|
|
148055
|
+
}
|
|
148056
|
+
}
|
|
147325
148057
|
function escapeRe(s) {
|
|
147326
148058
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
147327
148059
|
}
|
|
@@ -147379,6 +148111,7 @@ function checkReportStructure(reportMarkdown) {
|
|
|
147379
148111
|
problems.push(`finding-invalid-key-category:${firstSegment}`);
|
|
147380
148112
|
}
|
|
147381
148113
|
}
|
|
148114
|
+
auditArithmetic(findingsBody, problems);
|
|
147382
148115
|
}
|
|
147383
148116
|
const ledgerIdx = reportMarkdown.search(/^#{2,3}\s+Evidence\s+ledger/im);
|
|
147384
148117
|
if (ledgerIdx !== -1) {
|
|
@@ -147561,6 +148294,66 @@ function validateSelector(opts) {
|
|
|
147561
148294
|
until: val(opts.until)
|
|
147562
148295
|
};
|
|
147563
148296
|
}
|
|
148297
|
+
function collectAgentRuns(node, out) {
|
|
148298
|
+
if (Array.isArray(node)) {
|
|
148299
|
+
for (const item of node)
|
|
148300
|
+
collectAgentRuns(item, out);
|
|
148301
|
+
return;
|
|
148302
|
+
}
|
|
148303
|
+
if (node === null || typeof node !== "object")
|
|
148304
|
+
return;
|
|
148305
|
+
const obj = node;
|
|
148306
|
+
if (obj.actionKind === "agent.run" && obj.ok === true)
|
|
148307
|
+
out.push(obj);
|
|
148308
|
+
for (const value of Object.values(obj))
|
|
148309
|
+
collectAgentRuns(value, out);
|
|
148310
|
+
}
|
|
148311
|
+
function applyFinalProvenance(p, input) {
|
|
148312
|
+
const lines = readFileSync25(input.validationPath, "utf8").trimEnd().split(`
|
|
148313
|
+
`);
|
|
148314
|
+
if ((lines[lines.length - 1] ?? "").trim() !== "Verdict: PASS") {
|
|
148315
|
+
throw new Error(`final validation artifact does not end with "Verdict: PASS": ${input.validationPath}`);
|
|
148316
|
+
}
|
|
148317
|
+
const validatedAt = statSync17(input.validationPath).mtime.toISOString();
|
|
148318
|
+
let trace;
|
|
148319
|
+
try {
|
|
148320
|
+
trace = JSON.parse(readFileSync25(input.tracePath, "utf8"));
|
|
148321
|
+
} catch (err) {
|
|
148322
|
+
throw new Error(`could not parse run trace at ${input.tracePath}: ${err.message}`);
|
|
148323
|
+
}
|
|
148324
|
+
const collected = [];
|
|
148325
|
+
collectAgentRuns(trace, collected);
|
|
148326
|
+
const seen = new Set;
|
|
148327
|
+
const dispatches = [];
|
|
148328
|
+
for (const action of collected) {
|
|
148329
|
+
const actionId = action.actionId ?? "";
|
|
148330
|
+
if (actionId !== "" && seen.has(actionId))
|
|
148331
|
+
continue;
|
|
148332
|
+
if (actionId !== "")
|
|
148333
|
+
seen.add(actionId);
|
|
148334
|
+
const inv = action.invocation ?? {};
|
|
148335
|
+
dispatches.push({
|
|
148336
|
+
node: action.node ?? "",
|
|
148337
|
+
actionId,
|
|
148338
|
+
agent: inv.agent != null && inv.agent !== "" ? inv.agent : "not available: invocation agent absent",
|
|
148339
|
+
executor: inv.executor != null && inv.executor !== "" ? inv.executor : "not available: invocation executor absent",
|
|
148340
|
+
model: inv.model != null && inv.model !== "" ? inv.model : "not available: invocation model absent",
|
|
148341
|
+
timeoutMs: typeof inv.timeoutMs === "number" && inv.timeoutMs > 0 ? inv.timeoutMs : "unbounded"
|
|
148342
|
+
});
|
|
148343
|
+
}
|
|
148344
|
+
if (dispatches.length === 0) {
|
|
148345
|
+
throw new Error(`run trace records no successful agent.run action: ${input.tracePath}`);
|
|
148346
|
+
}
|
|
148347
|
+
const finalValidation = [...dispatches].reverse().find((d) => d.node === "validate") ?? dispatches[dispatches.length - 1];
|
|
148348
|
+
return {
|
|
148349
|
+
...p,
|
|
148350
|
+
validatedAt,
|
|
148351
|
+
dispatches,
|
|
148352
|
+
executor: finalValidation?.agent ?? NOT_AVAILABLE,
|
|
148353
|
+
model: finalValidation?.model ?? NOT_AVAILABLE,
|
|
148354
|
+
executorProfile: finalValidation?.executor ?? NOT_AVAILABLE
|
|
148355
|
+
};
|
|
148356
|
+
}
|
|
147564
148357
|
function buildProvenance(opts) {
|
|
147565
148358
|
let raw;
|
|
147566
148359
|
try {
|
|
@@ -147608,10 +148401,10 @@ function buildProvenance(opts) {
|
|
|
147608
148401
|
runId: opts.runId,
|
|
147609
148402
|
currentArtifactPath: opts.artifact,
|
|
147610
148403
|
baselineArtifactPath: opts.baseline ?? null,
|
|
147611
|
-
spurVersion: opts.spurVersion
|
|
148404
|
+
spurVersion: opts.spurVersion !== undefined && opts.spurVersion !== "" ? opts.spurVersion : typeof raw.spurVersion === "string" && raw.spurVersion !== "" ? raw.spurVersion : NOT_AVAILABLE,
|
|
147612
148405
|
schemaVersion: typeof raw.schemaVersion === "number" ? raw.schemaVersion : undefined,
|
|
147613
|
-
executor: opts.executor ??
|
|
147614
|
-
model: opts.model ??
|
|
148406
|
+
executor: opts.executor ?? "not available: probe did not receive an executor",
|
|
148407
|
+
model: opts.model ?? "not available: probe did not receive a model"
|
|
147615
148408
|
};
|
|
147616
148409
|
}
|
|
147617
148410
|
function probe(opts) {
|
|
@@ -147656,6 +148449,12 @@ function renderProvenanceFrontmatter(p) {
|
|
|
147656
148449
|
for (const c of p.coverage) {
|
|
147657
148450
|
lines.push(` - source: ${c.source}, status: ${c.status}, lastImportedAt: ${c.lastImportedAt ?? "null"}`);
|
|
147658
148451
|
}
|
|
148452
|
+
if (p.dispatches !== undefined && p.dispatches.length > 0) {
|
|
148453
|
+
lines.push("dispatches:");
|
|
148454
|
+
for (const d of p.dispatches) {
|
|
148455
|
+
lines.push(` - node: ${d.node}, actionId: ${d.actionId}, agent: ${d.agent}, executor: ${d.executor}, model: ${d.model}, timeoutMs: ${d.timeoutMs}`);
|
|
148456
|
+
}
|
|
148457
|
+
}
|
|
147659
148458
|
lines.push("---");
|
|
147660
148459
|
return lines.join(`
|
|
147661
148460
|
`);
|
|
@@ -147707,7 +148506,7 @@ function diffPorcelain(before, now, expects) {
|
|
|
147707
148506
|
const beforePaths = porcelainPaths(before);
|
|
147708
148507
|
return [...porcelainPaths(now)].filter((p) => !beforePaths.has(p) && !expects.has(p)).sort();
|
|
147709
148508
|
}
|
|
147710
|
-
var REPORT_SECTIONS, FINDING_FIELDS, DISPOSITIONS, FINDING_CATEGORIES, NOT_AVAILABLE = "not available", YAML_KEYS;
|
|
148509
|
+
var REPORT_SECTIONS, FINDING_FIELDS, DISPOSITIONS, DERIVED_SIGNAL, NUM = "-?\\d+(?:\\.\\d+)?", CALC_LINE, FINDING_CATEGORIES, NOT_AVAILABLE = "not available", YAML_KEYS;
|
|
147711
148510
|
var init_history_anatomy = __esm(() => {
|
|
147712
148511
|
init_artifact_digest();
|
|
147713
148512
|
REPORT_SECTIONS = [
|
|
@@ -147738,6 +148537,8 @@ var init_history_anatomy = __esm(() => {
|
|
|
147738
148537
|
"ownerSurface"
|
|
147739
148538
|
];
|
|
147740
148539
|
DISPOSITIONS = ["hit", "miss", "forced-recompute"];
|
|
148540
|
+
DERIVED_SIGNAL = /%|\b(larger|smaller|higher|lower|more than|less than|above|below|exceeds|dominates|gap|delta|difference|sum|sums|sum of|share|ratio|mean|average)\b/i;
|
|
148541
|
+
CALC_LINE = /`?calc`?\s*:\s*\S/i;
|
|
147741
148542
|
FINDING_CATEGORIES = [
|
|
147742
148543
|
"reliability",
|
|
147743
148544
|
"repetition",
|
|
@@ -147763,6 +148564,7 @@ var init_history_anatomy = __esm(() => {
|
|
|
147763
148564
|
"spurVersion",
|
|
147764
148565
|
"schemaVersion",
|
|
147765
148566
|
"executor",
|
|
148567
|
+
"executorProfile",
|
|
147766
148568
|
"model",
|
|
147767
148569
|
"cacheDisposition"
|
|
147768
148570
|
];
|
|
@@ -147976,16 +148778,19 @@ ${reasons}`, stderr: "" };
|
|
|
147976
148778
|
return {
|
|
147977
148779
|
exitCode: 1,
|
|
147978
148780
|
stdout: "",
|
|
147979
|
-
stderr: `usage: spur history anatomy-stamp --candidate <c.md> --provenance <p.json> --out <o.md>
|
|
148781
|
+
stderr: `usage: spur history anatomy-stamp --candidate <c.md> --provenance <p.json> --out <o.md> [--validation <v.txt> --trace <t.json>]
|
|
147980
148782
|
`
|
|
147981
148783
|
};
|
|
147982
148784
|
}
|
|
147983
148785
|
try {
|
|
147984
|
-
|
|
148786
|
+
let p = JSON.parse(readFileSync26(f.provenance, "utf8"));
|
|
148787
|
+
if (f.validation !== undefined && f.trace !== undefined) {
|
|
148788
|
+
p = applyFinalProvenance(p, { validationPath: f.validation, tracePath: f.trace });
|
|
148789
|
+
}
|
|
147985
148790
|
writeFileSync18(f.out, `${stampReport(readFileSync26(f.candidate, "utf8"), p)}
|
|
147986
148791
|
`);
|
|
147987
|
-
} catch {
|
|
147988
|
-
return { exitCode: 1, stdout: "", stderr: `stamp:
|
|
148792
|
+
} catch (err) {
|
|
148793
|
+
return { exitCode: 1, stdout: "", stderr: `stamp: ${err.message}
|
|
147989
148794
|
` };
|
|
147990
148795
|
}
|
|
147991
148796
|
return { exitCode: 0, stdout: "", stderr: "" };
|
|
@@ -156992,6 +157797,7 @@ var SHARED_OPTIONS = {
|
|
|
156992
157797
|
],
|
|
156993
157798
|
jsonSupported: ["--json", "Output machine-readable JSON where supported"],
|
|
156994
157799
|
section: ["--section <name>", "Section name to write"],
|
|
157800
|
+
sectionShowFilter: ["--section <name>", "Show only this section, byte-exact (repeatable)"],
|
|
156995
157801
|
fromFile: ["--from-file <path>", "File to read section body from (requires --section)"],
|
|
156996
157802
|
statusFilter: ["--status <s>", "Filter by status"],
|
|
156997
157803
|
last: ["--last <n>", "Limit results (default 20)"],
|
|
@@ -158817,11 +159623,13 @@ function registerTaskBatchCreateCommand(task, context) {
|
|
|
158817
159623
|
if (options.json) {
|
|
158818
159624
|
const ids = children.map((r) => r.ref.id);
|
|
158819
159625
|
const normalized = children.map((r, i) => ({ wbs: ids[i] ?? r.ref.id, normalized: r.normalized ?? [] })).filter((row) => row.normalized.length > 0);
|
|
159626
|
+
const warnings = children.map((r, i) => ({ wbs: ids[i] ?? r.ref.id, warnings: r.warnings ?? [] })).filter((row) => row.warnings.length > 0);
|
|
158820
159627
|
context.output.write(toEnvelopeJson({
|
|
158821
159628
|
created: children.length,
|
|
158822
159629
|
wbs: ids,
|
|
158823
159630
|
parentsWired,
|
|
158824
159631
|
...normalized.length > 0 ? { normalized } : {},
|
|
159632
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
158825
159633
|
...featureReopened.some((r) => r !== undefined) ? { featureReopened } : {},
|
|
158826
159634
|
...readiness
|
|
158827
159635
|
}, { enveloped: options.jsonEnvelope }));
|
|
@@ -158830,6 +159638,11 @@ function registerTaskBatchCreateCommand(task, context) {
|
|
|
158830
159638
|
for (const r of children) {
|
|
158831
159639
|
context.output.write(` ${r.ref.id} ${r.ref.filePath}`);
|
|
158832
159640
|
}
|
|
159641
|
+
for (const r of children) {
|
|
159642
|
+
for (const warning of r.warnings ?? []) {
|
|
159643
|
+
context.output.error(`${r.ref.id}: ${warning}`);
|
|
159644
|
+
}
|
|
159645
|
+
}
|
|
158833
159646
|
if (parentsWired.length > 0) {
|
|
158834
159647
|
context.output.write(`Wired ${parentsWired.length} parent(s):`);
|
|
158835
159648
|
for (const p of parentsWired) {
|
|
@@ -159135,10 +159948,20 @@ function registerTaskCreateCommand(task, context) {
|
|
|
159135
159948
|
cwd: context.cwd
|
|
159136
159949
|
});
|
|
159137
159950
|
}
|
|
159951
|
+
const warnings = result.warnings ?? [];
|
|
159138
159952
|
if (options.json) {
|
|
159139
|
-
context.output.write(toEnvelopeJson({
|
|
159953
|
+
context.output.write(toEnvelopeJson({
|
|
159954
|
+
...result,
|
|
159955
|
+
wbs: result.ref.id,
|
|
159956
|
+
filePath: result.ref.filePath,
|
|
159957
|
+
...readiness,
|
|
159958
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
159959
|
+
}, { enveloped: options.jsonEnvelope }));
|
|
159140
159960
|
} else {
|
|
159141
159961
|
context.output.write(`Created task ${result.ref.id}: ${result.ref.filePath}`);
|
|
159962
|
+
for (const warning of warnings) {
|
|
159963
|
+
context.output.error(warning);
|
|
159964
|
+
}
|
|
159142
159965
|
if (readiness.readiness.status === "ready") {
|
|
159143
159966
|
context.output.write(`Ready: checklist + post-check passed (${result.ref.id} promoted to todo)`);
|
|
159144
159967
|
}
|
|
@@ -159932,14 +160755,37 @@ function registerTaskSectionsCommand(task, context) {
|
|
|
159932
160755
|
|
|
159933
160756
|
// src/commands/task/show.ts
|
|
159934
160757
|
init_src2();
|
|
160758
|
+
function collectSection(value, previous = []) {
|
|
160759
|
+
return [...previous, value];
|
|
160760
|
+
}
|
|
160761
|
+
function parseCsvSections(csv) {
|
|
160762
|
+
if (csv === undefined)
|
|
160763
|
+
return [];
|
|
160764
|
+
return csv.split(",").map((name) => name.trim()).filter((name) => name !== "");
|
|
160765
|
+
}
|
|
159935
160766
|
function registerTaskShowCommand(task, context) {
|
|
159936
|
-
task.command("show").alias("get").summary("Show a task by WBS.").argument("<wbs>", "Task WBS number").option(...SHARED_OPTIONS.folderTasks).option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (wbs, options) => {
|
|
160767
|
+
task.command("show").alias("get").summary("Show a task by WBS.").argument("<wbs>", "Task WBS number").option(...SHARED_OPTIONS.folderTasks).option(...SHARED_OPTIONS.sectionShowFilter, collectSection, []).option("--sections <csv>", 'Comma-separated section names to show (e.g. "Background,Plan"); combined with --section').option("--summary", "Print frontmatter metadata plus a section index (name, heading, byte count) and total file size \u2014 no section bodies", false).option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (wbs, options) => {
|
|
160768
|
+
const requested = [...options.section ?? [], ...parseCsvSections(options.sections)];
|
|
160769
|
+
if (options.summary === true && requested.length > 0) {
|
|
160770
|
+
writeJsonError(context.output, options, "--summary cannot be combined with --section/--sections", "VALIDATION_FAILED");
|
|
160771
|
+
context.setExitCode(2);
|
|
160772
|
+
return;
|
|
160773
|
+
}
|
|
159937
160774
|
const svc = await makeService(context, options.folder);
|
|
159938
160775
|
try {
|
|
159939
|
-
const result = await svc.show(wbs);
|
|
160776
|
+
const result = options.summary === true ? await svc.showSummary(wbs) : requested.length > 0 ? await svc.showSections(wbs, requested) : await svc.show(wbs);
|
|
159940
160777
|
if (options.json) {
|
|
159941
160778
|
const { frontmatter, ...rest } = result;
|
|
159942
160779
|
context.output.write(toEnvelopeJson({ ...rest, frontmatter }, { enveloped: options.jsonEnvelope }));
|
|
160780
|
+
} else if ("sections" in result) {
|
|
160781
|
+
const header = `${taskStatusIcon(result.status)} ${result.status.toUpperCase()} \u2014 ${result.wbs}`;
|
|
160782
|
+
if (result.sections.every((entry) => entry.content === undefined)) {
|
|
160783
|
+
const lines = result.sections.map((entry) => ` ${entry.headingLine} \u2014 ${entry.byteCount} bytes`);
|
|
160784
|
+
context.output.write([`${header} \u2014 summary`, ...lines, ` total: ${result.totalBytes} bytes`].join(`
|
|
160785
|
+
`));
|
|
160786
|
+
} else {
|
|
160787
|
+
context.output.write([header, ...result.sections.map((entry) => entry.content ?? "")].join(""));
|
|
160788
|
+
}
|
|
159943
160789
|
} else {
|
|
159944
160790
|
context.output.write(`${taskStatusIcon(result.status)} ${result.status.toUpperCase()} \u2014 ${result.wbs}
|
|
159945
160791
|
|
|
@@ -159948,7 +160794,7 @@ ${result.content}`);
|
|
|
159948
160794
|
} catch (err) {
|
|
159949
160795
|
const cliCode = err?.cliCode;
|
|
159950
160796
|
writeJsonError(context.output, options, String(err), "INTERNAL_ERROR", cliCode !== undefined ? { cliCode } : undefined);
|
|
159951
|
-
context.setExitCode(1);
|
|
160797
|
+
context.setExitCode(cliCode === "VALIDATION_FAILED" ? 3 : 1);
|
|
159952
160798
|
}
|
|
159953
160799
|
});
|
|
159954
160800
|
}
|
|
@@ -165193,7 +166039,7 @@ init_src2();
|
|
|
165193
166039
|
var CLI_CONFIG = {
|
|
165194
166040
|
binaryName: "spur",
|
|
165195
166041
|
binaryLabel: "spur",
|
|
165196
|
-
binaryVersion: "0.4.
|
|
166042
|
+
binaryVersion: "0.4.31",
|
|
165197
166043
|
configDir: ".spur",
|
|
165198
166044
|
configFile: ".spur/config.yaml",
|
|
165199
166045
|
databaseFile: ".spur/spur.db"
|
|
@@ -172932,7 +173778,7 @@ init_dist5();
|
|
|
172932
173778
|
import { homedir as homedir17 } from "os";
|
|
172933
173779
|
import { basename as basename23, join as join95, relative as relative19, resolve as resolve37 } from "path";
|
|
172934
173780
|
// ../server/package.json
|
|
172935
|
-
var version4 = "0.4.
|
|
173781
|
+
var version4 = "0.4.31";
|
|
172936
173782
|
|
|
172937
173783
|
// ../server/src/modules/health/index.ts
|
|
172938
173784
|
var startedAt = Date.now();
|
|
@@ -180049,13 +180895,23 @@ function registerWorkflowTraceCommand(workflow, context) {
|
|
|
180049
180895
|
context.setExitCode(1);
|
|
180050
180896
|
return;
|
|
180051
180897
|
}
|
|
180052
|
-
|
|
180053
|
-
|
|
180898
|
+
let resolvedRunId = runId;
|
|
180899
|
+
if (runId !== undefined) {
|
|
180900
|
+
try {
|
|
180901
|
+
resolvedRunId = await svc.resolveRunId(runId);
|
|
180902
|
+
} catch (err) {
|
|
180903
|
+
writeJsonError(context.output, options, err.message, "NOT_FOUND");
|
|
180904
|
+
context.setExitCode(1);
|
|
180905
|
+
return;
|
|
180906
|
+
}
|
|
180907
|
+
}
|
|
180908
|
+
if (options.follow === true && resolvedRunId !== undefined) {
|
|
180909
|
+
const timedOut = options.output === true ? await followRunLog(svc, resolvedRunId, context.cwd, pollMs, (line) => context.output.write(line), undefined, undefined, timeoutMs) : await followTrace(svc, resolvedRunId, pollMs, (line) => context.output.write(line), undefined, undefined, timeoutMs);
|
|
180054
180910
|
if (timedOut)
|
|
180055
180911
|
context.setExitCode(1);
|
|
180056
180912
|
return;
|
|
180057
180913
|
}
|
|
180058
|
-
const result =
|
|
180914
|
+
const result = resolvedRunId ? await svc.trace(resolvedRunId) : await svc.trace({
|
|
180059
180915
|
workflow: options.workflow,
|
|
180060
180916
|
status: options.status,
|
|
180061
180917
|
since: options.since,
|