loopforge 1.0.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/LICENSE +21 -0
- package/README.md +75 -0
- package/dist/adapter.d.ts +17 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +228 -0
- package/dist/adapter.js.map +1 -0
- package/dist/backends/fs.d.ts +31 -0
- package/dist/backends/fs.d.ts.map +1 -0
- package/dist/backends/fs.js +308 -0
- package/dist/backends/fs.js.map +1 -0
- package/dist/backends/interface.d.ts +42 -0
- package/dist/backends/interface.d.ts.map +1 -0
- package/dist/backends/interface.js +7 -0
- package/dist/backends/interface.js.map +1 -0
- package/dist/builder.d.ts +17 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/builder.js +205 -0
- package/dist/builder.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +360 -0
- package/dist/cli.js.map +1 -0
- package/dist/engine.d.ts +42 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +595 -0
- package/dist/engine.js.map +1 -0
- package/dist/generate-schema.d.ts +12 -0
- package/dist/generate-schema.d.ts.map +1 -0
- package/dist/generate-schema.js +278 -0
- package/dist/generate-schema.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/loop-compiler.d.ts +37 -0
- package/dist/loop-compiler.d.ts.map +1 -0
- package/dist/loop-compiler.js +1232 -0
- package/dist/loop-compiler.js.map +1 -0
- package/dist/policy.d.ts +44 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +111 -0
- package/dist/policy.js.map +1 -0
- package/dist/protocol.d.ts +167 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +198 -0
- package/dist/protocol.js.map +1 -0
- package/dist/replay.d.ts +19 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +198 -0
- package/dist/replay.js.map +1 -0
- package/dist/tests/adapter.test.d.ts +2 -0
- package/dist/tests/adapter.test.d.ts.map +1 -0
- package/dist/tests/adapter.test.js +104 -0
- package/dist/tests/adapter.test.js.map +1 -0
- package/dist/tests/builder.test.d.ts +2 -0
- package/dist/tests/builder.test.d.ts.map +1 -0
- package/dist/tests/builder.test.js +122 -0
- package/dist/tests/builder.test.js.map +1 -0
- package/dist/tests/engine.test.d.ts +2 -0
- package/dist/tests/engine.test.d.ts.map +1 -0
- package/dist/tests/engine.test.js +200 -0
- package/dist/tests/engine.test.js.map +1 -0
- package/dist/tests/generate-schema.test.d.ts +8 -0
- package/dist/tests/generate-schema.test.d.ts.map +1 -0
- package/dist/tests/generate-schema.test.js +196 -0
- package/dist/tests/generate-schema.test.js.map +1 -0
- package/dist/tests/loop-compiler.test.d.ts +2 -0
- package/dist/tests/loop-compiler.test.d.ts.map +1 -0
- package/dist/tests/loop-compiler.test.js +387 -0
- package/dist/tests/loop-compiler.test.js.map +1 -0
- package/dist/tests/policy.test.d.ts +2 -0
- package/dist/tests/policy.test.d.ts.map +1 -0
- package/dist/tests/policy.test.js +59 -0
- package/dist/tests/policy.test.js.map +1 -0
- package/dist/tests/protocol.test.d.ts +2 -0
- package/dist/tests/protocol.test.d.ts.map +1 -0
- package/dist/tests/protocol.test.js +63 -0
- package/dist/tests/protocol.test.js.map +1 -0
- package/dist/tests/replay.test.d.ts +2 -0
- package/dist/tests/replay.test.d.ts.map +1 -0
- package/dist/tests/replay.test.js +194 -0
- package/dist/tests/replay.test.js.map +1 -0
- package/loop_policy.json +44 -0
- package/package.json +58 -0
|
@@ -0,0 +1,1232 @@
|
|
|
1
|
+
/** LoopForge-loop_compile — Loop Compiler (v3.5 core).
|
|
2
|
+
*
|
|
3
|
+
* Pure-function module for per-loop-iteration prompt compilation.
|
|
4
|
+
*
|
|
5
|
+
* Two layers:
|
|
6
|
+
* Layer 1 (Hard Gates): decideLevel() — 4-gate routing that CAN change compile level.
|
|
7
|
+
* Layer 2 (Soft Advisories): computeAdvisories() — warnings/alignment/health, NEVER
|
|
8
|
+
* change compile level directly.
|
|
9
|
+
*
|
|
10
|
+
* Compilation: compileL0() / compileL1() / compileL2() produce the actual prompt.
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
14
|
+
import { getPolicy } from "./policy.js";
|
|
15
|
+
import { makeLoopCompileResponse, makeLoopHealth, makeLoopObjective, makeRollingSummary, makeTaskAlignment, } from "./protocol.js";
|
|
16
|
+
import { routeTechniqueAdaptive } from "./builder.js";
|
|
17
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
18
|
+
// Repair cue detection
|
|
19
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
20
|
+
const REPAIR_CUES = [
|
|
21
|
+
"fix", "repair", "revise", "correct", "polish", "bug", "error",
|
|
22
|
+
"修复", "修改", "修正", "纠错", "补充", "改一下",
|
|
23
|
+
];
|
|
24
|
+
function detectsRepairSignal(request) {
|
|
25
|
+
let text = (request.new_since_last_round || "").toLowerCase();
|
|
26
|
+
if (request.last_round_result) {
|
|
27
|
+
text += " " + (request.last_round_result.output_summary || "").toLowerCase();
|
|
28
|
+
if (request.last_round_result.manual_fixes_needed) {
|
|
29
|
+
text += " " + request.last_round_result.manual_fixes_needed.toLowerCase();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return REPAIR_CUES.some((cue) => text.includes(cue));
|
|
33
|
+
}
|
|
34
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
35
|
+
// Tokenization helpers
|
|
36
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
37
|
+
function tokenize(text) {
|
|
38
|
+
const tokens = text.split(/\s+/);
|
|
39
|
+
const result = new Set();
|
|
40
|
+
for (let token of tokens) {
|
|
41
|
+
token = token.trim().replace(/^[.,;:!?()[\]{}'"]+|[.,;:!?()[\]{}'"]+$/g, "");
|
|
42
|
+
if (token.length >= 2) {
|
|
43
|
+
result.add(/^[\x00-\x7F]*$/.test(token) ? token.toLowerCase() : token);
|
|
44
|
+
}
|
|
45
|
+
// Add individual CJK chars as standalone tokens
|
|
46
|
+
for (const ch of token) {
|
|
47
|
+
if ((ch >= "一" && ch <= "鿿") ||
|
|
48
|
+
(ch >= "" && ch <= "ヿ")) {
|
|
49
|
+
result.add(ch);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
function jaccard(a, b) {
|
|
56
|
+
if (a.size === 0 || b.size === 0)
|
|
57
|
+
return 0.0;
|
|
58
|
+
let intersection = 0;
|
|
59
|
+
for (const item of a) {
|
|
60
|
+
if (b.has(item))
|
|
61
|
+
intersection++;
|
|
62
|
+
}
|
|
63
|
+
const union = a.size + b.size - intersection;
|
|
64
|
+
return union > 0 ? intersection / union : 0.0;
|
|
65
|
+
}
|
|
66
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
67
|
+
// Goal identity
|
|
68
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
69
|
+
export function computeGoalTextHash(task) {
|
|
70
|
+
const normalized = (task || "").trim().toLowerCase().replace(/\s+/g, " ");
|
|
71
|
+
return createHash("sha256").update(normalized, "utf-8").digest("hex").slice(0, 12);
|
|
72
|
+
}
|
|
73
|
+
export function deriveGoalId(loopId, task, explicitGoalId = "") {
|
|
74
|
+
if (explicitGoalId)
|
|
75
|
+
return explicitGoalId;
|
|
76
|
+
let taskPrefix = (task || "unnamed").slice(0, 60).trim().toLowerCase();
|
|
77
|
+
taskPrefix = taskPrefix.replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
78
|
+
return loopId ? `${loopId}:${taskPrefix}` : taskPrefix;
|
|
79
|
+
}
|
|
80
|
+
function makePreviousRound() {
|
|
81
|
+
return {
|
|
82
|
+
goal_id: "",
|
|
83
|
+
goal_text_hash: "",
|
|
84
|
+
quality_score: 0,
|
|
85
|
+
success: true,
|
|
86
|
+
task: "",
|
|
87
|
+
constraints_active: [],
|
|
88
|
+
prompt_text: "",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function getPreviousRound(loopId, roundNum, vaultContext) {
|
|
92
|
+
if (vaultContext === null)
|
|
93
|
+
return null;
|
|
94
|
+
const results = vaultContext.results || [];
|
|
95
|
+
if (!results.length)
|
|
96
|
+
return null;
|
|
97
|
+
for (const r of results) {
|
|
98
|
+
const lineage = (r.loop_lineage || r.lineage || {});
|
|
99
|
+
if (lineage.loop_id === loopId && lineage.round === roundNum) {
|
|
100
|
+
return {
|
|
101
|
+
goal_id: lineage.goal_id ?? "",
|
|
102
|
+
goal_text_hash: lineage.goal_text_hash ?? "",
|
|
103
|
+
quality_score: lineage.quality_score ?? 0,
|
|
104
|
+
success: r.success ?? true,
|
|
105
|
+
task: r.task ?? r.user_intent ?? "",
|
|
106
|
+
constraints_active: lineage.constraints_active ?? [],
|
|
107
|
+
prompt_text: r.full_prompt ?? "",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
function getRecentRounds(loopId, n, vaultContext) {
|
|
114
|
+
if (vaultContext === null)
|
|
115
|
+
return [];
|
|
116
|
+
const results = vaultContext.results || [];
|
|
117
|
+
const rounds = [];
|
|
118
|
+
for (const r of results) {
|
|
119
|
+
const lineage = (r.loop_lineage || r.lineage || {});
|
|
120
|
+
if (lineage.loop_id === loopId) {
|
|
121
|
+
rounds.push({
|
|
122
|
+
quality_score: lineage.quality_score ?? 0,
|
|
123
|
+
round: lineage.round ?? 0,
|
|
124
|
+
goal_text_hash: lineage.goal_text_hash ?? "",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
rounds.sort((a, b) => b.round - a.round);
|
|
129
|
+
return rounds.slice(0, n);
|
|
130
|
+
}
|
|
131
|
+
function getPreviousRoundTask(loopId, roundNum, vaultContext) {
|
|
132
|
+
const prev = getPreviousRound(loopId, roundNum, vaultContext);
|
|
133
|
+
return prev?.task ?? "";
|
|
134
|
+
}
|
|
135
|
+
function vaultGetLoopObjective(loopId, vaultContext) {
|
|
136
|
+
if (vaultContext === null)
|
|
137
|
+
return null;
|
|
138
|
+
// Check global entries first
|
|
139
|
+
const globalEntries = vaultContext.global_entries || [];
|
|
140
|
+
for (const entry of globalEntries) {
|
|
141
|
+
const lo = entry.loop_objective;
|
|
142
|
+
if (lo && entry.loop_id === loopId) {
|
|
143
|
+
return { loop_objective: lo, loop_id: loopId };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Check results
|
|
147
|
+
const results = vaultContext.results || [];
|
|
148
|
+
for (const r of results) {
|
|
149
|
+
const lo = r.loop_objective;
|
|
150
|
+
if (lo && r.loop_id === loopId) {
|
|
151
|
+
return { loop_objective: lo, loop_id: loopId };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
function countConsecutiveHashMismatches(loopId, vaultContext) {
|
|
157
|
+
if (vaultContext === null)
|
|
158
|
+
return 0;
|
|
159
|
+
const rounds = getRecentRounds(loopId, 20, vaultContext);
|
|
160
|
+
if (rounds.length < 2)
|
|
161
|
+
return 0;
|
|
162
|
+
let count = 0;
|
|
163
|
+
for (let i = 0; i < rounds.length - 1; i++) {
|
|
164
|
+
const currHash = rounds[i].goal_text_hash;
|
|
165
|
+
const prevHash = rounds[i + 1].goal_text_hash;
|
|
166
|
+
if (currHash && prevHash && currHash !== prevHash) {
|
|
167
|
+
count++;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return count;
|
|
174
|
+
}
|
|
175
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
176
|
+
// Strategy collapse detection
|
|
177
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
178
|
+
function strategyCollapse(loopId, vaultContext) {
|
|
179
|
+
const recent = getRecentRounds(loopId, 3, vaultContext);
|
|
180
|
+
if (recent.length < 3)
|
|
181
|
+
return false;
|
|
182
|
+
return recent.every((r) => r.quality_score < 3);
|
|
183
|
+
}
|
|
184
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
185
|
+
// Constraint Retirement (v3.5)
|
|
186
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
187
|
+
function computeConstraintRetirement(activeConstraints, loopId, currentRound, vaultContext) {
|
|
188
|
+
if (!activeConstraints.length || vaultContext === null) {
|
|
189
|
+
return { active: [...activeConstraints], retired: [] };
|
|
190
|
+
}
|
|
191
|
+
const results = vaultContext.results || [];
|
|
192
|
+
if (!results.length) {
|
|
193
|
+
return { active: [...activeConstraints], retired: [] };
|
|
194
|
+
}
|
|
195
|
+
const policy = getPolicy();
|
|
196
|
+
const window = policy.constraints.retire_window;
|
|
197
|
+
const targetRounds = new Set();
|
|
198
|
+
for (let r = currentRound - window; r < currentRound; r++) {
|
|
199
|
+
targetRounds.add(r);
|
|
200
|
+
}
|
|
201
|
+
// Collect all text per target round for activity detection
|
|
202
|
+
const roundTexts = new Map();
|
|
203
|
+
for (const r of results) {
|
|
204
|
+
const lineage = (r.loop_lineage || r.lineage || {});
|
|
205
|
+
if (lineage.loop_id !== loopId)
|
|
206
|
+
continue;
|
|
207
|
+
const rnd = lineage.round;
|
|
208
|
+
if (!targetRounds.has(rnd))
|
|
209
|
+
continue;
|
|
210
|
+
let text = ((r.task ?? r.user_intent ?? "") + " " +
|
|
211
|
+
(lineage.task ?? "") + " " +
|
|
212
|
+
(r.output_summary ?? "")).toLowerCase();
|
|
213
|
+
for (const v of r.constraint_violations || []) {
|
|
214
|
+
text += " " + String(v).toLowerCase();
|
|
215
|
+
}
|
|
216
|
+
roundTexts.set(rnd, text);
|
|
217
|
+
}
|
|
218
|
+
const retired = [];
|
|
219
|
+
const pruned = [];
|
|
220
|
+
for (const constraint of activeConstraints) {
|
|
221
|
+
const cLower = constraint.toLowerCase();
|
|
222
|
+
const cNormalized = cLower.replace(/-/g, " ");
|
|
223
|
+
let isActive = false;
|
|
224
|
+
for (const text of roundTexts.values()) {
|
|
225
|
+
if (text.includes(cLower) || text.includes(cNormalized)) {
|
|
226
|
+
isActive = true;
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (isActive || roundTexts.size < window) {
|
|
231
|
+
pruned.push(constraint);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
retired.push(constraint);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { active: pruned, retired };
|
|
238
|
+
}
|
|
239
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
240
|
+
// Rolling Summary (v3.5)
|
|
241
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
242
|
+
function buildRollingSummary(loopId, currentRound, vaultContext) {
|
|
243
|
+
if (vaultContext === null)
|
|
244
|
+
return null;
|
|
245
|
+
const results = vaultContext.results || [];
|
|
246
|
+
if (!results.length)
|
|
247
|
+
return null;
|
|
248
|
+
const policy = getPolicy();
|
|
249
|
+
const window = policy.summary.window;
|
|
250
|
+
// Collect rounds matching this loop_id, excluding current
|
|
251
|
+
const rounds = [];
|
|
252
|
+
for (const r of results) {
|
|
253
|
+
const lineage = (r.loop_lineage || r.lineage || {});
|
|
254
|
+
if (lineage.loop_id !== loopId)
|
|
255
|
+
continue;
|
|
256
|
+
const rnd = lineage.round;
|
|
257
|
+
if (rnd >= currentRound)
|
|
258
|
+
continue;
|
|
259
|
+
rounds.push({
|
|
260
|
+
round: rnd,
|
|
261
|
+
quality_score: lineage.quality_score ?? 0,
|
|
262
|
+
task: r.task ?? r.user_intent ?? "",
|
|
263
|
+
output_summary: r.output_summary ?? "",
|
|
264
|
+
constraint_violations: r.constraint_violations || [],
|
|
265
|
+
technique_used: r.technique_used ?? "",
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (!rounds.length)
|
|
269
|
+
return null;
|
|
270
|
+
// Sort by round descending, take last N
|
|
271
|
+
rounds.sort((a, b) => b.round - a.round);
|
|
272
|
+
const sampled = rounds.slice(0, window).reverse(); // chronological order
|
|
273
|
+
// Quality trajectory
|
|
274
|
+
const trajectory = sampled.map((r) => r.quality_score);
|
|
275
|
+
// Trajectory direction
|
|
276
|
+
let direction = "stable";
|
|
277
|
+
if (trajectory.length >= 2) {
|
|
278
|
+
const diffs = trajectory.slice(1).map((v, i) => v - trajectory[i]);
|
|
279
|
+
if (diffs.every((d) => d >= 0) && diffs.some((d) => d > 0)) {
|
|
280
|
+
direction = "improving";
|
|
281
|
+
}
|
|
282
|
+
else if (diffs.every((d) => d <= 0) && diffs.some((d) => d < 0)) {
|
|
283
|
+
direction = "declining";
|
|
284
|
+
}
|
|
285
|
+
else if (diffs.every((d) => d === 0)) {
|
|
286
|
+
direction = "stable";
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
direction = "volatile";
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// What worked
|
|
293
|
+
const whatWorked = [];
|
|
294
|
+
for (const r of sampled) {
|
|
295
|
+
if (r.quality_score >= 4 &&
|
|
296
|
+
r.output_summary) {
|
|
297
|
+
whatWorked.push(`R${r.round} (score=${r.quality_score}, ` +
|
|
298
|
+
`${r.technique_used || "n/a"}): ${String(r.output_summary).slice(0, 150)}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Recurring issues
|
|
302
|
+
const violationCounts = new Map();
|
|
303
|
+
for (const r of sampled) {
|
|
304
|
+
const seen = new Set();
|
|
305
|
+
for (const v of r.constraint_violations || []) {
|
|
306
|
+
const vNorm = String(v).trim().toLowerCase();
|
|
307
|
+
if (vNorm && !seen.has(vNorm)) {
|
|
308
|
+
violationCounts.set(vNorm, (violationCounts.get(vNorm) || 0) + 1);
|
|
309
|
+
seen.add(vNorm);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const recurringIssues = [...violationCounts.entries()]
|
|
314
|
+
.filter(([, count]) => count >= 2)
|
|
315
|
+
.sort((a, b) => b[1] - a[1])
|
|
316
|
+
.map(([v, count]) => `${v} (appeared in ${count} rounds)`);
|
|
317
|
+
// Key lessons
|
|
318
|
+
const keyLessons = [];
|
|
319
|
+
for (const r of sampled) {
|
|
320
|
+
if (r.quality_score >= 4 && r.output_summary) {
|
|
321
|
+
keyLessons.push(`[R${r.round}] ${String(r.output_summary).slice(0, 200)}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return makeRollingSummary({
|
|
325
|
+
quality_trajectory: trajectory,
|
|
326
|
+
trajectory_direction: direction,
|
|
327
|
+
what_worked: whatWorked,
|
|
328
|
+
recurring_issues: recurringIssues,
|
|
329
|
+
key_lessons: keyLessons,
|
|
330
|
+
rounds_sampled: sampled.length,
|
|
331
|
+
generated_at_round: currentRound,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
function formatRollingSummaryForPrompt(rs) {
|
|
335
|
+
if (rs === null || rs.rounds_sampled === 0)
|
|
336
|
+
return "";
|
|
337
|
+
const lines = [
|
|
338
|
+
"### Cross-Round Summary (Accumulated)",
|
|
339
|
+
"",
|
|
340
|
+
`**Sampled**: ${rs.rounds_sampled} prior rounds | **Direction**: ${rs.trajectory_direction}`,
|
|
341
|
+
`**Quality Trajectory**: [${rs.quality_trajectory.join(", ")}]`,
|
|
342
|
+
"",
|
|
343
|
+
];
|
|
344
|
+
if (rs.what_worked.length) {
|
|
345
|
+
lines.push("**What Worked (score >= 4)**:");
|
|
346
|
+
for (const w of rs.what_worked)
|
|
347
|
+
lines.push(`- ${w}`);
|
|
348
|
+
lines.push("");
|
|
349
|
+
}
|
|
350
|
+
if (rs.recurring_issues.length) {
|
|
351
|
+
lines.push("**Recurring Issues (appeared 2+ times)**:");
|
|
352
|
+
for (const ri of rs.recurring_issues)
|
|
353
|
+
lines.push(`- ⚠️ ${ri}`);
|
|
354
|
+
lines.push("");
|
|
355
|
+
}
|
|
356
|
+
if (rs.key_lessons.length) {
|
|
357
|
+
lines.push("**Key Lessons From High-Score Rounds**:");
|
|
358
|
+
for (const kl of rs.key_lessons)
|
|
359
|
+
lines.push(`- ${kl}`);
|
|
360
|
+
lines.push("");
|
|
361
|
+
}
|
|
362
|
+
return lines.join("\n");
|
|
363
|
+
}
|
|
364
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
365
|
+
// LAYER 1 — Hard Gates (can change compile level)
|
|
366
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
367
|
+
export function decideLevel(request, vaultContext) {
|
|
368
|
+
// Gate 1: Explicit override (never overrides round 1 or plan_source)
|
|
369
|
+
if (request.force_level !== "auto" &&
|
|
370
|
+
["l0", "l1", "l2"].includes(request.force_level)) {
|
|
371
|
+
if (request.round !== 1 && !request.plan_source) {
|
|
372
|
+
return request.force_level;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
// Gate 2: First call or explicit plan input → full rebuild
|
|
376
|
+
if (request.round === 1 || request.plan_source) {
|
|
377
|
+
return "l2";
|
|
378
|
+
}
|
|
379
|
+
// Derive goal_id
|
|
380
|
+
const goalId = deriveGoalId(request.loop_id, request.task, request.goal_id);
|
|
381
|
+
const prev = getPreviousRound(request.loop_id, request.round - 1, vaultContext);
|
|
382
|
+
if (prev === null)
|
|
383
|
+
return "l2";
|
|
384
|
+
// Gate 3: goal_id stability
|
|
385
|
+
if (goalId !== prev.goal_id)
|
|
386
|
+
return "l2";
|
|
387
|
+
// Gate 4: Explicit failures or new constraints → patch
|
|
388
|
+
const hasNewConstraints = request.constraints_from_plan.length > 0;
|
|
389
|
+
const hasNewFailures = request.last_round_result !== null &&
|
|
390
|
+
!request.last_round_result.success;
|
|
391
|
+
const hasRepair = detectsRepairSignal(request);
|
|
392
|
+
if (hasNewConstraints || hasNewFailures || hasRepair)
|
|
393
|
+
return "l1";
|
|
394
|
+
// Nothing triggered → fast path
|
|
395
|
+
return "l0";
|
|
396
|
+
}
|
|
397
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
398
|
+
// LAYER 2 — Soft Advisories (NEVER change compile level)
|
|
399
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
400
|
+
export function alignTask(proposedTask, request, vaultContext) {
|
|
401
|
+
const objectiveEntry = vaultGetLoopObjective(request.loop_id, vaultContext);
|
|
402
|
+
let objData = null;
|
|
403
|
+
if (request.loop_objective) {
|
|
404
|
+
objData = request.loop_objective;
|
|
405
|
+
}
|
|
406
|
+
else if (objectiveEntry) {
|
|
407
|
+
const lo = objectiveEntry.loop_objective;
|
|
408
|
+
if (lo && typeof lo === "object") {
|
|
409
|
+
objData = lo;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (objData === null)
|
|
413
|
+
return makeTaskAlignment();
|
|
414
|
+
const objective = objData.objective ?? "";
|
|
415
|
+
const successCriteria = objData.success_criteria ?? [];
|
|
416
|
+
const hardConstraints = objData.hard_constraints ?? [];
|
|
417
|
+
const proposedTokens = tokenize(proposedTask.toLowerCase());
|
|
418
|
+
const objText = `${objective} ${successCriteria.join(" ")} ${hardConstraints.join(" ")}`.toLowerCase();
|
|
419
|
+
const objTokens = tokenize(objText);
|
|
420
|
+
const score = proposedTokens.size && objTokens.size
|
|
421
|
+
? jaccard(proposedTokens, objTokens)
|
|
422
|
+
: 1.0;
|
|
423
|
+
if (score >= 0.5) {
|
|
424
|
+
return makeTaskAlignment({
|
|
425
|
+
is_aligned: true,
|
|
426
|
+
alignment_score: Math.round(score * 100) / 100,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
else if (score >= 0.3) {
|
|
430
|
+
return makeTaskAlignment({
|
|
431
|
+
is_aligned: true,
|
|
432
|
+
alignment_score: Math.round(score * 100) / 100,
|
|
433
|
+
warning: `Proposed task '${proposedTask.slice(0, 80)}' may be drifting from ` +
|
|
434
|
+
`loop objective '${objective}'. Consider narrowing scope.`,
|
|
435
|
+
escalation: "warn",
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
return makeTaskAlignment({
|
|
440
|
+
is_aligned: false,
|
|
441
|
+
alignment_score: Math.round(score * 100) / 100,
|
|
442
|
+
warning: `Proposed task '${proposedTask.slice(0, 80)}' is OFF-OBJECTIVE. ` +
|
|
443
|
+
`Loop objective: '${objective}'. Full realignment recommended.`,
|
|
444
|
+
escalation: "block",
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
export function checkLoopHealth(loopId, request, vaultContext) {
|
|
449
|
+
const objectiveEntry = vaultGetLoopObjective(loopId, vaultContext);
|
|
450
|
+
let obj = null;
|
|
451
|
+
if (request.loop_objective) {
|
|
452
|
+
obj = request.loop_objective;
|
|
453
|
+
}
|
|
454
|
+
else if (objectiveEntry) {
|
|
455
|
+
const lo = objectiveEntry.loop_objective;
|
|
456
|
+
if (lo && typeof lo === "object") {
|
|
457
|
+
obj = lo;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (obj === null)
|
|
461
|
+
return makeLoopHealth();
|
|
462
|
+
const objective = obj.objective ?? "";
|
|
463
|
+
const successCriteria = obj.success_criteria ?? [];
|
|
464
|
+
const hardConstraints = obj.hard_constraints ?? [];
|
|
465
|
+
// 1. goal_alignment
|
|
466
|
+
let goalAlignment = 1.0;
|
|
467
|
+
if (request.task) {
|
|
468
|
+
const taskTokens = tokenize(request.task.toLowerCase());
|
|
469
|
+
const objText = `${objective} ${successCriteria.join(" ")} ${hardConstraints.join(" ")}`.toLowerCase();
|
|
470
|
+
const objTokens = tokenize(objText);
|
|
471
|
+
goalAlignment =
|
|
472
|
+
taskTokens.size && objTokens.size
|
|
473
|
+
? jaccard(taskTokens, objTokens)
|
|
474
|
+
: 1.0;
|
|
475
|
+
}
|
|
476
|
+
// 2. constraint_integrity
|
|
477
|
+
let constraintIntegrity = 1.0;
|
|
478
|
+
if (request.last_round_result && hardConstraints.length) {
|
|
479
|
+
const outputText = request.last_round_result.output_summary.toLowerCase();
|
|
480
|
+
const present = hardConstraints.filter((c) => c
|
|
481
|
+
.toLowerCase()
|
|
482
|
+
.split(/\s+/)
|
|
483
|
+
.some((word) => outputText.includes(word))).length;
|
|
484
|
+
constraintIntegrity = present / hardConstraints.length;
|
|
485
|
+
}
|
|
486
|
+
// 3. drift_detected
|
|
487
|
+
const driftDetected = countConsecutiveHashMismatches(loopId, vaultContext) >= 3;
|
|
488
|
+
// 4. strategy_stability
|
|
489
|
+
const recent = getRecentRounds(loopId, 3, vaultContext);
|
|
490
|
+
const strategyStability = recent.length > 0
|
|
491
|
+
? recent.every((r) => r.quality_score >= 4)
|
|
492
|
+
: true;
|
|
493
|
+
// 5. task_continuity
|
|
494
|
+
let taskContinuity = 1.0;
|
|
495
|
+
const prevTask = getPreviousRoundTask(loopId, request.round - 1, vaultContext);
|
|
496
|
+
if (prevTask && request.task) {
|
|
497
|
+
const currTokens = tokenize(request.task.toLowerCase());
|
|
498
|
+
const prevTokens = tokenize(prevTask.toLowerCase());
|
|
499
|
+
taskContinuity =
|
|
500
|
+
currTokens.size && prevTokens.size
|
|
501
|
+
? jaccard(currTokens, prevTokens)
|
|
502
|
+
: 1.0;
|
|
503
|
+
}
|
|
504
|
+
// Escalation recommendation
|
|
505
|
+
let escalation = "none";
|
|
506
|
+
if (goalAlignment < 0.5) {
|
|
507
|
+
escalation = "l2";
|
|
508
|
+
}
|
|
509
|
+
else if (constraintIntegrity < 0.7) {
|
|
510
|
+
escalation = "l1";
|
|
511
|
+
}
|
|
512
|
+
else if (driftDetected) {
|
|
513
|
+
escalation = "l2";
|
|
514
|
+
}
|
|
515
|
+
return makeLoopHealth({
|
|
516
|
+
goal_alignment: Math.round(goalAlignment * 100) / 100,
|
|
517
|
+
constraint_integrity: Math.round(constraintIntegrity * 100) / 100,
|
|
518
|
+
drift_detected: driftDetected,
|
|
519
|
+
strategy_stability: strategyStability,
|
|
520
|
+
task_continuity: Math.round(taskContinuity * 100) / 100,
|
|
521
|
+
escalation_recommended: escalation,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
function computeSuggestedNextTask(loopId, vaultContext) {
|
|
525
|
+
if (vaultContext === null)
|
|
526
|
+
return "";
|
|
527
|
+
const results = vaultContext.results || [];
|
|
528
|
+
for (const r of results) {
|
|
529
|
+
const lineage = (r.loop_lineage || r.lineage || {});
|
|
530
|
+
if (lineage.loop_id === loopId) {
|
|
531
|
+
const task = r.task ?? r.user_intent ?? "";
|
|
532
|
+
if (task)
|
|
533
|
+
return `Previous round focused on: ${task.slice(0, 120)}`;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return "";
|
|
537
|
+
}
|
|
538
|
+
export function computeAdvisories(request, vaultContext) {
|
|
539
|
+
const warnings = [];
|
|
540
|
+
let alignment = null;
|
|
541
|
+
let health = null;
|
|
542
|
+
let suggested = "";
|
|
543
|
+
// goal_text_hash drift detection
|
|
544
|
+
const currentHash = computeGoalTextHash(request.task);
|
|
545
|
+
const prev = getPreviousRound(request.loop_id, request.round - 1, vaultContext);
|
|
546
|
+
if (prev && currentHash !== prev.goal_text_hash && prev.goal_text_hash) {
|
|
547
|
+
warnings.push(`goal_text_hash changed (${prev.goal_text_hash} → ${currentHash}) ` +
|
|
548
|
+
"but goal_id matched — wording drift detected");
|
|
549
|
+
}
|
|
550
|
+
// Strategy collapse check
|
|
551
|
+
if (strategyCollapse(request.loop_id, vaultContext)) {
|
|
552
|
+
warnings.push("strategy_collapse: 3 consecutive low-quality rounds — " +
|
|
553
|
+
"consider force_level=L2 rebuild");
|
|
554
|
+
}
|
|
555
|
+
// Repair cue detection
|
|
556
|
+
if (detectsRepairSignal(request)) {
|
|
557
|
+
warnings.push("repair signal detected — L1 patch applied");
|
|
558
|
+
}
|
|
559
|
+
// Task alignment
|
|
560
|
+
if (request.next_task_proposal) {
|
|
561
|
+
alignment = alignTask(request.next_task_proposal, request, vaultContext);
|
|
562
|
+
if (alignment.escalation !== "none") {
|
|
563
|
+
warnings.push(`task_alignment: ${alignment.warning}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// Loop health
|
|
567
|
+
const interval = Math.max(request.health_check_interval, 1);
|
|
568
|
+
if (request.round % interval === 0) {
|
|
569
|
+
health = checkLoopHealth(request.loop_id, request, vaultContext);
|
|
570
|
+
if (health.escalation_recommended !== "none") {
|
|
571
|
+
warnings.push(`loop_health recommends ${health.escalation_recommended}: ` +
|
|
572
|
+
`goal_alignment=${health.goal_alignment.toFixed(2)}, ` +
|
|
573
|
+
`constraint_integrity=${health.constraint_integrity.toFixed(2)}, ` +
|
|
574
|
+
`task_continuity=${health.task_continuity.toFixed(2)}`);
|
|
575
|
+
}
|
|
576
|
+
if (health.drift_detected) {
|
|
577
|
+
warnings.push("drift_detected: goal_text_hash diverged 3+ consecutive rounds");
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
// Forward hint
|
|
581
|
+
suggested = computeSuggestedNextTask(request.loop_id, vaultContext);
|
|
582
|
+
return { warnings, suggestedNextTask: suggested, alignment, health };
|
|
583
|
+
}
|
|
584
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
585
|
+
// Plan extraction
|
|
586
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
587
|
+
function extractObjectiveFromPlan(planPath) {
|
|
588
|
+
let text;
|
|
589
|
+
try {
|
|
590
|
+
text = readFileSync(planPath, "utf-8");
|
|
591
|
+
}
|
|
592
|
+
catch {
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
const sections = {
|
|
596
|
+
goal: [],
|
|
597
|
+
success: [],
|
|
598
|
+
constraints: [],
|
|
599
|
+
};
|
|
600
|
+
let currentSection = null;
|
|
601
|
+
const goalPatterns = ["goal", "objective", "目标", "目的", "意图"];
|
|
602
|
+
const successPatterns = [
|
|
603
|
+
"success criteria", "acceptance criteria", "验收标准", "成功标准",
|
|
604
|
+
"done when", "完成标准", "交付标准",
|
|
605
|
+
];
|
|
606
|
+
const constraintPatterns = [
|
|
607
|
+
"hard constraint", "constraint", "non-goal", "out of scope",
|
|
608
|
+
"硬约束", "约束", "非目标", "不做什么", "限制",
|
|
609
|
+
];
|
|
610
|
+
for (const line of text.split("\n")) {
|
|
611
|
+
const stripped = line.trim();
|
|
612
|
+
const low = stripped
|
|
613
|
+
.toLowerCase()
|
|
614
|
+
.replace(/^#+\s*/, "");
|
|
615
|
+
if (stripped.startsWith("#")) {
|
|
616
|
+
if (goalPatterns.some((p) => low.includes(p))) {
|
|
617
|
+
currentSection = "goal";
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
if (successPatterns.some((p) => low.includes(p))) {
|
|
621
|
+
currentSection = "success";
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
if (constraintPatterns.some((p) => low.includes(p))) {
|
|
625
|
+
currentSection = "constraints";
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
currentSection = null;
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
if (!currentSection)
|
|
632
|
+
continue;
|
|
633
|
+
if (/^[-*•]/.test(stripped)) {
|
|
634
|
+
const item = stripped.replace(/^[-*•]\s*/, "").trim();
|
|
635
|
+
if (item && item.length > 3) {
|
|
636
|
+
sections[currentSection].push(item);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
else if (stripped && currentSection === "goal") {
|
|
640
|
+
if (!sections.goal.length && stripped.length > 10) {
|
|
641
|
+
sections.goal.push(stripped);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (!Object.values(sections).some((v) => v.length))
|
|
646
|
+
return null;
|
|
647
|
+
return {
|
|
648
|
+
objective: sections.goal[0] ?? "",
|
|
649
|
+
success_criteria: sections.success,
|
|
650
|
+
hard_constraints: sections.constraints,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
function computeLoopObjectiveFromTask(request, _vaultContext) {
|
|
654
|
+
const task = request.task || "";
|
|
655
|
+
const constraints = [...request.constraints_from_plan];
|
|
656
|
+
let objective = task.trim().slice(0, 200);
|
|
657
|
+
const successCriteria = [];
|
|
658
|
+
const hardConstraints = [...constraints];
|
|
659
|
+
if (/test|测试/i.test(task)) {
|
|
660
|
+
successCriteria.push("All tests pass");
|
|
661
|
+
}
|
|
662
|
+
if (/compat|兼容/i.test(task)) {
|
|
663
|
+
successCriteria.push("Backward compatibility maintained");
|
|
664
|
+
}
|
|
665
|
+
if (/security|安全|audit/i.test(task)) {
|
|
666
|
+
successCriteria.push("No security vulnerabilities found");
|
|
667
|
+
}
|
|
668
|
+
// Try to extract richer objective from plan_source
|
|
669
|
+
if (request.plan_source) {
|
|
670
|
+
const planExtracted = extractObjectiveFromPlan(request.plan_source);
|
|
671
|
+
if (planExtracted) {
|
|
672
|
+
if (planExtracted.objective) {
|
|
673
|
+
objective = planExtracted.objective;
|
|
674
|
+
}
|
|
675
|
+
if (planExtracted.success_criteria) {
|
|
676
|
+
successCriteria.push(...planExtracted.success_criteria);
|
|
677
|
+
}
|
|
678
|
+
if (planExtracted.hard_constraints) {
|
|
679
|
+
hardConstraints.push(...planExtracted.hard_constraints);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
else {
|
|
683
|
+
hardConstraints.push(`Follow plan: ${request.plan_source}`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
if (!successCriteria.length) {
|
|
687
|
+
successCriteria.push("Task completed successfully");
|
|
688
|
+
}
|
|
689
|
+
if (!hardConstraints.length) {
|
|
690
|
+
hardConstraints.push("Do not modify files outside scope");
|
|
691
|
+
}
|
|
692
|
+
return makeLoopObjective({
|
|
693
|
+
objective,
|
|
694
|
+
success_criteria: successCriteria,
|
|
695
|
+
hard_constraints: hardConstraints,
|
|
696
|
+
created_at_round: 1,
|
|
697
|
+
loop_id: request.loop_id,
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
701
|
+
// Compilation — L0 / L1 / L2
|
|
702
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
703
|
+
function compileL0(request, vaultContext, prevRound) {
|
|
704
|
+
const prev = prevRound ??
|
|
705
|
+
getPreviousRound(request.loop_id, request.round - 1, vaultContext);
|
|
706
|
+
const cachedPrompt = prev?.prompt_text ?? "";
|
|
707
|
+
if (cachedPrompt) {
|
|
708
|
+
return makeLoopCompileResponse({
|
|
709
|
+
status: "ok",
|
|
710
|
+
prompt: cachedPrompt,
|
|
711
|
+
recompile_level: "l0",
|
|
712
|
+
diff_from_previous: `L0 cache hit — reusing prompt from round ${request.round - 1}`,
|
|
713
|
+
lineage: [`${request.loop_id}:r${request.round}`],
|
|
714
|
+
constraints_active: prev?.constraints_active ?? [],
|
|
715
|
+
constraints_retired: [],
|
|
716
|
+
technique_used: "cached",
|
|
717
|
+
loop_id: request.loop_id,
|
|
718
|
+
round: request.round,
|
|
719
|
+
goal_id: deriveGoalId(request.loop_id, request.task, request.goal_id),
|
|
720
|
+
goal_text_hash: computeGoalTextHash(request.task),
|
|
721
|
+
plan_source: request.plan_source,
|
|
722
|
+
warnings: [],
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
// Auto-escalate: no cached prompt → delegate to L2
|
|
726
|
+
const l2Response = compileL2(request, vaultContext);
|
|
727
|
+
l2Response.recompile_level = "l0";
|
|
728
|
+
l2Response.diff_from_previous =
|
|
729
|
+
`L0 auto-escalated to L2 — no cached prompt available from round ${request.round - 1}`;
|
|
730
|
+
return l2Response;
|
|
731
|
+
}
|
|
732
|
+
function compileL1(request, vaultContext, prevRound) {
|
|
733
|
+
const prev = prevRound ??
|
|
734
|
+
getPreviousRound(request.loop_id, request.round - 1, vaultContext);
|
|
735
|
+
const goalId = deriveGoalId(request.loop_id, request.task, request.goal_id);
|
|
736
|
+
const newConstraints = [...request.constraints_from_plan];
|
|
737
|
+
let activeRaw = [...(prev?.constraints_active ?? []), ...newConstraints];
|
|
738
|
+
// Deduplicate preserving order
|
|
739
|
+
activeRaw = [...new Set(activeRaw)];
|
|
740
|
+
// v3.5: Constraint retirement
|
|
741
|
+
const { active, retired } = computeConstraintRetirement(activeRaw, request.loop_id, request.round, vaultContext);
|
|
742
|
+
// v3.5: Rolling summary
|
|
743
|
+
const rollingSummary = buildRollingSummary(request.loop_id, request.round, vaultContext);
|
|
744
|
+
const rollingText = formatRollingSummaryForPrompt(rollingSummary);
|
|
745
|
+
const violations = request.last_round_result?.constraint_violations ?? [];
|
|
746
|
+
const diffParts = [];
|
|
747
|
+
if (newConstraints.length) {
|
|
748
|
+
diffParts.push(`new constraints: [${newConstraints.join(", ")}]`);
|
|
749
|
+
}
|
|
750
|
+
if (retired.length) {
|
|
751
|
+
diffParts.push(`retired constraints: [${retired.join(", ")}]`);
|
|
752
|
+
}
|
|
753
|
+
if (violations.length) {
|
|
754
|
+
diffParts.push(`violations from last round: [${violations.join(", ")}]`);
|
|
755
|
+
}
|
|
756
|
+
if (request.new_since_last_round) {
|
|
757
|
+
diffParts.push(`delta: ${request.new_since_last_round.slice(0, 200)}`);
|
|
758
|
+
}
|
|
759
|
+
// Build patched prompt
|
|
760
|
+
const lines = [
|
|
761
|
+
`## Loop Round ${request.round} — L1 Patch`,
|
|
762
|
+
"",
|
|
763
|
+
`**Goal**: ${request.task}`,
|
|
764
|
+
`**Loop ID**: ${request.loop_id}`,
|
|
765
|
+
`**Goal ID**: ${goalId}`,
|
|
766
|
+
"",
|
|
767
|
+
];
|
|
768
|
+
if (rollingText) {
|
|
769
|
+
lines.push(rollingText);
|
|
770
|
+
lines.push("");
|
|
771
|
+
}
|
|
772
|
+
if (active.length) {
|
|
773
|
+
lines.push("### Active Constraints (inherited + new, pruned)");
|
|
774
|
+
for (const c of active)
|
|
775
|
+
lines.push(`- ${c}`);
|
|
776
|
+
lines.push("");
|
|
777
|
+
}
|
|
778
|
+
if (retired.length) {
|
|
779
|
+
lines.push("### Retired Constraints (no recent activity)");
|
|
780
|
+
for (const c of retired)
|
|
781
|
+
lines.push(`- ~${c}~`);
|
|
782
|
+
lines.push("");
|
|
783
|
+
}
|
|
784
|
+
if (violations.length) {
|
|
785
|
+
lines.push("### Violations From Last Round (must fix)");
|
|
786
|
+
for (const v of violations)
|
|
787
|
+
lines.push(`- ${v}`);
|
|
788
|
+
lines.push("");
|
|
789
|
+
}
|
|
790
|
+
if (request.new_since_last_round) {
|
|
791
|
+
lines.push("### What Changed Since Last Round");
|
|
792
|
+
lines.push(request.new_since_last_round);
|
|
793
|
+
lines.push("");
|
|
794
|
+
}
|
|
795
|
+
if (request.last_round_result?.output_summary) {
|
|
796
|
+
lines.push("### Last Round Summary");
|
|
797
|
+
lines.push(request.last_round_result.output_summary);
|
|
798
|
+
lines.push("");
|
|
799
|
+
}
|
|
800
|
+
lines.push("### Task");
|
|
801
|
+
lines.push(request.task);
|
|
802
|
+
return makeLoopCompileResponse({
|
|
803
|
+
status: "ok",
|
|
804
|
+
prompt: lines.join("\n"),
|
|
805
|
+
recompile_level: "l1",
|
|
806
|
+
diff_from_previous: diffParts.length ? diffParts.join("; ") : "Patch applied.",
|
|
807
|
+
lineage: [`${request.loop_id}:r${request.round}`],
|
|
808
|
+
constraints_active: active,
|
|
809
|
+
constraints_retired: retired,
|
|
810
|
+
technique_used: "patch",
|
|
811
|
+
rolling_summary: rollingSummary,
|
|
812
|
+
loop_id: request.loop_id,
|
|
813
|
+
round: request.round,
|
|
814
|
+
goal_id: goalId,
|
|
815
|
+
goal_text_hash: computeGoalTextHash(request.task),
|
|
816
|
+
plan_source: request.plan_source,
|
|
817
|
+
warnings: [],
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
export function compileL2(request, vaultContext) {
|
|
821
|
+
const goalId = deriveGoalId(request.loop_id, request.task, request.goal_id);
|
|
822
|
+
// v3.5: Adaptive technique routing
|
|
823
|
+
const analysis = routeTechniqueAdaptive(request.task, vaultContext, request.loop_id);
|
|
824
|
+
const technique = analysis.technique;
|
|
825
|
+
const referenceFile = analysis.reference_file;
|
|
826
|
+
// v3.5: Rolling summary
|
|
827
|
+
const rollingSummary = buildRollingSummary(request.loop_id, request.round, vaultContext);
|
|
828
|
+
const rollingText = formatRollingSummaryForPrompt(rollingSummary);
|
|
829
|
+
// Generate loop objective at round 1 if not provided
|
|
830
|
+
let loopObjective = null;
|
|
831
|
+
if (request.round === 1) {
|
|
832
|
+
if (request.loop_objective) {
|
|
833
|
+
loopObjective = request.loop_objective;
|
|
834
|
+
}
|
|
835
|
+
else {
|
|
836
|
+
loopObjective = computeLoopObjectiveFromTask(request, vaultContext);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
let constraints = [...request.constraints_from_plan];
|
|
840
|
+
if (loopObjective) {
|
|
841
|
+
constraints = [...new Set([...constraints, ...loopObjective.hard_constraints])];
|
|
842
|
+
}
|
|
843
|
+
// ── Route to technique-specific specialist (v1.1 deep integration) ──
|
|
844
|
+
let prompt;
|
|
845
|
+
if (technique === "step-back") {
|
|
846
|
+
prompt = compileStepBack(request, goalId, constraints, loopObjective, rollingText, analysis);
|
|
847
|
+
}
|
|
848
|
+
else if (technique === "least-to-most") {
|
|
849
|
+
prompt = compileLeastToMost(request, goalId, constraints, loopObjective, rollingText, analysis);
|
|
850
|
+
}
|
|
851
|
+
else if (technique === "tree-of-thought") {
|
|
852
|
+
prompt = compileToT(request, goalId, constraints, loopObjective, rollingText, analysis);
|
|
853
|
+
}
|
|
854
|
+
else {
|
|
855
|
+
prompt = compileGeneric(request, goalId, constraints, loopObjective, rollingText, analysis, referenceFile, technique);
|
|
856
|
+
}
|
|
857
|
+
return makeLoopCompileResponse({
|
|
858
|
+
status: "ok",
|
|
859
|
+
prompt,
|
|
860
|
+
recompile_level: "l2",
|
|
861
|
+
diff_from_previous: request.round === 1 || request.plan_source
|
|
862
|
+
? "Full recompile — new goal or first call."
|
|
863
|
+
: "Full recompile — goal_id changed or strategy collapse.",
|
|
864
|
+
lineage: [`${request.loop_id}:r${request.round}`],
|
|
865
|
+
constraints_active: constraints,
|
|
866
|
+
constraints_retired: [],
|
|
867
|
+
technique_used: technique,
|
|
868
|
+
reference_file: referenceFile,
|
|
869
|
+
rolling_summary: rollingSummary,
|
|
870
|
+
loop_id: request.loop_id,
|
|
871
|
+
round: request.round,
|
|
872
|
+
goal_id: goalId,
|
|
873
|
+
goal_text_hash: computeGoalTextHash(request.task),
|
|
874
|
+
loop_objective: loopObjective,
|
|
875
|
+
plan_source: request.plan_source,
|
|
876
|
+
warnings: [],
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
function buildHeader(ctx, technique) {
|
|
880
|
+
const lines = [
|
|
881
|
+
`## LoopForge L2 Compile — Round ${ctx.request.round}`,
|
|
882
|
+
`**Technique**: ${technique} (embedded)`,
|
|
883
|
+
"",
|
|
884
|
+
];
|
|
885
|
+
if (ctx.rollingText) {
|
|
886
|
+
lines.push(ctx.rollingText);
|
|
887
|
+
lines.push("");
|
|
888
|
+
}
|
|
889
|
+
return lines;
|
|
890
|
+
}
|
|
891
|
+
function buildLoopObjectiveBlock(lo) {
|
|
892
|
+
if (!lo)
|
|
893
|
+
return [];
|
|
894
|
+
const lines = [
|
|
895
|
+
"### Loop Objective (Anchor)",
|
|
896
|
+
`**Objective**: ${lo.objective}`,
|
|
897
|
+
];
|
|
898
|
+
if (lo.success_criteria.length) {
|
|
899
|
+
lines.push("**Success Criteria**:");
|
|
900
|
+
for (const sc of lo.success_criteria)
|
|
901
|
+
lines.push(`- ${sc}`);
|
|
902
|
+
}
|
|
903
|
+
if (lo.hard_constraints.length) {
|
|
904
|
+
lines.push("**Hard Constraints**:");
|
|
905
|
+
for (const hc of lo.hard_constraints)
|
|
906
|
+
lines.push(`- ${hc}`);
|
|
907
|
+
}
|
|
908
|
+
lines.push("");
|
|
909
|
+
return lines;
|
|
910
|
+
}
|
|
911
|
+
function buildConstraintsBlock(constraints) {
|
|
912
|
+
if (!constraints.length)
|
|
913
|
+
return [];
|
|
914
|
+
const lines = ["### Active Constraints"];
|
|
915
|
+
for (const c of constraints)
|
|
916
|
+
lines.push(`- ${c}`);
|
|
917
|
+
lines.push("");
|
|
918
|
+
return lines;
|
|
919
|
+
}
|
|
920
|
+
function buildTaskBlock(ctx) {
|
|
921
|
+
const lines = [
|
|
922
|
+
"### Task",
|
|
923
|
+
ctx.request.task,
|
|
924
|
+
"",
|
|
925
|
+
];
|
|
926
|
+
if (ctx.request.domain) {
|
|
927
|
+
lines.push(`**Domain**: ${ctx.request.domain}`);
|
|
928
|
+
lines.push("");
|
|
929
|
+
}
|
|
930
|
+
return lines;
|
|
931
|
+
}
|
|
932
|
+
function buildIdentityBlock(ctx) {
|
|
933
|
+
return [
|
|
934
|
+
"### Loop Identity",
|
|
935
|
+
`- Loop ID: \`${ctx.request.loop_id}\``,
|
|
936
|
+
`- Goal ID: \`${ctx.goalId}\``,
|
|
937
|
+
`- Round: ${ctx.request.round}`,
|
|
938
|
+
"",
|
|
939
|
+
];
|
|
940
|
+
}
|
|
941
|
+
/** Generic compiler — reads technique reference file (fallback for zero-shot/few-shot/cot). */
|
|
942
|
+
function compileGeneric(request, goalId, constraints, loopObjective, rollingText, analysis, referenceFile, technique) {
|
|
943
|
+
const ctx = { request, goalId, constraints, loopObjective, rollingText };
|
|
944
|
+
const lines = [
|
|
945
|
+
...buildHeader(ctx, technique),
|
|
946
|
+
`Read the technique reference BEFORE generating the prompt:`,
|
|
947
|
+
` Technique: ${technique}`,
|
|
948
|
+
` Reference: ${referenceFile}`,
|
|
949
|
+
` Rationale: ${analysis.rationale}`,
|
|
950
|
+
"",
|
|
951
|
+
...buildLoopObjectiveBlock(loopObjective),
|
|
952
|
+
...buildConstraintsBlock(constraints),
|
|
953
|
+
...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []),
|
|
954
|
+
...buildTaskBlock(ctx),
|
|
955
|
+
...buildIdentityBlock(ctx),
|
|
956
|
+
"### Generation Instructions",
|
|
957
|
+
`1. Read \`${referenceFile}\` — study its structure rules, section count, and format requirements`,
|
|
958
|
+
"2. Generate a complete prompt following that technique's structure",
|
|
959
|
+
"3. Inject all hard constraints and the loop objective into the prompt",
|
|
960
|
+
"4. If Cross-Round Summary is present above, incorporate its recurring issues and key lessons",
|
|
961
|
+
"5. The prompt must be self-contained — ready for a coding agent to execute",
|
|
962
|
+
"6. Output only the generated prompt — no preamble, no meta-commentary",
|
|
963
|
+
];
|
|
964
|
+
return lines.join("\n");
|
|
965
|
+
}
|
|
966
|
+
// ── Step-Back specialist ───────────────────────────────────────────────────────
|
|
967
|
+
function compileStepBack(request, goalId, constraints, loopObjective, rollingText, analysis) {
|
|
968
|
+
const ctx = { request, goalId, constraints, loopObjective, rollingText };
|
|
969
|
+
const lines = [
|
|
970
|
+
...buildHeader(ctx, "step-back"),
|
|
971
|
+
`**Rationale**: ${analysis.rationale}`,
|
|
972
|
+
"",
|
|
973
|
+
"Generate a complete prompt using the **Step-Back** technique. Follow the 8-section skeleton below.",
|
|
974
|
+
"Embed all structural constraints — do NOT reference an external file.",
|
|
975
|
+
"",
|
|
976
|
+
...buildLoopObjectiveBlock(loopObjective),
|
|
977
|
+
...buildConstraintsBlock(constraints),
|
|
978
|
+
...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []),
|
|
979
|
+
...buildTaskBlock(ctx),
|
|
980
|
+
...buildIdentityBlock(ctx),
|
|
981
|
+
"",
|
|
982
|
+
"### 8-Section Skeleton (REQUIRED)",
|
|
983
|
+
"",
|
|
984
|
+
"| # | Section | Required | Notes |",
|
|
985
|
+
"|---|---------|----------|-------|",
|
|
986
|
+
"| 1 | 角色 | ✓ | Domain expert role appropriate to the task |",
|
|
987
|
+
"| 2 | 任务 | ✓ | One-sentence task summary |",
|
|
988
|
+
"| 3 | 输入 | ✓ | Target module/problem to analyse |",
|
|
989
|
+
"| 4 | 输出格式 | ✓ | Numbered list of expected outputs |",
|
|
990
|
+
"| 5 | **Step-Back 抽象框架** | ✓ | See format rules below |",
|
|
991
|
+
"| 6 | 具体实现要求 | ✓ | MUST start with: **\"基于上述抽象框架,实现以下所有功能\"** |",
|
|
992
|
+
"| 7 | 硬约束 | ✓ | Numbered list of hard constraints |",
|
|
993
|
+
"| 8 | 生成要求 | ✓ | Acceptance criteria |",
|
|
994
|
+
"",
|
|
995
|
+
"### Section 5 — Step-Back 抽象框架 (CRITICAL)",
|
|
996
|
+
"",
|
|
997
|
+
"Must contain **2-3 abstract frameworks**. Each framework is an independent ASCII diagram.",
|
|
998
|
+
"Frameworks are PARALLEL (peer-level), not sequential.",
|
|
999
|
+
"",
|
|
1000
|
+
"Each framework format:",
|
|
1001
|
+
"```",
|
|
1002
|
+
"### 框架N:[Framework Name]",
|
|
1003
|
+
"",
|
|
1004
|
+
"[ASCII diagram with principles/formulas/classification tables/rule tables]",
|
|
1005
|
+
"",
|
|
1006
|
+
"Diagram content requirements:",
|
|
1007
|
+
"- Core principle/formula (e.g. \"Total = Σ(dimension × weight)\")",
|
|
1008
|
+
"- Component table (e.g. weight allocation table, type mapping table)",
|
|
1009
|
+
"- Rule description (e.g. \"Missing data dimensions use neutral value\")",
|
|
1010
|
+
"```",
|
|
1011
|
+
"",
|
|
1012
|
+
"**Tightening rules**:",
|
|
1013
|
+
"- The step-back question must be MORE ABSTRACT than the original problem, but cover all required information",
|
|
1014
|
+
"- Abstract ≠ vague. Use precise formulas, principles, standards, definitions, or causal mechanisms",
|
|
1015
|
+
"- Tighten to the MINIMUM generalisation layer that still covers the original problem",
|
|
1016
|
+
"- Reasoning must RETURN to the original problem — do not stay at the principle level",
|
|
1017
|
+
"",
|
|
1018
|
+
"**Section 6 transition sentence (MANDATORY)**:",
|
|
1019
|
+
"\"基于上述抽象框架,实现以下所有功能。\"",
|
|
1020
|
+
"",
|
|
1021
|
+
"### Quality Checklist",
|
|
1022
|
+
"- [ ] Frameworks are abstract concepts/formulas/principles, NOT concrete code",
|
|
1023
|
+
"- [ ] Section 6 starts with the mandatory transition sentence",
|
|
1024
|
+
"- [ ] Abstraction level is consistent across frameworks",
|
|
1025
|
+
"- [ ] Step-back question is tightened to minimum necessary generalisation",
|
|
1026
|
+
"",
|
|
1027
|
+
"### Generation Instructions",
|
|
1028
|
+
"1. Extract 2-3 abstract principles/frameworks from the task domain",
|
|
1029
|
+
"2. Build ASCII diagrams for each framework (formulas, tables, rules)",
|
|
1030
|
+
"3. Apply the abstraction back to the concrete task",
|
|
1031
|
+
"4. Inject all hard constraints and the loop objective into the prompt",
|
|
1032
|
+
"5. If Cross-Round Summary is present above, incorporate its recurring issues and key lessons",
|
|
1033
|
+
"6. Output only the generated prompt — no preamble, no meta-commentary",
|
|
1034
|
+
];
|
|
1035
|
+
return lines.join("\n");
|
|
1036
|
+
}
|
|
1037
|
+
// ── Least-to-Most specialist ───────────────────────────────────────────────────
|
|
1038
|
+
function compileLeastToMost(request, goalId, constraints, loopObjective, rollingText, analysis) {
|
|
1039
|
+
const ctx = { request, goalId, constraints, loopObjective, rollingText };
|
|
1040
|
+
const lines = [
|
|
1041
|
+
...buildHeader(ctx, "least-to-most"),
|
|
1042
|
+
`**Rationale**: ${analysis.rationale}`,
|
|
1043
|
+
"",
|
|
1044
|
+
"Generate a complete prompt using the **Least-to-Most** technique. Follow the 8-section skeleton below.",
|
|
1045
|
+
"Embed all structural constraints — do NOT reference an external file.",
|
|
1046
|
+
"",
|
|
1047
|
+
...buildLoopObjectiveBlock(loopObjective),
|
|
1048
|
+
...buildConstraintsBlock(constraints),
|
|
1049
|
+
...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []),
|
|
1050
|
+
...buildTaskBlock(ctx),
|
|
1051
|
+
...buildIdentityBlock(ctx),
|
|
1052
|
+
"",
|
|
1053
|
+
"### 8-Section Skeleton (REQUIRED)",
|
|
1054
|
+
"",
|
|
1055
|
+
"| # | Section | Required | Notes |",
|
|
1056
|
+
"|---|---------|----------|-------|",
|
|
1057
|
+
"| 1 | 角色 | ✓ | Domain expert role appropriate to the task |",
|
|
1058
|
+
"| 2 | 任务 | ✓ | One-sentence task summary |",
|
|
1059
|
+
"| 3 | 输入 | ✓ | Target module |",
|
|
1060
|
+
"| 4 | 输出格式 | ✓ | Numbered list (e.g. \"1. DDL, 2. API, 3. Enum...\") |",
|
|
1061
|
+
"| 5 | **Least-to-Most 逐步推理框架** | ✓ | See format rules below |",
|
|
1062
|
+
"| 6 | 具体实现要求 | ✓ | Expand per output format list (NOT per sub-problem — sub-problems fully expanded in §5) |",
|
|
1063
|
+
"| 7 | 硬约束 | ✓ | Numbered list |",
|
|
1064
|
+
"| 8 | 生成要求 | ✓ | Acceptance criteria, must include: \"严格按照子问题顺序逐步实现\" |",
|
|
1065
|
+
"",
|
|
1066
|
+
"### Section 5 — Least-to-Most 逐步推理框架 (CRITICAL)",
|
|
1067
|
+
"",
|
|
1068
|
+
"Must contain **4-6 ordered sub-problems**. Each sub-problem format:",
|
|
1069
|
+
"",
|
|
1070
|
+
"```",
|
|
1071
|
+
"### 子问题 N:[Sub-problem Name]",
|
|
1072
|
+
"",
|
|
1073
|
+
"**目标:** [What this sub-problem solves, which prior sub-problem(s) it depends on]",
|
|
1074
|
+
"",
|
|
1075
|
+
"**要求:**",
|
|
1076
|
+
"- Specific requirement list",
|
|
1077
|
+
"- [If sub-problem involves enums/mapping tables, list them]",
|
|
1078
|
+
"",
|
|
1079
|
+
"---",
|
|
1080
|
+
"```",
|
|
1081
|
+
"",
|
|
1082
|
+
"**Critical rules**:",
|
|
1083
|
+
"- Sub-problem 1 is the SIMPLEST (e.g. \"Define enums and base data structures\")",
|
|
1084
|
+
"- Each sub-problem declares: \"基于子问题 N-1 的结论\"",
|
|
1085
|
+
"- The LAST sub-problem MUST be: **\"综合实现完整模块\"** — list all components to integrate",
|
|
1086
|
+
"- Sub-problems separated by `---`",
|
|
1087
|
+
"- Order goes from LEAST complex → MOST complex, dependency chain must be explicit",
|
|
1088
|
+
"- Each sub-problem must serve the original task — no unrelated steps",
|
|
1089
|
+
"- The final sub-problem must be equivalent to or directly address the original task",
|
|
1090
|
+
"",
|
|
1091
|
+
"### Quality Checklist",
|
|
1092
|
+
"- [ ] Sub-problems ordered simplest → most complex, dependency chain explicit",
|
|
1093
|
+
"- [ ] Each sub-problem declares its prerequisite sub-problems",
|
|
1094
|
+
"- [ ] Last sub-problem is \"综合实现完整模块\" with integration list",
|
|
1095
|
+
"- [ ] Section 6 expands per output format list (NOT per sub-problem)",
|
|
1096
|
+
"- [ ] Sub-problem count: 4-6, no more, no less",
|
|
1097
|
+
"",
|
|
1098
|
+
"### Generation Instructions",
|
|
1099
|
+
"1. Decompose the task into 4-6 ordered sub-problems with explicit dependencies",
|
|
1100
|
+
"2. Sub-problem 1 starts with the simplest building block (data structures, enums, base config)",
|
|
1101
|
+
"3. Each subsequent sub-problem builds on prior results",
|
|
1102
|
+
"4. Final sub-problem integrates all components into the complete module",
|
|
1103
|
+
"5. Inject all hard constraints and the loop objective into the prompt",
|
|
1104
|
+
"6. If Cross-Round Summary is present above, incorporate its recurring issues and key lessons",
|
|
1105
|
+
"7. Output only the generated prompt — no preamble, no meta-commentary",
|
|
1106
|
+
];
|
|
1107
|
+
return lines.join("\n");
|
|
1108
|
+
}
|
|
1109
|
+
// ── Tree-of-Thought specialist ──────────────────────────────────────────────────
|
|
1110
|
+
function compileToT(request, goalId, constraints, loopObjective, rollingText, analysis) {
|
|
1111
|
+
const ctx = { request, goalId, constraints, loopObjective, rollingText };
|
|
1112
|
+
const lines = [
|
|
1113
|
+
...buildHeader(ctx, "tree-of-thought"),
|
|
1114
|
+
`**Rationale**: ${analysis.rationale}`,
|
|
1115
|
+
"",
|
|
1116
|
+
"Generate a complete prompt using the **Tree-of-Thought** technique. Follow the 8-section skeleton below.",
|
|
1117
|
+
"Embed all structural constraints — do NOT reference an external file.",
|
|
1118
|
+
"",
|
|
1119
|
+
...buildLoopObjectiveBlock(loopObjective),
|
|
1120
|
+
...buildConstraintsBlock(constraints),
|
|
1121
|
+
...(request.plan_source ? [`**Plan Source**: ${request.plan_source}`, ""] : []),
|
|
1122
|
+
...buildTaskBlock(ctx),
|
|
1123
|
+
...buildIdentityBlock(ctx),
|
|
1124
|
+
"",
|
|
1125
|
+
"### 8-Section Skeleton (REQUIRED)",
|
|
1126
|
+
"",
|
|
1127
|
+
"| # | Section | Required | Notes |",
|
|
1128
|
+
"|---|---------|----------|-------|",
|
|
1129
|
+
"| 1 | 角色 | ✓ | Multi-path problem-solving expert; may introduce 3 expert personas |",
|
|
1130
|
+
"| 2 | 任务 | ✓ | One-sentence task summary |",
|
|
1131
|
+
"| 3 | 输入 | ✓ | High-risk complex problem |",
|
|
1132
|
+
"| 4 | 输出格式 | ✓ | \"先输出思维树过程,再输出最终答案/方案\" |",
|
|
1133
|
+
"| 5 | **思维树探索框架** | ✓ | See 3 sub-blocks below (REQUIRED) |",
|
|
1134
|
+
"| 6 | 具体实现要求 | ✓ | Expand per output format |",
|
|
1135
|
+
"| 7 | 硬约束 | ✓ | Include branch_count, max_depth, pruning rules, safety/performance constraints |",
|
|
1136
|
+
"| 8 | 生成要求 | ✓ | \"先探索多路径,再选择最优方案\" |",
|
|
1137
|
+
"",
|
|
1138
|
+
"### Section 5 — 思维树探索框架 (CRITICAL — 3 sub-blocks REQUIRED)",
|
|
1139
|
+
"",
|
|
1140
|
+
"**Sub-block A: Search Strategy Declaration**",
|
|
1141
|
+
"```",
|
|
1142
|
+
"搜索策略: [beam / dfs / expert-panel]",
|
|
1143
|
+
"分支数(branch_count): 2-4",
|
|
1144
|
+
"最大深度(max_depth): ≤3",
|
|
1145
|
+
"每轮保留数(keep_count): 1-2",
|
|
1146
|
+
"```",
|
|
1147
|
+
"Strategy selection guide:",
|
|
1148
|
+
"- **beam** (default): planning, creative, math search — generate multiple candidates per turn, keep top b",
|
|
1149
|
+
"- **dfs**: puzzles, constraint satisfaction, debugging — go deep on highest-score branch, backtrack on failure",
|
|
1150
|
+
"- **expert-panel**: 3+ experts each generate candidates, then cross-evaluate and revise",
|
|
1151
|
+
"",
|
|
1152
|
+
"**Sub-block B: Evaluation Criteria Table**",
|
|
1153
|
+
"| 标准 | 权重 | 说明 |",
|
|
1154
|
+
"|------|------|------|",
|
|
1155
|
+
"| 正确性 | 最高 | Logic/math correctness |",
|
|
1156
|
+
"| 可行性 | 高 | Can be actually implemented |",
|
|
1157
|
+
"| 约束匹配 | 高 | Satisfies hard constraints |",
|
|
1158
|
+
"| 性能 | 中 | Time/space efficiency |",
|
|
1159
|
+
"| 安全性 | 最高 | No vulnerabilities/privilege escalation |",
|
|
1160
|
+
"",
|
|
1161
|
+
"**Sub-block C: Thought Tree State Table Format**",
|
|
1162
|
+
"Require the model to output in this table format:",
|
|
1163
|
+
"```",
|
|
1164
|
+
"| 轮次 | 分支 | 候选方案 | 评估 | 决策 |",
|
|
1165
|
+
"|------|------|---------|------|------|",
|
|
1166
|
+
"| 1 | A | [方案描述] | 评分/判断 | 保留/剪枝 |",
|
|
1167
|
+
"| 1 | B | [方案描述] | 评分/判断 | 保留/剪枝 |",
|
|
1168
|
+
"| 1 | C | [方案描述] | 评分/判断 | 保留/剪枝 |",
|
|
1169
|
+
"| 2 | A1 | [深入展开] | ... | ... |",
|
|
1170
|
+
"| ... | ... | ... | ... | ... |",
|
|
1171
|
+
"",
|
|
1172
|
+
"最终选择: [最优方案 + 选择理由]",
|
|
1173
|
+
"```",
|
|
1174
|
+
"",
|
|
1175
|
+
"### Critical Rules",
|
|
1176
|
+
"- Hard constraints MUST be ranked FIRST in evaluation criteria",
|
|
1177
|
+
"- Branch count: 2-4, depth: ≤3 (prevents token explosion)",
|
|
1178
|
+
"- Every turn: generate multiple candidates → evaluate → keep/prune — never single-path",
|
|
1179
|
+
"- If all branches are low quality: backtrack to previous turn and regenerate",
|
|
1180
|
+
"- The core of ToT is NOT \"3 experts chatting\" — it's: candidate generation → state evaluation → search/prune → final selection",
|
|
1181
|
+
"- thought is a PUBLIC intermediate semantic unit, not hidden chain-of-thought",
|
|
1182
|
+
"- State table MUST have: round, branch ID, candidate description, evaluation score/judgment, keep/prune decision",
|
|
1183
|
+
"",
|
|
1184
|
+
"### Quality Checklist",
|
|
1185
|
+
"- [ ] Branch count 2-4, depth ≤3 (prevents token explosion)",
|
|
1186
|
+
"- [ ] Evaluation criteria table has ≥3 dimensions including correctness, feasibility, constraint matching",
|
|
1187
|
+
"- [ ] State table format explicit (round/branch/candidate/evaluation/decision)",
|
|
1188
|
+
"- [ ] Search strategy matches task type (beam/dfs/expert-panel)",
|
|
1189
|
+
"- [ ] Hard constraints listed FIRST in evaluation criteria",
|
|
1190
|
+
"- [ ] Not \"3 experts casually discussing\" — has explicit branch count, depth, scoring, and pruning rules",
|
|
1191
|
+
"- [ ] Ends with model outputting final selection and rationale",
|
|
1192
|
+
"",
|
|
1193
|
+
"### Generation Instructions",
|
|
1194
|
+
"1. Select the appropriate search strategy (beam/dfs/expert-panel) based on task type",
|
|
1195
|
+
"2. Define 2-4 candidate approaches as initial branches",
|
|
1196
|
+
"3. Build evaluation criteria table with hard constraints ranked first",
|
|
1197
|
+
"4. Define the state table format for tracking exploration",
|
|
1198
|
+
"5. Set branch count and max depth to prevent token explosion",
|
|
1199
|
+
"6. Inject all hard constraints and the loop objective into the prompt",
|
|
1200
|
+
"7. If Cross-Round Summary is present above, incorporate its recurring issues and key lessons",
|
|
1201
|
+
"8. Output only the generated prompt — no preamble, no meta-commentary",
|
|
1202
|
+
];
|
|
1203
|
+
return lines.join("\n");
|
|
1204
|
+
}
|
|
1205
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1206
|
+
// Top-level compile — ties layers together
|
|
1207
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
1208
|
+
export function compileLoop(request, vaultContext = null) {
|
|
1209
|
+
// Layer 1: Decide compile level
|
|
1210
|
+
const level = decideLevel(request, vaultContext);
|
|
1211
|
+
// Compile at the decided level
|
|
1212
|
+
let response;
|
|
1213
|
+
if (level === "l0") {
|
|
1214
|
+
response = compileL0(request, vaultContext);
|
|
1215
|
+
}
|
|
1216
|
+
else if (level === "l1") {
|
|
1217
|
+
response = compileL1(request, vaultContext);
|
|
1218
|
+
}
|
|
1219
|
+
else {
|
|
1220
|
+
response = compileL2(request, vaultContext);
|
|
1221
|
+
}
|
|
1222
|
+
// Layer 2: Compute advisories
|
|
1223
|
+
const { warnings, suggestedNextTask, alignment, health } = computeAdvisories(request, vaultContext);
|
|
1224
|
+
// Merge advisories into response
|
|
1225
|
+
response.warnings = warnings;
|
|
1226
|
+
response.suggested_next_task = suggestedNextTask;
|
|
1227
|
+
response.task_alignment = alignment;
|
|
1228
|
+
response.loop_health = health;
|
|
1229
|
+
response.recompile_level = level;
|
|
1230
|
+
return response;
|
|
1231
|
+
}
|
|
1232
|
+
//# sourceMappingURL=loop-compiler.js.map
|