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.
Files changed (193) hide show
  1. package/README.md +172 -95
  2. package/dist/backends/interface.d.ts +5 -10
  3. package/dist/backends/interface.d.ts.map +1 -1
  4. package/dist/backends/interface.js +3 -4
  5. package/dist/backends/interface.js.map +1 -1
  6. package/dist/canonical-state.d.ts +53 -0
  7. package/dist/canonical-state.d.ts.map +1 -0
  8. package/dist/canonical-state.js +161 -0
  9. package/dist/canonical-state.js.map +1 -0
  10. package/dist/cli.d.ts +4 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +227 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/enforcement-gate.d.ts.map +1 -1
  15. package/dist/enforcement-gate.js +28 -10
  16. package/dist/enforcement-gate.js.map +1 -1
  17. package/dist/engine.d.ts +9 -28
  18. package/dist/engine.d.ts.map +1 -1
  19. package/dist/engine.js +135 -361
  20. package/dist/engine.js.map +1 -1
  21. package/dist/evidence-provider.d.ts +146 -0
  22. package/dist/evidence-provider.d.ts.map +1 -0
  23. package/dist/evidence-provider.js +512 -0
  24. package/dist/evidence-provider.js.map +1 -0
  25. package/dist/index.d.ts +19 -13
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +15 -12
  28. package/dist/index.js.map +1 -1
  29. package/dist/init.d.ts +15 -0
  30. package/dist/init.d.ts.map +1 -0
  31. package/dist/init.js +49 -0
  32. package/dist/init.js.map +1 -0
  33. package/dist/interop.d.ts +35 -0
  34. package/dist/interop.d.ts.map +1 -0
  35. package/dist/interop.js +34 -0
  36. package/dist/interop.js.map +1 -0
  37. package/dist/loop-compiler.d.ts +14 -64
  38. package/dist/loop-compiler.d.ts.map +1 -1
  39. package/dist/loop-compiler.js +305 -2206
  40. package/dist/loop-compiler.js.map +1 -1
  41. package/dist/loop-extras-parser.d.ts +82 -0
  42. package/dist/loop-extras-parser.d.ts.map +1 -0
  43. package/dist/loop-extras-parser.js +217 -0
  44. package/dist/loop-extras-parser.js.map +1 -0
  45. package/dist/loop-store.d.ts +104 -0
  46. package/dist/loop-store.d.ts.map +1 -0
  47. package/dist/loop-store.js +421 -0
  48. package/dist/loop-store.js.map +1 -0
  49. package/dist/mcp/server.d.ts +9 -6
  50. package/dist/mcp/server.d.ts.map +1 -1
  51. package/dist/mcp/server.js +109 -73
  52. package/dist/mcp/server.js.map +1 -1
  53. package/dist/mcp/session.d.ts +102 -21
  54. package/dist/mcp/session.d.ts.map +1 -1
  55. package/dist/mcp/session.js +795 -400
  56. package/dist/mcp/session.js.map +1 -1
  57. package/dist/mcp/tools.d.ts +18 -268
  58. package/dist/mcp/tools.d.ts.map +1 -1
  59. package/dist/mcp/tools.js +226 -12
  60. package/dist/mcp/tools.js.map +1 -1
  61. package/dist/observability.d.ts +36 -16
  62. package/dist/observability.d.ts.map +1 -1
  63. package/dist/observability.js +82 -23
  64. package/dist/observability.js.map +1 -1
  65. package/dist/policy-metrics.d.ts +46 -0
  66. package/dist/policy-metrics.d.ts.map +1 -0
  67. package/dist/policy-metrics.js +146 -0
  68. package/dist/policy-metrics.js.map +1 -0
  69. package/dist/policy.d.ts +52 -147
  70. package/dist/policy.d.ts.map +1 -1
  71. package/dist/policy.js +118 -191
  72. package/dist/policy.js.map +1 -1
  73. package/dist/prompt-assembler.d.ts +30 -0
  74. package/dist/prompt-assembler.d.ts.map +1 -0
  75. package/dist/prompt-assembler.js +278 -0
  76. package/dist/prompt-assembler.js.map +1 -0
  77. package/dist/prompt-policy.d.ts +29 -0
  78. package/dist/prompt-policy.d.ts.map +1 -0
  79. package/dist/prompt-policy.js +51 -0
  80. package/dist/prompt-policy.js.map +1 -0
  81. package/dist/protocol.d.ts +75 -152
  82. package/dist/protocol.d.ts.map +1 -1
  83. package/dist/protocol.js +11 -53
  84. package/dist/protocol.js.map +1 -1
  85. package/dist/replay.d.ts.map +1 -1
  86. package/dist/replay.js +1 -27
  87. package/dist/replay.js.map +1 -1
  88. package/dist/round-coordinator.d.ts +90 -0
  89. package/dist/round-coordinator.d.ts.map +1 -0
  90. package/dist/round-coordinator.js +186 -0
  91. package/dist/round-coordinator.js.map +1 -0
  92. package/dist/round-driver.d.ts +53 -0
  93. package/dist/round-driver.d.ts.map +1 -0
  94. package/dist/round-driver.js +101 -0
  95. package/dist/round-driver.js.map +1 -0
  96. package/dist/round-transaction.d.ts +70 -0
  97. package/dist/round-transaction.d.ts.map +1 -0
  98. package/dist/round-transaction.js +290 -0
  99. package/dist/round-transaction.js.map +1 -0
  100. package/dist/self-eval.d.ts +26 -0
  101. package/dist/self-eval.d.ts.map +1 -0
  102. package/dist/self-eval.js +154 -0
  103. package/dist/self-eval.js.map +1 -0
  104. package/dist/storage.d.ts +39 -0
  105. package/dist/storage.d.ts.map +1 -0
  106. package/dist/storage.js +193 -0
  107. package/dist/storage.js.map +1 -0
  108. package/dist/verification-gate.d.ts +31 -7
  109. package/dist/verification-gate.d.ts.map +1 -1
  110. package/dist/verification-gate.js +250 -7
  111. package/dist/verification-gate.js.map +1 -1
  112. package/loop_policy.json +19 -53
  113. package/package.json +13 -10
  114. package/skills/perception/SKILL.md +80 -267
  115. package/dist/backends/fs.d.ts +0 -36
  116. package/dist/backends/fs.d.ts.map +0 -1
  117. package/dist/backends/fs.js +0 -362
  118. package/dist/backends/fs.js.map +0 -1
  119. package/dist/builder.d.ts +0 -19
  120. package/dist/builder.d.ts.map +0 -1
  121. package/dist/builder.js +0 -215
  122. package/dist/builder.js.map +0 -1
  123. package/dist/mcp-server.d.ts +0 -12
  124. package/dist/mcp-server.d.ts.map +0 -1
  125. package/dist/mcp-server.js +0 -14
  126. package/dist/mcp-server.js.map +0 -1
  127. package/dist/memory-bridge.d.ts +0 -47
  128. package/dist/memory-bridge.d.ts.map +0 -1
  129. package/dist/memory-bridge.js +0 -290
  130. package/dist/memory-bridge.js.map +0 -1
  131. package/dist/runtime.d.ts +0 -80
  132. package/dist/runtime.d.ts.map +0 -1
  133. package/dist/runtime.js +0 -699
  134. package/dist/runtime.js.map +0 -1
  135. package/dist/tests/_helpers.d.ts +0 -25
  136. package/dist/tests/_helpers.d.ts.map +0 -1
  137. package/dist/tests/_helpers.js +0 -47
  138. package/dist/tests/_helpers.js.map +0 -1
  139. package/dist/tests/builder.test.d.ts +0 -2
  140. package/dist/tests/builder.test.d.ts.map +0 -1
  141. package/dist/tests/builder.test.js +0 -112
  142. package/dist/tests/builder.test.js.map +0 -1
  143. package/dist/tests/enforcement-gate.test.d.ts +0 -2
  144. package/dist/tests/enforcement-gate.test.d.ts.map +0 -1
  145. package/dist/tests/enforcement-gate.test.js +0 -328
  146. package/dist/tests/enforcement-gate.test.js.map +0 -1
  147. package/dist/tests/engine.test.d.ts +0 -2
  148. package/dist/tests/engine.test.d.ts.map +0 -1
  149. package/dist/tests/engine.test.js +0 -375
  150. package/dist/tests/engine.test.js.map +0 -1
  151. package/dist/tests/generate-schema.test.d.ts +0 -8
  152. package/dist/tests/generate-schema.test.d.ts.map +0 -1
  153. package/dist/tests/generate-schema.test.js +0 -199
  154. package/dist/tests/generate-schema.test.js.map +0 -1
  155. package/dist/tests/loop-compiler.test.d.ts +0 -2
  156. package/dist/tests/loop-compiler.test.d.ts.map +0 -1
  157. package/dist/tests/loop-compiler.test.js +0 -1059
  158. package/dist/tests/loop-compiler.test.js.map +0 -1
  159. package/dist/tests/mcp.test.d.ts +0 -7
  160. package/dist/tests/mcp.test.d.ts.map +0 -1
  161. package/dist/tests/mcp.test.js +0 -505
  162. package/dist/tests/mcp.test.js.map +0 -1
  163. package/dist/tests/memory-bridge.test.d.ts +0 -8
  164. package/dist/tests/memory-bridge.test.d.ts.map +0 -1
  165. package/dist/tests/memory-bridge.test.js +0 -383
  166. package/dist/tests/memory-bridge.test.js.map +0 -1
  167. package/dist/tests/policy.test.d.ts +0 -2
  168. package/dist/tests/policy.test.d.ts.map +0 -1
  169. package/dist/tests/policy.test.js +0 -49
  170. package/dist/tests/policy.test.js.map +0 -1
  171. package/dist/tests/protocol.test.d.ts +0 -2
  172. package/dist/tests/protocol.test.d.ts.map +0 -1
  173. package/dist/tests/protocol.test.js +0 -73
  174. package/dist/tests/protocol.test.js.map +0 -1
  175. package/dist/tests/replay.test.d.ts +0 -2
  176. package/dist/tests/replay.test.d.ts.map +0 -1
  177. package/dist/tests/replay.test.js +0 -222
  178. package/dist/tests/replay.test.js.map +0 -1
  179. package/dist/tests/runtime.test.d.ts +0 -7
  180. package/dist/tests/runtime.test.d.ts.map +0 -1
  181. package/dist/tests/runtime.test.js +0 -337
  182. package/dist/tests/runtime.test.js.map +0 -1
  183. package/dist/tests/verification-gate.test.d.ts +0 -2
  184. package/dist/tests/verification-gate.test.d.ts.map +0 -1
  185. package/dist/tests/verification-gate.test.js +0 -407
  186. package/dist/tests/verification-gate.test.js.map +0 -1
  187. package/skills/prompt-techniques/SKILL.md +0 -52
  188. package/skills/prompt-techniques/references/chain-of-thought.md +0 -207
  189. package/skills/prompt-techniques/references/few-shot.md +0 -174
  190. package/skills/prompt-techniques/references/least-to-most.md +0 -182
  191. package/skills/prompt-techniques/references/step-back.md +0 -160
  192. package/skills/prompt-techniques/references/tree-of-thought.md +0 -191
  193. 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 { FSBackend, readLineageMd } from "./backends/fs.js";
11
- import { AgentStatus, Mode, makeAnalysis, makeExecutionEvidence, makeExecutionFeedback, makeLoopCompileRequest, makeLoopObjective, makeLoopRoundResult, makeSelfEvaluation, makeSessionState, makeTaskId, makeVaultConfig, SELF_EVAL_REGEX, } from "./protocol.js";
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
- // 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
- // ═══════════════════════════════════════════════════════════════════════════
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
- // 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) {
202
- if (backend)
203
- this.backend = backend;
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 FSBackend();
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: `loop:${loopId}:r${round}:delegations`,
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
- // Fallback: scan Markdown files if JSON vault had no matches
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, vault_config: makeVaultConfig(), feedback: null, skill_name: null, task_id: null });
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
- this.persistFeedbackToVault(signal);
616
- this.flushFeedbackBuffer();
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
- // Build LoopCompileRequest from LoopForgeRequest extras
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: extras.loop_id ?? request.task_id ?? "",
661
- round: extras.round ?? 1,
662
- goal_id: extras.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: extras.domain ?? "",
665
- next_task_proposal: extras.next_task_proposal ?? "",
666
- plan_source: extras.plan_source ?? null,
667
- constraints_from_plan: extras.constraints_from_plan ?? [],
668
- new_since_last_round: extras.new_since_last_round ?? "",
669
- force_level: extras.force_level ?? "auto",
670
- health_check_interval: extras.health_check_interval ?? 1,
671
- external_context: extras.external_context ?? "",
672
- max_rounds: extras.max_rounds ?? undefined,
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
- const lastRR = extras.last_round_result;
676
- if (lastRR) {
677
- if (typeof lastRR === "object" && !Array.isArray(lastRR)) {
678
- const rr = lastRR;
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);
683
- lcr.last_round_result = makeLoopRoundResult({
684
- round: rr.round ?? 0,
685
- success: rr.success ?? false,
686
- output_summary: rr.output_summary ?? "",
687
- constraint_violations: rr.constraint_violations ?? [],
688
- manual_fixes_needed: rr.manual_fixes_needed ?? "",
689
- // P0–P2: Cognitive evolution fields
690
- discovered_constraints: Array.isArray(rr.discovered_constraints)
691
- ? rr.discovered_constraints.filter((v) => typeof v === "string")
692
- : [],
693
- objective_refinement: typeof rr.objective_refinement === "string"
694
- ? rr.objective_refinement
695
- : "",
696
- emerged_subtasks: Array.isArray(rr.emerged_subtasks)
697
- ? rr.emerged_subtasks.filter((v) => typeof v === "string")
698
- : [],
699
- // P4: Execution evidence
700
- execution_evidence: executionEvidence,
701
- // P5: Self-correction
702
- retracted_constraints: Array.isArray(rr.retracted_constraints)
703
- ? rr.retracted_constraints.filter((v) => typeof v === "string")
704
- : [],
705
- revised_success_criteria: revisedCriteria,
706
- wrong_assumptions: Array.isArray(rr.wrong_assumptions)
707
- ? rr.wrong_assumptions.filter((v) => typeof v === "string")
708
- : [],
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),
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
- const lo = extras.loop_objective;
719
- if (lo && typeof lo === "object" && !Array.isArray(lo)) {
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
- this.persistLoopLineage(response, lcr);
801
- // Track technique for status queries
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: promptLines.join("\n"),
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(skillsDir = "skills", backend) {
852
- return new LoopForgeEngine(skillsDir, backend);
625
+ export function createEngine(store) {
626
+ return new LoopForgeEngine(store);
853
627
  }
854
628
  //# sourceMappingURL=engine.js.map