@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/parallel.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { buildBackendShellCommand, normalizeBackendLabel, runBackendCommand, } from "@mobrienv/autoloop-backends";
|
|
3
|
+
import { buildBackendShellCommand, normalizeBackendLabel, runBackendCommand, runBackendCommandAsync, } from "@mobrienv/autoloop-backends";
|
|
4
4
|
import { joinCsv, jsonBool, jsonField, jsonFieldRaw, listText, shellQuote, splitCsv, } from "@mobrienv/autoloop-core";
|
|
5
5
|
import { appendAgentEvent, appendEvent, extractField, readIfExists, } from "@mobrienv/autoloop-core/journal";
|
|
6
6
|
export function loadParallelBranchLaunch(branchDir) {
|
|
@@ -14,6 +14,7 @@ export function loadParallelBranchLaunch(branchDir) {
|
|
|
14
14
|
allowedEvents: csvFieldList(line, "allowed_events"),
|
|
15
15
|
prompt: extractField(line, "prompt"),
|
|
16
16
|
backendKind: extractField(line, "backend_kind"),
|
|
17
|
+
backendProvider: extractField(line, "backend_provider"),
|
|
17
18
|
backendCommand: extractField(line, "backend_command"),
|
|
18
19
|
backendArgs: csvFieldList(line, "backend_args"),
|
|
19
20
|
backendPromptMode: extractField(line, "backend_prompt_mode"),
|
|
@@ -30,6 +31,8 @@ export function parallelBranchBackendOverride(launch) {
|
|
|
30
31
|
const override = {};
|
|
31
32
|
if (launch.backendKind)
|
|
32
33
|
override.kind = launch.backendKind;
|
|
34
|
+
if (launch.backendProvider)
|
|
35
|
+
override.provider = launch.backendProvider;
|
|
33
36
|
if (launch.backendCommand)
|
|
34
37
|
override.command = launch.backendCommand;
|
|
35
38
|
if (launch.backendArgs.length > 0)
|
|
@@ -95,15 +98,42 @@ export function runProcess(command, timeoutMs, providerKind = "command") {
|
|
|
95
98
|
timedOut: result.timedOut,
|
|
96
99
|
};
|
|
97
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Async, non-blocking sibling of `runProcess` — used for the main-loop
|
|
103
|
+
* `command` backend so the harness can register a live PID for interrupt
|
|
104
|
+
* signaling (see `commandControlAdapter`) and keep servicing its SIGUSR1
|
|
105
|
+
* control-drain handler while the child runs. `onSpawn` fires synchronously
|
|
106
|
+
* once the child is forked, before the returned promise settles.
|
|
107
|
+
*/
|
|
108
|
+
export function runProcessAsync(command, timeoutMs, providerKind = "command", onSpawn) {
|
|
109
|
+
return runBackendCommandAsync(providerKind, command, timeoutMs, (pid) => onSpawn?.(pid)).then((result) => ({
|
|
110
|
+
output: result.output,
|
|
111
|
+
exitCode: result.exitCode,
|
|
112
|
+
timedOut: result.timedOut,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Side-file path for the `usage_from = "file"` cost-telemetry convention:
|
|
117
|
+
* `AUTOLOOP_USAGE_FILE`, exported for the child, read back by
|
|
118
|
+
* `recordCommandUsage` once the iteration completes. Shared between
|
|
119
|
+
* `buildBackendCommand` (export) and `iteration.ts` (read-back) so both sides
|
|
120
|
+
* agree on the same path without threading it through extra state.
|
|
121
|
+
*/
|
|
122
|
+
export function commandUsageFilePath(loop, iteration) {
|
|
123
|
+
return join(loop.paths.stateDir, `usage.${iteration}.json`);
|
|
124
|
+
}
|
|
98
125
|
export function buildBackendCommand(loop, iter) {
|
|
99
126
|
return buildBackendShellCommand({
|
|
100
127
|
paths: {
|
|
101
128
|
stateDir: loop.paths.stateDir,
|
|
102
129
|
piAdapterPath: loop.paths.piAdapterPath,
|
|
103
130
|
},
|
|
104
|
-
spec:
|
|
131
|
+
spec: iter.backend,
|
|
105
132
|
prompt: iter.prompt,
|
|
106
133
|
runtimeEnv: runtimeEnvLines(loop, String(iter.iteration), iter.recentEvent, joinCsv(iter.allowedRoles), joinCsv(iter.allowedEvents), ""),
|
|
134
|
+
usageFilePath: iter.backend.kind === "command" && iter.backend.usageFrom
|
|
135
|
+
? commandUsageFilePath(loop, iter.iteration)
|
|
136
|
+
: undefined,
|
|
107
137
|
});
|
|
108
138
|
}
|
|
109
139
|
export function buildReviewCommand(loop, iteration, reviewPrompt) {
|
|
@@ -215,13 +245,15 @@ export function appendIterationStart(loop, iter) {
|
|
|
215
245
|
jsonField("prompt", iter.prompt));
|
|
216
246
|
}
|
|
217
247
|
export function appendBackendStart(loop, iter) {
|
|
218
|
-
appendEvent(loop.paths.journalFile, loop.runtime.runId, String(iter.iteration), "backend.start", jsonField("backend_kind",
|
|
248
|
+
appendEvent(loop.paths.journalFile, loop.runtime.runId, String(iter.iteration), "backend.start", jsonField("backend_kind", iter.backend.kind) +
|
|
249
|
+
", " +
|
|
250
|
+
jsonField("command", iter.backend.command) +
|
|
219
251
|
", " +
|
|
220
|
-
jsonField("
|
|
252
|
+
jsonField("args", joinCsv(iter.backend.args)) +
|
|
221
253
|
", " +
|
|
222
|
-
jsonField("prompt_mode",
|
|
254
|
+
jsonField("prompt_mode", iter.backend.promptMode) +
|
|
223
255
|
", " +
|
|
224
|
-
jsonField("timeout_ms", String(
|
|
256
|
+
jsonField("timeout_ms", String(iter.backend.timeoutMs)));
|
|
225
257
|
}
|
|
226
258
|
export function appendBackendFinish(loop, iter, output, exitCode, timedOut) {
|
|
227
259
|
appendEvent(loop.paths.journalFile, loop.runtime.runId, String(iter.iteration), "backend.finish", jsonField("exit_code", String(exitCode)) +
|
package/dist/parallel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parallel.js","sourceRoot":"","sources":["../src/parallel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"parallel.js","sourceRoot":"","sources":["../src/parallel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { LoopContext } from "./types.js";
|
|
2
|
+
export interface PostconditionViolation {
|
|
3
|
+
/** Stable assertion id, e.g. "no_todo". */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Human-readable detail (sample offending lines / files). */
|
|
6
|
+
detail: string;
|
|
7
|
+
}
|
|
8
|
+
export interface PostconditionResult {
|
|
9
|
+
/** True when at least one guard was enabled and evaluated. */
|
|
10
|
+
ran: boolean;
|
|
11
|
+
/** True when no enabled guard was violated. */
|
|
12
|
+
passed: boolean;
|
|
13
|
+
violations: PostconditionViolation[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Evaluate the enabled required-absence guards at the acceptance gate. All are
|
|
17
|
+
* harness-side and independent of agent claims; a violation blocks completion.
|
|
18
|
+
* Guards that depend on git are skipped (not failed) outside a git work tree.
|
|
19
|
+
*/
|
|
20
|
+
export declare function runPostconditionGuards(loop: LoopContext, iteration: number): PostconditionResult;
|
|
21
|
+
/**
|
|
22
|
+
* Re-inject violated postconditions as operator guidance so the next iteration
|
|
23
|
+
* fixes them instead of completing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function reinjectPostconditionFailure(loop: LoopContext, iteration: number, result: PostconditionResult): void;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { dirname, relative, sep } from "node:path";
|
|
2
|
+
import { jsonBool, jsonField, jsonFieldRaw } from "@mobrienv/autoloop-core";
|
|
3
|
+
import { appendEvent, appendOperatorEvent, } from "@mobrienv/autoloop-core/journal";
|
|
4
|
+
import { collectAddedLines, isGitRepo, porcelainStatus, } from "./git-diff.js";
|
|
5
|
+
const MAX_SAMPLE = 10;
|
|
6
|
+
const TODO_RE = /\b(TODO|FIXME|XXX|HACK)\b/;
|
|
7
|
+
const SKIP_RE = /(\b(it|describe|test)\.(only|skip)\b|\bx(it|describe)\b|\bf(it|describe)\b|@pytest\.mark\.(skip|xfail)|\bunittest\.skip\b|#\[ignore\]|\bdescribe\.only\b)/;
|
|
8
|
+
const SECRET_RES = [
|
|
9
|
+
["aws-access-key", /\bAKIA[0-9A-Z]{16}\b/],
|
|
10
|
+
["private-key", /-----BEGIN [A-Z ]*PRIVATE KEY-----/],
|
|
11
|
+
["slack-token", /xox[baprs]-[0-9A-Za-z-]{10,}/],
|
|
12
|
+
["github-token", /\bghp_[0-9A-Za-z]{36}\b/],
|
|
13
|
+
[
|
|
14
|
+
"generic-secret",
|
|
15
|
+
/(api[_-]?key|secret|token|password|passwd)\s*[:=]\s*['"][A-Za-z0-9_\-./+]{16,}['"]/i,
|
|
16
|
+
],
|
|
17
|
+
];
|
|
18
|
+
function sample(lines) {
|
|
19
|
+
return lines
|
|
20
|
+
.slice(0, MAX_SAMPLE)
|
|
21
|
+
.map((l) => ` ${l.file}: ${l.text.trim()}`)
|
|
22
|
+
.join("\n");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Evaluate the enabled required-absence guards at the acceptance gate. All are
|
|
26
|
+
* harness-side and independent of agent claims; a violation blocks completion.
|
|
27
|
+
* Guards that depend on git are skipped (not failed) outside a git work tree.
|
|
28
|
+
*/
|
|
29
|
+
export function runPostconditionGuards(loop, iteration) {
|
|
30
|
+
const a = loop.acceptance;
|
|
31
|
+
const enabled = a.assertNoTodo ||
|
|
32
|
+
a.assertNoSkippedTests ||
|
|
33
|
+
a.assertNoSecrets ||
|
|
34
|
+
a.assertCleanTree;
|
|
35
|
+
if (!enabled)
|
|
36
|
+
return { ran: false, passed: true, violations: [] };
|
|
37
|
+
const iter = String(iteration);
|
|
38
|
+
const workDir = loop.paths.workDir;
|
|
39
|
+
const inRepo = isGitRepo(workDir);
|
|
40
|
+
const violations = [];
|
|
41
|
+
appendEvent(loop.paths.journalFile, loop.runtime.runId, iter, "postcondition.start", jsonFieldRaw("git_repo", jsonBool(inRepo)));
|
|
42
|
+
if (inRepo &&
|
|
43
|
+
(a.assertNoTodo || a.assertNoSkippedTests || a.assertNoSecrets)) {
|
|
44
|
+
const added = collectAddedLines(workDir);
|
|
45
|
+
if (a.assertNoTodo) {
|
|
46
|
+
const hits = added.filter((l) => TODO_RE.test(l.text));
|
|
47
|
+
if (hits.length)
|
|
48
|
+
violations.push({
|
|
49
|
+
id: "no_todo",
|
|
50
|
+
detail: `New TODO/FIXME/XXX/HACK markers:\n${sample(hits)}`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (a.assertNoSkippedTests) {
|
|
54
|
+
const hits = added.filter((l) => SKIP_RE.test(l.text));
|
|
55
|
+
if (hits.length)
|
|
56
|
+
violations.push({
|
|
57
|
+
id: "no_skipped_tests",
|
|
58
|
+
detail: `Skipped/only/xfail test markers:\n${sample(hits)}`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (a.assertNoSecrets) {
|
|
62
|
+
const hits = added.filter((l) => SECRET_RES.some(([, re]) => re.test(l.text)));
|
|
63
|
+
if (hits.length)
|
|
64
|
+
violations.push({
|
|
65
|
+
id: "no_secrets",
|
|
66
|
+
detail: `Possible committed secrets:\n${sample(hits)}`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (a.assertCleanTree && inRepo) {
|
|
71
|
+
// The harness writes its own journal/state during this very guard, so a
|
|
72
|
+
// tracked `.autoloop/` would make the guard trip on its own bookkeeping.
|
|
73
|
+
// clean_tree asserts the agent left the *project* clean — exclude the
|
|
74
|
+
// autoloop state dir from the assessment.
|
|
75
|
+
const stateRel = relative(workDir, dirname(loop.paths.journalFile));
|
|
76
|
+
const dirty = porcelainStatus(workDir).filter((line) => {
|
|
77
|
+
if (!stateRel || stateRel.startsWith(".."))
|
|
78
|
+
return true;
|
|
79
|
+
const path = line.slice(3); // strip the `XY ` porcelain status prefix
|
|
80
|
+
return path !== stateRel && !path.startsWith(`${stateRel}${sep}`);
|
|
81
|
+
});
|
|
82
|
+
if (dirty.length)
|
|
83
|
+
violations.push({
|
|
84
|
+
id: "clean_tree",
|
|
85
|
+
detail: `Working tree is not clean:\n${dirty.slice(0, MAX_SAMPLE).join("\n")}`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const passed = violations.length === 0;
|
|
89
|
+
appendEvent(loop.paths.journalFile, loop.runtime.runId, iter, "postcondition.result", jsonFieldRaw("passed", jsonBool(passed)) +
|
|
90
|
+
", " +
|
|
91
|
+
jsonField("violations", violations.map((v) => v.id).join(",")));
|
|
92
|
+
return { ran: true, passed, violations };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Re-inject violated postconditions as operator guidance so the next iteration
|
|
96
|
+
* fixes them instead of completing.
|
|
97
|
+
*/
|
|
98
|
+
export function reinjectPostconditionFailure(loop, iteration, result) {
|
|
99
|
+
const detail = result.violations
|
|
100
|
+
.map((v) => `- [${v.id}] ${v.detail}`)
|
|
101
|
+
.join("\n\n");
|
|
102
|
+
const message = "Completion was blocked: required-absence guards failed. " +
|
|
103
|
+
"Remove these before claiming completion.\n\n" +
|
|
104
|
+
detail;
|
|
105
|
+
appendOperatorEvent(loop.paths.journalFile, loop.runtime.runId, String(iteration), "operator.guidance", message);
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=postconditions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postconditions.js","sourceRoot":"","sources":["../src/postconditions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type Reconcile = "confirmed" | "false_done" | "unverifiable";
|
|
2
|
+
export interface PostFireFailure {
|
|
3
|
+
command: string;
|
|
4
|
+
exitCode: number;
|
|
5
|
+
tail: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PostFireResult {
|
|
8
|
+
reconcile: Reconcile;
|
|
9
|
+
ranChecks: number;
|
|
10
|
+
failures: PostFireFailure[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Reconcile a run's bare `completed` claim with an out-of-band re-check.
|
|
14
|
+
* `green = success` is treated as UNVERIFIED until a deterministic check passes:
|
|
15
|
+
* no checks → `unverifiable` (nothing to prove the claim), all pass →
|
|
16
|
+
* `confirmed`, any fail → `false_done`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function reconcileOutcome(ranChecks: number, allPassed: boolean): Reconcile;
|
|
19
|
+
/**
|
|
20
|
+
* Assemble the deterministic post-fire check commands for a run from its
|
|
21
|
+
* acceptance config: `verify_cmds`, a single `verify_cmd`, and any
|
|
22
|
+
* criterion-bound checks (`text :: cmd`). Pure — the caller supplies the raw
|
|
23
|
+
* config values so this is trivially testable.
|
|
24
|
+
*/
|
|
25
|
+
export declare function postFireCheckCommands(verifyCmds: string[], verifyCmd: string, criteria: string[]): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Run the deterministic checks out-of-band in `workDir` (the completed run's
|
|
28
|
+
* work tree) and reconcile. This IS the independent post-fire verifier for
|
|
29
|
+
* detached/scheduled runs: nobody watched the run, so the harness re-checks.
|
|
30
|
+
*/
|
|
31
|
+
export declare function verifyPostFire(workDir: string, cmds: string[], timeoutMs: number): PostFireResult;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { parseCriterion } from "./intent.js";
|
|
3
|
+
/**
|
|
4
|
+
* Reconcile a run's bare `completed` claim with an out-of-band re-check.
|
|
5
|
+
* `green = success` is treated as UNVERIFIED until a deterministic check passes:
|
|
6
|
+
* no checks → `unverifiable` (nothing to prove the claim), all pass →
|
|
7
|
+
* `confirmed`, any fail → `false_done`.
|
|
8
|
+
*/
|
|
9
|
+
export function reconcileOutcome(ranChecks, allPassed) {
|
|
10
|
+
if (ranChecks === 0)
|
|
11
|
+
return "unverifiable";
|
|
12
|
+
return allPassed ? "confirmed" : "false_done";
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Assemble the deterministic post-fire check commands for a run from its
|
|
16
|
+
* acceptance config: `verify_cmds`, a single `verify_cmd`, and any
|
|
17
|
+
* criterion-bound checks (`text :: cmd`). Pure — the caller supplies the raw
|
|
18
|
+
* config values so this is trivially testable.
|
|
19
|
+
*/
|
|
20
|
+
export function postFireCheckCommands(verifyCmds, verifyCmd, criteria) {
|
|
21
|
+
const cmds = [...verifyCmds];
|
|
22
|
+
const single = verifyCmd.trim();
|
|
23
|
+
if (single)
|
|
24
|
+
cmds.push(single);
|
|
25
|
+
for (const line of criteria) {
|
|
26
|
+
const { check } = parseCriterion(line);
|
|
27
|
+
if (check)
|
|
28
|
+
cmds.push(check);
|
|
29
|
+
}
|
|
30
|
+
return cmds;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Run the deterministic checks out-of-band in `workDir` (the completed run's
|
|
34
|
+
* work tree) and reconcile. This IS the independent post-fire verifier for
|
|
35
|
+
* detached/scheduled runs: nobody watched the run, so the harness re-checks.
|
|
36
|
+
*/
|
|
37
|
+
export function verifyPostFire(workDir, cmds, timeoutMs) {
|
|
38
|
+
const failures = [];
|
|
39
|
+
for (const command of cmds) {
|
|
40
|
+
const res = spawnSync(command, {
|
|
41
|
+
shell: "/bin/sh",
|
|
42
|
+
cwd: workDir,
|
|
43
|
+
encoding: "utf-8",
|
|
44
|
+
timeout: timeoutMs,
|
|
45
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
46
|
+
});
|
|
47
|
+
const timedOut = res.signal === "SIGTERM" ||
|
|
48
|
+
res.error?.code === "ETIMEDOUT";
|
|
49
|
+
const exitCode = timedOut ? 124 : (res.status ?? 1);
|
|
50
|
+
if (exitCode !== 0) {
|
|
51
|
+
failures.push({
|
|
52
|
+
command,
|
|
53
|
+
exitCode,
|
|
54
|
+
tail: `${res.stdout ?? ""}${res.stderr ?? ""}`.slice(-1000),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
reconcile: reconcileOutcome(cmds.length, failures.length === 0),
|
|
60
|
+
ranChecks: cmds.length,
|
|
61
|
+
failures,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=postfire-verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postfire-verify.js","sourceRoot":"","sources":["../src/postfire-verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAgB7C;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,SAAkB;IAElB,IAAI,SAAS,KAAK,CAAC;QAAE,OAAO,cAAc,CAAC;IAC3C,OAAO,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAoB,EACpB,SAAiB,EACjB,QAAkB;IAElB,MAAM,IAAI,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC;IAC7B,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,MAAM;QAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAe,EACf,IAAc,EACd,SAAiB;IAEjB,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,KAAK,MAAM,OAAO,IAAI,IAAI,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,EAAE;YAC7B,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,OAAO;YACZ,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,SAAS;YAClB,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,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YACnB,QAAQ,CAAC,IAAI,CAAC;gBACZ,OAAO;gBACP,QAAQ;gBACR,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;aAC5D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO;QACL,SAAS,EAAE,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC;QAC/D,SAAS,EAAE,IAAI,CAAC,MAAM;QACtB,QAAQ;KACT,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { LoopContext } from "./types.js";
|
|
2
|
+
export interface PrematureQuitCheck {
|
|
3
|
+
/** True when the run is quitting with authorized work left and no blocker. */
|
|
4
|
+
premature: boolean;
|
|
5
|
+
/** Human-readable reasons (open task ids, unmet required events). */
|
|
6
|
+
reasons: string[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Detect an announce-then-halt / silent drift-stop: the loop is stopping (e.g.
|
|
10
|
+
* a stall) but authorized work remains AND nothing is blocking it. That is a
|
|
11
|
+
* false finish that quietly under-delivers, distinct from a legitimate stop.
|
|
12
|
+
*
|
|
13
|
+
* Work remaining = open non-soft tasks OR unmet required completion events.
|
|
14
|
+
* Blocker = a transient/availability pause on the latest turn (an availability
|
|
15
|
+
* issue, not a premature quit).
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectPrematureQuit(loop: LoopContext, runLines: string[]): PrematureQuitCheck;
|
|
18
|
+
/** How many times this run has already been re-armed (bound the re-arm loop). */
|
|
19
|
+
export declare function countRearms(runLines: string[]): number;
|
|
20
|
+
/**
|
|
21
|
+
* Re-arm a premature quit: journal the re-arm and inject a nudge so the next
|
|
22
|
+
* iteration's prompt differs (breaking the stall) and the agent finishes the
|
|
23
|
+
* remaining authorized work instead of halting.
|
|
24
|
+
*/
|
|
25
|
+
export declare function rearmPrematureQuit(loop: LoopContext, iteration: number, check: PrematureQuitCheck): void;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsonField } from "@mobrienv/autoloop-core";
|
|
2
|
+
import { appendEvent, appendOperatorEvent, extractTopic, } from "@mobrienv/autoloop-core/journal";
|
|
3
|
+
import { materializeOpenFrom } from "@mobrienv/autoloop-core/tasks";
|
|
4
|
+
/**
|
|
5
|
+
* Detect an announce-then-halt / silent drift-stop: the loop is stopping (e.g.
|
|
6
|
+
* a stall) but authorized work remains AND nothing is blocking it. That is a
|
|
7
|
+
* false finish that quietly under-delivers, distinct from a legitimate stop.
|
|
8
|
+
*
|
|
9
|
+
* Work remaining = open non-soft tasks OR unmet required completion events.
|
|
10
|
+
* Blocker = a transient/availability pause on the latest turn (an availability
|
|
11
|
+
* issue, not a premature quit).
|
|
12
|
+
*/
|
|
13
|
+
export function detectPrematureQuit(loop, runLines) {
|
|
14
|
+
const reasons = [];
|
|
15
|
+
const openTasks = materializeOpenFrom(loop.paths.tasksFile).filter((t) => t.soft !== true);
|
|
16
|
+
if (openTasks.length) {
|
|
17
|
+
reasons.push(`${openTasks.length} open task(s): ${openTasks.map((t) => t.id).join(", ")}`);
|
|
18
|
+
}
|
|
19
|
+
const topics = new Set(runLines.map((l) => extractTopic(l)));
|
|
20
|
+
const unmet = loop.completion.requiredEvents.filter((e) => !topics.has(e));
|
|
21
|
+
if (unmet.length) {
|
|
22
|
+
reasons.push(`unmet required event(s): ${unmet.join(", ")}`);
|
|
23
|
+
}
|
|
24
|
+
// A transient/availability pause on the latest turn is a real blocker, not a
|
|
25
|
+
// premature quit — let the circuit breaker own it.
|
|
26
|
+
const blocked = lastNonSystemSignalIsTransient(runLines);
|
|
27
|
+
return { premature: reasons.length > 0 && !blocked, reasons };
|
|
28
|
+
}
|
|
29
|
+
function lastNonSystemSignalIsTransient(runLines) {
|
|
30
|
+
for (let i = runLines.length - 1; i >= 0; i--) {
|
|
31
|
+
const topic = extractTopic(runLines[i]);
|
|
32
|
+
if (topic === "backend.transient")
|
|
33
|
+
return true;
|
|
34
|
+
if (topic === "iteration.finish")
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
/** How many times this run has already been re-armed (bound the re-arm loop). */
|
|
40
|
+
export function countRearms(runLines) {
|
|
41
|
+
return runLines.filter((l) => extractTopic(l) === "premature.rearm").length;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Re-arm a premature quit: journal the re-arm and inject a nudge so the next
|
|
45
|
+
* iteration's prompt differs (breaking the stall) and the agent finishes the
|
|
46
|
+
* remaining authorized work instead of halting.
|
|
47
|
+
*/
|
|
48
|
+
export function rearmPrematureQuit(loop, iteration, check) {
|
|
49
|
+
appendEvent(loop.paths.journalFile, loop.runtime.runId, String(iteration), "premature.rearm", jsonField("reasons", check.reasons.join("; ")));
|
|
50
|
+
appendOperatorEvent(loop.paths.journalFile, loop.runtime.runId, String(iteration), "operator.guidance", "You stopped with authorized work remaining and no blocker. Do not halt: " +
|
|
51
|
+
"continue and finish the remaining work, then emit the completion event. " +
|
|
52
|
+
`Remaining:\n- ${check.reasons.join("\n- ")}`);
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=premature-quit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"premature-quit.js","sourceRoot":"","sources":["../src/premature-quit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LoopContext } from "./types.js";
|
|
2
|
+
export interface ProgressMetric {
|
|
3
|
+
name: string;
|
|
4
|
+
value: number;
|
|
5
|
+
iteration: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Extract the progress scalar from a metric command's stdout: the last numeric
|
|
9
|
+
* token (so `tests: 42 passed` → 42, `0.87` → 0.87). Returns null when no
|
|
10
|
+
* number is present.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parseMetricValue(stdout: string): number | null;
|
|
13
|
+
/**
|
|
14
|
+
* Run the preset-declared progress metric command HARNESS-side after an
|
|
15
|
+
* iteration and journal the scalar (`progress.metric`). No-op when no metric is
|
|
16
|
+
* configured or its output has no number. Returns the captured metric, or null.
|
|
17
|
+
*/
|
|
18
|
+
export declare function runProgressMetric(loop: LoopContext, iteration: number): ProgressMetric | null;
|
|
19
|
+
/** Read the journaled progress-metric series for a run (queryable). */
|
|
20
|
+
export declare function readProgressMetrics(runLines: string[]): ProgressMetric[];
|
package/dist/progress.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { jsonField, jsonFieldRaw } from "@mobrienv/autoloop-core";
|
|
3
|
+
import { appendEvent, extractField, extractTopic, } from "@mobrienv/autoloop-core/journal";
|
|
4
|
+
/**
|
|
5
|
+
* Extract the progress scalar from a metric command's stdout: the last numeric
|
|
6
|
+
* token (so `tests: 42 passed` → 42, `0.87` → 0.87). Returns null when no
|
|
7
|
+
* number is present.
|
|
8
|
+
*/
|
|
9
|
+
export function parseMetricValue(stdout) {
|
|
10
|
+
const matches = stdout.match(/-?\d+(?:\.\d+)?/g);
|
|
11
|
+
if (!matches || matches.length === 0)
|
|
12
|
+
return null;
|
|
13
|
+
const n = Number(matches[matches.length - 1]);
|
|
14
|
+
return Number.isFinite(n) ? n : null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Run the preset-declared progress metric command HARNESS-side after an
|
|
18
|
+
* iteration and journal the scalar (`progress.metric`). No-op when no metric is
|
|
19
|
+
* configured or its output has no number. Returns the captured metric, or null.
|
|
20
|
+
*/
|
|
21
|
+
export function runProgressMetric(loop, iteration) {
|
|
22
|
+
const cmd = loop.progress?.metricCmd?.trim();
|
|
23
|
+
if (!cmd)
|
|
24
|
+
return null;
|
|
25
|
+
const res = spawnSync(cmd, {
|
|
26
|
+
shell: "/bin/sh",
|
|
27
|
+
cwd: loop.paths.workDir,
|
|
28
|
+
encoding: "utf-8",
|
|
29
|
+
timeout: loop.progress?.timeoutMs ?? 60000,
|
|
30
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
31
|
+
});
|
|
32
|
+
const value = parseMetricValue(`${res.stdout ?? ""}`);
|
|
33
|
+
if (value === null)
|
|
34
|
+
return null;
|
|
35
|
+
const name = loop.progress?.name || "progress";
|
|
36
|
+
appendEvent(loop.paths.journalFile, loop.runtime.runId, String(iteration), "progress.metric", jsonField("name", name) + ", " + jsonFieldRaw("value", String(value)));
|
|
37
|
+
return { name, value, iteration };
|
|
38
|
+
}
|
|
39
|
+
/** Read the journaled progress-metric series for a run (queryable). */
|
|
40
|
+
export function readProgressMetrics(runLines) {
|
|
41
|
+
const out = [];
|
|
42
|
+
for (const line of runLines) {
|
|
43
|
+
if (extractTopic(line) !== "progress.metric")
|
|
44
|
+
continue;
|
|
45
|
+
const value = Number(extractField(line, "value"));
|
|
46
|
+
if (!Number.isFinite(value))
|
|
47
|
+
continue;
|
|
48
|
+
out.push({
|
|
49
|
+
name: extractField(line, "name") || "progress",
|
|
50
|
+
value,
|
|
51
|
+
iteration: Number(extractField(line, "iteration")) || 0,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../src/progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,GACb,MAAM,iCAAiC,CAAC;AASzC;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACjD,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9C,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAiB,EACjB,SAAiB;IAEjB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC7C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,EAAE;QACzB,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO;QACvB,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,IAAI,KAAK;QAC1C,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;KAC5B,CAAC,CAAC;IACH,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,CAAC;IACtD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,UAAU,CAAC;IAC/C,WAAW,CACT,IAAI,CAAC,KAAK,CAAC,WAAW,EACtB,IAAI,CAAC,OAAO,CAAC,KAAK,EAClB,MAAM,CAAC,SAAS,CAAC,EACjB,iBAAiB,EACjB,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,IAAI,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CACtE,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AACpC,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,mBAAmB,CAAC,QAAkB;IACpD,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,YAAY,CAAC,IAAI,CAAC,KAAK,iBAAiB;YAAE,SAAS;QACvD,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QACtC,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,UAAU;YAC9C,KAAK;YACL,SAAS,EAAE,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,IAAI,CAAC;SACxD,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/prompt.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TwoTierMemoryStats } from "@mobrienv/autoloop-core/memory-render";
|
|
2
2
|
import * as topology from "@mobrienv/autoloop-core/topology";
|
|
3
|
+
import { type ResolvedIterationBackend } from "./backend/types.js";
|
|
3
4
|
import type { LoopContext } from "./index.js";
|
|
4
5
|
export interface IterationContext {
|
|
5
6
|
iteration: number;
|
|
@@ -12,6 +13,9 @@ export interface IterationContext {
|
|
|
12
13
|
memoryText: string;
|
|
13
14
|
prompt: string;
|
|
14
15
|
roleAgent: string;
|
|
16
|
+
backend: ResolvedIterationBackend;
|
|
17
|
+
backendAgent: string;
|
|
18
|
+
backendModel: string;
|
|
15
19
|
}
|
|
16
20
|
interface DerivedRunContext {
|
|
17
21
|
scratchpadText: string;
|