@mobrienv/autoloop-harness 0.7.4 → 0.9.1
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/acceptance.d.ts +31 -0
- package/dist/acceptance.js +69 -0
- package/dist/acceptance.js.map +1 -0
- package/dist/ask.d.ts +16 -0
- package/dist/ask.js +47 -0
- package/dist/ask.js.map +1 -0
- package/dist/backend/types.d.ts +16 -0
- package/dist/backend/types.js +17 -0
- package/dist/backend/types.js.map +1 -0
- package/dist/circuit-breaker.d.ts +27 -0
- package/dist/circuit-breaker.js +41 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/completion-lint.d.ts +29 -0
- package/dist/completion-lint.js +51 -0
- package/dist/completion-lint.js.map +1 -0
- package/dist/config-helpers.d.ts +10 -2
- package/dist/config-helpers.js +272 -30
- package/dist/config-helpers.js.map +1 -1
- package/dist/control/acp-adapter.d.ts +12 -0
- package/dist/control/acp-adapter.js +54 -0
- package/dist/control/acp-adapter.js.map +1 -0
- package/dist/control/adapter.d.ts +12 -0
- package/dist/control/adapter.js +2 -0
- package/dist/control/adapter.js.map +1 -0
- package/dist/control/capabilities.d.ts +9 -0
- package/dist/control/capabilities.js +27 -0
- package/dist/control/capabilities.js.map +1 -0
- package/dist/control/claude-sdk-adapter.d.ts +15 -0
- package/dist/control/claude-sdk-adapter.js +79 -0
- package/dist/control/claude-sdk-adapter.js.map +1 -0
- package/dist/control/command-adapter.d.ts +19 -0
- package/dist/control/command-adapter.js +57 -0
- package/dist/control/command-adapter.js.map +1 -0
- package/dist/control/dispatch.d.ts +14 -0
- package/dist/control/dispatch.js +55 -0
- package/dist/control/dispatch.js.map +1 -0
- package/dist/control/index.d.ts +11 -0
- package/dist/control/index.js +9 -0
- package/dist/control/index.js.map +1 -0
- package/dist/control/kiro-adapter.d.ts +2 -0
- package/dist/control/kiro-adapter.js +2 -0
- package/dist/control/kiro-adapter.js.map +1 -0
- package/dist/control/paths.d.ts +19 -0
- package/dist/control/paths.js +40 -0
- package/dist/control/paths.js.map +1 -0
- package/dist/control/pi-adapter.d.ts +15 -0
- package/dist/control/pi-adapter.js +76 -0
- package/dist/control/pi-adapter.js.map +1 -0
- package/dist/control/queue.d.ts +14 -0
- package/dist/control/queue.js +82 -0
- package/dist/control/queue.js.map +1 -0
- package/dist/control/render.d.ts +10 -0
- package/dist/control/render.js +53 -0
- package/dist/control/render.js.map +1 -0
- package/dist/control/types.d.ts +56 -0
- package/dist/control/types.js +9 -0
- package/dist/control/types.js.map +1 -0
- package/dist/display.d.ts +9 -2
- package/dist/display.js +37 -1
- package/dist/display.js.map +1 -1
- package/dist/emit.d.ts +27 -0
- package/dist/emit.js +327 -6
- package/dist/emit.js.map +1 -1
- package/dist/events.d.ts +36 -3
- package/dist/fanout-runner.d.ts +25 -0
- package/dist/fanout-runner.js +54 -0
- package/dist/fanout-runner.js.map +1 -0
- package/dist/file-mod-audit.d.ts +29 -0
- package/dist/file-mod-audit.js +63 -0
- package/dist/file-mod-audit.js.map +1 -0
- package/dist/git-diff.d.ts +19 -0
- package/dist/git-diff.js +74 -0
- package/dist/git-diff.js.map +1 -0
- package/dist/guards.d.ts +37 -0
- package/dist/guards.js +78 -0
- package/dist/guards.js.map +1 -0
- package/dist/hooks.d.ts +78 -0
- package/dist/hooks.js +239 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +15 -2
- package/dist/index.js +359 -26
- package/dist/index.js.map +1 -1
- package/dist/intent.d.ts +45 -0
- package/dist/intent.js +127 -0
- package/dist/intent.js.map +1 -0
- package/dist/iteration-diff.d.ts +32 -0
- package/dist/iteration-diff.js +92 -0
- package/dist/iteration-diff.js.map +1 -0
- package/dist/iteration.d.ts +20 -0
- package/dist/iteration.js +551 -30
- package/dist/iteration.js.map +1 -1
- package/dist/metareview.d.ts +9 -1
- package/dist/metareview.js +158 -34
- package/dist/metareview.js.map +1 -1
- package/dist/notify.d.ts +22 -0
- package/dist/notify.js +110 -0
- package/dist/notify.js.map +1 -0
- package/dist/parallel.d.ts +17 -0
- package/dist/parallel.js +38 -6
- package/dist/parallel.js.map +1 -1
- package/dist/postconditions.d.ts +25 -0
- package/dist/postconditions.js +107 -0
- package/dist/postconditions.js.map +1 -0
- package/dist/postfire-verify.d.ts +31 -0
- package/dist/postfire-verify.js +64 -0
- package/dist/postfire-verify.js.map +1 -0
- package/dist/premature-quit.d.ts +25 -0
- package/dist/premature-quit.js +54 -0
- package/dist/premature-quit.js.map +1 -0
- package/dist/progress.d.ts +20 -0
- package/dist/progress.js +56 -0
- package/dist/progress.js.map +1 -0
- package/dist/prompt.d.ts +4 -0
- package/dist/prompt.js +42 -2
- package/dist/prompt.js.map +1 -1
- package/dist/provisional.d.ts +42 -0
- package/dist/provisional.js +92 -0
- package/dist/provisional.js.map +1 -0
- package/dist/registry-bridge.js +32 -1
- package/dist/registry-bridge.js.map +1 -1
- package/dist/resume.d.ts +61 -0
- package/dist/resume.js +226 -0
- package/dist/resume.js.map +1 -0
- package/dist/scratchpad.js +27 -8
- package/dist/scratchpad.js.map +1 -1
- package/dist/stage.d.ts +21 -0
- package/dist/stage.js +131 -0
- package/dist/stage.js.map +1 -0
- package/dist/stop.d.ts +32 -2
- package/dist/stop.js +169 -0
- package/dist/stop.js.map +1 -1
- package/dist/suspend-state.d.ts +32 -0
- package/dist/suspend-state.js +104 -0
- package/dist/suspend-state.js.map +1 -0
- package/dist/tamper.d.ts +21 -0
- package/dist/tamper.js +88 -0
- package/dist/tamper.js.map +1 -0
- package/dist/types.d.ts +294 -3
- package/dist/types.js +91 -1
- package/dist/types.js.map +1 -1
- package/dist/wave/finalize-wave.d.ts +15 -5
- package/dist/wave/finalize-wave.js +56 -2
- package/dist/wave/finalize-wave.js.map +1 -1
- package/dist/wave/launch-branches.d.ts +23 -2
- package/dist/wave/launch-branches.js +58 -3
- package/dist/wave/launch-branches.js.map +1 -1
- package/dist/wave/stage-branch-runner.d.ts +12 -0
- package/dist/wave/stage-branch-runner.js +130 -0
- package/dist/wave/stage-branch-runner.js.map +1 -0
- package/dist/wave/types.d.ts +40 -2
- package/dist/wave.d.ts +32 -1
- package/dist/wave.js +163 -34
- package/dist/wave.js.map +1 -1
- package/package.json +131 -3
package/dist/intent.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { jsonBool, jsonField, jsonFieldRaw } from "@mobrienv/autoloop-core";
|
|
3
|
+
import { appendEvent, appendOperatorEvent, } from "@mobrienv/autoloop-core/journal";
|
|
4
|
+
const CHECK_SEP = " :: ";
|
|
5
|
+
/** Parse one criterion line into text + optional bound check. */
|
|
6
|
+
export function parseCriterion(line) {
|
|
7
|
+
const idx = line.indexOf(CHECK_SEP);
|
|
8
|
+
if (idx === -1)
|
|
9
|
+
return { text: line.trim() };
|
|
10
|
+
return {
|
|
11
|
+
text: line.slice(0, idx).trim(),
|
|
12
|
+
check: line.slice(idx + CHECK_SEP.length).trim() || undefined,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Extract acceptance criteria from an objective's "Acceptance criteria" (or
|
|
17
|
+
* "Acceptance Criteria") section: the bullet/numbered lines following the
|
|
18
|
+
* heading, up to the next blank line or heading. Returns [] when absent.
|
|
19
|
+
*/
|
|
20
|
+
export function parseObjectiveCriteria(objective) {
|
|
21
|
+
const lines = objective.split("\n");
|
|
22
|
+
const out = [];
|
|
23
|
+
let inSection = false;
|
|
24
|
+
for (const raw of lines) {
|
|
25
|
+
const line = raw.trim();
|
|
26
|
+
if (/^#{0,6}\s*acceptance\s+criteria\b/i.test(line.replace(/[:*_]/g, ""))) {
|
|
27
|
+
inSection = true;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (!inSection)
|
|
31
|
+
continue;
|
|
32
|
+
if (line === "")
|
|
33
|
+
break;
|
|
34
|
+
// A new markdown heading ends the section.
|
|
35
|
+
if (/^#{1,6}\s/.test(line))
|
|
36
|
+
break;
|
|
37
|
+
const bullet = line.match(/^(?:[-*+]|\d+[.)])\s+(.*)$/);
|
|
38
|
+
if (bullet)
|
|
39
|
+
out.push(bullet[1].trim());
|
|
40
|
+
else
|
|
41
|
+
break; // non-bullet line ends a bullet list
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Capture the intent-binding contract at loop start: config criteria first,
|
|
47
|
+
* then any parsed from the objective (deduped by text). This is the bound
|
|
48
|
+
* record of "what was asked" the deterministic gate keys off.
|
|
49
|
+
*/
|
|
50
|
+
export function captureAcceptanceContract(loop) {
|
|
51
|
+
const raw = [
|
|
52
|
+
...(loop.acceptance.criteria ?? []),
|
|
53
|
+
...parseObjectiveCriteria(loop.objective ?? ""),
|
|
54
|
+
];
|
|
55
|
+
const seen = new Set();
|
|
56
|
+
const criteria = [];
|
|
57
|
+
for (const line of raw) {
|
|
58
|
+
const c = parseCriterion(line);
|
|
59
|
+
if (!c.text || seen.has(c.text))
|
|
60
|
+
continue;
|
|
61
|
+
seen.add(c.text);
|
|
62
|
+
criteria.push(c);
|
|
63
|
+
}
|
|
64
|
+
return criteria;
|
|
65
|
+
}
|
|
66
|
+
/** Journal the bound acceptance contract once at loop start (audit/durability). */
|
|
67
|
+
export function journalAcceptanceContract(loop) {
|
|
68
|
+
const criteria = captureAcceptanceContract(loop);
|
|
69
|
+
if (criteria.length === 0)
|
|
70
|
+
return;
|
|
71
|
+
appendEvent(loop.paths.journalFile, loop.runtime.runId, "", "acceptance.contract", jsonField("count", String(criteria.length)) +
|
|
72
|
+
", " +
|
|
73
|
+
jsonField("criteria", criteria.map((c) => c.text).join(" | ")) +
|
|
74
|
+
", " +
|
|
75
|
+
jsonFieldRaw("checked", String(criteria.filter((c) => c.check).length)));
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Evaluate the intent-binding criteria at the acceptance gate. Criteria that
|
|
79
|
+
* bind a deterministic check (`text :: cmd`) are run HARNESS-side in the work
|
|
80
|
+
* dir; a non-zero exit fails acceptance so a run that passes its tests but does
|
|
81
|
+
* not satisfy the stated intent is still rejected. Criteria without a check are
|
|
82
|
+
* advisory (recorded in the contract, not a hard blocker).
|
|
83
|
+
*/
|
|
84
|
+
export function runIntentCriteria(loop, iteration) {
|
|
85
|
+
const checked = captureAcceptanceContract(loop).filter((c) => c.check);
|
|
86
|
+
if (checked.length === 0)
|
|
87
|
+
return { ran: false, passed: true, failures: [] };
|
|
88
|
+
const iter = String(iteration);
|
|
89
|
+
const failures = [];
|
|
90
|
+
for (const c of checked) {
|
|
91
|
+
const check = c.check;
|
|
92
|
+
const res = spawnSync(check, {
|
|
93
|
+
shell: "/bin/sh",
|
|
94
|
+
cwd: loop.paths.workDir,
|
|
95
|
+
encoding: "utf-8",
|
|
96
|
+
timeout: loop.acceptance.timeoutMs,
|
|
97
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
98
|
+
});
|
|
99
|
+
const timedOut = res.signal === "SIGTERM" ||
|
|
100
|
+
res.error?.code === "ETIMEDOUT";
|
|
101
|
+
const exitCode = timedOut ? 124 : (res.status ?? 1);
|
|
102
|
+
const tail = `${res.stdout ?? ""}${res.stderr ?? ""}`.slice(-1000);
|
|
103
|
+
appendEvent(loop.paths.journalFile, loop.runtime.runId, iter, "acceptance.criterion", jsonField("text", c.text) +
|
|
104
|
+
", " +
|
|
105
|
+
jsonField("check", check) +
|
|
106
|
+
", " +
|
|
107
|
+
jsonField("exit_code", String(exitCode)));
|
|
108
|
+
if (exitCode !== 0)
|
|
109
|
+
failures.push({ text: c.text, check, exitCode, tail });
|
|
110
|
+
}
|
|
111
|
+
const passed = failures.length === 0;
|
|
112
|
+
appendEvent(loop.paths.journalFile, loop.runtime.runId, iter, "acceptance.intent_result", jsonFieldRaw("passed", jsonBool(passed)) +
|
|
113
|
+
", " +
|
|
114
|
+
jsonField("failed", String(failures.length)));
|
|
115
|
+
return { ran: true, passed, failures };
|
|
116
|
+
}
|
|
117
|
+
/** Re-inject failed intent criteria as operator guidance. */
|
|
118
|
+
export function reinjectIntentFailure(loop, iteration, result) {
|
|
119
|
+
const detail = result.failures
|
|
120
|
+
.map((f) => `- "${f.text}" (check \`${f.check}\` exited ${f.exitCode}):\n${f.tail}`)
|
|
121
|
+
.join("\n\n");
|
|
122
|
+
const message = "Completion was blocked: the build does not satisfy the stated acceptance " +
|
|
123
|
+
"criteria (intent-binding), even if tests pass. Satisfy these before " +
|
|
124
|
+
`claiming completion:\n\n${detail}`;
|
|
125
|
+
appendOperatorEvent(loop.paths.journalFile, loop.runtime.runId, String(iteration), "operator.guidance", message);
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=intent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intent.js","sourceRoot":"","sources":["../src/intent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EACL,WAAW,EACX,mBAAmB,GACpB,MAAM,iCAAiC,CAAC;AAuBzC,MAAM,SAAS,GAAG,MAAM,CAAC;AAEzB,iEAAiE;AACjE,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,GAAG,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;IAC7C,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE;QAC/B,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS;KAC9D,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,SAAiB;IACtD,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,oCAAoC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;YAC1E,SAAS,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,SAAS;YAAE,SAAS;QACzB,IAAI,IAAI,KAAK,EAAE;YAAE,MAAM;QACvB,2CAA2C;QAC3C,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,MAAM;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;QACxD,IAAI,MAAM;YAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;;YAClC,MAAM,CAAC,qCAAqC;IACnD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,IAAiB;IAEjB,MAAM,GAAG,GAAG;QACV,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC;QACnC,GAAG,sBAAsB,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC;KAChD,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,QAAQ,GAA0B,EAAE,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,SAAS;QAC1C,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACjB,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,yBAAyB,CAAC,IAAiB;IACzD,MAAM,QAAQ,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAClC,WAAW,CACT,IAAI,CAAC,KAAK,CAAC,WAAW,EACtB,IAAI,CAAC,OAAO,CAAC,KAAK,EAClB,EAAE,EACF,qBAAqB,EACrB,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI;QACJ,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9D,IAAI;QACJ,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAC1E,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAiB,EACjB,SAAiB;IAEjB,MAAM,OAAO,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACvE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAE5E,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAA6B,EAAE,CAAC;IAC9C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,CAAC,CAAC,KAAe,CAAC;QAChC,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE;YAC3B,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO;YACvB,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,SAAS;YAClC,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;SAC5B,CAAC,CAAC;QACH,MAAM,QAAQ,GACZ,GAAG,CAAC,MAAM,KAAK,SAAS;YACvB,GAAG,CAAC,KAA2C,EAAE,IAAI,KAAK,WAAW,CAAC;QACzE,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;QACpD,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;QACnE,WAAW,CACT,IAAI,CAAC,KAAK,CAAC,WAAW,EACtB,IAAI,CAAC,OAAO,CAAC,KAAK,EAClB,IAAI,EACJ,sBAAsB,EACtB,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC;YACvB,IAAI;YACJ,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC;YACzB,IAAI;YACJ,SAAS,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAC3C,CAAC;QACF,IAAI,QAAQ,KAAK,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC;IACrC,WAAW,CACT,IAAI,CAAC,KAAK,CAAC,WAAW,EACtB,IAAI,CAAC,OAAO,CAAC,KAAK,EAClB,IAAI,EACJ,0BAA0B,EAC1B,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI;QACJ,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAC/C,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AACzC,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,qBAAqB,CACnC,IAAiB,EACjB,SAAiB,EACjB,MAAoB;IAEpB,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ;SAC3B,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,MAAM,CAAC,CAAC,IAAI,cAAc,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,IAAI,EAAE,CAC1E;SACA,IAAI,CAAC,MAAM,CAAC,CAAC;IAChB,MAAM,OAAO,GACX,2EAA2E;QAC3E,sEAAsE;QACtE,2BAA2B,MAAM,EAAE,CAAC;IACtC,mBAAmB,CACjB,IAAI,CAAC,KAAK,CAAC,WAAW,EACtB,IAAI,CAAC,OAAO,CAAC,KAAK,EAClB,MAAM,CAAC,SAAS,CAAC,EACjB,mBAAmB,EACnB,OAAO,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface TextDelta {
|
|
2
|
+
aChars: number;
|
|
3
|
+
bChars: number;
|
|
4
|
+
charDelta: number;
|
|
5
|
+
linesAdded: number;
|
|
6
|
+
linesRemoved: number;
|
|
7
|
+
changed: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface IterationDiff {
|
|
10
|
+
iterA: number;
|
|
11
|
+
iterB: number;
|
|
12
|
+
prompt: TextDelta;
|
|
13
|
+
output: TextDelta;
|
|
14
|
+
cost: {
|
|
15
|
+
aUsd: number;
|
|
16
|
+
bUsd: number;
|
|
17
|
+
deltaUsd: number;
|
|
18
|
+
};
|
|
19
|
+
/** Event topics emitted in one iteration but not the other (structural). */
|
|
20
|
+
events: {
|
|
21
|
+
onlyInA: string[];
|
|
22
|
+
onlyInB: string[];
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Structural diff between two journaled iterations of a run: prompt (assembled
|
|
27
|
+
* context), backend output, cost, and the emitted-event topic set. Pure over
|
|
28
|
+
* the run's journal lines — the durable journal is the trustworthy source.
|
|
29
|
+
*/
|
|
30
|
+
export declare function diffIterations(runLines: string[], iterA: number, iterB: number): IterationDiff;
|
|
31
|
+
/** Render a structural iteration diff as a human-readable report. */
|
|
32
|
+
export declare function renderIterationDiff(runId: string, d: IterationDiff): string;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { extractField, extractIteration, extractTopic, } from "@mobrienv/autoloop-core/journal";
|
|
2
|
+
function linesOf(runLines, iter) {
|
|
3
|
+
const want = String(iter);
|
|
4
|
+
return runLines.filter((l) => extractIteration(l) === want);
|
|
5
|
+
}
|
|
6
|
+
function fieldFor(lines, topic, field) {
|
|
7
|
+
// Last matching line wins (the final value journaled for that iteration).
|
|
8
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
9
|
+
if (extractTopic(lines[i]) === topic)
|
|
10
|
+
return extractField(lines[i], field);
|
|
11
|
+
}
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
function topicsOf(lines) {
|
|
15
|
+
return new Set(lines.map((l) => extractTopic(l)).filter(Boolean));
|
|
16
|
+
}
|
|
17
|
+
function costOf(lines) {
|
|
18
|
+
let sum = 0;
|
|
19
|
+
for (const l of lines) {
|
|
20
|
+
if (extractTopic(l) !== "backend.usage")
|
|
21
|
+
continue;
|
|
22
|
+
const v = Number(extractField(l, "cost_usd"));
|
|
23
|
+
if (Number.isFinite(v))
|
|
24
|
+
sum += v;
|
|
25
|
+
}
|
|
26
|
+
return Math.round(sum * 1e6) / 1e6;
|
|
27
|
+
}
|
|
28
|
+
/** Multiset line diff: counts of lines present in b-not-a (added) / a-not-b (removed). */
|
|
29
|
+
function textDelta(a, b) {
|
|
30
|
+
const aLines = a ? a.split("\n") : [];
|
|
31
|
+
const bLines = b ? b.split("\n") : [];
|
|
32
|
+
const count = (xs) => {
|
|
33
|
+
const m = new Map();
|
|
34
|
+
for (const x of xs)
|
|
35
|
+
m.set(x, (m.get(x) ?? 0) + 1);
|
|
36
|
+
return m;
|
|
37
|
+
};
|
|
38
|
+
const am = count(aLines);
|
|
39
|
+
const bm = count(bLines);
|
|
40
|
+
let added = 0;
|
|
41
|
+
let removed = 0;
|
|
42
|
+
for (const [line, n] of bm)
|
|
43
|
+
added += Math.max(0, n - (am.get(line) ?? 0));
|
|
44
|
+
for (const [line, n] of am)
|
|
45
|
+
removed += Math.max(0, n - (bm.get(line) ?? 0));
|
|
46
|
+
return {
|
|
47
|
+
aChars: a.length,
|
|
48
|
+
bChars: b.length,
|
|
49
|
+
charDelta: b.length - a.length,
|
|
50
|
+
linesAdded: added,
|
|
51
|
+
linesRemoved: removed,
|
|
52
|
+
changed: a !== b,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Structural diff between two journaled iterations of a run: prompt (assembled
|
|
57
|
+
* context), backend output, cost, and the emitted-event topic set. Pure over
|
|
58
|
+
* the run's journal lines — the durable journal is the trustworthy source.
|
|
59
|
+
*/
|
|
60
|
+
export function diffIterations(runLines, iterA, iterB) {
|
|
61
|
+
const a = linesOf(runLines, iterA);
|
|
62
|
+
const b = linesOf(runLines, iterB);
|
|
63
|
+
const aTopics = topicsOf(a);
|
|
64
|
+
const bTopics = topicsOf(b);
|
|
65
|
+
const aUsd = costOf(a);
|
|
66
|
+
const bUsd = costOf(b);
|
|
67
|
+
return {
|
|
68
|
+
iterA,
|
|
69
|
+
iterB,
|
|
70
|
+
prompt: textDelta(fieldFor(a, "iteration.start", "prompt"), fieldFor(b, "iteration.start", "prompt")),
|
|
71
|
+
output: textDelta(fieldFor(a, "iteration.finish", "output"), fieldFor(b, "iteration.finish", "output")),
|
|
72
|
+
cost: { aUsd, bUsd, deltaUsd: Math.round((bUsd - aUsd) * 1e6) / 1e6 },
|
|
73
|
+
events: {
|
|
74
|
+
onlyInA: [...aTopics].filter((t) => !bTopics.has(t)).sort(),
|
|
75
|
+
onlyInB: [...bTopics].filter((t) => !aTopics.has(t)).sort(),
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Render a structural iteration diff as a human-readable report. */
|
|
80
|
+
export function renderIterationDiff(runId, d) {
|
|
81
|
+
const t = (label, x) => `${label}: ${x.changed ? "changed" : "identical"} (${x.aChars}→${x.bChars} chars, Δ${x.charDelta >= 0 ? "+" : ""}${x.charDelta}; +${x.linesAdded}/-${x.linesRemoved} lines)`;
|
|
82
|
+
return [
|
|
83
|
+
`## Diff ${runId}: iter ${d.iterA} → ${d.iterB}`,
|
|
84
|
+
"",
|
|
85
|
+
t("prompt/context", d.prompt),
|
|
86
|
+
t("output", d.output),
|
|
87
|
+
`cost: $${d.cost.aUsd} → $${d.cost.bUsd} (Δ${d.cost.deltaUsd >= 0 ? "+" : ""}${d.cost.deltaUsd})`,
|
|
88
|
+
`events only in ${d.iterA}: ${d.events.onlyInA.join(", ") || "(none)"}`,
|
|
89
|
+
`events only in ${d.iterB}: ${d.events.onlyInB.join(", ") || "(none)"}`,
|
|
90
|
+
].join("\n");
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=iteration-diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iteration-diff.js","sourceRoot":"","sources":["../src/iteration-diff.ts"],"names":[],"mappings":"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"}
|
package/dist/iteration.d.ts
CHANGED
|
@@ -10,7 +10,27 @@ export declare function resolveOutcome(ctx: {
|
|
|
10
10
|
completionEvent: string;
|
|
11
11
|
requiredEvents: string[];
|
|
12
12
|
completionPromise: string;
|
|
13
|
+
hasBlockingTasks: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Ralph-parity ordering policy. When true, a completion claim is rejected
|
|
16
|
+
* (falls through to the routed/promise/continue branches) unless
|
|
17
|
+
* `completionOrderOk` confirms no other event followed completionEvent in
|
|
18
|
+
* its own turn. Optional/false = order-insensitive set-membership check
|
|
19
|
+
* (unchanged default behavior).
|
|
20
|
+
*/
|
|
21
|
+
mustBeLast?: boolean;
|
|
22
|
+
/** Precomputed by the caller from full journal ordering (see completionEmittedLast). */
|
|
23
|
+
completionOrderOk?: boolean;
|
|
13
24
|
}): {
|
|
14
25
|
action: string;
|
|
15
26
|
outcome: string;
|
|
16
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* True iff `completionEvent`, wherever it was last emitted in the run, was
|
|
30
|
+
* the last non-system topic emitted in that same turn (iteration). Used to
|
|
31
|
+
* enforce `completion.must_be_last`: ralph rejects completion if other
|
|
32
|
+
* events follow it within the same turn, rather than autoloop's default
|
|
33
|
+
* order-insensitive set-membership check. Returns true (not applicable) if
|
|
34
|
+
* the completion event has not been emitted at all.
|
|
35
|
+
*/
|
|
36
|
+
export declare function completionEmittedLast(runLines: string[], completionEvent: string): boolean;
|