loopforge 1.3.0 → 1.7.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/LICENSE +1 -1
- package/README.md +2 -0
- package/dist/backends/fs.d.ts +5 -0
- package/dist/backends/fs.d.ts.map +1 -1
- package/dist/backends/fs.js +67 -16
- package/dist/backends/fs.js.map +1 -1
- package/dist/builder.d.ts +0 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +14 -25
- package/dist/builder.js.map +1 -1
- package/dist/engine.d.ts +14 -5
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +206 -105
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +4 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -9
- package/dist/index.js.map +1 -1
- package/dist/loop-compiler.d.ts +5 -2
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +533 -19
- package/dist/loop-compiler.js.map +1 -1
- package/dist/mcp/session.d.ts +21 -2
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +257 -16
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/tools.d.ts +153 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +162 -6
- package/dist/mcp/tools.js.map +1 -1
- package/dist/observability.d.ts +23 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +29 -0
- package/dist/observability.js.map +1 -0
- package/dist/policy.d.ts +15 -0
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +8 -0
- package/dist/policy.js.map +1 -1
- package/dist/protocol.d.ts +114 -5
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +44 -3
- package/dist/protocol.js.map +1 -1
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +51 -3
- package/dist/runtime.js.map +1 -1
- package/dist/tests/builder.test.js +1 -25
- package/dist/tests/builder.test.js.map +1 -1
- package/dist/tests/engine.test.js +213 -38
- package/dist/tests/engine.test.js.map +1 -1
- package/dist/tests/generate-schema.test.js +4 -4
- package/dist/tests/generate-schema.test.js.map +1 -1
- package/dist/tests/loop-compiler.test.js +212 -2
- package/dist/tests/loop-compiler.test.js.map +1 -1
- package/dist/tests/mcp.test.js +273 -0
- package/dist/tests/mcp.test.js.map +1 -1
- package/dist/tests/protocol.test.js +2 -4
- package/dist/tests/protocol.test.js.map +1 -1
- package/dist/tests/verification-gate.test.d.ts +2 -0
- package/dist/tests/verification-gate.test.d.ts.map +1 -0
- package/dist/tests/verification-gate.test.js +407 -0
- package/dist/tests/verification-gate.test.js.map +1 -0
- package/dist/verification-gate.d.ts +26 -0
- package/dist/verification-gate.d.ts.map +1 -0
- package/dist/verification-gate.js +232 -0
- package/dist/verification-gate.js.map +1 -0
- package/loop_policy.json +8 -0
- package/package.json +1 -2
- package/skills/perception/SKILL.md +102 -53
- package/dist/autonomous.d.ts +0 -84
- package/dist/autonomous.d.ts.map +0 -1
- package/dist/autonomous.js +0 -250
- package/dist/autonomous.js.map +0 -1
- package/dist/backends/ai.d.ts +0 -37
- package/dist/backends/ai.d.ts.map +0 -1
- package/dist/backends/ai.js +0 -98
- package/dist/backends/ai.js.map +0 -1
- package/dist/cli.d.ts +0 -7
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -422
- package/dist/cli.js.map +0 -1
- package/dist/tests/adapter.test.js +0 -104
- package/dist/tests/adapter.test.js.map +0 -1
- package/dist/tests/autonomous.test.d.ts +0 -7
- package/dist/tests/autonomous.test.d.ts.map +0 -1
- package/dist/tests/autonomous.test.js +0 -294
- package/dist/tests/autonomous.test.js.map +0 -1
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/** Verification Gate — Layer 1 cross-round consistency checks.
|
|
2
|
+
*
|
|
3
|
+
* Pure-function module. Validates an agent's SelfEvaluation against the
|
|
4
|
+
* loop's own lineage before it enters the compiler.
|
|
5
|
+
*
|
|
6
|
+
* Verdict semantics:
|
|
7
|
+
* - trusted: all checks passed; flags are informational only.
|
|
8
|
+
* - suspect: one or more warn-level flags; flags become warnings in
|
|
9
|
+
* the next prompt so the agent can clarify.
|
|
10
|
+
* - contradicted: one or more error-level flags; the quality score for
|
|
11
|
+
* this round is excluded from the quality trend (NOT
|
|
12
|
+
* modified). Flags become hard constraints — the agent
|
|
13
|
+
* must respond in the next round.
|
|
14
|
+
*/
|
|
15
|
+
import { makeVerificationFlag, makeVerificationResult } from "./protocol.js";
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
|
+
// Helpers
|
|
18
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
19
|
+
/** Extract the round number from a vault entry's loop_lineage.
|
|
20
|
+
* Returns 0 if the entry has no lineage or no round field.
|
|
21
|
+
* In practice, persistLoopLineage always writes round ≥ 1, so 0
|
|
22
|
+
* unambiguously means "not a valid round entry" in this context. */
|
|
23
|
+
function entryRound(entry) {
|
|
24
|
+
const lineage = (entry.loop_lineage ?? {});
|
|
25
|
+
return lineage.round ?? 0;
|
|
26
|
+
}
|
|
27
|
+
/** Read constraint_violations from a vault entry (entry-level, stored from
|
|
28
|
+
* the previous round's last_round_result at persist time). */
|
|
29
|
+
function entryViolations(entry) {
|
|
30
|
+
const viols = entry.constraint_violations;
|
|
31
|
+
if (Array.isArray(viols))
|
|
32
|
+
return viols.filter((v) => typeof v === "string");
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
36
|
+
// Individual checks — each returns a VerificationFlag or null
|
|
37
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
38
|
+
function checkProgressRegression(selfEval, prevSelfEval) {
|
|
39
|
+
if (!prevSelfEval?.execution_evidence)
|
|
40
|
+
return null;
|
|
41
|
+
if (!selfEval.execution_evidence)
|
|
42
|
+
return null;
|
|
43
|
+
const prevProgress = prevSelfEval.execution_evidence.progress_estimate;
|
|
44
|
+
const currProgress = selfEval.execution_evidence.progress_estimate;
|
|
45
|
+
if (typeof prevProgress !== "number" || typeof currProgress !== "number")
|
|
46
|
+
return null;
|
|
47
|
+
// Use delta with epsilon to avoid IEEE 754 rounding issues (0.8 - 0.2 > 0.6 in float)
|
|
48
|
+
if (prevProgress - currProgress <= 0.2 + 1e-10)
|
|
49
|
+
return null;
|
|
50
|
+
return makeVerificationFlag({
|
|
51
|
+
severity: "warn",
|
|
52
|
+
field: "progress_estimate",
|
|
53
|
+
check: "progress_regression",
|
|
54
|
+
detail: `Progress dropped from ${prevProgress.toFixed(2)} to ` +
|
|
55
|
+
`${currProgress.toFixed(2)} (delta: ${(currProgress - prevProgress).toFixed(2)})`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function checkEmptyChangeWithPassing(selfEval) {
|
|
59
|
+
const ev = selfEval.execution_evidence;
|
|
60
|
+
if (!ev)
|
|
61
|
+
return null;
|
|
62
|
+
const filesEmpty = ev.files_changed.length === 0;
|
|
63
|
+
const testsAllPass = ev.test_results !== null &&
|
|
64
|
+
ev.test_results.failed === 0 &&
|
|
65
|
+
ev.test_results.passed > 0;
|
|
66
|
+
if (!filesEmpty || !testsAllPass || !selfEval.success)
|
|
67
|
+
return null;
|
|
68
|
+
return makeVerificationFlag({
|
|
69
|
+
severity: "warn",
|
|
70
|
+
field: "execution_evidence",
|
|
71
|
+
check: "empty_change_with_passing",
|
|
72
|
+
detail: "Agent claims success with no files changed and all tests passing — " +
|
|
73
|
+
"verify that work was actually performed",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function checkSuccessWithRemainingCriteria(selfEval) {
|
|
77
|
+
if (!selfEval.success)
|
|
78
|
+
return null;
|
|
79
|
+
const remaining = selfEval.execution_evidence?.success_criteria_remaining;
|
|
80
|
+
if (!remaining || remaining.length === 0)
|
|
81
|
+
return null;
|
|
82
|
+
return makeVerificationFlag({
|
|
83
|
+
severity: "error",
|
|
84
|
+
field: "success",
|
|
85
|
+
check: "success_with_remaining_criteria",
|
|
86
|
+
detail: `Agent claims success but ${remaining.length} criteria remain unmet: ` +
|
|
87
|
+
remaining.slice(0, 3).join("; "),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function checkDuplicateConstraintDiscovery(selfEval, prevSelfEval, olderViolations) {
|
|
91
|
+
const discovered = selfEval.discovered_constraints;
|
|
92
|
+
if (!discovered || discovered.length === 0)
|
|
93
|
+
return null;
|
|
94
|
+
// Collect all previously-known constraints
|
|
95
|
+
const known = new Set();
|
|
96
|
+
for (const v of olderViolations)
|
|
97
|
+
known.add(v.toLowerCase().trim());
|
|
98
|
+
if (prevSelfEval) {
|
|
99
|
+
for (const d of prevSelfEval.discovered_constraints ?? []) {
|
|
100
|
+
known.add(d.toLowerCase().trim());
|
|
101
|
+
}
|
|
102
|
+
// Also treat previous violations as implicitly discovered
|
|
103
|
+
for (const v of prevSelfEval.constraint_violations) {
|
|
104
|
+
known.add(v.toLowerCase().trim());
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const d of discovered) {
|
|
108
|
+
if (known.has(d.toLowerCase().trim())) {
|
|
109
|
+
return makeVerificationFlag({
|
|
110
|
+
severity: "warn",
|
|
111
|
+
field: "discovered_constraints",
|
|
112
|
+
check: "duplicate_constraint_discovery",
|
|
113
|
+
detail: `Constraint "${d}" was already known from a previous round`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
function checkRecurringViolation(selfEval, prevSelfEval, vaultEntries, currentRound) {
|
|
120
|
+
const currViols = selfEval.constraint_violations;
|
|
121
|
+
if (!currViols || currViols.length === 0)
|
|
122
|
+
return null;
|
|
123
|
+
// Build the violation history: [round N-2, round N-1, round N]
|
|
124
|
+
const violationsByRound = [];
|
|
125
|
+
// Round N-2 violations come from the vault entry for round N-1
|
|
126
|
+
// (persistLoopLineage stores the PREVIOUS round's violations on each entry)
|
|
127
|
+
if (currentRound >= 3) {
|
|
128
|
+
const entryNMinus1 = vaultEntries.find((e) => entryRound(e) === currentRound - 1);
|
|
129
|
+
if (entryNMinus1) {
|
|
130
|
+
const viols = entryViolations(entryNMinus1);
|
|
131
|
+
if (viols.length)
|
|
132
|
+
violationsByRound.push(viols.map((v) => v.toLowerCase().trim()));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Round N-1 violations from prevSelfEval
|
|
136
|
+
if (prevSelfEval) {
|
|
137
|
+
const prevViols = prevSelfEval.constraint_violations;
|
|
138
|
+
violationsByRound.push(prevViols.map((v) => v.toLowerCase().trim()));
|
|
139
|
+
}
|
|
140
|
+
// Round N violations from current selfEval
|
|
141
|
+
violationsByRound.push(currViols.map((v) => v.toLowerCase().trim()));
|
|
142
|
+
// Need at least 3 rounds of data
|
|
143
|
+
if (violationsByRound.length < 3)
|
|
144
|
+
return null;
|
|
145
|
+
// Check each current violation against the previous 2 rounds
|
|
146
|
+
const [rNMinus2, rNMinus1, rN] = violationsByRound.slice(-3);
|
|
147
|
+
for (const v of rN) {
|
|
148
|
+
if (rNMinus1.includes(v) && rNMinus2.includes(v)) {
|
|
149
|
+
return makeVerificationFlag({
|
|
150
|
+
severity: "error",
|
|
151
|
+
field: "constraint_violations",
|
|
152
|
+
check: "recurring_violation",
|
|
153
|
+
detail: `Constraint violation "${v}" has appeared in 3 consecutive rounds ` +
|
|
154
|
+
`(rounds ${currentRound - 2}–${currentRound}) without resolution`,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
function checkRetractFreshConstraint(selfEval, prevSelfEval, currentRound) {
|
|
161
|
+
const retracted = selfEval.retracted_constraints;
|
|
162
|
+
if (!retracted || retracted.length === 0)
|
|
163
|
+
return null;
|
|
164
|
+
if (!prevSelfEval)
|
|
165
|
+
return null;
|
|
166
|
+
const lastRoundDiscoveries = new Set();
|
|
167
|
+
for (const d of prevSelfEval.discovered_constraints ?? []) {
|
|
168
|
+
lastRoundDiscoveries.add(d.toLowerCase().trim());
|
|
169
|
+
}
|
|
170
|
+
for (const r of retracted) {
|
|
171
|
+
if (lastRoundDiscoveries.has(r.toLowerCase().trim())) {
|
|
172
|
+
return makeVerificationFlag({
|
|
173
|
+
severity: "warn",
|
|
174
|
+
field: "retracted_constraints",
|
|
175
|
+
check: "retract_fresh_constraint",
|
|
176
|
+
detail: `Retracting constraint "${r}" that was just discovered in round ` +
|
|
177
|
+
`${currentRound - 1} — may indicate rapid flip-flopping`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
184
|
+
// Main entry point
|
|
185
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
186
|
+
/** Verify a SelfEvaluation against the loop's cross-round lineage.
|
|
187
|
+
*
|
|
188
|
+
* @param selfEval The agent's self-evaluation for the current round.
|
|
189
|
+
* @param currentRound The current round number (1-based).
|
|
190
|
+
* @param vaultEntries Vault entries for this loop (non-feedback only).
|
|
191
|
+
* @param prevSelfEval The agent's self-evaluation from the previous round
|
|
192
|
+
* (null for round 1).
|
|
193
|
+
*/
|
|
194
|
+
export function verifySelfEvaluation(selfEval, currentRound, vaultEntries, prevSelfEval = null) {
|
|
195
|
+
const flags = [];
|
|
196
|
+
// Collect violations from all previous vault entries for duplicate-discovery
|
|
197
|
+
// and other checks that need deeper history.
|
|
198
|
+
const olderViolations = [];
|
|
199
|
+
for (const entry of vaultEntries) {
|
|
200
|
+
for (const v of entryViolations(entry))
|
|
201
|
+
olderViolations.push(v);
|
|
202
|
+
}
|
|
203
|
+
// Run all checks
|
|
204
|
+
const checks = [
|
|
205
|
+
() => checkProgressRegression(selfEval, prevSelfEval),
|
|
206
|
+
() => checkEmptyChangeWithPassing(selfEval),
|
|
207
|
+
() => checkSuccessWithRemainingCriteria(selfEval),
|
|
208
|
+
() => checkDuplicateConstraintDiscovery(selfEval, prevSelfEval, olderViolations),
|
|
209
|
+
() => checkRecurringViolation(selfEval, prevSelfEval, vaultEntries, currentRound),
|
|
210
|
+
() => checkRetractFreshConstraint(selfEval, prevSelfEval, currentRound),
|
|
211
|
+
];
|
|
212
|
+
for (const run of checks) {
|
|
213
|
+
const flag = run();
|
|
214
|
+
if (flag)
|
|
215
|
+
flags.push(flag);
|
|
216
|
+
}
|
|
217
|
+
// Determine verdict from the most severe flag present
|
|
218
|
+
const hasError = flags.some((f) => f.severity === "error");
|
|
219
|
+
const hasWarn = flags.some((f) => f.severity === "warn");
|
|
220
|
+
let verdict;
|
|
221
|
+
if (hasError) {
|
|
222
|
+
verdict = "contradicted";
|
|
223
|
+
}
|
|
224
|
+
else if (hasWarn) {
|
|
225
|
+
verdict = "suspect";
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
verdict = "trusted";
|
|
229
|
+
}
|
|
230
|
+
return makeVerificationResult({ verdict, flags });
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=verification-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-gate.js","sourceRoot":"","sources":["../src/verification-gate.ts"],"names":[],"mappings":"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"}
|
package/loop_policy.json
CHANGED
|
@@ -47,5 +47,13 @@
|
|
|
47
47
|
"backend": {
|
|
48
48
|
"vault_path": ".promptcraft/prompt_vault.json",
|
|
49
49
|
"global_vault_path": "~/.promptcraft/global_vault.json"
|
|
50
|
+
},
|
|
51
|
+
"evolution": {
|
|
52
|
+
"max_discovered_constraints_per_round": 5,
|
|
53
|
+
"max_active_constraints": 15,
|
|
54
|
+
"max_objective_versions": 10,
|
|
55
|
+
"progress_stall_threshold": 0.05,
|
|
56
|
+
"progress_stall_rounds": 2,
|
|
57
|
+
"progress_mismatch_threshold": 0.3
|
|
50
58
|
}
|
|
51
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loopforge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Loop-Time Intelligence Layer for AI coding agents — per-iteration prompt compiler with structured memory, constraint inheritance, and drift correction.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"bin": {
|
|
27
|
-
"loopforge": "./dist/cli.js",
|
|
28
27
|
"loopforge-mcp": "./dist/mcp-server.js"
|
|
29
28
|
},
|
|
30
29
|
"files": [
|
|
@@ -16,7 +16,7 @@ beyond having `loopforge-mcp` registered as an MCP server.
|
|
|
16
16
|
## When to Activate
|
|
17
17
|
|
|
18
18
|
Activate when the user says any of:
|
|
19
|
-
- `/
|
|
19
|
+
- `/perception <task>` — start an autonomous multi-round loop
|
|
20
20
|
- "do this in a loop", "keep going until done", "iterate on this"
|
|
21
21
|
- Any task that clearly needs 3+ rounds (audit, refactor, migrate, review-and-fix cycles)
|
|
22
22
|
|
|
@@ -26,17 +26,20 @@ round (e.g. "audit the entire codebase and fix every issue").
|
|
|
26
26
|
## Core Workflow
|
|
27
27
|
|
|
28
28
|
```
|
|
29
|
-
|
|
30
|
-
│ 1. loopforge_start(task, constraints, maxRounds)
|
|
31
|
-
│ → sessionId + Round 1 prompt
|
|
32
|
-
│
|
|
33
|
-
│ 2. Execute the prompt (read, write, test, fix)
|
|
34
|
-
│
|
|
35
|
-
│
|
|
36
|
-
│ 3. loopforge_next(sessionId, output)
|
|
37
|
-
│ → if prompt: go to step 2
|
|
38
|
-
│ → if prompt=null: loop ended, read stopReason
|
|
39
|
-
|
|
29
|
+
┌──────────────────────────────────────────────────────────┐
|
|
30
|
+
│ 1. loopforge_start(task, constraints, maxRounds) │
|
|
31
|
+
│ → sessionId + Round 1 prompt │
|
|
32
|
+
│ │
|
|
33
|
+
│ 2. Execute the prompt (read, write, test, fix) │
|
|
34
|
+
│ Prepare a structured self-evaluation │
|
|
35
|
+
│ │
|
|
36
|
+
│ 3. loopforge_next(sessionId, evaluation, output?) │
|
|
37
|
+
│ → if prompt: go to step 2 │
|
|
38
|
+
│ → if prompt=null: loop ended, read stopReason │
|
|
39
|
+
│ │
|
|
40
|
+
│ After restart: loopforge_resume(loopId) │
|
|
41
|
+
│ → picks up from last saved round │
|
|
42
|
+
└──────────────────────────────────────────────────────────┘
|
|
40
43
|
```
|
|
41
44
|
|
|
42
45
|
### Step-by-step
|
|
@@ -57,18 +60,38 @@ Execute the compiled prompt exactly as you would any user task. Read files,
|
|
|
57
60
|
write code, run tests, fix bugs. The prompt already contains the task scope,
|
|
58
61
|
constraints, and quality expectations for this specific round.
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
After executing, prepare a structured self-evaluation. Pass it as the
|
|
64
|
+
`evaluation` parameter to `loopforge_next` — do NOT embed it as a text block
|
|
65
|
+
in the output. The `evaluation` parameter is a typed object with the fields
|
|
66
|
+
described below.
|
|
67
|
+
|
|
68
|
+
**New (v1.7+): Pass evaluation as a structured tool parameter.**
|
|
69
|
+
```json
|
|
63
70
|
{
|
|
64
71
|
"success": true,
|
|
65
72
|
"output_summary": "Fixed 3 reentrancy bugs in withdraw() and deposit(). All 12 tests pass.",
|
|
66
73
|
"constraint_violations": [],
|
|
67
|
-
"should_continue": true
|
|
74
|
+
"should_continue": true,
|
|
75
|
+
"discovered_constraints": [],
|
|
76
|
+
"objective_refinement": "",
|
|
77
|
+
"emerged_subtasks": [],
|
|
78
|
+
"execution_evidence": {
|
|
79
|
+
"files_changed": ["contracts/Token.sol", "test/Token.test.ts"],
|
|
80
|
+
"test_results": {"passed": 24, "failed": 0, "skipped": 0},
|
|
81
|
+
"success_criteria_met": ["No reentrancy vectors remain"],
|
|
82
|
+
"success_criteria_remaining": ["Access control verified", "Overflow checks complete"],
|
|
83
|
+
"progress_estimate": 0.4
|
|
84
|
+
},
|
|
85
|
+
"retracted_constraints": [],
|
|
86
|
+
"revised_success_criteria": [],
|
|
87
|
+
"wrong_assumptions": []
|
|
68
88
|
}
|
|
69
|
-
---end-loopforge-eval
|
|
70
89
|
```
|
|
71
90
|
|
|
91
|
+
**Legacy (still supported):** Embed a `---loopforge-eval` block in output text
|
|
92
|
+
when the agent cannot pass structured tool parameters. The new parameter form
|
|
93
|
+
is preferred — it's validated by the MCP client before reaching the server.
|
|
94
|
+
|
|
72
95
|
**Field rules:**
|
|
73
96
|
|
|
74
97
|
| Field | Type | Rule |
|
|
@@ -77,24 +100,36 @@ constraints, and quality expectations for this specific round.
|
|
|
77
100
|
| `output_summary` | string | Specific, actionable. What was DONE this round — not what was attempted |
|
|
78
101
|
| `constraint_violations` | string[] | Constraints the agent actually violated this round. Be honest — hiding violations corrupts the quality signal and defeats the circuit breaker |
|
|
79
102
|
| `should_continue` | boolean | `false` ONLY when the ENTIRE task is complete. Partial progress = `true` |
|
|
80
|
-
|
|
81
|
-
**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
103
|
+
| `discovered_constraints` | string[] | **P0 (optional)** — New constraints discovered this round. They become active guardrails for future rounds. Example: `["All external calls must use SafeERC20"]`. Empty `[]` if none. |
|
|
104
|
+
| `objective_refinement` | string | **P1 (optional)** — If this round deepened your understanding of what the task is really about, describe the refinement. APPENDED to (never replaces) the original objective. Empty `""` if unchanged. |
|
|
105
|
+
| `emerged_subtasks` | string[] | **P2 (optional)** — Sub-problems that surfaced during execution and need separate attention. Feed into next-round task suggestions. Example: `["Audit upgrade proxy", "Verify timelock"]`. Empty `[]` if none. |
|
|
106
|
+
| `execution_evidence` | object | **P4 (recommended)** — Structured record of what actually happened. `files_changed` (string[]), `test_results` ({passed, failed, skipped} | null), `success_criteria_met` (string[]), `success_criteria_remaining` (string[]), `progress_estimate` (0.0–1.0). Use this to give LoopForge real visibility into your progress. |
|
|
107
|
+
| `retracted_constraints` | string[] | **P5 (optional)** — Constraints you now believe are wrong. Removed from active guardrails. Only retract with evidence. Empty `[]` if none. |
|
|
108
|
+
| `revised_success_criteria` | object[] | **P5 (optional)** — Success criteria that need reformulation. Array of `{old: string, new: string}`. Applied to Loop Objective. Empty `[]` if none. |
|
|
109
|
+
| `wrong_assumptions` | string[] | **P5 (optional)** — Assumptions from earlier rounds that turned out to be incorrect. Recorded as key lessons. Empty `[]` if none. |
|
|
110
|
+
|
|
111
|
+
**Why this matters:** The evaluation is validated by the MCP client before
|
|
112
|
+
reaching the server — a missing or malformed evaluation causes an immediate
|
|
113
|
+
error, not a silent stall. The circuit breaker uses `success` + `violations`
|
|
114
|
+
to compute quality scores — lying here defeats the only mechanism that
|
|
115
|
+
prevents infinite loops. The optional fields (P0–P5) enable cognitive
|
|
116
|
+
evolution: discovering constraints, deepening understanding, surfacing
|
|
117
|
+
sub-problems, tracking real progress, and correcting wrong assumptions as
|
|
118
|
+
the loop executes.
|
|
85
119
|
|
|
86
120
|
**Step 3 — Advance to next round:**
|
|
87
121
|
```
|
|
88
122
|
Call loopforge_next with:
|
|
89
|
-
sessionId:
|
|
90
|
-
|
|
123
|
+
sessionId: from loopforge_start
|
|
124
|
+
evaluation: REQUIRED — structured self-evaluation object (see field table below)
|
|
125
|
+
output: OPTIONAL — raw output text for audit trail (can be omitted)
|
|
91
126
|
```
|
|
92
127
|
|
|
93
128
|
If the result has `prompt: null`, the loop has ended. Read `stopReason`:
|
|
94
129
|
- `task_complete` — you reported should_continue=false. Work is done.
|
|
95
130
|
- `circuit_breaker` — quality flat or declining for 3+ rounds. Review your approach.
|
|
96
131
|
- `max_rounds` — hit the round limit. Decide whether to extend or accept.
|
|
97
|
-
- `stalled` — no valid self-
|
|
132
|
+
- `stalled` — no valid self-evaluation provided. Check your evaluation parameter.
|
|
98
133
|
|
|
99
134
|
If the result has a non-null `prompt`, execute it and repeat from Step 2.
|
|
100
135
|
|
|
@@ -104,10 +139,12 @@ If the result has a non-null `prompt`, execute it and repeat from Step 2.
|
|
|
104
139
|
|------|-------------|-----------|------------|
|
|
105
140
|
| `loopforge_start` | Beginning of every loop | `task`, `maxRounds?`, `constraints?` | `sessionId`, round 1 `prompt` |
|
|
106
141
|
| `loopforge_next` | After executing every round | `sessionId`, `output` (with eval block) | next `prompt` or `null` + `stopReason` |
|
|
107
|
-
| `loopforge_status` | Mid-loop checkpoint, user asks "how's it going" | `sessionId` | `round`, `qualityTrajectory`, `status` |
|
|
142
|
+
| `loopforge_status` | Mid-loop checkpoint, user asks "how's it going" | `sessionId` | `round`, `qualityTrajectory`, `status`, `technique` |
|
|
108
143
|
| `loopforge_stop` | User wants to abort, fatal error | `sessionId` | `roundsCompleted`, final trajectory |
|
|
109
|
-
| `loopforge_list` | User asks "what loops are running" | — | `sessions[]` |
|
|
144
|
+
| `loopforge_list` | User asks "what loops are running" | — | `sessions[]` (includes vault-persisted) |
|
|
110
145
|
| `loopforge_replay` | After loop ends, user asks "show me what happened" | `sessionId` | `timeline[]` with all rounds |
|
|
146
|
+
| `loopforge_resume` | Resume a loop after process restart | `loopId` | next `prompt` or `null` + `stopReason` |
|
|
147
|
+
| `loopforge_health` | Check loop health mid-run | `loopId` | goal alignment, constraint integrity, drift, strategy |
|
|
111
148
|
|
|
112
149
|
## Quality Signals (Read the Room)
|
|
113
150
|
|
|
@@ -133,7 +170,8 @@ The prompt changes between rounds based on your trajectory. Pay attention to:
|
|
|
133
170
|
|
|
134
171
|
## Rules
|
|
135
172
|
|
|
136
|
-
1. **
|
|
173
|
+
1. **Always pass the evaluation parameter.** `loopforge_next` requires it.
|
|
174
|
+
If you omit it, the MCP client will reject the call with a schema error.
|
|
137
175
|
2. **Be honest in self-evaluation.** Lying about success or hiding violations
|
|
138
176
|
produces a false quality signal. The circuit breaker exists to stop bad
|
|
139
177
|
loops — if you fake quality=5 every round, it fires anyway.
|
|
@@ -142,11 +180,14 @@ The prompt changes between rounds based on your trajectory. Pay attention to:
|
|
|
142
180
|
narrow scope between rounds. Trust it — it sees the trajectory.
|
|
143
181
|
5. **Call loopforge_next within the same turn.** Don't leave a round hanging
|
|
144
182
|
across conversation turns. The MCP server is in-memory.
|
|
183
|
+
6. **After process restart, use loopforge_resume.** The session state is saved
|
|
184
|
+
to vault every round. Call `loopforge_resume` with the original `loopId` to
|
|
185
|
+
pick up where you left off — no need to restart from round 1.
|
|
145
186
|
|
|
146
187
|
## Example
|
|
147
188
|
|
|
148
189
|
```
|
|
149
|
-
User: /
|
|
190
|
+
User: /perception "Audit the ERC20 token in contracts/Token.sol for security issues"
|
|
150
191
|
|
|
151
192
|
Agent:
|
|
152
193
|
→ loopforge_start({ task: "Audit ERC20 token in contracts/Token.sol for security issues",
|
|
@@ -154,33 +195,41 @@ Agent:
|
|
|
154
195
|
constraints: ["must not break existing tests", "follow check-effects-interactions"] })
|
|
155
196
|
← { sessionId: "abc-123", round: 1, prompt: "## LoopForge Loop Compile — Round 1\n...", level: "l2" }
|
|
156
197
|
|
|
157
|
-
[Executes prompt: reads Token.sol, identifies 4 issues,
|
|
158
|
-
|
|
159
|
-
Output ends with:
|
|
160
|
-
---loopforge-eval
|
|
161
|
-
{"success":false,"output_summary":"Found 4 potential issues: 2 reentrancy, 1 overflow, 1 access control. Not yet fixed.","constraint_violations":[],"should_continue":true}
|
|
162
|
-
---end-loopforge-eval
|
|
198
|
+
[Executes prompt: reads Token.sol, identifies 4 issues, discovers a new constraint]
|
|
163
199
|
|
|
164
|
-
→ loopforge_next({ sessionId: "abc-123",
|
|
200
|
+
→ loopforge_next({ sessionId: "abc-123",
|
|
201
|
+
evaluation: {
|
|
202
|
+
success: false,
|
|
203
|
+
output_summary: "Found 4 potential issues: 2 reentrancy, 1 overflow, 1 access control. Not yet fixed.",
|
|
204
|
+
constraint_violations: [],
|
|
205
|
+
should_continue: true,
|
|
206
|
+
discovered_constraints: ["All _mint() calls must emit Transfer event per ERC20 spec"]
|
|
207
|
+
} })
|
|
165
208
|
← { sessionId: "abc-123", round: 2, prompt: "## LoopForge Loop Compile — Round 2\n...", level: "l1" }
|
|
166
209
|
|
|
167
|
-
[Executes prompt: fixes 3 issues,
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
→ loopforge_next({ sessionId: "abc-123",
|
|
210
|
+
[Executes prompt: fixes 3 issues, deepens understanding of access control problem]
|
|
211
|
+
|
|
212
|
+
→ loopforge_next({ sessionId: "abc-123",
|
|
213
|
+
evaluation: {
|
|
214
|
+
success: false,
|
|
215
|
+
output_summary: "Fixed reentrancy in withdraw() and overflow in calcReward(). Access control in setOwner() needs design discussion — discovered it's part of a larger upgradeability concern.",
|
|
216
|
+
constraint_violations: [],
|
|
217
|
+
should_continue: true,
|
|
218
|
+
objective_refinement: "Access control audit scope expanded: setOwner() is unprotected because the contract follows an upgradeable proxy pattern where owner is set via initializer — need to verify the initializer guard and proxy admin separately",
|
|
219
|
+
emerged_subtasks: ["Audit upgrade proxy initialization flow", "Verify proxy admin is not renounced without recovery path"]
|
|
220
|
+
} })
|
|
221
|
+
← { sessionId: "abc-123", round: 3, prompt: "## LoopForge Loop Compile — Round 3\n Suggested Next Task: Audit upgrade proxy initialization flow; Verify proxy admin is not renounced without recovery path\n...", level: "l0" }
|
|
222
|
+
|
|
223
|
+
[Executes prompt: audits proxy pattern, documents findings, all fixes pass tests]
|
|
224
|
+
|
|
225
|
+
→ loopforge_next({ sessionId: "abc-123",
|
|
226
|
+
evaluation: {
|
|
227
|
+
success: true,
|
|
228
|
+
output_summary: "All fixable issues resolved. Proxy initialization verified — initializer modifier prevents double-init. Proxy admin held by multisig — low risk. 24/24 tests pass.",
|
|
229
|
+
constraint_violations: [],
|
|
230
|
+
should_continue: false
|
|
231
|
+
} })
|
|
183
232
|
← { sessionId: "abc-123", round: 3, prompt: null, stopReason: "task_complete", quality: 5 }
|
|
184
233
|
|
|
185
|
-
Reports to user: "Audit complete in 3 rounds. 3 issues fixed, 1 design question flagged."
|
|
234
|
+
Reports to user: "Audit complete in 3 rounds. 3 issues fixed, 1 design question flagged. Discovered 1 new constraint during audit. Objective deepened to include proxy pattern verification."
|
|
186
235
|
```
|
package/dist/autonomous.d.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/** LoopForge — Autonomous loop driver (v1.1).
|
|
2
|
-
*
|
|
3
|
-
* Closes the feedback loop: compile → agent executes → extract self-eval →
|
|
4
|
-
* auto-feedback → compile next → ... until task complete or circuit breaker.
|
|
5
|
-
*
|
|
6
|
-
* Zero API dependencies — LoopForge does NOT call AI APIs. The caller
|
|
7
|
-
* provides an execute callback or feeds agent output round by round.
|
|
8
|
-
*/
|
|
9
|
-
import { type LoopHealth, type SelfEvaluation } from "./protocol.js";
|
|
10
|
-
import { LoopForgeEngine } from "./engine.js";
|
|
11
|
-
export interface AutonomousConfig {
|
|
12
|
-
/** The task description — becomes the loop objective. */
|
|
13
|
-
task: string;
|
|
14
|
-
/** Kebab-case loop identifier. */
|
|
15
|
-
loopId: string;
|
|
16
|
-
/** Optional explicit goal ID. Derived from task if omitted. */
|
|
17
|
-
goalId?: string;
|
|
18
|
-
/** Maximum rounds before forced stop. Default 20. */
|
|
19
|
-
maxRounds?: number;
|
|
20
|
-
/** Health check interval (rounds). Default 3. */
|
|
21
|
-
healthCheckInterval?: number;
|
|
22
|
-
/** Optional path to a plan/spec file for constraint extraction. */
|
|
23
|
-
planSource?: string;
|
|
24
|
-
/** Constraints extracted from the plan (provided by caller). */
|
|
25
|
-
constraintsFromPlan?: string[];
|
|
26
|
-
/** Domain hint for technique routing. */
|
|
27
|
-
domain?: string;
|
|
28
|
-
}
|
|
29
|
-
export interface RoundOutput {
|
|
30
|
-
round: number;
|
|
31
|
-
/** The compiled prompt — ready for agent execution. */
|
|
32
|
-
prompt: string;
|
|
33
|
-
/** L0 | L1 | L2 */
|
|
34
|
-
recompileLevel: string;
|
|
35
|
-
/** Selected prompting technique. */
|
|
36
|
-
techniqueUsed: string;
|
|
37
|
-
/** Cross-round health snapshot (null on round 1 if no prior data). */
|
|
38
|
-
health: LoopHealth | null;
|
|
39
|
-
/** Quality score from the PREVIOUS round (null on round 1). */
|
|
40
|
-
lastQualityScore: number | null;
|
|
41
|
-
}
|
|
42
|
-
export type StopReason = "task_complete" | "circuit_breaker" | "max_rounds" | "stalled" | "extraction_failed";
|
|
43
|
-
export interface AutonomousResult {
|
|
44
|
-
/** Total rounds completed. */
|
|
45
|
-
roundsCompleted: number;
|
|
46
|
-
/** Why the loop stopped. */
|
|
47
|
-
stopReason: StopReason;
|
|
48
|
-
/** Health snapshot at stop. */
|
|
49
|
-
healthAtStop: LoopHealth | null;
|
|
50
|
-
/** Quality trajectory [round1_score, round2_score, ...]. */
|
|
51
|
-
qualityTrajectory: number[];
|
|
52
|
-
/** Whether the loop achieved its objective. */
|
|
53
|
-
success: boolean;
|
|
54
|
-
}
|
|
55
|
-
export interface RunOneRoundResult {
|
|
56
|
-
/** If stopNow is true, the loop should terminate. */
|
|
57
|
-
stopNow: boolean;
|
|
58
|
-
stopReason: StopReason | null;
|
|
59
|
-
/** The compiled prompt (null if stopping). */
|
|
60
|
-
roundOutput: RoundOutput | null;
|
|
61
|
-
/** Extracted self-evaluation (null on round 1 or if extraction failed). */
|
|
62
|
-
selfEval: SelfEvaluation | null;
|
|
63
|
-
/** Whether structured extraction succeeded. */
|
|
64
|
-
extractionSucceeded: boolean;
|
|
65
|
-
}
|
|
66
|
-
/** Run one round of the autonomous loop.
|
|
67
|
-
*
|
|
68
|
-
* Round 1 (agentOutput = null): compile the first prompt (L2 full build).
|
|
69
|
-
* Round N (agentOutput = string): extract self-eval, record auto-feedback,
|
|
70
|
-
* compile next prompt (L0/L1/L2 decided by the 4-gate router).
|
|
71
|
-
*
|
|
72
|
-
* Returns stopNow = true when the loop should terminate. */
|
|
73
|
-
export declare function runOneRound(engine: LoopForgeEngine, config: AutonomousConfig, currentRound: number, agentOutput: string | null): RunOneRoundResult;
|
|
74
|
-
/** Execute callback: takes a compiled prompt, returns the agent's output. */
|
|
75
|
-
export type AgentExecutor = (prompt: string, round: number) => Promise<string>;
|
|
76
|
-
/** Run a fully autonomous loop from start to finish.
|
|
77
|
-
*
|
|
78
|
-
* The `execute` callback is called with each compiled prompt.
|
|
79
|
-
* LoopForge does NOT call any AI API — the caller provides execution.
|
|
80
|
-
*
|
|
81
|
-
* Stops when: task complete, circuit breaker triggered, max rounds
|
|
82
|
-
* reached, or self-eval extraction fails. */
|
|
83
|
-
export declare function runAutonomousLoop(engine: LoopForgeEngine, config: AutonomousConfig, execute: AgentExecutor): Promise<AutonomousResult>;
|
|
84
|
-
//# sourceMappingURL=autonomous.d.ts.map
|
package/dist/autonomous.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"autonomous.d.ts","sourceRoot":"","sources":["../src/autonomous.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAKL,KAAK,UAAU,EACf,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,eAAe,EAIhB,MAAM,aAAa,CAAC;AAMrB,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,sEAAsE;IACtE,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,+DAA+D;IAC/D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,MAAM,UAAU,GAClB,eAAe,GACf,iBAAiB,GACjB,YAAY,GACZ,SAAS,GACT,mBAAmB,CAAC;AAExB,MAAM,WAAW,gBAAgB;IAC/B,8BAA8B;IAC9B,eAAe,EAAE,MAAM,CAAC;IACxB,4BAA4B;IAC5B,UAAU,EAAE,UAAU,CAAC;IACvB,+BAA+B;IAC/B,YAAY,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,4DAA4D;IAC5D,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;CAClB;AAMD,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,8CAA8C;IAC9C,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,2EAA2E;IAC3E,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,+CAA+C;IAC/C,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;6DAM6D;AAC7D,wBAAgB,WAAW,CACzB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,gBAAgB,EACxB,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,iBAAiB,CAwLnB;AAMD,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAE/E;;;;;;8CAM8C;AAC9C,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,gBAAgB,CAAC,CAiE3B"}
|