@obversa/runtime 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.d.ts +2 -0
- package/dist/api.js +258 -303
- package/dist/api.js.map +1 -1
- package/dist/bin/record.d.ts +2 -0
- package/dist/bin/record.js +24 -0
- package/dist/bin/record.js.map +1 -0
- package/dist/chunk-XDMDT67Q.js +598 -0
- package/dist/chunk-XDMDT67Q.js.map +1 -0
- package/dist/{chunk-DV5P4QLI.js → chunk-ZXVFWRW5.js} +7 -5
- package/dist/chunk-ZXVFWRW5.js.map +1 -0
- package/dist/core/judge.d.ts +83 -0
- package/dist/core/types.d.ts +53 -2
- package/dist/runtime/monitor.d.ts +8 -5
- package/dist/runtime/render-record.d.ts +68 -0
- package/dist/workflow-support.js +1 -1
- package/dist/workflow.d.ts +8 -2
- package/package.json +7 -4
- package/dist/chunk-DV5P4QLI.js.map +0 -1
package/dist/api.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { compileGraph } from './chunk-DEW5R23M.js';
|
|
2
2
|
export { compileGraph } from './chunk-DEW5R23M.js';
|
|
3
|
-
import { LoopError, needDecisionsOf, DEFAULT_FANOUT_CONCURRENCY, setMeta, jobMeta, describeConditions, Budget, currentBranch, assertDistinctSeats, seatIdentity, copyJobMeta, predicate, setLabel, resolveEnv, scrubCapture, withConditionPreparation, toCondition, prepareCondition, validateCallbackRequest, reportedUsage, validateIncompleteResultEvidence, engineSelection, validateAgentResult, requireFinalResultText, isRepo, addWorktree, childContext, stageAll, commit, mergeBranch, removeWorktree, deleteBranch, headSha, hasStagedChanges, reviewPanel, createCallbackGate, defineAgent, isInfrastructureError, validateCallbackResponse, gitRoot, captureGitWorkspaceSnapshot, workspaceFingerprint, revisionFromOutcome, agentJob, commandJob, approval, mergeNoCommit, mergeAbort, nodeJobContext, outcomeFromAgentText, requireNonEmptyFiles, requireNoFiles, panelReviewers, delegateNodeJob, branchCommits, RECORDED_ENGINE_USAGE, compareGitWorkspaceSnapshots } from './chunk-
|
|
4
|
-
export { LoopError, RECORDED_ENGINE_USAGE, agentCheck, agentJob, all, always, any, approval, bodyPassed, callbackRequestDigest, commandJob, commandSucceeds, copyJobMeta, createCallbackGate, defineAgent, defineSkill, describeConditions, failed, finalResultPart, finalResultText, fnJob, fromFile, gateJob, jobMeta, kickback, minConfidence, never, not, passed, predicate, prove, quorum, renderPlan, reviewContext, reviewPanel, revisionRequest, toCondition, validateAgentResult, validateCallbackRequest, validateCallbackResponse, withEnv } from './chunk-
|
|
3
|
+
import { LoopError, needDecisionsOf, DEFAULT_FANOUT_CONCURRENCY, setMeta, jobMeta, describeConditions, Budget, currentBranch, assertDistinctSeats, seatIdentity, copyJobMeta, predicate, setLabel, resolveEnv, scrubCapture, withConditionPreparation, toCondition, prepareCondition, validateCallbackRequest, reportedUsage, validateIncompleteResultEvidence, engineSelection, validateAgentResult, requireFinalResultText, isRepo, addWorktree, childContext, stageAll, commit, mergeBranch, removeWorktree, deleteBranch, headSha, hasStagedChanges, reviewPanel, createCallbackGate, defineAgent, isInfrastructureError, validateCallbackResponse, gitRoot, captureGitWorkspaceSnapshot, workspaceFingerprint, revisionFromOutcome, agentJob, commandJob, approval, mergeNoCommit, mergeAbort, normalizeFeedbackSeverity, nodeJobContext, outcomeFromAgentText, requireNonEmptyFiles, requireNoFiles, panelReviewers, delegateNodeJob, branchCommits, RECORDED_ENGINE_USAGE, compareGitWorkspaceSnapshots } from './chunk-ZXVFWRW5.js';
|
|
4
|
+
export { LoopError, RECORDED_ENGINE_USAGE, agentCheck, agentJob, all, always, any, approval, bodyPassed, callbackRequestDigest, commandJob, commandSucceeds, copyJobMeta, createCallbackGate, defineAgent, defineSkill, describeConditions, failed, finalResultPart, finalResultText, fnJob, fromFile, gateJob, jobMeta, kickback, minConfidence, never, not, passed, predicate, prove, quorum, renderPlan, reviewContext, reviewPanel, revisionRequest, toCondition, validateAgentResult, validateCallbackRequest, validateCallbackResponse, withEnv } from './chunk-ZXVFWRW5.js';
|
|
5
5
|
import { runRuntimeProcess, processText } from './chunk-NIBHM5I5.js';
|
|
6
6
|
import { loadRunDefinition, StorageError, validateDomainEventBatch, validateNewDomainEvent, validateDomainEventEnvelope, validateDomainEventId, validateArtifactReference, validateStorageId } from './chunk-RZLMX3IA.js';
|
|
7
7
|
export { StorageError, loadRunDefinition, persistRunDefinition, validateArtifactReference, validateArtifactScope, validateDomainEventBatch, validateDomainEventEnvelope, validateDomainEventId, validateEventStreamRef, validateNewArtifact, validateNewDomainEvent, validateRunDefinition, validateRunStartRecord, validateRunStoragePolicy, validateRunStorageRecord } from './chunk-RZLMX3IA.js';
|
|
8
8
|
export { resolveGraphPlan, validateGraphDescription } from './chunk-3L6YNPN6.js';
|
|
9
9
|
import { cloneFrozenJson, JsonValueError, digestJson, canonicalJson, GraphValidationError } from './chunk-5GLEABOU.js';
|
|
10
10
|
export { GraphValidationError, JsonValueError } from './chunk-5GLEABOU.js';
|
|
11
|
-
import {
|
|
11
|
+
import { newRunId, startSupervisor, formatEvent, runningTotal } from './chunk-XDMDT67Q.js';
|
|
12
|
+
export { formatEvent, renderRecord, summarizeRecord } from './chunk-XDMDT67Q.js';
|
|
13
|
+
import { randomUUID, createHash } from 'crypto';
|
|
12
14
|
import { SUBAGENT_TOOLS, snapshotApprovalSubject, assertApprovalPermissionsAdmitted, validateAcceptedResultRecord, EngineIncompleteResultError, EngineError, classifyEngineFailure, LANE_DEAD_FAILURES, validateCallbackEvent, ApprovalSubjectError, isEngine, cloneFrozenJson as cloneFrozenJson$1, validateApprovalRecord, digestJson as digestJson$1, GraphExecutionError, validateApprovalSubject, modelIdentity, validateActionDecision } from '@obversa/api';
|
|
13
15
|
export { ApprovalSubjectError, EngineError, EngineIncompleteResultError, GraphExecutionError, LANE_DEAD_FAILURES, acceptedResultMatches, approvalSubjectDigest, classifyEngineFailure, createApprovalCallbackGate, validateAcceptedResultRecord, validateApprovalRecord, validateCallbackEvent } from '@obversa/api';
|
|
14
16
|
import { readFileSync, mkdirSync, existsSync, writeFileSync, appendFileSync, lstatSync, realpathSync } from 'fs';
|
|
@@ -17,9 +19,9 @@ import { join, resolve, basename, extname, dirname, isAbsolute, relative, sep }
|
|
|
17
19
|
import pLimit from 'p-limit';
|
|
18
20
|
import toposort from 'toposort';
|
|
19
21
|
import '@obversa/core/command';
|
|
20
|
-
import { homedir } from 'os';
|
|
21
22
|
import { createServer } from 'http';
|
|
22
23
|
import { isDeepStrictEqual } from 'util';
|
|
24
|
+
import { homedir } from 'os';
|
|
23
25
|
import { hasExactFields, isObject, bindingFromSubject } from '@obversa/api/approval-support';
|
|
24
26
|
import { acceptedResultSupport } from '@obversa/api/accepted-result-support';
|
|
25
27
|
import { fileURLToPath } from 'url';
|
|
@@ -119,6 +121,111 @@ Write a concise MERGE SYNTHESIS for the commit body: what this line of work acco
|
|
|
119
121
|
return requireFinalResultText(out).trim();
|
|
120
122
|
}
|
|
121
123
|
|
|
124
|
+
// src/core/judge.ts
|
|
125
|
+
function stopQuestions(what = "the draft") {
|
|
126
|
+
return {
|
|
127
|
+
holds: {
|
|
128
|
+
type: "noul",
|
|
129
|
+
instructions: `For this use case, does ${what} hold as it stands?`,
|
|
130
|
+
criteria: {
|
|
131
|
+
true: "The latest findings are nits, taste or nothing, and a reader of this kind of work would not notice what remains.",
|
|
132
|
+
false: "The latest findings include a block, or shoulds a reader of this kind of work would notice."
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
worth_doing: {
|
|
136
|
+
type: "noul",
|
|
137
|
+
instructions: "Read the latest findings themselves. For this use case, are they worth acting on?",
|
|
138
|
+
criteria: {
|
|
139
|
+
true: "They name things a reader of this kind of work would stumble on, misread or distrust.",
|
|
140
|
+
false: "They are taste, edge cases, or polish past the bar the use case sets; acting on them would not change what a reader gets."
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
worth_another_round: {
|
|
144
|
+
type: "noul",
|
|
145
|
+
instructions: "Given the whole history and whether the latest findings are worth doing, is another round worth it?",
|
|
146
|
+
criteria: {
|
|
147
|
+
true: "The findings are worth doing, and the rounds so far have fixed what was found, so one more pass would land them.",
|
|
148
|
+
false: "The findings are not worth doing for this use case; or the same class of finding keeps returning; or the change between rounds is small; or the work is being polished past the bar."
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
stop_reason: {
|
|
152
|
+
type: "choice",
|
|
153
|
+
instructions: "Are we at the point of diminishing returns? If so, which kind; if not, continue.",
|
|
154
|
+
criteria: {
|
|
155
|
+
holds: `${what} holds for this use case.`,
|
|
156
|
+
over_polishing: "The remaining findings are taste, nits or edge cases past the bar.",
|
|
157
|
+
not_converging: "The same class of finding keeps returning, so another round will not fix it.",
|
|
158
|
+
continue: "Another round is worth it."
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function judge(seat, opts) {
|
|
164
|
+
if (!Number.isSafeInteger(opts.cap) || opts.cap < 1) {
|
|
165
|
+
throw new TypeError("judge cap must be a positive integer");
|
|
166
|
+
}
|
|
167
|
+
return { kind: "judge", seat, cap: opts.cap, questions: opts.questions ?? stopQuestions() };
|
|
168
|
+
}
|
|
169
|
+
function isJudge(value) {
|
|
170
|
+
return typeof value === "object" && value !== null && value.kind === "judge";
|
|
171
|
+
}
|
|
172
|
+
function hasBlockFinding(findings) {
|
|
173
|
+
return (findings ?? []).some((finding) => normalizeFeedbackSeverity(finding.severity) === "block");
|
|
174
|
+
}
|
|
175
|
+
var SEVERITIES = ["block", "should-fix", "nice-to-have", "approve"];
|
|
176
|
+
function countBySeverity(findings) {
|
|
177
|
+
const counts = Object.fromEntries(SEVERITIES.map((severity) => [severity, 0]));
|
|
178
|
+
for (const finding of findings) counts[normalizeFeedbackSeverity(finding.severity)] += 1;
|
|
179
|
+
return counts;
|
|
180
|
+
}
|
|
181
|
+
function judgeDecision(answers) {
|
|
182
|
+
const worth = answers.worth_another_round?.noul ?? answers.worth_another_round?.probability;
|
|
183
|
+
const doing = answers.worth_doing?.noul ?? answers.worth_doing?.probability;
|
|
184
|
+
const holds = answers.holds?.noul ?? answers.holds?.probability;
|
|
185
|
+
const reason = answers.stop_reason?.choice ?? "unknown";
|
|
186
|
+
if (reason !== "unknown" && reason !== "continue") {
|
|
187
|
+
return { again: false, reason: `the judge chose ${reason}` };
|
|
188
|
+
}
|
|
189
|
+
if (typeof holds === "number" && holds >= 0.5) {
|
|
190
|
+
return { again: false, reason: `the judge says it holds (${holds.toFixed(2)})` };
|
|
191
|
+
}
|
|
192
|
+
if (typeof doing === "number" && doing < 0.5) {
|
|
193
|
+
return { again: false, reason: `the judge says the findings are not worth doing (${doing.toFixed(2)})` };
|
|
194
|
+
}
|
|
195
|
+
if (typeof worth === "number" && worth < 0.5) {
|
|
196
|
+
return { again: false, reason: `the judge says another round is not worth it (${worth.toFixed(2)})` };
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
again: true,
|
|
200
|
+
reason: `the judge says another round is worth it${typeof worth === "number" ? ` (${worth.toFixed(2)})` : ""}`
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
async function askJudge(cfg, state, ctx, path) {
|
|
204
|
+
const judgeJob = agentJob({
|
|
205
|
+
label: "refine:judge",
|
|
206
|
+
engine: cfg.seat.engine,
|
|
207
|
+
model: cfg.seat.identity.model,
|
|
208
|
+
workspaceMode: "none",
|
|
209
|
+
tools: [],
|
|
210
|
+
leaf: true,
|
|
211
|
+
prompt: JSON.stringify({ state, questions: cfg.questions })
|
|
212
|
+
});
|
|
213
|
+
const outcome = await judgeJob({ ...ctx, depth: ctx.depth + 1, path: [...path, "refine-judge"] });
|
|
214
|
+
let answers = {};
|
|
215
|
+
if (outcome.status === "pass") {
|
|
216
|
+
try {
|
|
217
|
+
const parsed = JSON.parse(String(outcome.data ?? ""));
|
|
218
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
219
|
+
answers = parsed;
|
|
220
|
+
}
|
|
221
|
+
} catch {
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const decision = judgeDecision(answers);
|
|
225
|
+
ctx.emit({ kind: "refine:judge", ts: Date.now(), path: [...path], answers, reason: decision.reason });
|
|
226
|
+
return { answers, decision };
|
|
227
|
+
}
|
|
228
|
+
|
|
122
229
|
// src/core/dag.ts
|
|
123
230
|
function slug(s) {
|
|
124
231
|
return s.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/(^-+|-+$)/g, "") || "node";
|
|
@@ -158,10 +265,11 @@ function validateKickbackBudget(budget, nodes) {
|
|
|
158
265
|
message: `dag maxKickbacks names unknown target "${target}"`
|
|
159
266
|
});
|
|
160
267
|
}
|
|
268
|
+
if (isJudge(limit2)) continue;
|
|
161
269
|
if (!Number.isInteger(limit2) || limit2 < 0) {
|
|
162
270
|
throw new LoopError({
|
|
163
271
|
code: "CONFIG",
|
|
164
|
-
message: `dag maxKickbacks for "${target}" must be a non-negative integer`
|
|
272
|
+
message: `dag maxKickbacks for "${target}" must be a non-negative integer or a judge`
|
|
165
273
|
});
|
|
166
274
|
}
|
|
167
275
|
}
|
|
@@ -220,8 +328,18 @@ function dag(config) {
|
|
|
220
328
|
const stopOnError = config.stopOnError ?? true;
|
|
221
329
|
const maxKickbacks = config.maxKickbacks ?? 0;
|
|
222
330
|
const perTargetBudget = typeof maxKickbacks !== "number";
|
|
223
|
-
const
|
|
331
|
+
const targetJudge = (target) => {
|
|
332
|
+
if (typeof maxKickbacks === "number") return void 0;
|
|
333
|
+
const budget = maxKickbacks[target];
|
|
334
|
+
return isJudge(budget) ? budget : void 0;
|
|
335
|
+
};
|
|
336
|
+
const targetLimit = (target) => {
|
|
337
|
+
if (typeof maxKickbacks === "number") return maxKickbacks;
|
|
338
|
+
const budget = maxKickbacks[target] ?? 0;
|
|
339
|
+
return isJudge(budget) ? budget.cap : budget;
|
|
340
|
+
};
|
|
224
341
|
const routeKickbacks = config.maxKickbacks !== void 0 && (perTargetBudget || maxKickbacks > 0);
|
|
342
|
+
const judgeHistory = /* @__PURE__ */ new Map();
|
|
225
343
|
const dependents = new Map(names.map((n) => [n, []]));
|
|
226
344
|
for (const [dep, name] of edges) dependents.get(dep).push(name);
|
|
227
345
|
const ancestorsOf = (name) => {
|
|
@@ -533,6 +651,30 @@ function dag(config) {
|
|
|
533
651
|
emitKickback(from, to, reason, false, count, limit3, note);
|
|
534
652
|
continue;
|
|
535
653
|
}
|
|
654
|
+
const requestFindings = request.findings ?? [];
|
|
655
|
+
const cfgJudge = targetJudge(to);
|
|
656
|
+
const withinBudget = perTargetBudget ? count <= limit3 : used < limit3;
|
|
657
|
+
let effectiveReason = reason;
|
|
658
|
+
if (cfgJudge !== void 0 && withinBudget && !hasBlockFinding(requestFindings)) {
|
|
659
|
+
const history = judgeHistory.get(to) ?? [];
|
|
660
|
+
const state = {
|
|
661
|
+
latestFindings: requestFindings,
|
|
662
|
+
rounds: history,
|
|
663
|
+
round: count,
|
|
664
|
+
cap: cfgJudge.cap
|
|
665
|
+
};
|
|
666
|
+
const { decision } = await askJudge(cfgJudge, state, parent, path);
|
|
667
|
+
judgeHistory.set(to, [
|
|
668
|
+
...history,
|
|
669
|
+
{ round: count, findings: requestFindings, counts: countBySeverity(requestFindings) }
|
|
670
|
+
]);
|
|
671
|
+
if (!decision.again) {
|
|
672
|
+
rejected.add(from);
|
|
673
|
+
emitKickback(from, to, `${reason} (${decision.reason})`, false, count, limit3, decision.reason);
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
effectiveReason = `${reason} (${decision.reason})`;
|
|
677
|
+
}
|
|
536
678
|
if (perTargetBudget ? count > limit3 : used >= limit3) {
|
|
537
679
|
emitKickback(
|
|
538
680
|
from,
|
|
@@ -550,7 +692,7 @@ function dag(config) {
|
|
|
550
692
|
break;
|
|
551
693
|
}
|
|
552
694
|
used += 1;
|
|
553
|
-
emitKickback(from, to,
|
|
695
|
+
emitKickback(from, to, effectiveReason, true, count, limit3);
|
|
554
696
|
const dirty = dirtyFrom(to);
|
|
555
697
|
for (const d of dirty) {
|
|
556
698
|
memo.delete(d);
|
|
@@ -559,8 +701,8 @@ function dag(config) {
|
|
|
559
701
|
}
|
|
560
702
|
pendingKickback.set(to, {
|
|
561
703
|
status: "fail",
|
|
562
|
-
summary: `Kicked back from "${from}": ${
|
|
563
|
-
revision: { ...request, source: request.source ?? from }
|
|
704
|
+
summary: `Kicked back from "${from}": ${effectiveReason}`,
|
|
705
|
+
revision: { ...request, reason: effectiveReason, source: request.source ?? from }
|
|
564
706
|
});
|
|
565
707
|
stopped = false;
|
|
566
708
|
await Promise.all(names.map(run2));
|
|
@@ -1613,259 +1755,6 @@ function assertSafeDirectory(root, dir, label) {
|
|
|
1613
1755
|
throw new Error(`unsafe ${label}: resolves outside the workspace`);
|
|
1614
1756
|
}
|
|
1615
1757
|
}
|
|
1616
|
-
var NOISE2 = /* @__PURE__ */ new Set([
|
|
1617
|
-
"engine:text",
|
|
1618
|
-
"engine:thinking"
|
|
1619
|
-
]);
|
|
1620
|
-
function runsHome() {
|
|
1621
|
-
return join(process.env.OBVERSA_HOME ?? join(homedir(), ".obversa"), "runs");
|
|
1622
|
-
}
|
|
1623
|
-
function slug2(value) {
|
|
1624
|
-
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32) || "run";
|
|
1625
|
-
}
|
|
1626
|
-
function newRunId(title) {
|
|
1627
|
-
return `${slug2(title)}-${randomBytes(3).toString("hex")}`;
|
|
1628
|
-
}
|
|
1629
|
-
function startSupervisor(input) {
|
|
1630
|
-
const dir = join(runsHome(), input.runId);
|
|
1631
|
-
const eventsPath = join(dir, "events.jsonl");
|
|
1632
|
-
const proofsPath = join(dir, "proofs.jsonl");
|
|
1633
|
-
const statusPath = join(dir, "status.json");
|
|
1634
|
-
mkdirSync(dir, { recursive: true });
|
|
1635
|
-
writeBestEffort(eventsPath, "");
|
|
1636
|
-
writeBestEffort(proofsPath, "");
|
|
1637
|
-
const startedAt = Date.now();
|
|
1638
|
-
const status = {
|
|
1639
|
-
runId: input.runId,
|
|
1640
|
-
pid: process.pid,
|
|
1641
|
-
cwd: input.cwd,
|
|
1642
|
-
title: input.title,
|
|
1643
|
-
startedAt,
|
|
1644
|
-
updatedAt: startedAt,
|
|
1645
|
-
status: "running",
|
|
1646
|
-
shape: input.shape,
|
|
1647
|
-
live: {
|
|
1648
|
-
path: [],
|
|
1649
|
-
iteration: 0,
|
|
1650
|
-
usage: {
|
|
1651
|
-
inputTokens: 0,
|
|
1652
|
-
outputTokens: 0,
|
|
1653
|
-
cacheReadInputTokens: 0,
|
|
1654
|
-
cacheCreationInputTokens: 0,
|
|
1655
|
-
calls: 0,
|
|
1656
|
-
unmeasuredCalls: 0
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
};
|
|
1660
|
-
const active = /* @__PURE__ */ new Map();
|
|
1661
|
-
let proofCount = 0;
|
|
1662
|
-
const writeStatus = () => {
|
|
1663
|
-
status.updatedAt = Date.now();
|
|
1664
|
-
writeBestEffort(statusPath, JSON.stringify(status, null, 2));
|
|
1665
|
-
};
|
|
1666
|
-
const refreshCurrent = () => {
|
|
1667
|
-
status.live.active = active.size ? Object.fromEntries(active.entries()) : void 0;
|
|
1668
|
-
status.live.current = [...active.values()].sort((a, b) => {
|
|
1669
|
-
const deadline = (a.deadlineAt ?? Number.POSITIVE_INFINITY) - (b.deadlineAt ?? Number.POSITIVE_INFINITY);
|
|
1670
|
-
return deadline || b.startedAt - a.startedAt;
|
|
1671
|
-
})[0];
|
|
1672
|
-
};
|
|
1673
|
-
const activeKey = (kind, path) => `${kind}:${path.join("\0")}`;
|
|
1674
|
-
writeStatus();
|
|
1675
|
-
const sink = (event) => {
|
|
1676
|
-
if (!NOISE2.has(event.kind)) appendBestEffort(eventsPath, event);
|
|
1677
|
-
switch (event.kind) {
|
|
1678
|
-
case "run:end":
|
|
1679
|
-
status.live.lastOutcome = outcomeSummary(event.outcome);
|
|
1680
|
-
break;
|
|
1681
|
-
case "loop:iteration":
|
|
1682
|
-
status.live.path = event.path;
|
|
1683
|
-
status.live.iteration = event.iteration;
|
|
1684
|
-
break;
|
|
1685
|
-
case "loop:condition":
|
|
1686
|
-
status.live.lastGate = {
|
|
1687
|
-
which: event.which,
|
|
1688
|
-
met: event.result.met,
|
|
1689
|
-
confidence: event.result.confidence,
|
|
1690
|
-
reason: event.result.reason
|
|
1691
|
-
};
|
|
1692
|
-
break;
|
|
1693
|
-
case "condition:result":
|
|
1694
|
-
status.live.lastGate = {
|
|
1695
|
-
which: event.label,
|
|
1696
|
-
met: event.result.met,
|
|
1697
|
-
confidence: event.result.confidence,
|
|
1698
|
-
reason: event.result.reason
|
|
1699
|
-
};
|
|
1700
|
-
break;
|
|
1701
|
-
case "dag:node": {
|
|
1702
|
-
const path = [...event.path, event.node];
|
|
1703
|
-
status.live.path = path;
|
|
1704
|
-
if (event.phase === "start") {
|
|
1705
|
-
active.set(activeKey("dag-node", path), {
|
|
1706
|
-
kind: "dag-node",
|
|
1707
|
-
path,
|
|
1708
|
-
node: event.node,
|
|
1709
|
-
startedAt: event.ts,
|
|
1710
|
-
timeoutMs: event.timeoutMs,
|
|
1711
|
-
deadlineAt: event.timeoutMs ? event.ts + event.timeoutMs : void 0
|
|
1712
|
-
});
|
|
1713
|
-
} else {
|
|
1714
|
-
active.delete(activeKey("dag-node", path));
|
|
1715
|
-
}
|
|
1716
|
-
refreshCurrent();
|
|
1717
|
-
break;
|
|
1718
|
-
}
|
|
1719
|
-
case "job:start": {
|
|
1720
|
-
const path = [...event.path, event.label];
|
|
1721
|
-
active.set(activeKey("job", path), {
|
|
1722
|
-
kind: "job",
|
|
1723
|
-
path: event.path,
|
|
1724
|
-
label: event.label,
|
|
1725
|
-
startedAt: event.ts,
|
|
1726
|
-
timeoutMs: event.timeoutMs,
|
|
1727
|
-
deadlineAt: event.timeoutMs ? event.ts + event.timeoutMs : void 0
|
|
1728
|
-
});
|
|
1729
|
-
refreshCurrent();
|
|
1730
|
-
break;
|
|
1731
|
-
}
|
|
1732
|
-
case "loop:end":
|
|
1733
|
-
case "dag:end":
|
|
1734
|
-
case "job:end":
|
|
1735
|
-
status.live.path = event.path;
|
|
1736
|
-
status.live.lastOutcome = outcomeSummary(event.outcome);
|
|
1737
|
-
if (event.kind === "job:end") {
|
|
1738
|
-
active.delete(activeKey("job", [...event.path, event.label]));
|
|
1739
|
-
refreshCurrent();
|
|
1740
|
-
}
|
|
1741
|
-
break;
|
|
1742
|
-
case "engine:usage":
|
|
1743
|
-
status.live.usage.calls += 1;
|
|
1744
|
-
if (event.usage.kind === "unknown") {
|
|
1745
|
-
status.live.usage.unmeasuredCalls += 1;
|
|
1746
|
-
} else {
|
|
1747
|
-
status.live.usage.inputTokens += event.usage.inputTokens;
|
|
1748
|
-
status.live.usage.outputTokens += event.usage.outputTokens;
|
|
1749
|
-
status.live.usage.cacheReadInputTokens += event.usage.cacheReadInputTokens ?? 0;
|
|
1750
|
-
status.live.usage.cacheCreationInputTokens += event.usage.cacheCreationInputTokens ?? 0;
|
|
1751
|
-
}
|
|
1752
|
-
break;
|
|
1753
|
-
case "proof": {
|
|
1754
|
-
const proof = {
|
|
1755
|
-
name: event.name,
|
|
1756
|
-
path: event.path,
|
|
1757
|
-
artifact: event.artifact
|
|
1758
|
-
};
|
|
1759
|
-
proofCount += 1;
|
|
1760
|
-
appendBestEffort(proofsPath, proof);
|
|
1761
|
-
status.evidence = {
|
|
1762
|
-
count: proofCount,
|
|
1763
|
-
indexPath: proofsPath,
|
|
1764
|
-
latest: proof
|
|
1765
|
-
};
|
|
1766
|
-
break;
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
if (!NOISE2.has(event.kind)) writeStatus();
|
|
1770
|
-
};
|
|
1771
|
-
const finish = (outcome) => {
|
|
1772
|
-
status.status = outcome.status;
|
|
1773
|
-
status.endedAt = Date.now();
|
|
1774
|
-
status.live.current = void 0;
|
|
1775
|
-
status.live.active = void 0;
|
|
1776
|
-
status.live.lastOutcome = outcomeSummary(outcome);
|
|
1777
|
-
writeStatus();
|
|
1778
|
-
};
|
|
1779
|
-
return { runId: input.runId, dir, sink, finish };
|
|
1780
|
-
}
|
|
1781
|
-
function outcomeSummary(outcome) {
|
|
1782
|
-
return {
|
|
1783
|
-
status: outcome.status,
|
|
1784
|
-
summary: outcome.summary,
|
|
1785
|
-
late: outcome.late
|
|
1786
|
-
};
|
|
1787
|
-
}
|
|
1788
|
-
function writeBestEffort(path, text6) {
|
|
1789
|
-
try {
|
|
1790
|
-
writeFileSync(path, text6);
|
|
1791
|
-
} catch {
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
function appendBestEffort(path, value) {
|
|
1795
|
-
try {
|
|
1796
|
-
appendFileSync(path, `${JSON.stringify(value)}
|
|
1797
|
-
`);
|
|
1798
|
-
} catch {
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
function runningTotal(totals) {
|
|
1802
|
-
const read = totals.cacheReadInputTokens ?? 0;
|
|
1803
|
-
const unmeasured = totals.unmeasuredCalls ?? 0;
|
|
1804
|
-
const missing = unmeasured > 0 ? `, usage unknown on ${unmeasured} call${unmeasured === 1 ? "" : "s"}` : "";
|
|
1805
|
-
const base = `${totals.inputTokens}/${totals.outputTokens} tok`;
|
|
1806
|
-
return `${base}${read > 0 ? `, ${read} tok from cache` : ""}${missing}`;
|
|
1807
|
-
}
|
|
1808
|
-
function toLine(value) {
|
|
1809
|
-
return value.replace(/[\u0000-\u001f\u007f]+/g, " ");
|
|
1810
|
-
}
|
|
1811
|
-
function formatEvent(event, totals) {
|
|
1812
|
-
return toLine(renderEvent(event, totals));
|
|
1813
|
-
}
|
|
1814
|
-
function renderEvent(event, totals) {
|
|
1815
|
-
const at = event.path.length ? `${event.path.join(" \u203A ")} ` : "";
|
|
1816
|
-
switch (event.kind) {
|
|
1817
|
-
case "run:start":
|
|
1818
|
-
return "\u25B8 run";
|
|
1819
|
-
case "run:end":
|
|
1820
|
-
return `\u25C2 run ${event.outcome.status}${event.outcome.late ? " late" : ""} (${runningTotal(event.usage)})`;
|
|
1821
|
-
case "loop:start":
|
|
1822
|
-
return `${at}\u25B8 loop${event.max ? ` (max ${event.max})` : ""}`;
|
|
1823
|
-
case "dag:start":
|
|
1824
|
-
return `${at}\u25B8 dag (${event.nodes.length} nodes)`;
|
|
1825
|
-
case "loop:iteration":
|
|
1826
|
-
return `${at}\xB7 iteration ${event.iteration}`;
|
|
1827
|
-
case "loop:condition":
|
|
1828
|
-
return `${at}\xB7 ${event.which} ${event.result.met ? "met" : "not met"}: ${event.result.reason}`;
|
|
1829
|
-
case "condition:result":
|
|
1830
|
-
return `${at}\xB7 ${event.label} ${event.result.met ? "met" : "not met"}: ${event.result.reason}`;
|
|
1831
|
-
case "loop:review":
|
|
1832
|
-
return `${at}\xB7 review: ${event.outcome.status}${event.outcome.late ? " late" : ""}`;
|
|
1833
|
-
case "loop:end":
|
|
1834
|
-
return `${at}\u25C2 ${event.outcome.status}${event.outcome.late ? " late" : ""} (${event.iterations} iter)`;
|
|
1835
|
-
case "dag:node":
|
|
1836
|
-
return `${at}\xB7 node ${event.node}: ${event.phase}${event.outcome ? ` (${event.outcome.status}${event.outcome.late ? " late" : ""})` : ""}`;
|
|
1837
|
-
case "dag:kickback":
|
|
1838
|
-
return `${at}\u21A9 kickback ${event.accepted ? "accepted" : "rejected"} ${event.from} -> ${event.to} [${event.count}/${event.limit}]: ${event.reason}${event.note ? ` (${event.note})` : ""}`;
|
|
1839
|
-
case "dag:end":
|
|
1840
|
-
return `${at}\u25C2 dag ${event.outcome.status}${event.outcome.late ? " late" : ""}`;
|
|
1841
|
-
case "job:start":
|
|
1842
|
-
return `${at}\u2022 ${event.label}`;
|
|
1843
|
-
case "advisor:consult":
|
|
1844
|
-
return `${at}\u25C7 advisor ${event.label} #${event.call}: ${event.question}`;
|
|
1845
|
-
case "proof":
|
|
1846
|
-
return `${at}\u25C8 proof ${event.name}: ${event.artifact.title ?? event.artifact.path ?? event.artifact.kind}`;
|
|
1847
|
-
case "job:end":
|
|
1848
|
-
return `${at}\u2022 ${event.label}: ${event.outcome.status}${event.outcome.late ? " late" : ""}`;
|
|
1849
|
-
case "engine:tool":
|
|
1850
|
-
return `${at} tool ${event.name} ${event.phase}`;
|
|
1851
|
-
case "engine:usage": {
|
|
1852
|
-
const call = event.usage.kind === "unknown" ? "usage unknown" : `${event.usage.inputTokens}/${event.usage.outputTokens} tok`;
|
|
1853
|
-
return `${at} ${event.model}: ${call}${totals ? ` (run ${runningTotal(totals)})` : ""}`;
|
|
1854
|
-
}
|
|
1855
|
-
case "loop:stall":
|
|
1856
|
-
return `${at}\u23F9 stalled after ${event.report.iterations.length} no-progress iterations: ${event.report.reason}`;
|
|
1857
|
-
case "limit:wait":
|
|
1858
|
-
return `${at}\u23F8 limit ${event.code}: waiting ${Math.round(event.waitMs / 1e3)}s`;
|
|
1859
|
-
case "limit:pause":
|
|
1860
|
-
return `${at}\u23F8 paused (${event.code}): ${event.reason}`;
|
|
1861
|
-
case "log":
|
|
1862
|
-
return `${at}${event.message}`;
|
|
1863
|
-
case "error":
|
|
1864
|
-
return `${at}\u2717 ${event.code}: ${event.message}`;
|
|
1865
|
-
default:
|
|
1866
|
-
return `${at}${event.kind}`;
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
1758
|
function applyEvent(states, event) {
|
|
1870
1759
|
switch (event.kind) {
|
|
1871
1760
|
case "callback-requested": {
|
|
@@ -2162,14 +2051,11 @@ var MonitorFold = class {
|
|
|
2162
2051
|
...event.note !== void 0 ? { note: event.note } : {}
|
|
2163
2052
|
});
|
|
2164
2053
|
}
|
|
2165
|
-
const line =
|
|
2166
|
-
if (
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
line.summary = event.outcome.summary;
|
|
2054
|
+
const line = formatEvent(event, this.usage);
|
|
2055
|
+
if (line !== "") {
|
|
2056
|
+
this.events.push({ ts: event.ts, line });
|
|
2057
|
+
if (this.events.length > EVENT_TAIL) this.events.splice(0, this.events.length - EVENT_TAIL);
|
|
2170
2058
|
}
|
|
2171
|
-
this.events.push(line);
|
|
2172
|
-
if (this.events.length > EVENT_TAIL) this.events.splice(0, this.events.length - EVENT_TAIL);
|
|
2173
2059
|
}
|
|
2174
2060
|
finish(outcome) {
|
|
2175
2061
|
this.status = "done";
|
|
@@ -2359,6 +2245,15 @@ function page(name) {
|
|
|
2359
2245
|
(() => {
|
|
2360
2246
|
const el = (id) => document.getElementById(id);
|
|
2361
2247
|
const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
|
|
2248
|
+
// What the question is about: the raw input, and a link when it names one,
|
|
2249
|
+
// so a reviewer can open the thing being approved instead of taking the
|
|
2250
|
+
// question's word for it.
|
|
2251
|
+
function aboutHtml(input) {
|
|
2252
|
+
if (input === null || input === undefined) return '';
|
|
2253
|
+
const url = input && typeof input === 'object' && !Array.isArray(input) && typeof input.url === 'string' ? input.url : null;
|
|
2254
|
+
const link = url ? '<p><a href="' + esc(url) + '" target="_blank" rel="noopener">' + esc(url) + '</a></p>' : '';
|
|
2255
|
+
return '<pre>' + esc(JSON.stringify(input, null, 2)) + '</pre>' + link;
|
|
2256
|
+
}
|
|
2362
2257
|
async function answer(requestId, approved) {
|
|
2363
2258
|
const box = document.querySelector('[data-note="' + CSS.escape(requestId) + '"]');
|
|
2364
2259
|
const note = box && box.value ? box.value : '';
|
|
@@ -2384,8 +2279,8 @@ function page(name) {
|
|
|
2384
2279
|
+ '<span class="phase" data-phase="' + esc(v.phase) + '" data-status="' + esc(v.outcome ? v.outcome.status : '') + '">' + esc(v.phase === 'done' && v.outcome ? v.outcome.status : v.phase) + '</span></li>'; }).join('');
|
|
2385
2280
|
el('kickbacks').innerHTML = s.kickbacks.map((k) => '<p class="kick">' + esc(k.from) + ' sent work back to ' + esc(k.to) + (k.accepted ? '' : ' (not accepted' + (k.note ? ': ' + esc(k.note) : '') + ')') + ': ' + esc(k.reason) + ' (' + k.count + ' of ' + k.limit + ')</p>').join('');
|
|
2386
2281
|
el('pending-title').hidden = s.pending.length === 0;
|
|
2387
|
-
el('pending').innerHTML = s.pending.map((p) => '<div><p>' + esc(p.decisionText) + '</p
|
|
2388
|
-
el('record').textContent = s.events.slice(-40).map((e) => new Date(e.ts).toISOString().slice(11, 19) + ' ' + e.
|
|
2282
|
+
el('pending').innerHTML = s.pending.map((p) => '<div><p>' + esc(p.decisionText) + '</p>' + aboutHtml(p.input) + '<form onsubmit="return false"><input data-note="' + esc(p.requestId) + '" placeholder="a note, if any"><button class="yes" data-answer="yes" data-request="' + esc(p.requestId) + '">Yes</button><button data-answer="no" data-request="' + esc(p.requestId) + '">No</button></form></div>').join('');
|
|
2283
|
+
el('record').textContent = s.events.slice(-40).map((e) => new Date(e.ts).toISOString().slice(11, 19) + ' ' + e.line).join('\\n');
|
|
2389
2284
|
if (s.status !== 'done') setTimeout(render, 1000);
|
|
2390
2285
|
}
|
|
2391
2286
|
render();
|
|
@@ -2733,24 +2628,29 @@ function writesOf(config) {
|
|
|
2733
2628
|
if (!writes.length || new Set(writes).size !== writes.length) throw new TypeError("writes must contain unique paths");
|
|
2734
2629
|
return writes;
|
|
2735
2630
|
}
|
|
2736
|
-
function
|
|
2737
|
-
if (
|
|
2738
|
-
if (!Number.isSafeInteger(
|
|
2739
|
-
return
|
|
2631
|
+
function refineCount(refine, label) {
|
|
2632
|
+
if (refine === void 0) return 0;
|
|
2633
|
+
if (!Number.isSafeInteger(refine) || refine < 0) throw new TypeError(`${label} must be a non-negative integer`);
|
|
2634
|
+
return refine;
|
|
2635
|
+
}
|
|
2636
|
+
function refineCap(refine) {
|
|
2637
|
+
return isJudge(refine) ? refine.cap : refine;
|
|
2740
2638
|
}
|
|
2741
2639
|
function optionalFlag(optional) {
|
|
2742
2640
|
if (optional === void 0) return void 0;
|
|
2743
2641
|
if (typeof optional !== "boolean") throw new TypeError("optional must be a boolean");
|
|
2744
2642
|
return optional;
|
|
2745
2643
|
}
|
|
2746
|
-
function
|
|
2747
|
-
|
|
2644
|
+
function refineOf(config) {
|
|
2645
|
+
if (config.refine === void 0) return 1;
|
|
2646
|
+
if (isJudge(config.refine)) return config.refine;
|
|
2647
|
+
return refineCount(config.refine, "refine");
|
|
2748
2648
|
}
|
|
2749
|
-
function
|
|
2750
|
-
if ("reviewedBy" in config && config.reviewedBy) return
|
|
2751
|
-
if (receivesKickback) return
|
|
2752
|
-
if (config.
|
|
2753
|
-
throw new TypeError("
|
|
2649
|
+
function refineForStage(config, receivesKickback) {
|
|
2650
|
+
if ("reviewedBy" in config && config.reviewedBy) return refineOf(config);
|
|
2651
|
+
if (receivesKickback) return refineOf(config);
|
|
2652
|
+
if (config.refine !== void 0) {
|
|
2653
|
+
throw new TypeError("refine must be on a reviewed stage or a kickback target");
|
|
2754
2654
|
}
|
|
2755
2655
|
return 0;
|
|
2756
2656
|
}
|
|
@@ -3035,6 +2935,62 @@ function unchangedNoteGuard(label, job, writes) {
|
|
|
3035
2935
|
return outcome;
|
|
3036
2936
|
};
|
|
3037
2937
|
}
|
|
2938
|
+
function lineDiffCount(before, after) {
|
|
2939
|
+
if (before === void 0) return after.split("\n").length;
|
|
2940
|
+
const a = new Set(before.split("\n"));
|
|
2941
|
+
const b = new Set(after.split("\n"));
|
|
2942
|
+
let changed = 0;
|
|
2943
|
+
for (const line of a) if (!b.has(line)) changed += 1;
|
|
2944
|
+
for (const line of b) if (!a.has(line)) changed += 1;
|
|
2945
|
+
return changed;
|
|
2946
|
+
}
|
|
2947
|
+
function judgedReview(brief, config, cfgJudge, panel) {
|
|
2948
|
+
const useCase = /^Use case:\s*([\s\S]*?)\n\s*\n/m.exec(brief.brief)?.[1]?.replace(/\s+/g, " ").trim();
|
|
2949
|
+
const file = writesOf(config)[0];
|
|
2950
|
+
const history = [];
|
|
2951
|
+
let previousDraft;
|
|
2952
|
+
return async (ctx) => {
|
|
2953
|
+
let draft;
|
|
2954
|
+
let changedLines;
|
|
2955
|
+
if (file !== void 0) {
|
|
2956
|
+
try {
|
|
2957
|
+
draft = await readFile(join(ctx.workspace.dir, file), "utf8");
|
|
2958
|
+
changedLines = lineDiffCount(previousDraft, draft);
|
|
2959
|
+
} catch {
|
|
2960
|
+
}
|
|
2961
|
+
previousDraft = draft;
|
|
2962
|
+
}
|
|
2963
|
+
const panelOutcome = await panel({
|
|
2964
|
+
...ctx,
|
|
2965
|
+
depth: ctx.depth + 1,
|
|
2966
|
+
path: [...ctx.path, "review-panel"]
|
|
2967
|
+
});
|
|
2968
|
+
if (panelOutcome.status === "pass") return panelOutcome;
|
|
2969
|
+
const findings = revisionFromOutcome(panelOutcome)?.findings ?? [];
|
|
2970
|
+
if (hasBlockFinding(findings)) return panelOutcome;
|
|
2971
|
+
const round2 = history.length + 1;
|
|
2972
|
+
const state = {
|
|
2973
|
+
...useCase !== void 0 ? { useCase } : {},
|
|
2974
|
+
...file !== void 0 ? { file } : {},
|
|
2975
|
+
...draft !== void 0 ? { draft } : {},
|
|
2976
|
+
latestFindings: findings,
|
|
2977
|
+
rounds: history,
|
|
2978
|
+
round: round2,
|
|
2979
|
+
cap: cfgJudge.cap
|
|
2980
|
+
};
|
|
2981
|
+
const { decision } = await askJudge(cfgJudge, state, ctx, ctx.path);
|
|
2982
|
+
history.push({
|
|
2983
|
+
round: round2,
|
|
2984
|
+
findings,
|
|
2985
|
+
counts: countBySeverity(findings),
|
|
2986
|
+
...changedLines !== void 0 ? { changedLines } : {}
|
|
2987
|
+
});
|
|
2988
|
+
if (!decision.again) {
|
|
2989
|
+
return { status: "pass", confidence: panelOutcome.confidence, summary: decision.reason, data: panelOutcome.data };
|
|
2990
|
+
}
|
|
2991
|
+
return { ...panelOutcome, summary: `${panelOutcome.summary} (${decision.reason})` };
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
3038
2994
|
function stageJob(brief, named, roles, files, declaredFiles, targetFiles, familyTargetStageNames, familyTargetFamilies) {
|
|
3039
2995
|
const config = named.config;
|
|
3040
2996
|
if ("agent" in config && config.agent !== void 0) {
|
|
@@ -3044,7 +3000,13 @@ function stageJob(brief, named, roles, files, declaredFiles, targetFiles, family
|
|
|
3044
3000
|
if (config.reviewedBy === void 0) return job;
|
|
3045
3001
|
const reviewers = panelRole(roles, config.reviewedBy);
|
|
3046
3002
|
const panel = reviewerPanel(brief, named, reviewers, files, declaredFiles, void 0);
|
|
3047
|
-
const
|
|
3003
|
+
const refine = refineOf(config);
|
|
3004
|
+
const cap = refineCap(refine);
|
|
3005
|
+
const review = isJudge(refine) ? judgedReview(brief, config, refine, panel) : async (ctx) => panel({
|
|
3006
|
+
...ctx,
|
|
3007
|
+
depth: ctx.depth + 1,
|
|
3008
|
+
path: [...ctx.path, "review-panel"]
|
|
3009
|
+
});
|
|
3048
3010
|
const reviewLoop = loop({
|
|
3049
3011
|
name: `${named.name}-review`,
|
|
3050
3012
|
body: job,
|
|
@@ -3060,15 +3022,9 @@ function stageJob(brief, named, roles, files, declaredFiles, targetFiles, family
|
|
|
3060
3022
|
}
|
|
3061
3023
|
return true;
|
|
3062
3024
|
}, `${named.name} writes`),
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
...ctx,
|
|
3067
|
-
depth: ctx.depth + 1,
|
|
3068
|
-
path: [...ctx.path, "review-panel"]
|
|
3069
|
-
}),
|
|
3070
|
-
max: retry + 1,
|
|
3071
|
-
maxReviewRestarts: retry,
|
|
3025
|
+
review,
|
|
3026
|
+
max: cap + 1,
|
|
3027
|
+
maxReviewRestarts: cap,
|
|
3072
3028
|
noProgress: { window: 2, gate: true }
|
|
3073
3029
|
});
|
|
3074
3030
|
return copyJobMeta(
|
|
@@ -3267,7 +3223,7 @@ function workflow(name, config) {
|
|
|
3267
3223
|
if (reviewedBy !== void 0 && stageConfig.sendsBackTo !== void 0) {
|
|
3268
3224
|
throw new TypeError(`stage ${stageName} cannot use both reviewedBy and sendsBackTo`);
|
|
3269
3225
|
}
|
|
3270
|
-
|
|
3226
|
+
refineForStage(stageConfig, incomingTargets.has(stageName));
|
|
3271
3227
|
optionalFlag(stageConfig.optional);
|
|
3272
3228
|
if (stageConfig.retrySafe !== void 0 && typeof stageConfig.retrySafe !== "boolean") {
|
|
3273
3229
|
throw new TypeError("retrySafe must be a boolean");
|
|
@@ -3307,10 +3263,9 @@ function workflow(name, config) {
|
|
|
3307
3263
|
for (const named of config.stages) {
|
|
3308
3264
|
if (named.config.sendsBackTo !== void 0) {
|
|
3309
3265
|
const target = config.stages.find((candidate) => candidate.name === named.config.sendsBackTo);
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
);
|
|
3266
|
+
const refine = refineOf(target.config);
|
|
3267
|
+
const existing = maxKickbacks[named.config.sendsBackTo];
|
|
3268
|
+
maxKickbacks[named.config.sendsBackTo] = isJudge(refine) ? refine : Math.max(isJudge(existing) ? 0 : existing ?? 0, refine);
|
|
3314
3269
|
}
|
|
3315
3270
|
}
|
|
3316
3271
|
const stageJobIdentity = resumeIdentity(workflowName, config);
|
|
@@ -9160,7 +9115,7 @@ function pipeline(name, stages, opts) {
|
|
|
9160
9115
|
});
|
|
9161
9116
|
return dag({ name, nodes, ...opts });
|
|
9162
9117
|
}
|
|
9163
|
-
function
|
|
9118
|
+
function slug2(s) {
|
|
9164
9119
|
return s.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/(^-+|-+$)/g, "") || "x";
|
|
9165
9120
|
}
|
|
9166
9121
|
function tournament(config) {
|
|
@@ -9193,7 +9148,7 @@ function tournament(config) {
|
|
|
9193
9148
|
Array.from(
|
|
9194
9149
|
{ length: config.n },
|
|
9195
9150
|
(_, i) => limit2(async () => {
|
|
9196
|
-
const branch = `lines/${
|
|
9151
|
+
const branch = `lines/${slug2(config.name)}-cand-${i}`;
|
|
9197
9152
|
const wt = await addWorktree(base.dir, {
|
|
9198
9153
|
branch,
|
|
9199
9154
|
base: "HEAD",
|
|
@@ -9271,7 +9226,7 @@ function tournament(config) {
|
|
|
9271
9226
|
}
|
|
9272
9227
|
|
|
9273
9228
|
// src/core/isolated.ts
|
|
9274
|
-
var
|
|
9229
|
+
var slug3 = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "job";
|
|
9275
9230
|
var forkSeq = 0;
|
|
9276
9231
|
function isolated(job, opts = {}) {
|
|
9277
9232
|
const label = opts.label ?? "isolated";
|
|
@@ -9292,7 +9247,7 @@ function isolated(job, opts = {}) {
|
|
|
9292
9247
|
);
|
|
9293
9248
|
return job(parent);
|
|
9294
9249
|
}
|
|
9295
|
-
const branch = `lines/${
|
|
9250
|
+
const branch = `lines/${slug3(label)}-${forkSeq += 1}`;
|
|
9296
9251
|
const wt = await addWorktree(base.dir, {
|
|
9297
9252
|
branch,
|
|
9298
9253
|
base: "HEAD",
|
|
@@ -9326,7 +9281,7 @@ function isolated(job, opts = {}) {
|
|
|
9326
9281
|
...outcome.summary === void 0 ? {} : { summary: outcome.summary }
|
|
9327
9282
|
}) : void 0;
|
|
9328
9283
|
const sha = await commit(
|
|
9329
|
-
message ?? { subject: `chore(${
|
|
9284
|
+
message ?? { subject: `chore(${slug3(label)}): worktree changes` },
|
|
9330
9285
|
{ cwd: wt.dir, signal: parent.signal }
|
|
9331
9286
|
);
|
|
9332
9287
|
const merged = await mergeLock(
|
|
@@ -9770,6 +9725,6 @@ function defineJob(job) {
|
|
|
9770
9725
|
return job;
|
|
9771
9726
|
}
|
|
9772
9727
|
|
|
9773
|
-
export { EXIT_PAUSED, RESUME_STAGE_OUTCOMES, assertGraph, briefFromFile, confidenceCondition, confidenceFromText, convergence, costReport, createAcceptedResultRecord, createCallbackClient, createGitWorktreeProvider, createGraphExecutor, createProofCache, createStoredCallbackClient, dag, dag2 as dagGraphType, defineAgentFromMarkdown, defineJob, directRouter, exitCodeFor, fallbackEngine, formatCostReport,
|
|
9728
|
+
export { EXIT_PAUSED, RESUME_STAGE_OUTCOMES, assertGraph, briefFromFile, confidenceCondition, confidenceFromText, convergence, costReport, createAcceptedResultRecord, createCallbackClient, createGitWorktreeProvider, createGraphExecutor, createProofCache, createStoredCallbackClient, dag, dag2 as dagGraphType, defineAgentFromMarkdown, defineJob, directRouter, exitCodeFor, fallbackEngine, formatCostReport, formatPreflight, interruptRunPreflight, isJudge, isolated, judge, lastDecisionLine, lastGateBrief, loop, parallel, person, pipeline, preflight, preflightEngine, projectTeamRooms, ratchet, readRunPreflight, replayCallbackClient, resolveAcceptedResult, resolveApproval, run, sampled, sequence, stage, stopQuestions, team, teamGraphType, tournament, workflow, writeProofArtifact, writeScope };
|
|
9774
9729
|
//# sourceMappingURL=api.js.map
|
|
9775
9730
|
//# sourceMappingURL=api.js.map
|