loopforge 1.15.1 → 2.0.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/README.md +172 -95
- package/dist/backends/interface.d.ts +5 -10
- package/dist/backends/interface.d.ts.map +1 -1
- package/dist/backends/interface.js +3 -4
- 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.map +1 -1
- package/dist/enforcement-gate.js +28 -10
- package/dist/enforcement-gate.js.map +1 -1
- package/dist/engine.d.ts +9 -28
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +135 -361
- package/dist/engine.js.map +1 -1
- package/dist/evidence-provider.d.ts +146 -0
- package/dist/evidence-provider.d.ts.map +1 -0
- package/dist/evidence-provider.js +512 -0
- package/dist/evidence-provider.js.map +1 -0
- package/dist/index.d.ts +19 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -12
- 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 +35 -0
- package/dist/interop.d.ts.map +1 -0
- package/dist/interop.js +34 -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 +305 -2206
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-extras-parser.d.ts +82 -0
- package/dist/loop-extras-parser.d.ts.map +1 -0
- package/dist/loop-extras-parser.js +217 -0
- package/dist/loop-extras-parser.js.map +1 -0
- package/dist/loop-store.d.ts +104 -0
- package/dist/loop-store.d.ts.map +1 -0
- package/dist/loop-store.js +421 -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 +102 -21
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +795 -400
- 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 +226 -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 +52 -147
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +118 -191
- 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 +278 -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 +75 -152
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +11 -53
- 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/self-eval.d.ts +26 -0
- package/dist/self-eval.d.ts.map +1 -0
- package/dist/self-eval.js +154 -0
- package/dist/self-eval.js.map +1 -0
- package/dist/storage.d.ts +39 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +193 -0
- package/dist/storage.js.map +1 -0
- package/dist/verification-gate.d.ts +31 -7
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +250 -7
- package/dist/verification-gate.js.map +1 -1
- package/loop_policy.json +19 -53
- package/package.json +13 -10
- package/skills/perception/SKILL.md +80 -267
- package/dist/backends/fs.d.ts +0 -36
- package/dist/backends/fs.d.ts.map +0 -1
- package/dist/backends/fs.js +0 -362
- package/dist/backends/fs.js.map +0 -1
- package/dist/builder.d.ts +0 -19
- package/dist/builder.d.ts.map +0 -1
- package/dist/builder.js +0 -215
- 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/runtime.d.ts +0 -80
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -699
- package/dist/runtime.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 -112
- package/dist/tests/builder.test.js.map +0 -1
- package/dist/tests/enforcement-gate.test.d.ts +0 -2
- package/dist/tests/enforcement-gate.test.d.ts.map +0 -1
- package/dist/tests/enforcement-gate.test.js +0 -328
- package/dist/tests/enforcement-gate.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 -375
- 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 -1059
- 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 -505
- 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 -337
- 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,174 +7,14 @@
|
|
|
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, makeExecutionFeedback, makeLoopCompileRequest, makeLoopObjective, makeLoopRoundResult, makeSessionState, makeTaskId, } from "./protocol.js";
|
|
12
12
|
import { compileLoop } from "./loop-compiler.js";
|
|
13
13
|
import { logEvent } from "./observability.js";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
|
|
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
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
84
|
-
// Self-Evaluation extraction (v1.1 — autonomous loop feedback)
|
|
85
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
86
|
-
/** Extract a structured SelfEvaluation from agent output text.
|
|
87
|
-
* Returns null if no valid self-eval block is found.
|
|
88
|
-
* The agent is instructed to output JSON between the delimiters. */
|
|
89
|
-
export function extractSelfEvaluation(text) {
|
|
90
|
-
const match = text.match(SELF_EVAL_REGEX);
|
|
91
|
-
if (!match)
|
|
92
|
-
return null;
|
|
93
|
-
try {
|
|
94
|
-
const raw = JSON.parse(match[1]);
|
|
95
|
-
// Validate required fields
|
|
96
|
-
if (typeof raw.success !== "boolean")
|
|
97
|
-
return null;
|
|
98
|
-
if (typeof raw.output_summary !== "string")
|
|
99
|
-
return null;
|
|
100
|
-
if (!Array.isArray(raw.constraint_violations))
|
|
101
|
-
return null;
|
|
102
|
-
if (typeof raw.should_continue !== "boolean")
|
|
103
|
-
return null;
|
|
104
|
-
return buildSelfEvaluation(raw);
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
/** Build a SelfEvaluation from a parsed JSON object.
|
|
111
|
-
* Lenient parsing: missing optional fields get sensible defaults.
|
|
112
|
-
* Used by extractSelfEvaluation() (regex path) and MCP tool handler
|
|
113
|
-
* (structured evaluation parameter path). */
|
|
114
|
-
export function buildSelfEvaluation(raw) {
|
|
115
|
-
// P4: Parse execution evidence from raw JSON
|
|
116
|
-
const executionEvidence = parseExecutionEvidence(raw.execution_evidence);
|
|
117
|
-
// P5: Parse corrections
|
|
118
|
-
const retractedConstraints = Array.isArray(raw.retracted_constraints)
|
|
119
|
-
? raw.retracted_constraints.filter((v) => typeof v === "string")
|
|
120
|
-
: [];
|
|
121
|
-
const revisedCriteria = parseCriterionRevisions(raw.revised_success_criteria);
|
|
122
|
-
const wrongAssumptions = Array.isArray(raw.wrong_assumptions)
|
|
123
|
-
? raw.wrong_assumptions.filter((v) => typeof v === "string")
|
|
124
|
-
: [];
|
|
125
|
-
// Multi-agent: Parse worker delegation results
|
|
126
|
-
const workerResults = parseWorkerResults(raw.worker_results);
|
|
127
|
-
return makeSelfEvaluation({
|
|
128
|
-
success: typeof raw.success === "boolean" ? raw.success : false,
|
|
129
|
-
output_summary: typeof raw.output_summary === "string" ? raw.output_summary : "",
|
|
130
|
-
constraint_violations: Array.isArray(raw.constraint_violations)
|
|
131
|
-
? raw.constraint_violations.filter((v) => typeof v === "string")
|
|
132
|
-
: [],
|
|
133
|
-
should_continue: typeof raw.should_continue === "boolean" ? raw.should_continue : true,
|
|
134
|
-
// P0–P2: Optional evolution fields
|
|
135
|
-
discovered_constraints: Array.isArray(raw.discovered_constraints)
|
|
136
|
-
? raw.discovered_constraints.filter((v) => typeof v === "string")
|
|
137
|
-
: [],
|
|
138
|
-
objective_refinement: typeof raw.objective_refinement === "string"
|
|
139
|
-
? raw.objective_refinement
|
|
140
|
-
: "",
|
|
141
|
-
emerged_subtasks: Array.isArray(raw.emerged_subtasks)
|
|
142
|
-
? raw.emerged_subtasks.filter((v) => typeof v === "string")
|
|
143
|
-
: [],
|
|
144
|
-
// P4: Execution evidence
|
|
145
|
-
execution_evidence: executionEvidence,
|
|
146
|
-
// P5: Self-correction
|
|
147
|
-
retracted_constraints: retractedConstraints,
|
|
148
|
-
revised_success_criteria: revisedCriteria,
|
|
149
|
-
wrong_assumptions: wrongAssumptions,
|
|
150
|
-
// Multi-agent: Worker delegation results
|
|
151
|
-
worker_results: workerResults,
|
|
152
|
-
// v1.10: Checkpoint compression
|
|
153
|
-
compression_checkpoint: typeof raw.compression_checkpoint === "boolean" ? raw.compression_checkpoint : false,
|
|
154
|
-
checkpoint_label: typeof raw.checkpoint_label === "string" ? raw.checkpoint_label : "",
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
/** Fallback heuristic when structured self-eval extraction fails.
|
|
158
|
-
* Scans agent output for completion and error signals.
|
|
159
|
-
* Returns a low-confidence SelfEvaluation — the autonomous runner
|
|
160
|
-
* may choose to warn the user or continue cautiously. */
|
|
161
|
-
export function heuristicSelfEvaluation(text) {
|
|
162
|
-
const lower = text.toLowerCase();
|
|
163
|
-
const hasError = /error|failed|exception|cannot|unable|失败|错误|异常/.test(lower);
|
|
164
|
-
const hasCompletion = /done|complete|finished|完成|成功/.test(lower);
|
|
165
|
-
const hasRemaining = /remaining|continue|still need|next|todo|剩余|继续|下一步/.test(lower);
|
|
166
|
-
// Extract a reasonable summary from the last meaningful paragraph
|
|
167
|
-
const paragraphs = text.split(/\n\n+/).filter((p) => p.trim().length > 30);
|
|
168
|
-
const summary = paragraphs.length > 0
|
|
169
|
-
? paragraphs[paragraphs.length - 1].trim().slice(0, 300)
|
|
170
|
-
: text.trim().slice(0, 300);
|
|
171
|
-
return makeSelfEvaluation({
|
|
172
|
-
success: !hasError && (hasCompletion || !hasRemaining),
|
|
173
|
-
output_summary: summary || "[heuristic fallback — could not parse structured self-eval]",
|
|
174
|
-
constraint_violations: [],
|
|
175
|
-
should_continue: hasRemaining && !hasError,
|
|
176
|
-
});
|
|
177
|
-
}
|
|
14
|
+
import { parseExecutionEvidence, parseCriterionRevisions, parseWorkerResults, } from "./self-eval.js";
|
|
15
|
+
import { parseLoopExtras } from "./loop-extras-parser.js";
|
|
16
|
+
// ── Re-export self-evaluation utilities (moved to self-eval.ts) ──────────
|
|
17
|
+
export { parseExecutionEvidence, parseCriterionRevisions, parseWorkerResults, extractSelfEvaluation, buildSelfEvaluation, heuristicSelfEvaluation, } from "./self-eval.js";
|
|
178
18
|
function makeEngineMetrics() {
|
|
179
19
|
return {
|
|
180
20
|
vaultWriteErrors: 0,
|
|
@@ -196,15 +36,16 @@ export class LoopForgeEngine {
|
|
|
196
36
|
metrics = null;
|
|
197
37
|
feedbackWriteBuffer = [];
|
|
198
38
|
lastTask = null;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
39
|
+
constructor(storeOrBackend) {
|
|
40
|
+
if (storeOrBackend) {
|
|
41
|
+
this.backend = "readSession" in storeOrBackend
|
|
42
|
+
? new LoopStoreBackend(storeOrBackend)
|
|
43
|
+
: storeOrBackend;
|
|
44
|
+
}
|
|
204
45
|
}
|
|
205
46
|
resolveBackend() {
|
|
206
47
|
if (this.backend === null) {
|
|
207
|
-
this.backend = new
|
|
48
|
+
this.backend = new LoopStoreBackend(new FileLoopStore(getPolicy().backend.root_dir));
|
|
208
49
|
}
|
|
209
50
|
return this.backend;
|
|
210
51
|
}
|
|
@@ -319,7 +160,6 @@ export class LoopForgeEngine {
|
|
|
319
160
|
constraints_active: response.constraints_active,
|
|
320
161
|
task: request.task,
|
|
321
162
|
success: true,
|
|
322
|
-
technique_used: response.technique_used,
|
|
323
163
|
};
|
|
324
164
|
let lastOutputSummary = "";
|
|
325
165
|
let lastViolations = [];
|
|
@@ -335,8 +175,6 @@ export class LoopForgeEngine {
|
|
|
335
175
|
timestamp: new Date().toISOString().replace(/\.\d+Z$/, ""),
|
|
336
176
|
user_intent: `loop_compile round ${response.round} — ${response.goal_id}`,
|
|
337
177
|
task_type: "loop_lineage",
|
|
338
|
-
skill_used: response.technique_used,
|
|
339
|
-
technique_used: response.technique_used,
|
|
340
178
|
loop_id: response.loop_id,
|
|
341
179
|
loop_lineage: structuredLineage,
|
|
342
180
|
loop_objective: loopObjDict,
|
|
@@ -356,26 +194,6 @@ export class LoopForgeEngine {
|
|
|
356
194
|
this.metrics.vaultWriteErrors++;
|
|
357
195
|
logEvent("vault_write_error", { error: "persist_lineage_json" });
|
|
358
196
|
}
|
|
359
|
-
// 2. Markdown write (secondary, non-blocking)
|
|
360
|
-
try {
|
|
361
|
-
this.resolveBackend().writeLineageMd(response.loop_id, response.round, response.prompt || "", {
|
|
362
|
-
goal_id: response.goal_id,
|
|
363
|
-
goal_text_hash: response.goal_text_hash,
|
|
364
|
-
recompile_level: response.recompile_level,
|
|
365
|
-
constraints_active: response.constraints_active,
|
|
366
|
-
task: request.task,
|
|
367
|
-
technique_used: response.technique_used,
|
|
368
|
-
loop_objective: loopObjDict,
|
|
369
|
-
success: true,
|
|
370
|
-
output_summary: lastOutputSummary,
|
|
371
|
-
constraint_violations: lastViolations,
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
catch {
|
|
375
|
-
if (this.metrics)
|
|
376
|
-
this.metrics.vaultWriteErrors++;
|
|
377
|
-
logEvent("vault_write_error", { error: "persist_lineage_md" });
|
|
378
|
-
}
|
|
379
197
|
return vaultOk;
|
|
380
198
|
}
|
|
381
199
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -387,9 +205,14 @@ export class LoopForgeEngine {
|
|
|
387
205
|
recordDelegation(loopId, round, entries) {
|
|
388
206
|
if (!entries.length)
|
|
389
207
|
return;
|
|
208
|
+
const taskId = `loop:${loopId}:r${round}:delegations`;
|
|
209
|
+
const existing = this.resolveBackend().queryEntries({ prefix: taskId })
|
|
210
|
+
.some((candidate) => candidate.task_id === taskId);
|
|
211
|
+
if (existing)
|
|
212
|
+
return;
|
|
390
213
|
const entry = {
|
|
391
214
|
id: randomUUID(),
|
|
392
|
-
task_id:
|
|
215
|
+
task_id: taskId,
|
|
393
216
|
version_tag: "v1",
|
|
394
217
|
is_active: true,
|
|
395
218
|
timestamp: new Date().toISOString().replace(/\.\d+Z$/, ""),
|
|
@@ -449,18 +272,8 @@ export class LoopForgeEngine {
|
|
|
449
272
|
entry.success = fbSuccess.get(rnd);
|
|
450
273
|
}
|
|
451
274
|
}
|
|
452
|
-
|
|
453
|
-
let finalResults = results;
|
|
454
|
-
if (!finalResults.length) {
|
|
455
|
-
const mdResults = this.resolveBackend().scanLineageMd(loopId);
|
|
456
|
-
if (mdResults.length)
|
|
457
|
-
finalResults = mdResults;
|
|
458
|
-
}
|
|
459
|
-
// Normalize technique_used field
|
|
275
|
+
const finalResults = results;
|
|
460
276
|
for (const entry of finalResults) {
|
|
461
|
-
if (!entry.technique_used) {
|
|
462
|
-
entry.technique_used = entry.skill_used;
|
|
463
|
-
}
|
|
464
277
|
const lineage = (entry.loop_lineage ?? entry.lineage ?? {});
|
|
465
278
|
if (!entry.output_summary) {
|
|
466
279
|
entry.output_summary = lineage.output_summary ?? "";
|
|
@@ -470,19 +283,6 @@ export class LoopForgeEngine {
|
|
|
470
283
|
lineage.constraint_violations ?? [];
|
|
471
284
|
}
|
|
472
285
|
}
|
|
473
|
-
// Enrich: attach full prompt text from Markdown for L0 cache reuse
|
|
474
|
-
for (const entry of finalResults) {
|
|
475
|
-
if (entry.full_prompt)
|
|
476
|
-
continue;
|
|
477
|
-
const lineage = (entry.loop_lineage ?? entry.lineage ?? {});
|
|
478
|
-
const roundNum = lineage.round;
|
|
479
|
-
if (roundNum) {
|
|
480
|
-
const mdEntry = readLineageMd(loopId, roundNum);
|
|
481
|
-
if (mdEntry?.full_prompt) {
|
|
482
|
-
entry.full_prompt = mdEntry.full_prompt;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
286
|
if (!finalResults.length)
|
|
487
287
|
return null;
|
|
488
288
|
return { results: finalResults, global_entries: [] };
|
|
@@ -500,7 +300,6 @@ export class LoopForgeEngine {
|
|
|
500
300
|
response: {
|
|
501
301
|
status: AgentStatus.ERROR,
|
|
502
302
|
prompt: null,
|
|
503
|
-
analysis: null,
|
|
504
303
|
error: "Feedback mode requires a feedback payload.",
|
|
505
304
|
},
|
|
506
305
|
};
|
|
@@ -545,7 +344,6 @@ export class LoopForgeEngine {
|
|
|
545
344
|
this.state.circuit_breaker_count = 0;
|
|
546
345
|
}
|
|
547
346
|
logEvent("round_complete", {
|
|
548
|
-
technique: "feedback",
|
|
549
347
|
success,
|
|
550
348
|
loopId: loopId ?? "unknown",
|
|
551
349
|
round: loopRound ?? this.state.call_count,
|
|
@@ -555,12 +353,6 @@ export class LoopForgeEngine {
|
|
|
555
353
|
response: {
|
|
556
354
|
status: AgentStatus.OK,
|
|
557
355
|
prompt: `## Feedback Recorded\n\nSuccess: ${success}\nSignals: 1`,
|
|
558
|
-
analysis: makeAnalysis({
|
|
559
|
-
technique: "feedback",
|
|
560
|
-
rationale: `success=${success}`,
|
|
561
|
-
independence: "n/a",
|
|
562
|
-
cognitive_load: "low",
|
|
563
|
-
}),
|
|
564
356
|
error: null,
|
|
565
357
|
},
|
|
566
358
|
};
|
|
@@ -574,8 +366,8 @@ export class LoopForgeEngine {
|
|
|
574
366
|
* and emerged_subtasks for the compiler to consume next round.
|
|
575
367
|
* Call this BEFORE invokeLoopCompile for the next round so that
|
|
576
368
|
* hydrateLoopContext picks up the latest success flags. */
|
|
577
|
-
autoFeedback(selfEval, loopId, round, task) {
|
|
578
|
-
this.ensureInit({ task, mode: Mode.FEEDBACK,
|
|
369
|
+
autoFeedback(selfEval, loopId, round, task, roundTransaction) {
|
|
370
|
+
this.ensureInit({ task, mode: Mode.FEEDBACK, feedback: null, skill_name: null, task_id: null });
|
|
579
371
|
const fb = makeExecutionFeedback({
|
|
580
372
|
output: selfEval.output_summary,
|
|
581
373
|
success: selfEval.success,
|
|
@@ -583,6 +375,27 @@ export class LoopForgeEngine {
|
|
|
583
375
|
manual_fixes_needed: "",
|
|
584
376
|
});
|
|
585
377
|
const taskId = `loop:${loopId}:r${round}:feedback`;
|
|
378
|
+
const transactionId = typeof roundTransaction?.round_id === "string"
|
|
379
|
+
? roundTransaction.round_id
|
|
380
|
+
: null;
|
|
381
|
+
const transactionPersisted = () => {
|
|
382
|
+
if (!transactionId)
|
|
383
|
+
return false;
|
|
384
|
+
return this.resolveBackend().queryEntries({
|
|
385
|
+
prefix: taskId,
|
|
386
|
+
feedbackOnly: true,
|
|
387
|
+
}).some((entry) => {
|
|
388
|
+
if (entry.task_id !== taskId)
|
|
389
|
+
return false;
|
|
390
|
+
const lineage = entry.loop_lineage;
|
|
391
|
+
const transaction = lineage?.round_transaction;
|
|
392
|
+
return transaction !== null &&
|
|
393
|
+
typeof transaction === "object" &&
|
|
394
|
+
!Array.isArray(transaction) &&
|
|
395
|
+
transaction.round_id === transactionId;
|
|
396
|
+
});
|
|
397
|
+
};
|
|
398
|
+
const alreadyCommitted = transactionPersisted();
|
|
586
399
|
// Multi-agent: Merge sub-agent discovered constraints into the main constraint flow
|
|
587
400
|
const subDiscovered = (selfEval.worker_results ?? [])
|
|
588
401
|
.flatMap((w) => w.discoveredConstraints ?? [])
|
|
@@ -611,9 +424,21 @@ export class LoopForgeEngine {
|
|
|
611
424
|
wrong_assumptions: selfEval.wrong_assumptions ?? [],
|
|
612
425
|
// Multi-agent: Worker delegation results
|
|
613
426
|
worker_results: selfEval.worker_results ?? [],
|
|
427
|
+
loop_lineage: roundTransaction
|
|
428
|
+
? {
|
|
429
|
+
round,
|
|
430
|
+
round_id: roundTransaction.round_id,
|
|
431
|
+
round_transaction: roundTransaction,
|
|
432
|
+
}
|
|
433
|
+
: {},
|
|
614
434
|
};
|
|
615
|
-
|
|
616
|
-
|
|
435
|
+
if (!alreadyCommitted) {
|
|
436
|
+
this.persistFeedbackToVault(signal);
|
|
437
|
+
this.flushFeedbackBuffer();
|
|
438
|
+
if (transactionId && !transactionPersisted()) {
|
|
439
|
+
throw new Error(`Round feedback commit failed: ${transactionId}`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
617
442
|
// Multi-agent: Auto-record delegation journal from worker_results
|
|
618
443
|
if (selfEval.worker_results && selfEval.worker_results.length > 0) {
|
|
619
444
|
const entries = selfEval.worker_results.map((w, i) => ({
|
|
@@ -627,6 +452,10 @@ export class LoopForgeEngine {
|
|
|
627
452
|
}));
|
|
628
453
|
this.recordDelegation(loopId, round, entries);
|
|
629
454
|
}
|
|
455
|
+
// Replaying a committed transaction may repair derived delegation data,
|
|
456
|
+
// but must never apply the feedback to mutable engine state twice.
|
|
457
|
+
if (alreadyCommitted)
|
|
458
|
+
return fb.success;
|
|
630
459
|
// Update state
|
|
631
460
|
this.state.call_count++;
|
|
632
461
|
this.state.success_trend.push(fb.success);
|
|
@@ -644,80 +473,87 @@ export class LoopForgeEngine {
|
|
|
644
473
|
loopId,
|
|
645
474
|
round,
|
|
646
475
|
success: fb.success,
|
|
647
|
-
technique: this.state?.last_technique ?? "feedback",
|
|
648
476
|
});
|
|
649
477
|
return fb.success;
|
|
650
478
|
}
|
|
651
479
|
// ═══════════════════════════════════════════════════════════════════════
|
|
652
480
|
// Loop Compile (public, primary)
|
|
653
481
|
// ═══════════════════════════════════════════════════════════════════════
|
|
654
|
-
invokeLoopCompile(request, hydrateResults) {
|
|
482
|
+
invokeLoopCompile(request, hydrateResults, options = {}) {
|
|
655
483
|
this.ensureInit(request);
|
|
656
484
|
this.lastTask = request.task;
|
|
657
|
-
//
|
|
485
|
+
// Parse extras via typed extraction pipeline (loop-extras-parser.ts).
|
|
486
|
+
// Errors are collected but never thrown — the compiler always gets
|
|
487
|
+
// best-effort defaults so a malformed request doesn't crash the engine.
|
|
658
488
|
const extras = request;
|
|
489
|
+
const { parsed, ctx } = parseLoopExtras(extras, request.task_id ?? "");
|
|
490
|
+
if (ctx.errors.length > 0) {
|
|
491
|
+
logEvent("extras_parse_errors", {
|
|
492
|
+
errors: ctx.errors.map((e) => `${e.field}: ${e.message}`),
|
|
493
|
+
});
|
|
494
|
+
}
|
|
659
495
|
const lcr = makeLoopCompileRequest({
|
|
660
|
-
loop_id:
|
|
661
|
-
round:
|
|
662
|
-
goal_id:
|
|
496
|
+
loop_id: parsed.loop_id,
|
|
497
|
+
round: parsed.round,
|
|
498
|
+
goal_id: parsed.goal_id,
|
|
663
499
|
task: request.task,
|
|
664
|
-
domain:
|
|
665
|
-
next_task_proposal:
|
|
666
|
-
plan_source:
|
|
667
|
-
constraints_from_plan:
|
|
668
|
-
new_since_last_round:
|
|
669
|
-
force_level:
|
|
670
|
-
health_check_interval:
|
|
671
|
-
external_context:
|
|
672
|
-
max_rounds:
|
|
500
|
+
domain: parsed.domain,
|
|
501
|
+
next_task_proposal: parsed.next_task_proposal,
|
|
502
|
+
plan_source: parsed.plan_source,
|
|
503
|
+
constraints_from_plan: parsed.constraints_from_plan,
|
|
504
|
+
new_since_last_round: parsed.new_since_last_round,
|
|
505
|
+
force_level: parsed.force_level,
|
|
506
|
+
health_check_interval: parsed.health_check_interval,
|
|
507
|
+
external_context: parsed.external_context,
|
|
508
|
+
max_rounds: parsed.max_rounds,
|
|
509
|
+
verification_flags: parsed.verification_flags,
|
|
510
|
+
attempt: parsed.attempt,
|
|
511
|
+
consecutive_rejections: parsed.consecutive_rejections,
|
|
512
|
+
rejection_notice: parsed.rejection_notice,
|
|
673
513
|
});
|
|
674
|
-
// Convert last_round_result if present
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
worker_results: parseWorkerResults(rr.worker_results),
|
|
714
|
-
});
|
|
715
|
-
}
|
|
514
|
+
// Convert last_round_result if present (object already validated by parser)
|
|
515
|
+
if (parsed.last_round_result) {
|
|
516
|
+
const rr = parsed.last_round_result;
|
|
517
|
+
// Parse P4 execution evidence (shared helper)
|
|
518
|
+
const executionEvidence = parseExecutionEvidence(rr.execution_evidence);
|
|
519
|
+
// Parse P5 revised_success_criteria (shared parser)
|
|
520
|
+
const revisedCriteria = parseCriterionRevisions(rr.revised_success_criteria);
|
|
521
|
+
lcr.last_round_result = makeLoopRoundResult({
|
|
522
|
+
round: rr.round ?? 0,
|
|
523
|
+
success: rr.success ?? false,
|
|
524
|
+
output_summary: rr.output_summary ?? "",
|
|
525
|
+
constraint_violations: rr.constraint_violations ?? [],
|
|
526
|
+
manual_fixes_needed: rr.manual_fixes_needed ?? "",
|
|
527
|
+
// P0–P2: Cognitive evolution fields
|
|
528
|
+
discovered_constraints: Array.isArray(rr.discovered_constraints)
|
|
529
|
+
? rr.discovered_constraints.filter((v) => typeof v === "string")
|
|
530
|
+
: [],
|
|
531
|
+
objective_refinement: typeof rr.objective_refinement === "string"
|
|
532
|
+
? rr.objective_refinement
|
|
533
|
+
: "",
|
|
534
|
+
emerged_subtasks: Array.isArray(rr.emerged_subtasks)
|
|
535
|
+
? rr.emerged_subtasks.filter((v) => typeof v === "string")
|
|
536
|
+
: [],
|
|
537
|
+
// P4: Execution evidence
|
|
538
|
+
execution_evidence: executionEvidence,
|
|
539
|
+
// P5: Self-correction
|
|
540
|
+
retracted_constraints: Array.isArray(rr.retracted_constraints)
|
|
541
|
+
? rr.retracted_constraints.filter((v) => typeof v === "string")
|
|
542
|
+
: [],
|
|
543
|
+
revised_success_criteria: revisedCriteria,
|
|
544
|
+
wrong_assumptions: Array.isArray(rr.wrong_assumptions)
|
|
545
|
+
? rr.wrong_assumptions.filter((v) => typeof v === "string")
|
|
546
|
+
: [],
|
|
547
|
+
// v1.10: Checkpoint boundary
|
|
548
|
+
compression_checkpoint: typeof rr.compression_checkpoint === "boolean" ? rr.compression_checkpoint : false,
|
|
549
|
+
checkpoint_label: typeof rr.checkpoint_label === "string" ? rr.checkpoint_label : "",
|
|
550
|
+
// Multi-agent: Worker delegation results (shared parser)
|
|
551
|
+
worker_results: parseWorkerResults(rr.worker_results),
|
|
552
|
+
});
|
|
716
553
|
}
|
|
717
|
-
// Convert loop_objective if present
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
const obj = lo;
|
|
554
|
+
// Convert loop_objective if present (object already validated by parser)
|
|
555
|
+
if (parsed.loop_objective) {
|
|
556
|
+
const obj = parsed.loop_objective;
|
|
721
557
|
lcr.loop_objective = makeLoopObjective({
|
|
722
558
|
objective: obj.objective ?? "",
|
|
723
559
|
success_criteria: obj.success_criteria ?? [],
|
|
@@ -742,84 +578,22 @@ export class LoopForgeEngine {
|
|
|
742
578
|
response: {
|
|
743
579
|
status: AgentStatus.ERROR,
|
|
744
580
|
prompt: null,
|
|
745
|
-
analysis: null,
|
|
746
581
|
error: `loop_compile failed: ${exc}`,
|
|
747
582
|
},
|
|
748
583
|
};
|
|
749
584
|
}
|
|
750
|
-
// Build prompt text from response.
|
|
751
|
-
// Compile functions (buildL2Prompt / specialist compilers via buildHeader)
|
|
752
|
-
// already produce their own round-level headers with technique + identity.
|
|
753
|
-
const promptLines = [response.prompt];
|
|
754
|
-
if (response.warnings.length) {
|
|
755
|
-
promptLines.push("");
|
|
756
|
-
promptLines.push("### Warnings");
|
|
757
|
-
for (const w of response.warnings) {
|
|
758
|
-
promptLines.push(`- ⚠️ ${w}`);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
// Verification gate flags (v1.6) — injected after compiler warnings
|
|
762
|
-
const verificationFlags = extras.verification_flags ?? [];
|
|
763
|
-
if (verificationFlags.length) {
|
|
764
|
-
promptLines.push("");
|
|
765
|
-
promptLines.push("### Verification Gate");
|
|
766
|
-
for (const f of verificationFlags) {
|
|
767
|
-
const icon = f.severity === "error" ? "🚫" : "⚠️";
|
|
768
|
-
promptLines.push(`- ${icon} [${f.check}] ${f.detail}`);
|
|
769
|
-
}
|
|
770
|
-
const hasError = verificationFlags.some((f) => f.severity === "error");
|
|
771
|
-
if (hasError) {
|
|
772
|
-
promptLines.push("");
|
|
773
|
-
promptLines.push("**Gate Verdict: CONTRADICTED** — this round's success flag has been excluded " +
|
|
774
|
-
"from the success trend. Address each 🚫 flag explicitly in your next response.");
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
if (response.loop_health) {
|
|
778
|
-
const h = response.loop_health;
|
|
779
|
-
promptLines.push("");
|
|
780
|
-
promptLines.push("### Loop Health");
|
|
781
|
-
promptLines.push(`- Goal Alignment: ${h.goal_alignment.toFixed(2)}`);
|
|
782
|
-
promptLines.push(`- Constraint Integrity: ${h.constraint_integrity.toFixed(2)}`);
|
|
783
|
-
promptLines.push(`- Task Continuity: ${h.task_continuity.toFixed(2)}`);
|
|
784
|
-
promptLines.push(`- Drift Detected: ${h.drift_detected}`);
|
|
785
|
-
promptLines.push(`- Strategy Stability: ${h.strategy_stability}`);
|
|
786
|
-
}
|
|
787
|
-
if (response.task_alignment &&
|
|
788
|
-
response.task_alignment.escalation !== "none") {
|
|
789
|
-
promptLines.push("");
|
|
790
|
-
promptLines.push("### Task Alignment Advisory");
|
|
791
|
-
promptLines.push(`- Score: ${response.task_alignment.alignment_score.toFixed(2)}`);
|
|
792
|
-
promptLines.push(`- Escalation: ${response.task_alignment.escalation}`);
|
|
793
|
-
promptLines.push(`- ${response.task_alignment.warning}`);
|
|
794
|
-
}
|
|
795
|
-
if (response.suggested_next_task) {
|
|
796
|
-
promptLines.push("");
|
|
797
|
-
promptLines.push(`### Suggested Next Task\n${response.suggested_next_task}`);
|
|
798
|
-
}
|
|
799
585
|
// Persist lineage to vault
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
if (this.state) {
|
|
803
|
-
this.state.last_technique = response.technique_used;
|
|
586
|
+
if (options.persistLineage !== false) {
|
|
587
|
+
this.persistLoopLineage(response, lcr);
|
|
804
588
|
}
|
|
805
589
|
return {
|
|
806
590
|
status: AgentStatus.OK,
|
|
807
591
|
response: {
|
|
808
592
|
status: AgentStatus.OK,
|
|
809
|
-
prompt:
|
|
810
|
-
analysis: makeAnalysis({
|
|
811
|
-
technique: response.technique_used,
|
|
812
|
-
rationale: `Recompile level: ${response.recompile_level}`,
|
|
813
|
-
independence: "n/a",
|
|
814
|
-
cognitive_load: response.recompile_level === "l0"
|
|
815
|
-
? "low"
|
|
816
|
-
: response.recompile_level === "l1"
|
|
817
|
-
? "medium"
|
|
818
|
-
: "high",
|
|
819
|
-
reference_file: response.reference_file,
|
|
820
|
-
}),
|
|
593
|
+
prompt: response.prompt,
|
|
821
594
|
error: null,
|
|
822
595
|
state_file_content: response.state_file_content,
|
|
596
|
+
prompt_artifact: response.prompt_artifact,
|
|
823
597
|
},
|
|
824
598
|
};
|
|
825
599
|
}
|
|
@@ -848,7 +622,7 @@ export class LoopForgeEngine {
|
|
|
848
622
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
849
623
|
// Factory
|
|
850
624
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
851
|
-
export function createEngine(
|
|
852
|
-
return new LoopForgeEngine(
|
|
625
|
+
export function createEngine(store) {
|
|
626
|
+
return new LoopForgeEngine(store);
|
|
853
627
|
}
|
|
854
628
|
//# sourceMappingURL=engine.js.map
|