loopforge 2.0.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +7 -5
  2. package/dist/backends/interface.d.ts +4 -3
  3. package/dist/backends/interface.d.ts.map +1 -1
  4. package/dist/backends/interface.js +4 -1
  5. package/dist/backends/interface.js.map +1 -1
  6. package/dist/canonical-state.d.ts +23 -2
  7. package/dist/canonical-state.d.ts.map +1 -1
  8. package/dist/canonical-state.js +90 -6
  9. package/dist/canonical-state.js.map +1 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +6 -2
  12. package/dist/cli.js.map +1 -1
  13. package/dist/enforcement-gate.d.ts +1 -1
  14. package/dist/enforcement-gate.d.ts.map +1 -1
  15. package/dist/enforcement-gate.js +86 -7
  16. package/dist/enforcement-gate.js.map +1 -1
  17. package/dist/engine.d.ts +3 -33
  18. package/dist/engine.d.ts.map +1 -1
  19. package/dist/engine.js +127 -343
  20. package/dist/engine.js.map +1 -1
  21. package/dist/evidence-provider.d.ts +38 -5
  22. package/dist/evidence-provider.d.ts.map +1 -1
  23. package/dist/evidence-provider.js +132 -46
  24. package/dist/evidence-provider.js.map +1 -1
  25. package/dist/index.d.ts +10 -12
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +6 -9
  28. package/dist/index.js.map +1 -1
  29. package/dist/interop.d.ts +5 -0
  30. package/dist/interop.d.ts.map +1 -1
  31. package/dist/interop.js +5 -0
  32. package/dist/interop.js.map +1 -1
  33. package/dist/loop-compiler.d.ts.map +1 -1
  34. package/dist/loop-compiler.js +515 -50
  35. package/dist/loop-compiler.js.map +1 -1
  36. package/dist/loop-extras-parser.d.ts +82 -0
  37. package/dist/loop-extras-parser.d.ts.map +1 -0
  38. package/dist/loop-extras-parser.js +217 -0
  39. package/dist/loop-extras-parser.js.map +1 -0
  40. package/dist/loop-store.d.ts +26 -4
  41. package/dist/loop-store.d.ts.map +1 -1
  42. package/dist/loop-store.js +118 -8
  43. package/dist/loop-store.js.map +1 -1
  44. package/dist/mcp/server.js +1 -1
  45. package/dist/mcp/session.d.ts +29 -6
  46. package/dist/mcp/session.d.ts.map +1 -1
  47. package/dist/mcp/session.js +187 -161
  48. package/dist/mcp/session.js.map +1 -1
  49. package/dist/mcp/tools.d.ts.map +1 -1
  50. package/dist/mcp/tools.js +33 -5
  51. package/dist/mcp/tools.js.map +1 -1
  52. package/dist/observability.d.ts +5 -36
  53. package/dist/observability.d.ts.map +1 -1
  54. package/dist/observability.js +9 -79
  55. package/dist/observability.js.map +1 -1
  56. package/dist/policy-metrics.d.ts +1 -0
  57. package/dist/policy-metrics.d.ts.map +1 -1
  58. package/dist/policy-metrics.js +2 -0
  59. package/dist/policy-metrics.js.map +1 -1
  60. package/dist/policy.d.ts +58 -8
  61. package/dist/policy.d.ts.map +1 -1
  62. package/dist/policy.js +25 -12
  63. package/dist/policy.js.map +1 -1
  64. package/dist/prompt-assembler.d.ts.map +1 -1
  65. package/dist/prompt-assembler.js +129 -4
  66. package/dist/prompt-assembler.js.map +1 -1
  67. package/dist/protocol.d.ts +118 -87
  68. package/dist/protocol.d.ts.map +1 -1
  69. package/dist/protocol.js +40 -30
  70. package/dist/protocol.js.map +1 -1
  71. package/dist/replay.d.ts +0 -1
  72. package/dist/replay.d.ts.map +1 -1
  73. package/dist/replay.js +1 -87
  74. package/dist/replay.js.map +1 -1
  75. package/dist/round-coordinator.d.ts +4 -6
  76. package/dist/round-coordinator.d.ts.map +1 -1
  77. package/dist/round-coordinator.js +75 -80
  78. package/dist/round-coordinator.js.map +1 -1
  79. package/dist/round-driver.d.ts +1 -0
  80. package/dist/round-driver.d.ts.map +1 -1
  81. package/dist/round-driver.js +2 -0
  82. package/dist/round-driver.js.map +1 -1
  83. package/dist/round-transaction.d.ts.map +1 -1
  84. package/dist/round-transaction.js +2 -22
  85. package/dist/round-transaction.js.map +1 -1
  86. package/dist/run.d.ts +51 -0
  87. package/dist/run.d.ts.map +1 -0
  88. package/dist/run.js +200 -0
  89. package/dist/run.js.map +1 -0
  90. package/dist/self-eval.d.ts +23 -0
  91. package/dist/self-eval.d.ts.map +1 -0
  92. package/dist/self-eval.js +151 -0
  93. package/dist/self-eval.js.map +1 -0
  94. package/dist/storage.d.ts +3 -2
  95. package/dist/storage.d.ts.map +1 -1
  96. package/dist/storage.js +70 -68
  97. package/dist/storage.js.map +1 -1
  98. package/dist/token-utils.d.ts +22 -0
  99. package/dist/token-utils.d.ts.map +1 -0
  100. package/dist/token-utils.js +46 -0
  101. package/dist/token-utils.js.map +1 -0
  102. package/dist/verification-gate.d.ts +17 -23
  103. package/dist/verification-gate.d.ts.map +1 -1
  104. package/dist/verification-gate.js +241 -135
  105. package/dist/verification-gate.js.map +1 -1
  106. package/loop_policy.json +21 -12
  107. package/package.json +2 -2
  108. package/dist/runtime.d.ts +0 -94
  109. package/dist/runtime.d.ts.map +0 -1
  110. package/dist/runtime.js +0 -718
  111. package/dist/runtime.js.map +0 -1
package/dist/engine.d.ts CHANGED
@@ -7,30 +7,8 @@
7
7
  */
8
8
  import type { VaultBackend } from "./backends/interface.js";
9
9
  import type { LoopStore } from "./loop-store.js";
10
- import { type AgentLoopResult, type CriterionRevision, type ExecutionEvidence, type LoopForgeRequest, type SelfEvaluation, type SessionState } from "./protocol.js";
11
- /** Parse ExecutionEvidence from a raw JSON object. Shared by buildSelfEvaluation
12
- * and invokeLoopCompile — both parse the same execution_evidence shape. */
13
- export declare function parseExecutionEvidence(raw: Record<string, unknown> | undefined | null): ExecutionEvidence | undefined;
14
- /** Parse CriterionRevision[] from a raw JSON array. Shared by buildSelfEvaluation
15
- * and invokeLoopCompile — both parse the same revised_success_criteria shape. */
16
- export declare function parseCriterionRevisions(raw: unknown): CriterionRevision[];
17
- /** Parse WorkerResult[] from a raw JSON array. Shared by buildSelfEvaluation
18
- * and invokeLoopCompile — both parse the same worker_results shape. */
19
- export declare function parseWorkerResults(raw: unknown): import("./protocol.js").WorkerResult[];
20
- /** Extract a structured SelfEvaluation from agent output text.
21
- * Returns null if no valid self-eval block is found.
22
- * The agent is instructed to output JSON between the delimiters. */
23
- export declare function extractSelfEvaluation(text: string): SelfEvaluation | null;
24
- /** Build a SelfEvaluation from a parsed JSON object.
25
- * Lenient parsing: missing optional fields get sensible defaults.
26
- * Used by extractSelfEvaluation() (regex path) and MCP tool handler
27
- * (structured evaluation parameter path). */
28
- export declare function buildSelfEvaluation(raw: Record<string, unknown>): SelfEvaluation;
29
- /** Fallback heuristic when structured self-eval extraction fails.
30
- * Scans agent output for completion and error signals.
31
- * Returns a low-confidence SelfEvaluation — the autonomous runner
32
- * may choose to warn the user or continue cautiously. */
33
- export declare function heuristicSelfEvaluation(text: string): SelfEvaluation | null;
10
+ import { type AgentLoopResult, type LoopForgeRequest, type SelfEvaluation, type SessionState } from "./protocol.js";
11
+ export { parseExecutionEvidence, parseCriterionRevisions, parseWorkerResults, extractSelfEvaluation, buildSelfEvaluation, } from "./self-eval.js";
34
12
  /** A single sub-agent delegation record (v1.9 — AgentTool mode). */
35
13
  export interface DelegationEntry {
36
14
  index: number;
@@ -43,19 +21,12 @@ export interface DelegationEntry {
43
21
  }
44
22
  export interface EngineMetrics {
45
23
  vaultWriteErrors: number;
46
- vaultWriteTimeouts: number;
47
- vaultWriteBytes: number;
48
- silentAnalysisErrors: number;
49
- hydrateCacheMisses: number;
50
- feedbackBufferFlushes: number;
51
- feedbackBufferMaxSize: number;
52
24
  sessionStart: number;
53
25
  }
54
26
  export declare class LoopForgeEngine {
55
27
  state: SessionState | null;
56
28
  private backend;
57
29
  private metrics;
58
- private feedbackWriteBuffer;
59
30
  lastTask: string | null;
60
31
  constructor(storeOrBackend?: LoopStore | VaultBackend);
61
32
  private resolveBackend;
@@ -65,7 +36,7 @@ export declare class LoopForgeEngine {
65
36
  getMetrics(): EngineMetrics;
66
37
  private ensureInit;
67
38
  private persistFeedbackToVault;
68
- flushFeedbackBuffer(): number;
39
+ private buildFeedbackEntry;
69
40
  private persistLoopLineage;
70
41
  /** Record sub-agent delegations for this round into the vault.
71
42
  * Written as a lightweight journal entry so the main agent's rolling
@@ -83,7 +54,6 @@ export declare class LoopForgeEngine {
83
54
  invokeLoopCompile(request: LoopForgeRequest, hydrateResults?: Record<string, unknown> | null, options?: {
84
55
  persistLineage?: boolean;
85
56
  }): AgentLoopResult;
86
- shouldBreak(): boolean;
87
57
  }
88
58
  export declare function createEngine(store?: LoopStore): LoopForgeEngine;
89
59
  //# sourceMappingURL=engine.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,yBAAyB,CAAC;AAExE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EASL,KAAK,eAAe,EAIpB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,YAAY,EAElB,MAAM,eAAe,CAAC;AAWvB,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AAMxB,oEAAoE;AACpE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,qBAAqB,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB;AAaD,qBAAa,eAAe;IAC1B,KAAK,EAAE,YAAY,GAAG,IAAI,CAAQ;IAClC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAA8B;IAC7C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;gBAEnB,cAAc,CAAC,EAAE,SAAS,GAAG,YAAY;IAQrD,OAAO,CAAC,cAAc;IAStB,iFAAiF;IACjF,UAAU,IAAI,YAAY;IAI1B,6EAA6E;IAC7E,UAAU,IAAI,aAAa;IAO3B,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,sBAAsB;IAgB9B,OAAO,CAAC,kBAAkB;IAqC1B,OAAO,CAAC,kBAAkB;IA8D1B;;yEAEqE;IACrE,gBAAgB,CACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,eAAe,EAAE,GACzB,IAAI;IAwCP,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IA2DlE,cAAc,CACZ,OAAO,EAAE,gBAAgB,EACzB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAC/C,eAAe;IAyElB;;;;;gEAK4D;IAC5D,YAAY,CACV,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACzC,OAAO;IAqHV,iBAAiB,CACf,OAAO,EAAE,gBAAgB,EACzB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC/C,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO,GACzC,eAAe;CAiJnB;AAMD,wBAAgB,YAAY,CAC1B,KAAK,CAAC,EAAE,SAAS,GAChB,eAAe,CAEjB"}
package/dist/engine.js CHANGED
@@ -8,184 +8,16 @@
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { getPolicy } from "./policy.js";
10
10
  import { FileLoopStore, LoopStoreBackend } from "./loop-store.js";
11
- import { AgentStatus, Mode, makeExecutionEvidence, makeExecutionFeedback, makeLoopCompileRequest, makeLoopObjective, makeLoopRoundResult, makeSelfEvaluation, makeSessionState, makeTaskId, SELF_EVAL_REGEX, } from "./protocol.js";
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
- // v1.16: Agent's declared next action
156
- next_action: typeof raw.next_action === "string" ? raw.next_action : undefined,
157
- });
158
- }
159
- /** Fallback heuristic when structured self-eval extraction fails.
160
- * Scans agent output for completion and error signals.
161
- * Returns a low-confidence SelfEvaluation — the autonomous runner
162
- * may choose to warn the user or continue cautiously. */
163
- export function heuristicSelfEvaluation(text) {
164
- const lower = text.toLowerCase();
165
- const hasError = /error|failed|exception|cannot|unable|失败|错误|异常/.test(lower);
166
- const hasCompletion = /done|complete|finished|完成|成功/.test(lower);
167
- const hasRemaining = /remaining|continue|still need|next|todo|剩余|继续|下一步/.test(lower);
168
- // Extract a reasonable summary from the last meaningful paragraph
169
- const paragraphs = text.split(/\n\n+/).filter((p) => p.trim().length > 30);
170
- const summary = paragraphs.length > 0
171
- ? paragraphs[paragraphs.length - 1].trim().slice(0, 300)
172
- : text.trim().slice(0, 300);
173
- return makeSelfEvaluation({
174
- success: !hasError && (hasCompletion || !hasRemaining),
175
- output_summary: summary || "[heuristic fallback — could not parse structured self-eval]",
176
- constraint_violations: [],
177
- should_continue: hasRemaining && !hasError,
178
- });
179
- }
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, } from "./self-eval.js";
180
18
  function makeEngineMetrics() {
181
19
  return {
182
20
  vaultWriteErrors: 0,
183
- vaultWriteTimeouts: 0,
184
- vaultWriteBytes: 0,
185
- silentAnalysisErrors: 0,
186
- hydrateCacheMisses: 0,
187
- feedbackBufferFlushes: 0,
188
- feedbackBufferMaxSize: 0,
189
21
  sessionStart: Date.now(),
190
22
  };
191
23
  }
@@ -196,7 +28,6 @@ export class LoopForgeEngine {
196
28
  state = null;
197
29
  backend = null;
198
30
  metrics = null;
199
- feedbackWriteBuffer = [];
200
31
  lastTask = null;
201
32
  constructor(storeOrBackend) {
202
33
  if (storeOrBackend) {
@@ -237,72 +68,53 @@ export class LoopForgeEngine {
237
68
  if (this.metrics === null) {
238
69
  this.metrics = makeEngineMetrics();
239
70
  }
240
- this.feedbackWriteBuffer.push(signal);
241
- const bufLen = this.feedbackWriteBuffer.length;
242
- if (bufLen > this.metrics.feedbackBufferMaxSize) {
243
- this.metrics.feedbackBufferMaxSize = bufLen;
71
+ // v2.6: write directly — the buffer was always flushed immediately
72
+ // (autoFeedback calls persistFeedbackToVault then immediately flushFeedbackBuffer).
73
+ const entry = this.buildFeedbackEntry(signal);
74
+ if (!entry)
75
+ return;
76
+ try {
77
+ this.resolveBackend().appendEntry(entry);
244
78
  }
245
- const policy = getPolicy();
246
- if (bufLen >= policy.engine.feedback_flush_interval) {
247
- this.flushFeedbackBuffer();
79
+ catch {
80
+ if (this.metrics)
81
+ this.metrics.vaultWriteErrors++;
82
+ logEvent("vault_write_error", { error: "feedback_persist" });
248
83
  }
249
84
  }
250
- flushFeedbackBuffer() {
251
- if (!this.feedbackWriteBuffer.length)
252
- return 0;
253
- const records = this.feedbackWriteBuffer.splice(0);
254
- if (this.metrics)
255
- this.metrics.feedbackBufferFlushes++;
256
- const now = new Date().toISOString().replace(/\.\d+Z$/, "");
257
- const entries = [];
258
- for (const signal of records) {
259
- try {
260
- const entry = {
261
- id: randomUUID(),
262
- task_id: signal.task_id ?? "feedback",
263
- version_tag: "v1",
264
- is_active: true,
265
- timestamp: now,
266
- user_intent: String(signal.task_type ?? "").slice(0, 200),
85
+ buildFeedbackEntry(signal) {
86
+ try {
87
+ return {
88
+ id: randomUUID(),
89
+ task_id: signal.task_id ?? "feedback",
90
+ version_tag: "v1",
91
+ is_active: true,
92
+ timestamp: new Date().toISOString().replace(/\.\d+Z$/, ""),
93
+ user_intent: String(signal.task_type ?? "").slice(0, 200),
94
+ success: signal.success ?? false,
95
+ execution_feedback: JSON.stringify({
267
96
  success: signal.success ?? false,
268
- execution_feedback: JSON.stringify({
269
- success: signal.success ?? false,
270
- status: signal.success
271
- ? "success"
272
- : "partial",
273
- constraint_compliance: {
274
- all_hard_constraints_met: !Array.isArray(signal.violations) || signal.violations.length === 0,
275
- violations: signal.violations ?? [],
276
- },
277
- output_summary: signal.task_type ?? "",
278
- improvement_notes: signal.manual_fixes ?? "",
279
- }),
280
- task_type: signal.task_type ?? "",
281
- tags: signal.skill_used ? [signal.skill_used] : [],
282
- skill_used: signal.skill_used ?? "",
283
- loop_id: signal.loop_id,
284
- loop_lineage: signal.loop_lineage ?? {},
285
- };
286
- entries.push(entry);
287
- }
288
- catch {
289
- if (this.metrics)
290
- this.metrics.vaultWriteErrors++;
291
- logEvent("vault_write_error", { error: "feedback_entry_build" });
292
- }
97
+ status: signal.success ? "success" : "partial",
98
+ constraint_compliance: {
99
+ all_hard_constraints_met: !Array.isArray(signal.violations) || signal.violations.length === 0,
100
+ violations: signal.violations ?? [],
101
+ },
102
+ output_summary: signal.task_type ?? "",
103
+ improvement_notes: signal.manual_fixes ?? "",
104
+ }),
105
+ task_type: signal.task_type ?? "",
106
+ tags: signal.skill_used ? [signal.skill_used] : [],
107
+ skill_used: signal.skill_used ?? "",
108
+ loop_id: signal.loop_id,
109
+ loop_lineage: signal.loop_lineage ?? {},
110
+ };
293
111
  }
294
- if (entries.length > 0) {
295
- try {
296
- this.resolveBackend().appendEntries(entries);
297
- }
298
- catch {
299
- if (this.metrics)
300
- this.metrics.vaultWriteErrors += entries.length;
301
- logEvent("vault_write_error", { error: "feedback_append_entries", count: entries.length });
302
- return 0;
303
- }
112
+ catch {
113
+ if (this.metrics)
114
+ this.metrics.vaultWriteErrors++;
115
+ logEvent("vault_write_error", { error: "feedback_entry_build" });
116
+ return null;
304
117
  }
305
- return entries.length;
306
118
  }
307
119
  // ═══════════════════════════════════════════════════════════════════════
308
120
  // Lineage persistence
@@ -490,21 +302,12 @@ export class LoopForgeEngine {
490
302
  round: loopRound,
491
303
  };
492
304
  this.persistFeedbackToVault(signal);
493
- // Flush immediately so next compile cycle sees success flags
494
- this.flushFeedbackBuffer();
495
305
  // Update state
496
306
  this.state.call_count++;
497
307
  this.state.success_trend.push(success);
498
308
  if (this.state.success_trend.length > 20) {
499
309
  this.state.success_trend = this.state.success_trend.slice(-20);
500
310
  }
501
- // Circuit breaker
502
- if (this.shouldBreak()) {
503
- this.state.circuit_breaker_count++;
504
- }
505
- else {
506
- this.state.circuit_breaker_count = 0;
507
- }
508
311
  logEvent("round_complete", {
509
312
  success,
510
313
  loopId: loopId ?? "unknown",
@@ -596,7 +399,6 @@ export class LoopForgeEngine {
596
399
  };
597
400
  if (!alreadyCommitted) {
598
401
  this.persistFeedbackToVault(signal);
599
- this.flushFeedbackBuffer();
600
402
  if (transactionId && !transactionPersisted()) {
601
403
  throw new Error(`Round feedback commit failed: ${transactionId}`);
602
404
  }
@@ -624,13 +426,9 @@ export class LoopForgeEngine {
624
426
  if (this.state.success_trend.length > 20) {
625
427
  this.state.success_trend = this.state.success_trend.slice(-20);
626
428
  }
627
- // Circuit breaker
628
- if (this.shouldBreak()) {
629
- this.state.circuit_breaker_count++;
630
- }
631
- else {
632
- this.state.circuit_breaker_count = 0;
633
- }
429
+ // v2.5: Old binary-success circuit breaker removed — stalled loops
430
+ // are now detected by the enforcement gate (R4/R5) using
431
+ // progress_estimate gradients rather than success=true/false count.
634
432
  logEvent("round_complete", {
635
433
  loopId,
636
434
  round,
@@ -644,82 +442,88 @@ export class LoopForgeEngine {
644
442
  invokeLoopCompile(request, hydrateResults, options = {}) {
645
443
  this.ensureInit(request);
646
444
  this.lastTask = request.task;
647
- // Build LoopCompileRequest from LoopForgeRequest extras
445
+ // Parse extras via typed extraction pipeline (loop-extras-parser.ts).
446
+ // Errors are collected but never thrown — the compiler always gets
447
+ // best-effort defaults so a malformed request doesn't crash the engine.
648
448
  const extras = request;
449
+ const { parsed, ctx } = parseLoopExtras(extras, request.task_id ?? "");
450
+ if (ctx.errors.length > 0) {
451
+ logEvent("extras_parse_errors", {
452
+ errors: ctx.errors.map((e) => `${e.field}: ${e.message}`),
453
+ });
454
+ }
649
455
  const lcr = makeLoopCompileRequest({
650
- loop_id: extras.loop_id ?? request.task_id ?? "",
651
- round: extras.round ?? 1,
652
- goal_id: extras.goal_id ?? "",
456
+ loop_id: parsed.loop_id,
457
+ round: parsed.round,
458
+ goal_id: parsed.goal_id,
653
459
  task: request.task,
654
- domain: extras.domain ?? "",
655
- next_task_proposal: extras.next_task_proposal ?? "",
656
- plan_source: extras.plan_source ?? null,
657
- constraints_from_plan: extras.constraints_from_plan ?? [],
658
- new_since_last_round: extras.new_since_last_round ?? "",
659
- force_level: extras.force_level ?? "auto",
660
- health_check_interval: extras.health_check_interval ?? 1,
661
- external_context: extras.external_context ?? "",
662
- max_rounds: extras.max_rounds ?? undefined,
663
- verification_flags: Array.isArray(extras.verification_flags)
664
- ? extras.verification_flags
665
- : [],
666
- attempt: typeof extras.attempt === "number"
667
- ? Math.max(1, Math.trunc(extras.attempt))
668
- : 1,
669
- consecutive_rejections: typeof extras.consecutive_rejections === "number"
670
- ? Math.max(0, Math.trunc(extras.consecutive_rejections))
671
- : 0,
672
- rejection_notice: typeof extras.rejection_notice === "string"
673
- ? extras.rejection_notice
674
- : "",
460
+ domain: parsed.domain,
461
+ next_task_proposal: parsed.next_task_proposal,
462
+ plan_source: parsed.plan_source,
463
+ constraints_from_plan: parsed.constraints_from_plan,
464
+ new_since_last_round: parsed.new_since_last_round,
465
+ force_level: parsed.force_level,
466
+ health_check_interval: parsed.health_check_interval,
467
+ external_context: parsed.external_context,
468
+ max_rounds: parsed.max_rounds,
469
+ verification_flags: parsed.verification_flags,
470
+ attempt: parsed.attempt,
471
+ consecutive_rejections: parsed.consecutive_rejections,
472
+ rejection_notice: parsed.rejection_notice,
675
473
  });
676
- // Convert last_round_result if present
677
- const lastRR = extras.last_round_result;
678
- if (lastRR) {
679
- if (typeof lastRR === "object" && !Array.isArray(lastRR)) {
680
- const rr = lastRR;
681
- // Parse P4 execution evidence (shared helper)
682
- const executionEvidence = parseExecutionEvidence(rr.execution_evidence);
683
- // Parse P5 revised_success_criteria (shared parser)
684
- const revisedCriteria = parseCriterionRevisions(rr.revised_success_criteria);
685
- lcr.last_round_result = makeLoopRoundResult({
686
- round: rr.round ?? 0,
687
- success: rr.success ?? false,
688
- output_summary: rr.output_summary ?? "",
689
- constraint_violations: rr.constraint_violations ?? [],
690
- manual_fixes_needed: rr.manual_fixes_needed ?? "",
691
- // P0–P2: Cognitive evolution fields
692
- discovered_constraints: Array.isArray(rr.discovered_constraints)
693
- ? rr.discovered_constraints.filter((v) => typeof v === "string")
694
- : [],
695
- objective_refinement: typeof rr.objective_refinement === "string"
696
- ? rr.objective_refinement
697
- : "",
698
- emerged_subtasks: Array.isArray(rr.emerged_subtasks)
699
- ? rr.emerged_subtasks.filter((v) => typeof v === "string")
700
- : [],
701
- // P4: Execution evidence
702
- execution_evidence: executionEvidence,
703
- // P5: Self-correction
704
- retracted_constraints: Array.isArray(rr.retracted_constraints)
705
- ? rr.retracted_constraints.filter((v) => typeof v === "string")
706
- : [],
707
- revised_success_criteria: revisedCriteria,
708
- wrong_assumptions: Array.isArray(rr.wrong_assumptions)
709
- ? rr.wrong_assumptions.filter((v) => typeof v === "string")
710
- : [],
711
- // v1.10: Checkpoint boundary
712
- compression_checkpoint: typeof rr.compression_checkpoint === "boolean" ? rr.compression_checkpoint : false,
713
- checkpoint_label: typeof rr.checkpoint_label === "string" ? rr.checkpoint_label : "",
714
- // Multi-agent: Worker delegation results (shared parser)
715
- worker_results: parseWorkerResults(rr.worker_results),
716
- });
717
- }
474
+ // Convert last_round_result if present (object already validated by parser)
475
+ if (parsed.last_round_result) {
476
+ const rr = parsed.last_round_result;
477
+ // Parse P4 execution evidence (shared helper)
478
+ const executionEvidence = parseExecutionEvidence(rr.execution_evidence);
479
+ // Parse P5 revised_success_criteria (shared parser)
480
+ const revisedCriteria = parseCriterionRevisions(rr.revised_success_criteria);
481
+ lcr.last_round_result = makeLoopRoundResult({
482
+ round: rr.round ?? 0,
483
+ success: rr.success ?? false,
484
+ output_summary: rr.output_summary ?? "",
485
+ constraint_violations: rr.constraint_violations ?? [],
486
+ manual_fixes_needed: rr.manual_fixes_needed ?? "",
487
+ // P0–P2: Cognitive evolution fields
488
+ discovered_constraints: Array.isArray(rr.discovered_constraints)
489
+ ? rr.discovered_constraints.filter((v) => typeof v === "string")
490
+ : [],
491
+ objective_refinement: typeof rr.objective_refinement === "string"
492
+ ? rr.objective_refinement
493
+ : "",
494
+ emerged_subtasks: Array.isArray(rr.emerged_subtasks)
495
+ ? rr.emerged_subtasks.filter((v) => typeof v === "string")
496
+ : [],
497
+ // P4: Execution evidence
498
+ execution_evidence: executionEvidence,
499
+ // P5: Self-correction
500
+ retracted_constraints: Array.isArray(rr.retracted_constraints)
501
+ ? rr.retracted_constraints.filter((v) => typeof v === "string")
502
+ : [],
503
+ revised_success_criteria: revisedCriteria,
504
+ wrong_assumptions: Array.isArray(rr.wrong_assumptions)
505
+ ? rr.wrong_assumptions.filter((v) => typeof v === "string")
506
+ : [],
507
+ // v1.10: Checkpoint boundary
508
+ compression_checkpoint: typeof rr.compression_checkpoint === "boolean" ? rr.compression_checkpoint : false,
509
+ checkpoint_label: typeof rr.checkpoint_label === "string" ? rr.checkpoint_label : "",
510
+ // v2.2: Sub-goal lifecycle
511
+ completed_subtasks: Array.isArray(rr.completed_subtasks)
512
+ ? rr.completed_subtasks.filter((v) => typeof v === "string")
513
+ : [],
514
+ blocked_subtasks: Array.isArray(rr.blocked_subtasks)
515
+ ? rr.blocked_subtasks.filter((v) => typeof v === "string")
516
+ : [],
517
+ canceled_subtasks: Array.isArray(rr.canceled_subtasks)
518
+ ? rr.canceled_subtasks.filter((v) => typeof v === "string")
519
+ : [],
520
+ // Multi-agent: Worker delegation results (shared parser)
521
+ worker_results: parseWorkerResults(rr.worker_results),
522
+ });
718
523
  }
719
- // Convert loop_objective if present
720
- const lo = extras.loop_objective;
721
- if (lo && typeof lo === "object" && !Array.isArray(lo)) {
722
- const obj = lo;
524
+ // Convert loop_objective if present (object already validated by parser)
525
+ if (parsed.loop_objective) {
526
+ const obj = parsed.loop_objective;
723
527
  lcr.loop_objective = makeLoopObjective({
724
528
  objective: obj.objective ?? "",
725
529
  success_criteria: obj.success_criteria ?? [],
@@ -760,30 +564,10 @@ export class LoopForgeEngine {
760
564
  error: null,
761
565
  state_file_content: response.state_file_content,
762
566
  prompt_artifact: response.prompt_artifact,
567
+ warnings: response.warnings,
763
568
  },
764
569
  };
765
570
  }
766
- // ═══════════════════════════════════════════════════════════════════════
767
- // Circuit breaker
768
- // ═══════════════════════════════════════════════════════════════════════
769
- shouldBreak() {
770
- if (!this.state)
771
- return false;
772
- const policy = getPolicy();
773
- const maxCB = policy.engine.max_circuit_breaker;
774
- if (this.state.success_trend.length < maxCB)
775
- return false;
776
- const recent = this.state.success_trend.slice(-maxCB);
777
- // Trip only when all recent rounds are failures (no false-positives on all-success)
778
- const allFailed = recent.every((v) => v === false);
779
- if (allFailed) {
780
- logEvent("circuit_breaker", {
781
- trend: recent,
782
- totalRounds: this.state.success_trend.length,
783
- });
784
- }
785
- return allFailed;
786
- }
787
571
  }
788
572
  // ═══════════════════════════════════════════════════════════════════════════
789
573
  // Factory