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.
- package/README.md +7 -5
- package/dist/backends/interface.d.ts +4 -3
- package/dist/backends/interface.d.ts.map +1 -1
- package/dist/backends/interface.js +4 -1
- package/dist/backends/interface.js.map +1 -1
- package/dist/canonical-state.d.ts +23 -2
- package/dist/canonical-state.d.ts.map +1 -1
- package/dist/canonical-state.js +90 -6
- package/dist/canonical-state.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -2
- package/dist/cli.js.map +1 -1
- package/dist/enforcement-gate.d.ts +1 -1
- package/dist/enforcement-gate.d.ts.map +1 -1
- package/dist/enforcement-gate.js +86 -7
- package/dist/enforcement-gate.js.map +1 -1
- package/dist/engine.d.ts +3 -33
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +127 -343
- package/dist/engine.js.map +1 -1
- package/dist/evidence-provider.d.ts +38 -5
- package/dist/evidence-provider.d.ts.map +1 -1
- package/dist/evidence-provider.js +132 -46
- package/dist/evidence-provider.js.map +1 -1
- package/dist/index.d.ts +10 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -9
- package/dist/index.js.map +1 -1
- package/dist/interop.d.ts +5 -0
- package/dist/interop.d.ts.map +1 -1
- package/dist/interop.js +5 -0
- package/dist/interop.js.map +1 -1
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +515 -50
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-extras-parser.d.ts +82 -0
- package/dist/loop-extras-parser.d.ts.map +1 -0
- package/dist/loop-extras-parser.js +217 -0
- package/dist/loop-extras-parser.js.map +1 -0
- package/dist/loop-store.d.ts +26 -4
- package/dist/loop-store.d.ts.map +1 -1
- package/dist/loop-store.js +118 -8
- package/dist/loop-store.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/session.d.ts +29 -6
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +187 -161
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +33 -5
- package/dist/mcp/tools.js.map +1 -1
- package/dist/observability.d.ts +5 -36
- package/dist/observability.d.ts.map +1 -1
- package/dist/observability.js +9 -79
- package/dist/observability.js.map +1 -1
- package/dist/policy-metrics.d.ts +1 -0
- package/dist/policy-metrics.d.ts.map +1 -1
- package/dist/policy-metrics.js +2 -0
- package/dist/policy-metrics.js.map +1 -1
- package/dist/policy.d.ts +58 -8
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +25 -12
- package/dist/policy.js.map +1 -1
- package/dist/prompt-assembler.d.ts.map +1 -1
- package/dist/prompt-assembler.js +129 -4
- package/dist/prompt-assembler.js.map +1 -1
- package/dist/protocol.d.ts +118 -87
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +40 -30
- package/dist/protocol.js.map +1 -1
- package/dist/replay.d.ts +0 -1
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +1 -87
- package/dist/replay.js.map +1 -1
- package/dist/round-coordinator.d.ts +4 -6
- package/dist/round-coordinator.d.ts.map +1 -1
- package/dist/round-coordinator.js +75 -80
- package/dist/round-coordinator.js.map +1 -1
- package/dist/round-driver.d.ts +1 -0
- package/dist/round-driver.d.ts.map +1 -1
- package/dist/round-driver.js +2 -0
- package/dist/round-driver.js.map +1 -1
- package/dist/round-transaction.d.ts.map +1 -1
- package/dist/round-transaction.js +2 -22
- package/dist/round-transaction.js.map +1 -1
- package/dist/run.d.ts +51 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +200 -0
- package/dist/run.js.map +1 -0
- package/dist/self-eval.d.ts +23 -0
- package/dist/self-eval.d.ts.map +1 -0
- package/dist/self-eval.js +151 -0
- package/dist/self-eval.js.map +1 -0
- package/dist/storage.d.ts +3 -2
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +70 -68
- package/dist/storage.js.map +1 -1
- package/dist/token-utils.d.ts +22 -0
- package/dist/token-utils.d.ts.map +1 -0
- package/dist/token-utils.js +46 -0
- package/dist/token-utils.js.map +1 -0
- package/dist/verification-gate.d.ts +17 -23
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +241 -135
- package/dist/verification-gate.js.map +1 -1
- package/loop_policy.json +21 -12
- package/package.json +2 -2
- package/dist/runtime.d.ts +0 -94
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -718
- 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
|
|
11
|
-
|
|
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
|
-
|
|
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
|
package/dist/engine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,
|
|
11
|
+
import { AgentStatus, Mode, makeExecutionFeedback, makeLoopCompileRequest, makeLoopObjective, makeLoopRoundResult, makeSessionState, makeTaskId, } from "./protocol.js";
|
|
12
12
|
import { compileLoop } from "./loop-compiler.js";
|
|
13
13
|
import { logEvent } from "./observability.js";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
* and invokeLoopCompile — both parse the same execution_evidence shape. */
|
|
19
|
-
export function parseExecutionEvidence(raw) {
|
|
20
|
-
if (!raw || typeof raw !== "object")
|
|
21
|
-
return undefined;
|
|
22
|
-
const testResults = raw.test_results;
|
|
23
|
-
return makeExecutionEvidence({
|
|
24
|
-
files_changed: Array.isArray(raw.files_changed)
|
|
25
|
-
? raw.files_changed.filter((v) => typeof v === "string")
|
|
26
|
-
: [],
|
|
27
|
-
test_results: testResults && typeof testResults.passed === "number"
|
|
28
|
-
? {
|
|
29
|
-
passed: testResults.passed,
|
|
30
|
-
failed: testResults.failed ?? 0,
|
|
31
|
-
skipped: testResults.skipped ?? 0,
|
|
32
|
-
}
|
|
33
|
-
: null,
|
|
34
|
-
success_criteria_met: Array.isArray(raw.success_criteria_met)
|
|
35
|
-
? raw.success_criteria_met.filter((v) => typeof v === "string")
|
|
36
|
-
: [],
|
|
37
|
-
success_criteria_remaining: Array.isArray(raw.success_criteria_remaining)
|
|
38
|
-
? raw.success_criteria_remaining.filter((v) => typeof v === "string")
|
|
39
|
-
: [],
|
|
40
|
-
progress_estimate: typeof raw.progress_estimate === "number"
|
|
41
|
-
? Math.max(0, Math.min(1, raw.progress_estimate))
|
|
42
|
-
: 0.0,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
/** Parse CriterionRevision[] from a raw JSON array. Shared by buildSelfEvaluation
|
|
46
|
-
* and invokeLoopCompile — both parse the same revised_success_criteria shape. */
|
|
47
|
-
export function parseCriterionRevisions(raw) {
|
|
48
|
-
if (!Array.isArray(raw))
|
|
49
|
-
return [];
|
|
50
|
-
return raw
|
|
51
|
-
.filter((v) => typeof v === "object" && v !== null &&
|
|
52
|
-
typeof v.old === "string" &&
|
|
53
|
-
typeof v.new === "string")
|
|
54
|
-
.map((v) => {
|
|
55
|
-
const r = v;
|
|
56
|
-
return { old: r.old, new: r.new };
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
/** Parse WorkerResult[] from a raw JSON array. Shared by buildSelfEvaluation
|
|
60
|
-
* and invokeLoopCompile — both parse the same worker_results shape. */
|
|
61
|
-
export function parseWorkerResults(raw) {
|
|
62
|
-
if (!Array.isArray(raw))
|
|
63
|
-
return [];
|
|
64
|
-
return raw
|
|
65
|
-
.filter((v) => typeof v === "object" && v !== null &&
|
|
66
|
-
typeof v.agentId === "string" &&
|
|
67
|
-
typeof v.subTask === "string" &&
|
|
68
|
-
typeof v.resultSummary === "string")
|
|
69
|
-
.map((v) => {
|
|
70
|
-
const w = v;
|
|
71
|
-
return {
|
|
72
|
-
agentId: w.agentId,
|
|
73
|
-
subAgentType: typeof w.subAgentType === "string" ? w.subAgentType : "general-purpose",
|
|
74
|
-
subTask: w.subTask,
|
|
75
|
-
resultSummary: w.resultSummary,
|
|
76
|
-
success: typeof w.success === "boolean" ? w.success : false,
|
|
77
|
-
discoveredConstraints: Array.isArray(w.discoveredConstraints)
|
|
78
|
-
? w.discoveredConstraints.filter((c) => typeof c === "string")
|
|
79
|
-
: [],
|
|
80
|
-
};
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
84
|
-
// Self-Evaluation extraction (v1.1 — autonomous loop feedback)
|
|
85
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
86
|
-
/** Extract a structured SelfEvaluation from agent output text.
|
|
87
|
-
* Returns null if no valid self-eval block is found.
|
|
88
|
-
* The agent is instructed to output JSON between the delimiters. */
|
|
89
|
-
export function extractSelfEvaluation(text) {
|
|
90
|
-
const match = text.match(SELF_EVAL_REGEX);
|
|
91
|
-
if (!match)
|
|
92
|
-
return null;
|
|
93
|
-
try {
|
|
94
|
-
const raw = JSON.parse(match[1]);
|
|
95
|
-
// Validate required fields
|
|
96
|
-
if (typeof raw.success !== "boolean")
|
|
97
|
-
return null;
|
|
98
|
-
if (typeof raw.output_summary !== "string")
|
|
99
|
-
return null;
|
|
100
|
-
if (!Array.isArray(raw.constraint_violations))
|
|
101
|
-
return null;
|
|
102
|
-
if (typeof raw.should_continue !== "boolean")
|
|
103
|
-
return null;
|
|
104
|
-
return buildSelfEvaluation(raw);
|
|
105
|
-
}
|
|
106
|
-
catch {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
/** Build a SelfEvaluation from a parsed JSON object.
|
|
111
|
-
* Lenient parsing: missing optional fields get sensible defaults.
|
|
112
|
-
* Used by extractSelfEvaluation() (regex path) and MCP tool handler
|
|
113
|
-
* (structured evaluation parameter path). */
|
|
114
|
-
export function buildSelfEvaluation(raw) {
|
|
115
|
-
// P4: Parse execution evidence from raw JSON
|
|
116
|
-
const executionEvidence = parseExecutionEvidence(raw.execution_evidence);
|
|
117
|
-
// P5: Parse corrections
|
|
118
|
-
const retractedConstraints = Array.isArray(raw.retracted_constraints)
|
|
119
|
-
? raw.retracted_constraints.filter((v) => typeof v === "string")
|
|
120
|
-
: [];
|
|
121
|
-
const revisedCriteria = parseCriterionRevisions(raw.revised_success_criteria);
|
|
122
|
-
const wrongAssumptions = Array.isArray(raw.wrong_assumptions)
|
|
123
|
-
? raw.wrong_assumptions.filter((v) => typeof v === "string")
|
|
124
|
-
: [];
|
|
125
|
-
// Multi-agent: Parse worker delegation results
|
|
126
|
-
const workerResults = parseWorkerResults(raw.worker_results);
|
|
127
|
-
return makeSelfEvaluation({
|
|
128
|
-
success: typeof raw.success === "boolean" ? raw.success : false,
|
|
129
|
-
output_summary: typeof raw.output_summary === "string" ? raw.output_summary : "",
|
|
130
|
-
constraint_violations: Array.isArray(raw.constraint_violations)
|
|
131
|
-
? raw.constraint_violations.filter((v) => typeof v === "string")
|
|
132
|
-
: [],
|
|
133
|
-
should_continue: typeof raw.should_continue === "boolean" ? raw.should_continue : true,
|
|
134
|
-
// P0–P2: Optional evolution fields
|
|
135
|
-
discovered_constraints: Array.isArray(raw.discovered_constraints)
|
|
136
|
-
? raw.discovered_constraints.filter((v) => typeof v === "string")
|
|
137
|
-
: [],
|
|
138
|
-
objective_refinement: typeof raw.objective_refinement === "string"
|
|
139
|
-
? raw.objective_refinement
|
|
140
|
-
: "",
|
|
141
|
-
emerged_subtasks: Array.isArray(raw.emerged_subtasks)
|
|
142
|
-
? raw.emerged_subtasks.filter((v) => typeof v === "string")
|
|
143
|
-
: [],
|
|
144
|
-
// P4: Execution evidence
|
|
145
|
-
execution_evidence: executionEvidence,
|
|
146
|
-
// P5: Self-correction
|
|
147
|
-
retracted_constraints: retractedConstraints,
|
|
148
|
-
revised_success_criteria: revisedCriteria,
|
|
149
|
-
wrong_assumptions: wrongAssumptions,
|
|
150
|
-
// Multi-agent: Worker delegation results
|
|
151
|
-
worker_results: workerResults,
|
|
152
|
-
// v1.10: Checkpoint compression
|
|
153
|
-
compression_checkpoint: typeof raw.compression_checkpoint === "boolean" ? raw.compression_checkpoint : false,
|
|
154
|
-
checkpoint_label: typeof raw.checkpoint_label === "string" ? raw.checkpoint_label : "",
|
|
155
|
-
// 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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
246
|
-
|
|
247
|
-
|
|
79
|
+
catch {
|
|
80
|
+
if (this.metrics)
|
|
81
|
+
this.metrics.vaultWriteErrors++;
|
|
82
|
+
logEvent("vault_write_error", { error: "feedback_persist" });
|
|
248
83
|
}
|
|
249
84
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
this.
|
|
297
|
-
}
|
|
298
|
-
|
|
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
|
-
//
|
|
628
|
-
|
|
629
|
-
|
|
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
|
-
//
|
|
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:
|
|
651
|
-
round:
|
|
652
|
-
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:
|
|
655
|
-
next_task_proposal:
|
|
656
|
-
plan_source:
|
|
657
|
-
constraints_from_plan:
|
|
658
|
-
new_since_last_round:
|
|
659
|
-
force_level:
|
|
660
|
-
health_check_interval:
|
|
661
|
-
external_context:
|
|
662
|
-
max_rounds:
|
|
663
|
-
verification_flags:
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
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
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
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
|
-
|
|
721
|
-
|
|
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
|