loopforge 1.12.0 → 2.0.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/README.md +172 -95
- package/dist/backends/interface.d.ts +3 -9
- package/dist/backends/interface.d.ts.map +1 -1
- package/dist/backends/interface.js +1 -5
- package/dist/backends/interface.js.map +1 -1
- package/dist/canonical-state.d.ts +53 -0
- package/dist/canonical-state.d.ts.map +1 -0
- package/dist/canonical-state.js +161 -0
- package/dist/canonical-state.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +227 -0
- package/dist/cli.js.map +1 -0
- package/dist/enforcement-gate.d.ts +44 -0
- package/dist/enforcement-gate.d.ts.map +1 -0
- package/dist/enforcement-gate.js +271 -0
- package/dist/enforcement-gate.js.map +1 -0
- package/dist/engine.d.ts +17 -7
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +158 -249
- package/dist/engine.js.map +1 -1
- package/dist/evidence-provider.d.ts +113 -0
- package/dist/evidence-provider.d.ts.map +1 -0
- package/dist/evidence-provider.js +419 -0
- package/dist/evidence-provider.js.map +1 -0
- package/dist/index.d.ts +19 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -10
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +15 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +49 -0
- package/dist/init.js.map +1 -0
- package/dist/interop.d.ts +30 -0
- package/dist/interop.d.ts.map +1 -0
- package/dist/interop.js +29 -0
- package/dist/interop.js.map +1 -0
- package/dist/loop-compiler.d.ts +14 -64
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +307 -2001
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-store.d.ts +82 -0
- package/dist/loop-store.d.ts.map +1 -0
- package/dist/loop-store.js +332 -0
- package/dist/loop-store.js.map +1 -0
- package/dist/mcp/server.d.ts +9 -6
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +109 -73
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session.d.ts +84 -20
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +718 -316
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/tools.d.ts +18 -268
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +225 -12
- package/dist/mcp/tools.js.map +1 -1
- package/dist/observability.d.ts +36 -16
- package/dist/observability.d.ts.map +1 -1
- package/dist/observability.js +82 -23
- package/dist/observability.js.map +1 -1
- package/dist/policy-metrics.d.ts +46 -0
- package/dist/policy-metrics.d.ts.map +1 -0
- package/dist/policy-metrics.js +146 -0
- package/dist/policy-metrics.js.map +1 -0
- package/dist/policy.d.ts +58 -75
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +122 -93
- package/dist/policy.js.map +1 -1
- package/dist/prompt-assembler.d.ts +30 -0
- package/dist/prompt-assembler.d.ts.map +1 -0
- package/dist/prompt-assembler.js +257 -0
- package/dist/prompt-assembler.js.map +1 -0
- package/dist/prompt-policy.d.ts +29 -0
- package/dist/prompt-policy.d.ts.map +1 -0
- package/dist/prompt-policy.js +51 -0
- package/dist/prompt-policy.js.map +1 -0
- package/dist/protocol.d.ts +117 -91
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +40 -36
- package/dist/protocol.js.map +1 -1
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +1 -27
- package/dist/replay.js.map +1 -1
- package/dist/round-coordinator.d.ts +90 -0
- package/dist/round-coordinator.d.ts.map +1 -0
- package/dist/round-coordinator.js +186 -0
- package/dist/round-coordinator.js.map +1 -0
- package/dist/round-driver.d.ts +53 -0
- package/dist/round-driver.d.ts.map +1 -0
- package/dist/round-driver.js +101 -0
- package/dist/round-driver.js.map +1 -0
- package/dist/round-transaction.d.ts +70 -0
- package/dist/round-transaction.d.ts.map +1 -0
- package/dist/round-transaction.js +290 -0
- package/dist/round-transaction.js.map +1 -0
- package/dist/runtime.d.ts +35 -17
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +357 -344
- package/dist/runtime.js.map +1 -1
- package/dist/storage.d.ts +38 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +201 -0
- package/dist/storage.js.map +1 -0
- package/dist/verification-gate.d.ts +39 -7
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +198 -9
- package/dist/verification-gate.js.map +1 -1
- package/loop_policy.json +26 -47
- package/package.json +13 -10
- package/skills/perception/SKILL.md +80 -249
- package/dist/backends/fs.d.ts +0 -36
- package/dist/backends/fs.d.ts.map +0 -1
- package/dist/backends/fs.js +0 -358
- package/dist/backends/fs.js.map +0 -1
- package/dist/builder.d.ts +0 -12
- package/dist/builder.d.ts.map +0 -1
- package/dist/builder.js +0 -259
- package/dist/builder.js.map +0 -1
- package/dist/mcp-server.d.ts +0 -12
- package/dist/mcp-server.d.ts.map +0 -1
- package/dist/mcp-server.js +0 -14
- package/dist/mcp-server.js.map +0 -1
- package/dist/memory-bridge.d.ts +0 -47
- package/dist/memory-bridge.d.ts.map +0 -1
- package/dist/memory-bridge.js +0 -290
- package/dist/memory-bridge.js.map +0 -1
- package/dist/tests/_helpers.d.ts +0 -25
- package/dist/tests/_helpers.d.ts.map +0 -1
- package/dist/tests/_helpers.js +0 -47
- package/dist/tests/_helpers.js.map +0 -1
- package/dist/tests/builder.test.d.ts +0 -2
- package/dist/tests/builder.test.d.ts.map +0 -1
- package/dist/tests/builder.test.js +0 -110
- package/dist/tests/builder.test.js.map +0 -1
- package/dist/tests/engine.test.d.ts +0 -2
- package/dist/tests/engine.test.d.ts.map +0 -1
- package/dist/tests/engine.test.js +0 -371
- package/dist/tests/engine.test.js.map +0 -1
- package/dist/tests/generate-schema.test.d.ts +0 -8
- package/dist/tests/generate-schema.test.d.ts.map +0 -1
- package/dist/tests/generate-schema.test.js +0 -199
- package/dist/tests/generate-schema.test.js.map +0 -1
- package/dist/tests/loop-compiler.test.d.ts +0 -2
- package/dist/tests/loop-compiler.test.d.ts.map +0 -1
- package/dist/tests/loop-compiler.test.js +0 -1051
- package/dist/tests/loop-compiler.test.js.map +0 -1
- package/dist/tests/mcp.test.d.ts +0 -7
- package/dist/tests/mcp.test.d.ts.map +0 -1
- package/dist/tests/mcp.test.js +0 -485
- package/dist/tests/mcp.test.js.map +0 -1
- package/dist/tests/memory-bridge.test.d.ts +0 -8
- package/dist/tests/memory-bridge.test.d.ts.map +0 -1
- package/dist/tests/memory-bridge.test.js +0 -383
- package/dist/tests/memory-bridge.test.js.map +0 -1
- package/dist/tests/policy.test.d.ts +0 -2
- package/dist/tests/policy.test.d.ts.map +0 -1
- package/dist/tests/policy.test.js +0 -49
- package/dist/tests/policy.test.js.map +0 -1
- package/dist/tests/protocol.test.d.ts +0 -2
- package/dist/tests/protocol.test.d.ts.map +0 -1
- package/dist/tests/protocol.test.js +0 -73
- package/dist/tests/protocol.test.js.map +0 -1
- package/dist/tests/replay.test.d.ts +0 -2
- package/dist/tests/replay.test.d.ts.map +0 -1
- package/dist/tests/replay.test.js +0 -222
- package/dist/tests/replay.test.js.map +0 -1
- package/dist/tests/runtime.test.d.ts +0 -7
- package/dist/tests/runtime.test.d.ts.map +0 -1
- package/dist/tests/runtime.test.js +0 -323
- package/dist/tests/runtime.test.js.map +0 -1
- package/dist/tests/verification-gate.test.d.ts +0 -2
- package/dist/tests/verification-gate.test.d.ts.map +0 -1
- package/dist/tests/verification-gate.test.js +0 -407
- package/dist/tests/verification-gate.test.js.map +0 -1
- package/skills/prompt-techniques/SKILL.md +0 -52
- package/skills/prompt-techniques/references/chain-of-thought.md +0 -207
- package/skills/prompt-techniques/references/few-shot.md +0 -174
- package/skills/prompt-techniques/references/least-to-most.md +0 -182
- package/skills/prompt-techniques/references/step-back.md +0 -160
- package/skills/prompt-techniques/references/tree-of-thought.md +0 -191
- package/skills/prompt-techniques/references/zero-shot.md +0 -97
package/dist/engine.js
CHANGED
|
@@ -7,11 +7,80 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { getPolicy } from "./policy.js";
|
|
10
|
-
import {
|
|
11
|
-
import { AgentStatus, Mode,
|
|
10
|
+
import { FileLoopStore, LoopStoreBackend } from "./loop-store.js";
|
|
11
|
+
import { AgentStatus, Mode, makeExecutionEvidence, makeExecutionFeedback, makeLoopCompileRequest, makeLoopObjective, makeLoopRoundResult, makeSelfEvaluation, makeSessionState, makeTaskId, SELF_EVAL_REGEX, } from "./protocol.js";
|
|
12
12
|
import { compileLoop } from "./loop-compiler.js";
|
|
13
13
|
import { logEvent } from "./observability.js";
|
|
14
14
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
15
|
+
// Shared helpers
|
|
16
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
|
+
/** Parse ExecutionEvidence from a raw JSON object. Shared by buildSelfEvaluation
|
|
18
|
+
* and invokeLoopCompile — both parse the same execution_evidence shape. */
|
|
19
|
+
export function parseExecutionEvidence(raw) {
|
|
20
|
+
if (!raw || typeof raw !== "object")
|
|
21
|
+
return undefined;
|
|
22
|
+
const testResults = raw.test_results;
|
|
23
|
+
return makeExecutionEvidence({
|
|
24
|
+
files_changed: Array.isArray(raw.files_changed)
|
|
25
|
+
? raw.files_changed.filter((v) => typeof v === "string")
|
|
26
|
+
: [],
|
|
27
|
+
test_results: testResults && typeof testResults.passed === "number"
|
|
28
|
+
? {
|
|
29
|
+
passed: testResults.passed,
|
|
30
|
+
failed: testResults.failed ?? 0,
|
|
31
|
+
skipped: testResults.skipped ?? 0,
|
|
32
|
+
}
|
|
33
|
+
: null,
|
|
34
|
+
success_criteria_met: Array.isArray(raw.success_criteria_met)
|
|
35
|
+
? raw.success_criteria_met.filter((v) => typeof v === "string")
|
|
36
|
+
: [],
|
|
37
|
+
success_criteria_remaining: Array.isArray(raw.success_criteria_remaining)
|
|
38
|
+
? raw.success_criteria_remaining.filter((v) => typeof v === "string")
|
|
39
|
+
: [],
|
|
40
|
+
progress_estimate: typeof raw.progress_estimate === "number"
|
|
41
|
+
? Math.max(0, Math.min(1, raw.progress_estimate))
|
|
42
|
+
: 0.0,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** Parse CriterionRevision[] from a raw JSON array. Shared by buildSelfEvaluation
|
|
46
|
+
* and invokeLoopCompile — both parse the same revised_success_criteria shape. */
|
|
47
|
+
export function parseCriterionRevisions(raw) {
|
|
48
|
+
if (!Array.isArray(raw))
|
|
49
|
+
return [];
|
|
50
|
+
return raw
|
|
51
|
+
.filter((v) => typeof v === "object" && v !== null &&
|
|
52
|
+
typeof v.old === "string" &&
|
|
53
|
+
typeof v.new === "string")
|
|
54
|
+
.map((v) => {
|
|
55
|
+
const r = v;
|
|
56
|
+
return { old: r.old, new: r.new };
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** Parse WorkerResult[] from a raw JSON array. Shared by buildSelfEvaluation
|
|
60
|
+
* and invokeLoopCompile — both parse the same worker_results shape. */
|
|
61
|
+
export function parseWorkerResults(raw) {
|
|
62
|
+
if (!Array.isArray(raw))
|
|
63
|
+
return [];
|
|
64
|
+
return raw
|
|
65
|
+
.filter((v) => typeof v === "object" && v !== null &&
|
|
66
|
+
typeof v.agentId === "string" &&
|
|
67
|
+
typeof v.subTask === "string" &&
|
|
68
|
+
typeof v.resultSummary === "string")
|
|
69
|
+
.map((v) => {
|
|
70
|
+
const w = v;
|
|
71
|
+
return {
|
|
72
|
+
agentId: w.agentId,
|
|
73
|
+
subAgentType: typeof w.subAgentType === "string" ? w.subAgentType : "general-purpose",
|
|
74
|
+
subTask: w.subTask,
|
|
75
|
+
resultSummary: w.resultSummary,
|
|
76
|
+
success: typeof w.success === "boolean" ? w.success : false,
|
|
77
|
+
discoveredConstraints: Array.isArray(w.discoveredConstraints)
|
|
78
|
+
? w.discoveredConstraints.filter((c) => typeof c === "string")
|
|
79
|
+
: [],
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
15
84
|
// Self-Evaluation extraction (v1.1 — autonomous loop feedback)
|
|
16
85
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
17
86
|
/** Extract a structured SelfEvaluation from agent output text.
|
|
@@ -44,70 +113,17 @@ export function extractSelfEvaluation(text) {
|
|
|
44
113
|
* (structured evaluation parameter path). */
|
|
45
114
|
export function buildSelfEvaluation(raw) {
|
|
46
115
|
// P4: Parse execution evidence from raw JSON
|
|
47
|
-
|
|
48
|
-
const rawEvidence = raw.execution_evidence;
|
|
49
|
-
if (rawEvidence && typeof rawEvidence === "object") {
|
|
50
|
-
const testResults = rawEvidence.test_results;
|
|
51
|
-
executionEvidence = makeExecutionEvidence({
|
|
52
|
-
files_changed: Array.isArray(rawEvidence.files_changed)
|
|
53
|
-
? rawEvidence.files_changed.filter((v) => typeof v === "string")
|
|
54
|
-
: [],
|
|
55
|
-
test_results: testResults && typeof testResults.passed === "number"
|
|
56
|
-
? {
|
|
57
|
-
passed: testResults.passed,
|
|
58
|
-
failed: testResults.failed ?? 0,
|
|
59
|
-
skipped: testResults.skipped ?? 0,
|
|
60
|
-
}
|
|
61
|
-
: null,
|
|
62
|
-
success_criteria_met: Array.isArray(rawEvidence.success_criteria_met)
|
|
63
|
-
? rawEvidence.success_criteria_met.filter((v) => typeof v === "string")
|
|
64
|
-
: [],
|
|
65
|
-
success_criteria_remaining: Array.isArray(rawEvidence.success_criteria_remaining)
|
|
66
|
-
? rawEvidence.success_criteria_remaining.filter((v) => typeof v === "string")
|
|
67
|
-
: [],
|
|
68
|
-
progress_estimate: typeof rawEvidence.progress_estimate === "number"
|
|
69
|
-
? Math.max(0, Math.min(1, rawEvidence.progress_estimate))
|
|
70
|
-
: 0.0,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
116
|
+
const executionEvidence = parseExecutionEvidence(raw.execution_evidence);
|
|
73
117
|
// P5: Parse corrections
|
|
74
118
|
const retractedConstraints = Array.isArray(raw.retracted_constraints)
|
|
75
119
|
? raw.retracted_constraints.filter((v) => typeof v === "string")
|
|
76
120
|
: [];
|
|
77
|
-
const revisedCriteria =
|
|
78
|
-
? raw.revised_success_criteria
|
|
79
|
-
.filter((v) => typeof v === "object" && v !== null &&
|
|
80
|
-
typeof v.old === "string" &&
|
|
81
|
-
typeof v.new === "string")
|
|
82
|
-
.map((v) => {
|
|
83
|
-
const r = v;
|
|
84
|
-
return { old: r.old, new: r.new };
|
|
85
|
-
})
|
|
86
|
-
: [];
|
|
121
|
+
const revisedCriteria = parseCriterionRevisions(raw.revised_success_criteria);
|
|
87
122
|
const wrongAssumptions = Array.isArray(raw.wrong_assumptions)
|
|
88
123
|
? raw.wrong_assumptions.filter((v) => typeof v === "string")
|
|
89
124
|
: [];
|
|
90
125
|
// Multi-agent: Parse worker delegation results
|
|
91
|
-
const workerResults =
|
|
92
|
-
? raw.worker_results
|
|
93
|
-
.filter((v) => typeof v === "object" && v !== null &&
|
|
94
|
-
typeof v.agentId === "string" &&
|
|
95
|
-
typeof v.subTask === "string" &&
|
|
96
|
-
typeof v.resultSummary === "string")
|
|
97
|
-
.map((v) => {
|
|
98
|
-
const w = v;
|
|
99
|
-
return {
|
|
100
|
-
agentId: w.agentId,
|
|
101
|
-
subAgentType: typeof w.subAgentType === "string" ? w.subAgentType : "general-purpose",
|
|
102
|
-
subTask: w.subTask,
|
|
103
|
-
resultSummary: w.resultSummary,
|
|
104
|
-
success: typeof w.success === "boolean" ? w.success : false,
|
|
105
|
-
discoveredConstraints: Array.isArray(w.discoveredConstraints)
|
|
106
|
-
? w.discoveredConstraints.filter((c) => typeof c === "string")
|
|
107
|
-
: [],
|
|
108
|
-
};
|
|
109
|
-
})
|
|
110
|
-
: [];
|
|
126
|
+
const workerResults = parseWorkerResults(raw.worker_results);
|
|
111
127
|
return makeSelfEvaluation({
|
|
112
128
|
success: typeof raw.success === "boolean" ? raw.success : false,
|
|
113
129
|
output_summary: typeof raw.output_summary === "string" ? raw.output_summary : "",
|
|
@@ -136,6 +152,8 @@ export function buildSelfEvaluation(raw) {
|
|
|
136
152
|
// v1.10: Checkpoint compression
|
|
137
153
|
compression_checkpoint: typeof raw.compression_checkpoint === "boolean" ? raw.compression_checkpoint : false,
|
|
138
154
|
checkpoint_label: typeof raw.checkpoint_label === "string" ? raw.checkpoint_label : "",
|
|
155
|
+
// v1.16: Agent's declared next action
|
|
156
|
+
next_action: typeof raw.next_action === "string" ? raw.next_action : undefined,
|
|
139
157
|
});
|
|
140
158
|
}
|
|
141
159
|
/** Fallback heuristic when structured self-eval extraction fails.
|
|
@@ -175,21 +193,21 @@ function makeEngineMetrics() {
|
|
|
175
193
|
// LoopForgeEngine
|
|
176
194
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
177
195
|
export class LoopForgeEngine {
|
|
178
|
-
skillsDir;
|
|
179
196
|
state = null;
|
|
180
197
|
backend = null;
|
|
181
198
|
metrics = null;
|
|
182
199
|
feedbackWriteBuffer = [];
|
|
183
200
|
lastTask = null;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
201
|
+
constructor(storeOrBackend) {
|
|
202
|
+
if (storeOrBackend) {
|
|
203
|
+
this.backend = "readSession" in storeOrBackend
|
|
204
|
+
? new LoopStoreBackend(storeOrBackend)
|
|
205
|
+
: storeOrBackend;
|
|
206
|
+
}
|
|
189
207
|
}
|
|
190
208
|
resolveBackend() {
|
|
191
209
|
if (this.backend === null) {
|
|
192
|
-
this.backend = new
|
|
210
|
+
this.backend = new LoopStoreBackend(new FileLoopStore(getPolicy().backend.root_dir));
|
|
193
211
|
}
|
|
194
212
|
return this.backend;
|
|
195
213
|
}
|
|
@@ -304,7 +322,6 @@ export class LoopForgeEngine {
|
|
|
304
322
|
constraints_active: response.constraints_active,
|
|
305
323
|
task: request.task,
|
|
306
324
|
success: true,
|
|
307
|
-
technique_used: response.technique_used,
|
|
308
325
|
};
|
|
309
326
|
let lastOutputSummary = "";
|
|
310
327
|
let lastViolations = [];
|
|
@@ -320,8 +337,6 @@ export class LoopForgeEngine {
|
|
|
320
337
|
timestamp: new Date().toISOString().replace(/\.\d+Z$/, ""),
|
|
321
338
|
user_intent: `loop_compile round ${response.round} — ${response.goal_id}`,
|
|
322
339
|
task_type: "loop_lineage",
|
|
323
|
-
skill_used: response.technique_used,
|
|
324
|
-
technique_used: response.technique_used,
|
|
325
340
|
loop_id: response.loop_id,
|
|
326
341
|
loop_lineage: structuredLineage,
|
|
327
342
|
loop_objective: loopObjDict,
|
|
@@ -341,26 +356,6 @@ export class LoopForgeEngine {
|
|
|
341
356
|
this.metrics.vaultWriteErrors++;
|
|
342
357
|
logEvent("vault_write_error", { error: "persist_lineage_json" });
|
|
343
358
|
}
|
|
344
|
-
// 2. Markdown write (secondary, non-blocking)
|
|
345
|
-
try {
|
|
346
|
-
this.resolveBackend().writeLineageMd(response.loop_id, response.round, response.prompt || "", {
|
|
347
|
-
goal_id: response.goal_id,
|
|
348
|
-
goal_text_hash: response.goal_text_hash,
|
|
349
|
-
recompile_level: response.recompile_level,
|
|
350
|
-
constraints_active: response.constraints_active,
|
|
351
|
-
task: request.task,
|
|
352
|
-
technique_used: response.technique_used,
|
|
353
|
-
loop_objective: loopObjDict,
|
|
354
|
-
success: true,
|
|
355
|
-
output_summary: lastOutputSummary,
|
|
356
|
-
constraint_violations: lastViolations,
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
catch {
|
|
360
|
-
if (this.metrics)
|
|
361
|
-
this.metrics.vaultWriteErrors++;
|
|
362
|
-
logEvent("vault_write_error", { error: "persist_lineage_md" });
|
|
363
|
-
}
|
|
364
359
|
return vaultOk;
|
|
365
360
|
}
|
|
366
361
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -372,9 +367,14 @@ export class LoopForgeEngine {
|
|
|
372
367
|
recordDelegation(loopId, round, entries) {
|
|
373
368
|
if (!entries.length)
|
|
374
369
|
return;
|
|
370
|
+
const taskId = `loop:${loopId}:r${round}:delegations`;
|
|
371
|
+
const existing = this.resolveBackend().queryEntries({ prefix: taskId })
|
|
372
|
+
.some((candidate) => candidate.task_id === taskId);
|
|
373
|
+
if (existing)
|
|
374
|
+
return;
|
|
375
375
|
const entry = {
|
|
376
376
|
id: randomUUID(),
|
|
377
|
-
task_id:
|
|
377
|
+
task_id: taskId,
|
|
378
378
|
version_tag: "v1",
|
|
379
379
|
is_active: true,
|
|
380
380
|
timestamp: new Date().toISOString().replace(/\.\d+Z$/, ""),
|
|
@@ -434,18 +434,8 @@ export class LoopForgeEngine {
|
|
|
434
434
|
entry.success = fbSuccess.get(rnd);
|
|
435
435
|
}
|
|
436
436
|
}
|
|
437
|
-
|
|
438
|
-
let finalResults = results;
|
|
439
|
-
if (!finalResults.length) {
|
|
440
|
-
const mdResults = this.resolveBackend().scanLineageMd(loopId);
|
|
441
|
-
if (mdResults.length)
|
|
442
|
-
finalResults = mdResults;
|
|
443
|
-
}
|
|
444
|
-
// Normalize technique_used field
|
|
437
|
+
const finalResults = results;
|
|
445
438
|
for (const entry of finalResults) {
|
|
446
|
-
if (!entry.technique_used) {
|
|
447
|
-
entry.technique_used = entry.skill_used;
|
|
448
|
-
}
|
|
449
439
|
const lineage = (entry.loop_lineage ?? entry.lineage ?? {});
|
|
450
440
|
if (!entry.output_summary) {
|
|
451
441
|
entry.output_summary = lineage.output_summary ?? "";
|
|
@@ -455,19 +445,6 @@ export class LoopForgeEngine {
|
|
|
455
445
|
lineage.constraint_violations ?? [];
|
|
456
446
|
}
|
|
457
447
|
}
|
|
458
|
-
// Enrich: attach full prompt text from Markdown for L0 cache reuse
|
|
459
|
-
for (const entry of finalResults) {
|
|
460
|
-
if (entry.full_prompt)
|
|
461
|
-
continue;
|
|
462
|
-
const lineage = (entry.loop_lineage ?? entry.lineage ?? {});
|
|
463
|
-
const roundNum = lineage.round;
|
|
464
|
-
if (roundNum) {
|
|
465
|
-
const mdEntry = readLineageMd(loopId, roundNum);
|
|
466
|
-
if (mdEntry?.full_prompt) {
|
|
467
|
-
entry.full_prompt = mdEntry.full_prompt;
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
448
|
if (!finalResults.length)
|
|
472
449
|
return null;
|
|
473
450
|
return { results: finalResults, global_entries: [] };
|
|
@@ -485,7 +462,6 @@ export class LoopForgeEngine {
|
|
|
485
462
|
response: {
|
|
486
463
|
status: AgentStatus.ERROR,
|
|
487
464
|
prompt: null,
|
|
488
|
-
analysis: null,
|
|
489
465
|
error: "Feedback mode requires a feedback payload.",
|
|
490
466
|
},
|
|
491
467
|
};
|
|
@@ -530,7 +506,6 @@ export class LoopForgeEngine {
|
|
|
530
506
|
this.state.circuit_breaker_count = 0;
|
|
531
507
|
}
|
|
532
508
|
logEvent("round_complete", {
|
|
533
|
-
technique: "feedback",
|
|
534
509
|
success,
|
|
535
510
|
loopId: loopId ?? "unknown",
|
|
536
511
|
round: loopRound ?? this.state.call_count,
|
|
@@ -540,12 +515,6 @@ export class LoopForgeEngine {
|
|
|
540
515
|
response: {
|
|
541
516
|
status: AgentStatus.OK,
|
|
542
517
|
prompt: `## Feedback Recorded\n\nSuccess: ${success}\nSignals: 1`,
|
|
543
|
-
analysis: makeAnalysis({
|
|
544
|
-
technique: "feedback",
|
|
545
|
-
rationale: `success=${success}`,
|
|
546
|
-
independence: "n/a",
|
|
547
|
-
cognitive_load: "low",
|
|
548
|
-
}),
|
|
549
518
|
error: null,
|
|
550
519
|
},
|
|
551
520
|
};
|
|
@@ -559,8 +528,8 @@ export class LoopForgeEngine {
|
|
|
559
528
|
* and emerged_subtasks for the compiler to consume next round.
|
|
560
529
|
* Call this BEFORE invokeLoopCompile for the next round so that
|
|
561
530
|
* hydrateLoopContext picks up the latest success flags. */
|
|
562
|
-
autoFeedback(selfEval, loopId, round, task) {
|
|
563
|
-
this.ensureInit({ task, mode: Mode.FEEDBACK,
|
|
531
|
+
autoFeedback(selfEval, loopId, round, task, roundTransaction) {
|
|
532
|
+
this.ensureInit({ task, mode: Mode.FEEDBACK, feedback: null, skill_name: null, task_id: null });
|
|
564
533
|
const fb = makeExecutionFeedback({
|
|
565
534
|
output: selfEval.output_summary,
|
|
566
535
|
success: selfEval.success,
|
|
@@ -568,6 +537,27 @@ export class LoopForgeEngine {
|
|
|
568
537
|
manual_fixes_needed: "",
|
|
569
538
|
});
|
|
570
539
|
const taskId = `loop:${loopId}:r${round}:feedback`;
|
|
540
|
+
const transactionId = typeof roundTransaction?.round_id === "string"
|
|
541
|
+
? roundTransaction.round_id
|
|
542
|
+
: null;
|
|
543
|
+
const transactionPersisted = () => {
|
|
544
|
+
if (!transactionId)
|
|
545
|
+
return false;
|
|
546
|
+
return this.resolveBackend().queryEntries({
|
|
547
|
+
prefix: taskId,
|
|
548
|
+
feedbackOnly: true,
|
|
549
|
+
}).some((entry) => {
|
|
550
|
+
if (entry.task_id !== taskId)
|
|
551
|
+
return false;
|
|
552
|
+
const lineage = entry.loop_lineage;
|
|
553
|
+
const transaction = lineage?.round_transaction;
|
|
554
|
+
return transaction !== null &&
|
|
555
|
+
typeof transaction === "object" &&
|
|
556
|
+
!Array.isArray(transaction) &&
|
|
557
|
+
transaction.round_id === transactionId;
|
|
558
|
+
});
|
|
559
|
+
};
|
|
560
|
+
const alreadyCommitted = transactionPersisted();
|
|
571
561
|
// Multi-agent: Merge sub-agent discovered constraints into the main constraint flow
|
|
572
562
|
const subDiscovered = (selfEval.worker_results ?? [])
|
|
573
563
|
.flatMap((w) => w.discoveredConstraints ?? [])
|
|
@@ -596,9 +586,21 @@ export class LoopForgeEngine {
|
|
|
596
586
|
wrong_assumptions: selfEval.wrong_assumptions ?? [],
|
|
597
587
|
// Multi-agent: Worker delegation results
|
|
598
588
|
worker_results: selfEval.worker_results ?? [],
|
|
589
|
+
loop_lineage: roundTransaction
|
|
590
|
+
? {
|
|
591
|
+
round,
|
|
592
|
+
round_id: roundTransaction.round_id,
|
|
593
|
+
round_transaction: roundTransaction,
|
|
594
|
+
}
|
|
595
|
+
: {},
|
|
599
596
|
};
|
|
600
|
-
|
|
601
|
-
|
|
597
|
+
if (!alreadyCommitted) {
|
|
598
|
+
this.persistFeedbackToVault(signal);
|
|
599
|
+
this.flushFeedbackBuffer();
|
|
600
|
+
if (transactionId && !transactionPersisted()) {
|
|
601
|
+
throw new Error(`Round feedback commit failed: ${transactionId}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
602
604
|
// Multi-agent: Auto-record delegation journal from worker_results
|
|
603
605
|
if (selfEval.worker_results && selfEval.worker_results.length > 0) {
|
|
604
606
|
const entries = selfEval.worker_results.map((w, i) => ({
|
|
@@ -612,6 +614,10 @@ export class LoopForgeEngine {
|
|
|
612
614
|
}));
|
|
613
615
|
this.recordDelegation(loopId, round, entries);
|
|
614
616
|
}
|
|
617
|
+
// Replaying a committed transaction may repair derived delegation data,
|
|
618
|
+
// but must never apply the feedback to mutable engine state twice.
|
|
619
|
+
if (alreadyCommitted)
|
|
620
|
+
return fb.success;
|
|
615
621
|
// Update state
|
|
616
622
|
this.state.call_count++;
|
|
617
623
|
this.state.success_trend.push(fb.success);
|
|
@@ -629,14 +635,13 @@ export class LoopForgeEngine {
|
|
|
629
635
|
loopId,
|
|
630
636
|
round,
|
|
631
637
|
success: fb.success,
|
|
632
|
-
technique: this.state?.last_technique ?? "feedback",
|
|
633
638
|
});
|
|
634
639
|
return fb.success;
|
|
635
640
|
}
|
|
636
641
|
// ═══════════════════════════════════════════════════════════════════════
|
|
637
642
|
// Loop Compile (public, primary)
|
|
638
643
|
// ═══════════════════════════════════════════════════════════════════════
|
|
639
|
-
invokeLoopCompile(request, hydrateResults) {
|
|
644
|
+
invokeLoopCompile(request, hydrateResults, options = {}) {
|
|
640
645
|
this.ensureInit(request);
|
|
641
646
|
this.lastTask = request.task;
|
|
642
647
|
// Build LoopCompileRequest from LoopForgeRequest extras
|
|
@@ -654,46 +659,29 @@ export class LoopForgeEngine {
|
|
|
654
659
|
force_level: extras.force_level ?? "auto",
|
|
655
660
|
health_check_interval: extras.health_check_interval ?? 1,
|
|
656
661
|
external_context: extras.external_context ?? "",
|
|
662
|
+
max_rounds: extras.max_rounds ?? undefined,
|
|
663
|
+
verification_flags: Array.isArray(extras.verification_flags)
|
|
664
|
+
? extras.verification_flags
|
|
665
|
+
: [],
|
|
666
|
+
attempt: typeof extras.attempt === "number"
|
|
667
|
+
? Math.max(1, Math.trunc(extras.attempt))
|
|
668
|
+
: 1,
|
|
669
|
+
consecutive_rejections: typeof extras.consecutive_rejections === "number"
|
|
670
|
+
? Math.max(0, Math.trunc(extras.consecutive_rejections))
|
|
671
|
+
: 0,
|
|
672
|
+
rejection_notice: typeof extras.rejection_notice === "string"
|
|
673
|
+
? extras.rejection_notice
|
|
674
|
+
: "",
|
|
657
675
|
});
|
|
658
676
|
// Convert last_round_result if present
|
|
659
677
|
const lastRR = extras.last_round_result;
|
|
660
678
|
if (lastRR) {
|
|
661
679
|
if (typeof lastRR === "object" && !Array.isArray(lastRR)) {
|
|
662
680
|
const rr = lastRR;
|
|
663
|
-
// Parse P4 execution evidence
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
const testResults = rawEvidence.test_results;
|
|
668
|
-
executionEvidence = makeExecutionEvidence({
|
|
669
|
-
files_changed: Array.isArray(rawEvidence.files_changed)
|
|
670
|
-
? rawEvidence.files_changed.filter((v) => typeof v === "string")
|
|
671
|
-
: [],
|
|
672
|
-
test_results: testResults && typeof testResults.passed === "number"
|
|
673
|
-
? {
|
|
674
|
-
passed: testResults.passed,
|
|
675
|
-
failed: testResults.failed ?? 0,
|
|
676
|
-
skipped: testResults.skipped ?? 0,
|
|
677
|
-
}
|
|
678
|
-
: null,
|
|
679
|
-
success_criteria_met: Array.isArray(rawEvidence.success_criteria_met)
|
|
680
|
-
? rawEvidence.success_criteria_met.filter((v) => typeof v === "string")
|
|
681
|
-
: [],
|
|
682
|
-
success_criteria_remaining: Array.isArray(rawEvidence.success_criteria_remaining)
|
|
683
|
-
? rawEvidence.success_criteria_remaining.filter((v) => typeof v === "string")
|
|
684
|
-
: [],
|
|
685
|
-
progress_estimate: typeof rawEvidence.progress_estimate === "number"
|
|
686
|
-
? Math.max(0, Math.min(1, rawEvidence.progress_estimate))
|
|
687
|
-
: 0.0,
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
// Parse P5 revised_success_criteria
|
|
691
|
-
const revisedCriteria = Array.isArray(rr.revised_success_criteria)
|
|
692
|
-
? rr.revised_success_criteria
|
|
693
|
-
.filter((v) => typeof v === "object" && v !== null &&
|
|
694
|
-
typeof v.old === "string" && typeof v.new === "string")
|
|
695
|
-
.map((v) => ({ old: v.old, new: v.new }))
|
|
696
|
-
: [];
|
|
681
|
+
// Parse P4 execution evidence (shared helper)
|
|
682
|
+
const executionEvidence = parseExecutionEvidence(rr.execution_evidence);
|
|
683
|
+
// Parse P5 revised_success_criteria (shared parser)
|
|
684
|
+
const revisedCriteria = parseCriterionRevisions(rr.revised_success_criteria);
|
|
697
685
|
lcr.last_round_result = makeLoopRoundResult({
|
|
698
686
|
round: rr.round ?? 0,
|
|
699
687
|
success: rr.success ?? false,
|
|
@@ -723,21 +711,8 @@ export class LoopForgeEngine {
|
|
|
723
711
|
// v1.10: Checkpoint boundary
|
|
724
712
|
compression_checkpoint: typeof rr.compression_checkpoint === "boolean" ? rr.compression_checkpoint : false,
|
|
725
713
|
checkpoint_label: typeof rr.checkpoint_label === "string" ? rr.checkpoint_label : "",
|
|
726
|
-
// Multi-agent: Worker delegation results
|
|
727
|
-
worker_results:
|
|
728
|
-
? rr.worker_results
|
|
729
|
-
.filter((v) => typeof v?.agentId === "string" && typeof v?.subTask === "string")
|
|
730
|
-
.map((v) => ({
|
|
731
|
-
agentId: v.agentId,
|
|
732
|
-
subAgentType: typeof v.subAgentType === "string" ? v.subAgentType : "general-purpose",
|
|
733
|
-
subTask: v.subTask,
|
|
734
|
-
resultSummary: typeof v.resultSummary === "string" ? v.resultSummary : "",
|
|
735
|
-
success: typeof v.success === "boolean" ? v.success : false,
|
|
736
|
-
discoveredConstraints: Array.isArray(v.discoveredConstraints)
|
|
737
|
-
? v.discoveredConstraints
|
|
738
|
-
: [],
|
|
739
|
-
}))
|
|
740
|
-
: [],
|
|
714
|
+
// Multi-agent: Worker delegation results (shared parser)
|
|
715
|
+
worker_results: parseWorkerResults(rr.worker_results),
|
|
741
716
|
});
|
|
742
717
|
}
|
|
743
718
|
}
|
|
@@ -769,88 +744,22 @@ export class LoopForgeEngine {
|
|
|
769
744
|
response: {
|
|
770
745
|
status: AgentStatus.ERROR,
|
|
771
746
|
prompt: null,
|
|
772
|
-
analysis: null,
|
|
773
747
|
error: `loop_compile failed: ${exc}`,
|
|
774
748
|
},
|
|
775
749
|
};
|
|
776
750
|
}
|
|
777
|
-
// Build prompt text from response
|
|
778
|
-
const promptLines = [
|
|
779
|
-
`## LoopForge Loop Compile — Round ${response.round}`,
|
|
780
|
-
`**Recompile Level**: ${response.recompile_level.toUpperCase()}`,
|
|
781
|
-
`**Loop ID**: ${response.loop_id}`,
|
|
782
|
-
`**Goal ID**: ${response.goal_id}`,
|
|
783
|
-
"",
|
|
784
|
-
response.prompt,
|
|
785
|
-
];
|
|
786
|
-
if (response.warnings.length) {
|
|
787
|
-
promptLines.push("");
|
|
788
|
-
promptLines.push("### Warnings");
|
|
789
|
-
for (const w of response.warnings) {
|
|
790
|
-
promptLines.push(`- ⚠️ ${w}`);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
// Verification gate flags (v1.6) — injected after compiler warnings
|
|
794
|
-
const verificationFlags = extras.verification_flags ?? [];
|
|
795
|
-
if (verificationFlags.length) {
|
|
796
|
-
promptLines.push("");
|
|
797
|
-
promptLines.push("### Verification Gate");
|
|
798
|
-
for (const f of verificationFlags) {
|
|
799
|
-
const icon = f.severity === "error" ? "🚫" : "⚠️";
|
|
800
|
-
promptLines.push(`- ${icon} [${f.check}] ${f.detail}`);
|
|
801
|
-
}
|
|
802
|
-
const hasError = verificationFlags.some((f) => f.severity === "error");
|
|
803
|
-
if (hasError) {
|
|
804
|
-
promptLines.push("");
|
|
805
|
-
promptLines.push("**Gate Verdict: CONTRADICTED** — this round's success flag has been excluded " +
|
|
806
|
-
"from the success trend. Address each 🚫 flag explicitly in your next response.");
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
if (response.loop_health) {
|
|
810
|
-
const h = response.loop_health;
|
|
811
|
-
promptLines.push("");
|
|
812
|
-
promptLines.push("### Loop Health");
|
|
813
|
-
promptLines.push(`- Goal Alignment: ${h.goal_alignment.toFixed(2)}`);
|
|
814
|
-
promptLines.push(`- Constraint Integrity: ${h.constraint_integrity.toFixed(2)}`);
|
|
815
|
-
promptLines.push(`- Task Continuity: ${h.task_continuity.toFixed(2)}`);
|
|
816
|
-
promptLines.push(`- Drift Detected: ${h.drift_detected}`);
|
|
817
|
-
promptLines.push(`- Strategy Stability: ${h.strategy_stability}`);
|
|
818
|
-
}
|
|
819
|
-
if (response.task_alignment &&
|
|
820
|
-
response.task_alignment.escalation !== "none") {
|
|
821
|
-
promptLines.push("");
|
|
822
|
-
promptLines.push("### Task Alignment Advisory");
|
|
823
|
-
promptLines.push(`- Score: ${response.task_alignment.alignment_score.toFixed(2)}`);
|
|
824
|
-
promptLines.push(`- Escalation: ${response.task_alignment.escalation}`);
|
|
825
|
-
promptLines.push(`- ${response.task_alignment.warning}`);
|
|
826
|
-
}
|
|
827
|
-
if (response.suggested_next_task) {
|
|
828
|
-
promptLines.push("");
|
|
829
|
-
promptLines.push(`### Suggested Next Task\n${response.suggested_next_task}`);
|
|
830
|
-
}
|
|
831
751
|
// Persist lineage to vault
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
if (this.state) {
|
|
835
|
-
this.state.last_technique = response.technique_used;
|
|
752
|
+
if (options.persistLineage !== false) {
|
|
753
|
+
this.persistLoopLineage(response, lcr);
|
|
836
754
|
}
|
|
837
755
|
return {
|
|
838
756
|
status: AgentStatus.OK,
|
|
839
757
|
response: {
|
|
840
758
|
status: AgentStatus.OK,
|
|
841
|
-
prompt:
|
|
842
|
-
analysis: makeAnalysis({
|
|
843
|
-
technique: response.technique_used,
|
|
844
|
-
rationale: `Recompile level: ${response.recompile_level}`,
|
|
845
|
-
independence: "n/a",
|
|
846
|
-
cognitive_load: response.recompile_level === "l0"
|
|
847
|
-
? "low"
|
|
848
|
-
: response.recompile_level === "l1"
|
|
849
|
-
? "medium"
|
|
850
|
-
: "high",
|
|
851
|
-
reference_file: response.reference_file,
|
|
852
|
-
}),
|
|
759
|
+
prompt: response.prompt,
|
|
853
760
|
error: null,
|
|
761
|
+
state_file_content: response.state_file_content,
|
|
762
|
+
prompt_artifact: response.prompt_artifact,
|
|
854
763
|
},
|
|
855
764
|
};
|
|
856
765
|
}
|
|
@@ -879,7 +788,7 @@ export class LoopForgeEngine {
|
|
|
879
788
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
880
789
|
// Factory
|
|
881
790
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
882
|
-
export function createEngine(
|
|
883
|
-
return new LoopForgeEngine(
|
|
791
|
+
export function createEngine(store) {
|
|
792
|
+
return new LoopForgeEngine(store);
|
|
884
793
|
}
|
|
885
794
|
//# sourceMappingURL=engine.js.map
|