loopforge 1.8.0 → 1.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/fs.d.ts.map +1 -1
- package/dist/backends/fs.js +13 -10
- package/dist/backends/fs.js.map +1 -1
- package/dist/backends/interface.d.ts +2 -0
- package/dist/backends/interface.d.ts.map +1 -1
- package/dist/builder.d.ts +13 -10
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +117 -96
- package/dist/builder.js.map +1 -1
- package/dist/enforcement-gate.d.ts +44 -0
- package/dist/enforcement-gate.d.ts.map +1 -0
- package/dist/enforcement-gate.js +253 -0
- package/dist/enforcement-gate.js.map +1 -0
- package/dist/engine.d.ts +13 -6
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +127 -171
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/loop-compiler.d.ts +10 -2
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +882 -583
- package/dist/loop-compiler.js.map +1 -1
- package/dist/mcp/session.d.ts +12 -2
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +127 -64
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/tools.js +4 -4
- package/dist/memory-bridge.js +1 -1
- package/dist/memory-bridge.js.map +1 -1
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +1 -1
- package/dist/policy.d.ts +80 -10
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +101 -30
- package/dist/policy.js.map +1 -1
- package/dist/protocol.d.ts +70 -16
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +40 -8
- package/dist/protocol.js.map +1 -1
- package/dist/replay.js +7 -7
- package/dist/replay.js.map +1 -1
- package/dist/runtime.d.ts +11 -7
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +125 -144
- package/dist/runtime.js.map +1 -1
- package/dist/tests/builder.test.js +42 -28
- package/dist/tests/builder.test.js.map +1 -1
- package/dist/tests/enforcement-gate.test.d.ts +2 -0
- package/dist/tests/enforcement-gate.test.d.ts.map +1 -0
- package/dist/tests/enforcement-gate.test.js +328 -0
- package/dist/tests/enforcement-gate.test.js.map +1 -0
- package/dist/tests/engine.test.js +22 -22
- package/dist/tests/engine.test.js.map +1 -1
- package/dist/tests/generate-schema.test.js +10 -7
- package/dist/tests/generate-schema.test.js.map +1 -1
- package/dist/tests/loop-compiler.test.js +273 -47
- package/dist/tests/loop-compiler.test.js.map +1 -1
- package/dist/tests/mcp.test.js +31 -11
- package/dist/tests/mcp.test.js.map +1 -1
- package/dist/tests/memory-bridge.test.js +0 -1
- package/dist/tests/memory-bridge.test.js.map +1 -1
- package/dist/tests/policy.test.js +6 -16
- package/dist/tests/policy.test.js.map +1 -1
- package/dist/tests/protocol.test.js +3 -2
- package/dist/tests/protocol.test.js.map +1 -1
- package/dist/tests/replay.test.js +9 -9
- package/dist/tests/replay.test.js.map +1 -1
- package/dist/tests/runtime.test.js +27 -13
- package/dist/tests/runtime.test.js.map +1 -1
- package/dist/tests/verification-gate.test.js +1 -1
- package/dist/tests/verification-gate.test.js.map +1 -1
- package/dist/verification-gate.d.ts +8 -2
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +5 -4
- package/dist/verification-gate.js.map +1 -1
- package/loop_policy.json +13 -26
- package/package.json +1 -1
- package/skills/perception/SKILL.md +42 -23
- package/skills/prompt-techniques/SKILL.md +52 -52
- package/skills/prompt-techniques/references/chain-of-thought.md +207 -207
- package/skills/prompt-techniques/references/few-shot.md +174 -174
- package/skills/prompt-techniques/references/least-to-most.md +182 -182
- package/skills/prompt-techniques/references/step-back.md +160 -160
- package/skills/prompt-techniques/references/tree-of-thought.md +191 -191
- package/skills/prompt-techniques/references/zero-shot.md +97 -97
package/dist/engine.js
CHANGED
|
@@ -9,10 +9,78 @@ import { randomUUID } from "node:crypto";
|
|
|
9
9
|
import { getPolicy } from "./policy.js";
|
|
10
10
|
import { FSBackend, readLineageMd } from "./backends/fs.js";
|
|
11
11
|
import { AgentStatus, Mode, makeAnalysis, makeExecutionEvidence, makeExecutionFeedback, makeLoopCompileRequest, makeLoopObjective, makeLoopRoundResult, makeSelfEvaluation, makeSessionState, makeTaskId, makeVaultConfig, SELF_EVAL_REGEX, } from "./protocol.js";
|
|
12
|
-
import { scoreQuality } from "./builder.js";
|
|
13
12
|
import { compileLoop } from "./loop-compiler.js";
|
|
14
13
|
import { logEvent } from "./observability.js";
|
|
15
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
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
16
84
|
// Self-Evaluation extraction (v1.1 — autonomous loop feedback)
|
|
17
85
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
18
86
|
/** Extract a structured SelfEvaluation from agent output text.
|
|
@@ -45,70 +113,17 @@ export function extractSelfEvaluation(text) {
|
|
|
45
113
|
* (structured evaluation parameter path). */
|
|
46
114
|
export function buildSelfEvaluation(raw) {
|
|
47
115
|
// P4: Parse execution evidence from raw JSON
|
|
48
|
-
|
|
49
|
-
const rawEvidence = raw.execution_evidence;
|
|
50
|
-
if (rawEvidence && typeof rawEvidence === "object") {
|
|
51
|
-
const testResults = rawEvidence.test_results;
|
|
52
|
-
executionEvidence = makeExecutionEvidence({
|
|
53
|
-
files_changed: Array.isArray(rawEvidence.files_changed)
|
|
54
|
-
? rawEvidence.files_changed.filter((v) => typeof v === "string")
|
|
55
|
-
: [],
|
|
56
|
-
test_results: testResults && typeof testResults.passed === "number"
|
|
57
|
-
? {
|
|
58
|
-
passed: testResults.passed,
|
|
59
|
-
failed: testResults.failed ?? 0,
|
|
60
|
-
skipped: testResults.skipped ?? 0,
|
|
61
|
-
}
|
|
62
|
-
: null,
|
|
63
|
-
success_criteria_met: Array.isArray(rawEvidence.success_criteria_met)
|
|
64
|
-
? rawEvidence.success_criteria_met.filter((v) => typeof v === "string")
|
|
65
|
-
: [],
|
|
66
|
-
success_criteria_remaining: Array.isArray(rawEvidence.success_criteria_remaining)
|
|
67
|
-
? rawEvidence.success_criteria_remaining.filter((v) => typeof v === "string")
|
|
68
|
-
: [],
|
|
69
|
-
progress_estimate: typeof rawEvidence.progress_estimate === "number"
|
|
70
|
-
? Math.max(0, Math.min(1, rawEvidence.progress_estimate))
|
|
71
|
-
: 0.0,
|
|
72
|
-
});
|
|
73
|
-
}
|
|
116
|
+
const executionEvidence = parseExecutionEvidence(raw.execution_evidence);
|
|
74
117
|
// P5: Parse corrections
|
|
75
118
|
const retractedConstraints = Array.isArray(raw.retracted_constraints)
|
|
76
119
|
? raw.retracted_constraints.filter((v) => typeof v === "string")
|
|
77
120
|
: [];
|
|
78
|
-
const revisedCriteria =
|
|
79
|
-
? raw.revised_success_criteria
|
|
80
|
-
.filter((v) => typeof v === "object" && v !== null &&
|
|
81
|
-
typeof v.old === "string" &&
|
|
82
|
-
typeof v.new === "string")
|
|
83
|
-
.map((v) => {
|
|
84
|
-
const r = v;
|
|
85
|
-
return { old: r.old, new: r.new };
|
|
86
|
-
})
|
|
87
|
-
: [];
|
|
121
|
+
const revisedCriteria = parseCriterionRevisions(raw.revised_success_criteria);
|
|
88
122
|
const wrongAssumptions = Array.isArray(raw.wrong_assumptions)
|
|
89
123
|
? raw.wrong_assumptions.filter((v) => typeof v === "string")
|
|
90
124
|
: [];
|
|
91
125
|
// Multi-agent: Parse worker delegation results
|
|
92
|
-
const workerResults =
|
|
93
|
-
? raw.worker_results
|
|
94
|
-
.filter((v) => typeof v === "object" && v !== null &&
|
|
95
|
-
typeof v.agentId === "string" &&
|
|
96
|
-
typeof v.subTask === "string" &&
|
|
97
|
-
typeof v.resultSummary === "string")
|
|
98
|
-
.map((v) => {
|
|
99
|
-
const w = v;
|
|
100
|
-
return {
|
|
101
|
-
agentId: w.agentId,
|
|
102
|
-
subAgentType: typeof w.subAgentType === "string" ? w.subAgentType : "general-purpose",
|
|
103
|
-
subTask: w.subTask,
|
|
104
|
-
resultSummary: w.resultSummary,
|
|
105
|
-
success: typeof w.success === "boolean" ? w.success : false,
|
|
106
|
-
discoveredConstraints: Array.isArray(w.discoveredConstraints)
|
|
107
|
-
? w.discoveredConstraints.filter((c) => typeof c === "string")
|
|
108
|
-
: [],
|
|
109
|
-
};
|
|
110
|
-
})
|
|
111
|
-
: [];
|
|
126
|
+
const workerResults = parseWorkerResults(raw.worker_results);
|
|
112
127
|
return makeSelfEvaluation({
|
|
113
128
|
success: typeof raw.success === "boolean" ? raw.success : false,
|
|
114
129
|
output_summary: typeof raw.output_summary === "string" ? raw.output_summary : "",
|
|
@@ -134,6 +149,9 @@ export function buildSelfEvaluation(raw) {
|
|
|
134
149
|
wrong_assumptions: wrongAssumptions,
|
|
135
150
|
// Multi-agent: Worker delegation results
|
|
136
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 : "",
|
|
137
155
|
});
|
|
138
156
|
}
|
|
139
157
|
/** Fallback heuristic when structured self-eval extraction fails.
|
|
@@ -173,15 +191,14 @@ function makeEngineMetrics() {
|
|
|
173
191
|
// LoopForgeEngine
|
|
174
192
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
175
193
|
export class LoopForgeEngine {
|
|
176
|
-
skillsDir;
|
|
177
194
|
state = null;
|
|
178
195
|
backend = null;
|
|
179
196
|
metrics = null;
|
|
180
197
|
feedbackWriteBuffer = [];
|
|
181
198
|
lastTask = null;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
199
|
+
// skillsDir parameter retained for API backward compatibility (v1.15).
|
|
200
|
+
// Previously stored as this.skillsDir but never read — now accepted & dropped.
|
|
201
|
+
constructor(_skillsDir = "skills", backend) {
|
|
185
202
|
if (backend)
|
|
186
203
|
this.backend = backend;
|
|
187
204
|
}
|
|
@@ -244,12 +261,12 @@ export class LoopForgeEngine {
|
|
|
244
261
|
is_active: true,
|
|
245
262
|
timestamp: now,
|
|
246
263
|
user_intent: String(signal.task_type ?? "").slice(0, 200),
|
|
247
|
-
|
|
264
|
+
success: signal.success ?? false,
|
|
248
265
|
execution_feedback: JSON.stringify({
|
|
249
|
-
|
|
266
|
+
success: signal.success ?? false,
|
|
267
|
+
status: signal.success
|
|
250
268
|
? "success"
|
|
251
269
|
: "partial",
|
|
252
|
-
quality_score: signal.quality_score ?? 0,
|
|
253
270
|
constraint_compliance: {
|
|
254
271
|
all_hard_constraints_met: !Array.isArray(signal.violations) || signal.violations.length === 0,
|
|
255
272
|
violations: signal.violations ?? [],
|
|
@@ -299,7 +316,6 @@ export class LoopForgeEngine {
|
|
|
299
316
|
goal_id: response.goal_id,
|
|
300
317
|
goal_text_hash: response.goal_text_hash,
|
|
301
318
|
recompile_level: response.recompile_level,
|
|
302
|
-
quality_score: 0,
|
|
303
319
|
constraints_active: response.constraints_active,
|
|
304
320
|
task: request.task,
|
|
305
321
|
success: true,
|
|
@@ -319,7 +335,6 @@ export class LoopForgeEngine {
|
|
|
319
335
|
timestamp: new Date().toISOString().replace(/\.\d+Z$/, ""),
|
|
320
336
|
user_intent: `loop_compile round ${response.round} — ${response.goal_id}`,
|
|
321
337
|
task_type: "loop_lineage",
|
|
322
|
-
quality_score: 0,
|
|
323
338
|
skill_used: response.technique_used,
|
|
324
339
|
technique_used: response.technique_used,
|
|
325
340
|
loop_id: response.loop_id,
|
|
@@ -352,7 +367,6 @@ export class LoopForgeEngine {
|
|
|
352
367
|
technique_used: response.technique_used,
|
|
353
368
|
loop_objective: loopObjDict,
|
|
354
369
|
success: true,
|
|
355
|
-
quality_score: 0,
|
|
356
370
|
output_summary: lastOutputSummary,
|
|
357
371
|
constraint_violations: lastViolations,
|
|
358
372
|
});
|
|
@@ -410,30 +424,29 @@ export class LoopForgeEngine {
|
|
|
410
424
|
hydrateLoopContext(loopId) {
|
|
411
425
|
const prefix = `loop:${loopId}:r`;
|
|
412
426
|
const results = this.resolveBackend().queryEntries({ prefix });
|
|
413
|
-
// Merge feedback
|
|
427
|
+
// Merge feedback success flags into lineage entries
|
|
414
428
|
const fbEntries = this.resolveBackend().queryEntries({
|
|
415
429
|
prefix,
|
|
416
430
|
feedbackOnly: true,
|
|
417
431
|
});
|
|
418
|
-
const
|
|
432
|
+
const fbSuccess = new Map();
|
|
419
433
|
for (const fe of fbEntries) {
|
|
420
434
|
const tid = String(fe.task_id ?? "");
|
|
421
435
|
const parts = tid.split(":r");
|
|
422
436
|
if (parts.length >= 2) {
|
|
423
437
|
const roundStr = parts[1].split(":")[0];
|
|
424
438
|
const fbRound = parseInt(roundStr, 10);
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
fbQuality.set(fbRound, Math.max(fbQuality.get(fbRound) ?? 0, fbScore));
|
|
439
|
+
if (!Number.isNaN(fbRound) && fe.success !== undefined) {
|
|
440
|
+
fbSuccess.set(fbRound, fe.success);
|
|
428
441
|
}
|
|
429
442
|
}
|
|
430
443
|
}
|
|
431
444
|
for (const entry of results) {
|
|
432
445
|
const lineage = (entry.loop_lineage ?? entry.lineage ?? {});
|
|
433
446
|
const rnd = lineage.round;
|
|
434
|
-
if (rnd &&
|
|
435
|
-
lineage.
|
|
436
|
-
entry.
|
|
447
|
+
if (rnd && fbSuccess.has(rnd)) {
|
|
448
|
+
lineage.success = fbSuccess.get(rnd);
|
|
449
|
+
entry.success = fbSuccess.get(rnd);
|
|
437
450
|
}
|
|
438
451
|
}
|
|
439
452
|
// Fallback: scan Markdown files if JSON vault had no matches
|
|
@@ -456,10 +469,6 @@ export class LoopForgeEngine {
|
|
|
456
469
|
entry.constraint_violations =
|
|
457
470
|
lineage.constraint_violations ?? [];
|
|
458
471
|
}
|
|
459
|
-
// v1.7: Ensure quality_score is always populated for failure lineage weighting
|
|
460
|
-
if (entry.quality_score === undefined || entry.quality_score === null) {
|
|
461
|
-
entry.quality_score = lineage.quality_score ?? 0;
|
|
462
|
-
}
|
|
463
472
|
}
|
|
464
473
|
// Enrich: attach full prompt text from Markdown for L0 cache reuse
|
|
465
474
|
for (const entry of finalResults) {
|
|
@@ -499,11 +508,6 @@ export class LoopForgeEngine {
|
|
|
499
508
|
const success = fb.success;
|
|
500
509
|
const violations = fb.constraint_violations ?? [];
|
|
501
510
|
const fixes = fb.manual_fixes_needed ?? "";
|
|
502
|
-
const quality = scoreQuality({
|
|
503
|
-
success,
|
|
504
|
-
constraint_violations: violations,
|
|
505
|
-
manual_fixes_needed: fixes,
|
|
506
|
-
});
|
|
507
511
|
// Loop-aware task_id for feedback→lineage backfill
|
|
508
512
|
const loopId = request.loop_id;
|
|
509
513
|
const loopRound = request.round;
|
|
@@ -517,7 +521,7 @@ export class LoopForgeEngine {
|
|
|
517
521
|
const signal = {
|
|
518
522
|
task_id: taskId,
|
|
519
523
|
task_type: request.task.slice(0, 80),
|
|
520
|
-
|
|
524
|
+
success,
|
|
521
525
|
skill_used: request.skill_name ?? "",
|
|
522
526
|
violations,
|
|
523
527
|
manual_fixes: fixes,
|
|
@@ -525,13 +529,13 @@ export class LoopForgeEngine {
|
|
|
525
529
|
round: loopRound,
|
|
526
530
|
};
|
|
527
531
|
this.persistFeedbackToVault(signal);
|
|
528
|
-
// Flush immediately so next compile cycle sees
|
|
532
|
+
// Flush immediately so next compile cycle sees success flags
|
|
529
533
|
this.flushFeedbackBuffer();
|
|
530
534
|
// Update state
|
|
531
535
|
this.state.call_count++;
|
|
532
|
-
this.state.
|
|
533
|
-
if (this.state.
|
|
534
|
-
this.state.
|
|
536
|
+
this.state.success_trend.push(success);
|
|
537
|
+
if (this.state.success_trend.length > 20) {
|
|
538
|
+
this.state.success_trend = this.state.success_trend.slice(-20);
|
|
535
539
|
}
|
|
536
540
|
// Circuit breaker
|
|
537
541
|
if (this.shouldBreak()) {
|
|
@@ -542,7 +546,7 @@ export class LoopForgeEngine {
|
|
|
542
546
|
}
|
|
543
547
|
logEvent("round_complete", {
|
|
544
548
|
technique: "feedback",
|
|
545
|
-
|
|
549
|
+
success,
|
|
546
550
|
loopId: loopId ?? "unknown",
|
|
547
551
|
round: loopRound ?? this.state.call_count,
|
|
548
552
|
});
|
|
@@ -550,10 +554,10 @@ export class LoopForgeEngine {
|
|
|
550
554
|
status: AgentStatus.OK,
|
|
551
555
|
response: {
|
|
552
556
|
status: AgentStatus.OK,
|
|
553
|
-
prompt: `## Feedback Recorded\n\
|
|
557
|
+
prompt: `## Feedback Recorded\n\nSuccess: ${success}\nSignals: 1`,
|
|
554
558
|
analysis: makeAnalysis({
|
|
555
559
|
technique: "feedback",
|
|
556
|
-
rationale: `
|
|
560
|
+
rationale: `success=${success}`,
|
|
557
561
|
independence: "n/a",
|
|
558
562
|
cognitive_load: "low",
|
|
559
563
|
}),
|
|
@@ -569,7 +573,7 @@ export class LoopForgeEngine {
|
|
|
569
573
|
* P0–P2: Also persists discovered_constraints, objective_refinement,
|
|
570
574
|
* and emerged_subtasks for the compiler to consume next round.
|
|
571
575
|
* Call this BEFORE invokeLoopCompile for the next round so that
|
|
572
|
-
* hydrateLoopContext picks up the latest
|
|
576
|
+
* hydrateLoopContext picks up the latest success flags. */
|
|
573
577
|
autoFeedback(selfEval, loopId, round, task) {
|
|
574
578
|
this.ensureInit({ task, mode: Mode.FEEDBACK, vault_config: makeVaultConfig(), feedback: null, skill_name: null, task_id: null });
|
|
575
579
|
const fb = makeExecutionFeedback({
|
|
@@ -578,11 +582,6 @@ export class LoopForgeEngine {
|
|
|
578
582
|
constraint_violations: selfEval.constraint_violations,
|
|
579
583
|
manual_fixes_needed: "",
|
|
580
584
|
});
|
|
581
|
-
const quality = scoreQuality({
|
|
582
|
-
success: fb.success,
|
|
583
|
-
constraint_violations: fb.constraint_violations,
|
|
584
|
-
manual_fixes_needed: fb.manual_fixes_needed,
|
|
585
|
-
});
|
|
586
585
|
const taskId = `loop:${loopId}:r${round}:feedback`;
|
|
587
586
|
// Multi-agent: Merge sub-agent discovered constraints into the main constraint flow
|
|
588
587
|
const subDiscovered = (selfEval.worker_results ?? [])
|
|
@@ -594,7 +593,7 @@ export class LoopForgeEngine {
|
|
|
594
593
|
const signal = {
|
|
595
594
|
task_id: taskId,
|
|
596
595
|
task_type: task.slice(0, 80),
|
|
597
|
-
|
|
596
|
+
success: fb.success,
|
|
598
597
|
skill_used: "",
|
|
599
598
|
violations: selfEval.constraint_violations,
|
|
600
599
|
manual_fixes: "",
|
|
@@ -630,9 +629,9 @@ export class LoopForgeEngine {
|
|
|
630
629
|
}
|
|
631
630
|
// Update state
|
|
632
631
|
this.state.call_count++;
|
|
633
|
-
this.state.
|
|
634
|
-
if (this.state.
|
|
635
|
-
this.state.
|
|
632
|
+
this.state.success_trend.push(fb.success);
|
|
633
|
+
if (this.state.success_trend.length > 20) {
|
|
634
|
+
this.state.success_trend = this.state.success_trend.slice(-20);
|
|
636
635
|
}
|
|
637
636
|
// Circuit breaker
|
|
638
637
|
if (this.shouldBreak()) {
|
|
@@ -644,10 +643,10 @@ export class LoopForgeEngine {
|
|
|
644
643
|
logEvent("round_complete", {
|
|
645
644
|
loopId,
|
|
646
645
|
round,
|
|
647
|
-
|
|
646
|
+
success: fb.success,
|
|
648
647
|
technique: this.state?.last_technique ?? "feedback",
|
|
649
648
|
});
|
|
650
|
-
return
|
|
649
|
+
return fb.success;
|
|
651
650
|
}
|
|
652
651
|
// ═══════════════════════════════════════════════════════════════════════
|
|
653
652
|
// Loop Compile (public, primary)
|
|
@@ -670,53 +669,23 @@ export class LoopForgeEngine {
|
|
|
670
669
|
force_level: extras.force_level ?? "auto",
|
|
671
670
|
health_check_interval: extras.health_check_interval ?? 1,
|
|
672
671
|
external_context: extras.external_context ?? "",
|
|
672
|
+
max_rounds: extras.max_rounds ?? undefined,
|
|
673
673
|
});
|
|
674
674
|
// Convert last_round_result if present
|
|
675
675
|
const lastRR = extras.last_round_result;
|
|
676
676
|
if (lastRR) {
|
|
677
677
|
if (typeof lastRR === "object" && !Array.isArray(lastRR)) {
|
|
678
678
|
const rr = lastRR;
|
|
679
|
-
// Parse P4 execution evidence
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
const testResults = rawEvidence.test_results;
|
|
684
|
-
executionEvidence = makeExecutionEvidence({
|
|
685
|
-
files_changed: Array.isArray(rawEvidence.files_changed)
|
|
686
|
-
? rawEvidence.files_changed.filter((v) => typeof v === "string")
|
|
687
|
-
: [],
|
|
688
|
-
test_results: testResults && typeof testResults.passed === "number"
|
|
689
|
-
? {
|
|
690
|
-
passed: testResults.passed,
|
|
691
|
-
failed: testResults.failed ?? 0,
|
|
692
|
-
skipped: testResults.skipped ?? 0,
|
|
693
|
-
}
|
|
694
|
-
: null,
|
|
695
|
-
success_criteria_met: Array.isArray(rawEvidence.success_criteria_met)
|
|
696
|
-
? rawEvidence.success_criteria_met.filter((v) => typeof v === "string")
|
|
697
|
-
: [],
|
|
698
|
-
success_criteria_remaining: Array.isArray(rawEvidence.success_criteria_remaining)
|
|
699
|
-
? rawEvidence.success_criteria_remaining.filter((v) => typeof v === "string")
|
|
700
|
-
: [],
|
|
701
|
-
progress_estimate: typeof rawEvidence.progress_estimate === "number"
|
|
702
|
-
? Math.max(0, Math.min(1, rawEvidence.progress_estimate))
|
|
703
|
-
: 0.0,
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
// Parse P5 revised_success_criteria
|
|
707
|
-
const revisedCriteria = Array.isArray(rr.revised_success_criteria)
|
|
708
|
-
? rr.revised_success_criteria
|
|
709
|
-
.filter((v) => typeof v === "object" && v !== null &&
|
|
710
|
-
typeof v.old === "string" && typeof v.new === "string")
|
|
711
|
-
.map((v) => ({ old: v.old, new: v.new }))
|
|
712
|
-
: [];
|
|
679
|
+
// Parse P4 execution evidence (shared helper)
|
|
680
|
+
const executionEvidence = parseExecutionEvidence(rr.execution_evidence);
|
|
681
|
+
// Parse P5 revised_success_criteria (shared parser)
|
|
682
|
+
const revisedCriteria = parseCriterionRevisions(rr.revised_success_criteria);
|
|
713
683
|
lcr.last_round_result = makeLoopRoundResult({
|
|
714
684
|
round: rr.round ?? 0,
|
|
715
685
|
success: rr.success ?? false,
|
|
716
686
|
output_summary: rr.output_summary ?? "",
|
|
717
687
|
constraint_violations: rr.constraint_violations ?? [],
|
|
718
688
|
manual_fixes_needed: rr.manual_fixes_needed ?? "",
|
|
719
|
-
quality_score: rr.quality_score ?? 0,
|
|
720
689
|
// P0–P2: Cognitive evolution fields
|
|
721
690
|
discovered_constraints: Array.isArray(rr.discovered_constraints)
|
|
722
691
|
? rr.discovered_constraints.filter((v) => typeof v === "string")
|
|
@@ -737,21 +706,11 @@ export class LoopForgeEngine {
|
|
|
737
706
|
wrong_assumptions: Array.isArray(rr.wrong_assumptions)
|
|
738
707
|
? rr.wrong_assumptions.filter((v) => typeof v === "string")
|
|
739
708
|
: [],
|
|
740
|
-
//
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
agentId: v.agentId,
|
|
746
|
-
subAgentType: typeof v.subAgentType === "string" ? v.subAgentType : "general-purpose",
|
|
747
|
-
subTask: v.subTask,
|
|
748
|
-
resultSummary: typeof v.resultSummary === "string" ? v.resultSummary : "",
|
|
749
|
-
success: typeof v.success === "boolean" ? v.success : false,
|
|
750
|
-
discoveredConstraints: Array.isArray(v.discoveredConstraints)
|
|
751
|
-
? v.discoveredConstraints
|
|
752
|
-
: [],
|
|
753
|
-
}))
|
|
754
|
-
: [],
|
|
709
|
+
// v1.10: Checkpoint boundary
|
|
710
|
+
compression_checkpoint: typeof rr.compression_checkpoint === "boolean" ? rr.compression_checkpoint : false,
|
|
711
|
+
checkpoint_label: typeof rr.checkpoint_label === "string" ? rr.checkpoint_label : "",
|
|
712
|
+
// Multi-agent: Worker delegation results (shared parser)
|
|
713
|
+
worker_results: parseWorkerResults(rr.worker_results),
|
|
755
714
|
});
|
|
756
715
|
}
|
|
757
716
|
}
|
|
@@ -788,15 +747,10 @@ export class LoopForgeEngine {
|
|
|
788
747
|
},
|
|
789
748
|
};
|
|
790
749
|
}
|
|
791
|
-
// Build prompt text from response
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
`**Loop ID**: ${response.loop_id}`,
|
|
796
|
-
`**Goal ID**: ${response.goal_id}`,
|
|
797
|
-
"",
|
|
798
|
-
response.prompt,
|
|
799
|
-
];
|
|
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];
|
|
800
754
|
if (response.warnings.length) {
|
|
801
755
|
promptLines.push("");
|
|
802
756
|
promptLines.push("### Warnings");
|
|
@@ -816,8 +770,8 @@ export class LoopForgeEngine {
|
|
|
816
770
|
const hasError = verificationFlags.some((f) => f.severity === "error");
|
|
817
771
|
if (hasError) {
|
|
818
772
|
promptLines.push("");
|
|
819
|
-
promptLines.push("**Gate Verdict: CONTRADICTED** — this round's
|
|
820
|
-
"from the
|
|
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.");
|
|
821
775
|
}
|
|
822
776
|
}
|
|
823
777
|
if (response.loop_health) {
|
|
@@ -865,6 +819,7 @@ export class LoopForgeEngine {
|
|
|
865
819
|
reference_file: response.reference_file,
|
|
866
820
|
}),
|
|
867
821
|
error: null,
|
|
822
|
+
state_file_content: response.state_file_content,
|
|
868
823
|
},
|
|
869
824
|
};
|
|
870
825
|
}
|
|
@@ -876,17 +831,18 @@ export class LoopForgeEngine {
|
|
|
876
831
|
return false;
|
|
877
832
|
const policy = getPolicy();
|
|
878
833
|
const maxCB = policy.engine.max_circuit_breaker;
|
|
879
|
-
if (this.state.
|
|
834
|
+
if (this.state.success_trend.length < maxCB)
|
|
880
835
|
return false;
|
|
881
|
-
const recent = this.state.
|
|
882
|
-
|
|
883
|
-
|
|
836
|
+
const recent = this.state.success_trend.slice(-maxCB);
|
|
837
|
+
// Trip only when all recent rounds are failures (no false-positives on all-success)
|
|
838
|
+
const allFailed = recent.every((v) => v === false);
|
|
839
|
+
if (allFailed) {
|
|
884
840
|
logEvent("circuit_breaker", {
|
|
885
841
|
trend: recent,
|
|
886
|
-
totalRounds: this.state.
|
|
842
|
+
totalRounds: this.state.success_trend.length,
|
|
887
843
|
});
|
|
888
844
|
}
|
|
889
|
-
return
|
|
845
|
+
return allFailed;
|
|
890
846
|
}
|
|
891
847
|
}
|
|
892
848
|
// ═══════════════════════════════════════════════════════════════════════════
|