progmune-runtime 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/.mcp.json +11 -0
  2. package/.progmune_allowlist +50 -0
  3. package/.test_report/test_report.md +87 -0
  4. package/Dockerfile +2 -10
  5. package/FAQ.md +167 -0
  6. package/demo-project/auth.ts +55 -0
  7. package/demo-project/tsconfig.json +8 -0
  8. package/dist/ab-stats.js +11 -0
  9. package/dist/acl-breakdown.js +13 -0
  10. package/dist/all-sessions.js +11 -0
  11. package/dist/antibody-stats.js +11 -0
  12. package/dist/audit.js +218 -0
  13. package/dist/benchmark-count.js +7 -0
  14. package/dist/benchmark-full.js +17 -0
  15. package/dist/benchmark-pass-rate.js +54 -0
  16. package/dist/benchmark-report.js +67 -0
  17. package/dist/benchmark-save.js +62 -0
  18. package/dist/benchmark-status.js +15 -0
  19. package/dist/branch-ledger.js +365 -0
  20. package/dist/branch-tree-count.js +14 -0
  21. package/dist/check.js +506 -0
  22. package/dist/common-fixpath.js +12 -0
  23. package/dist/constraint-types.js +12 -0
  24. package/dist/deterministic-replay.js +277 -0
  25. package/dist/emitter.js +112 -12
  26. package/dist/exec-metrics.js +11 -0
  27. package/dist/execute.js +242 -0
  28. package/dist/extract-ir.js +550 -5
  29. package/dist/failure-collector.js +143 -0
  30. package/dist/failure-corpus.js +481 -35
  31. package/dist/failure-report.js +11 -0
  32. package/dist/failures.js +11 -0
  33. package/dist/fast-path-hits.js +13 -0
  34. package/dist/feedback.js +6 -3
  35. package/dist/file-lock.js +82 -0
  36. package/dist/find-session.js +10 -0
  37. package/dist/fingerprint-list.js +15 -0
  38. package/dist/gen-history-log.js +13 -0
  39. package/dist/generate.js +2 -1
  40. package/dist/generate_500.js +4 -2
  41. package/dist/genome.js +11 -0
  42. package/dist/heatmap-data.js +11 -0
  43. package/dist/heatmap.js +11 -0
  44. package/dist/immune-reporter.js +137 -0
  45. package/dist/learned.js +11 -0
  46. package/dist/ledger-registry.js +241 -0
  47. package/dist/llm.js +43 -2
  48. package/dist/load-benchmarks.js +47 -0
  49. package/dist/main.js +2 -1
  50. package/dist/mcp-server.mjs +446 -34
  51. package/dist/memory-layer.js +51 -13
  52. package/dist/metrics.js +11 -0
  53. package/dist/obs-web.js +561 -0
  54. package/dist/p0_ssg_demo.js +255 -40
  55. package/dist/planner.js +933 -65
  56. package/dist/protocol-registry.js +105 -0
  57. package/dist/recent-session.js +12 -0
  58. package/dist/repair-proposal.js +353 -0
  59. package/dist/report.js +32 -0
  60. package/dist/runtime-invariants.js +161 -0
  61. package/dist/runtime-types.js +117 -0
  62. package/dist/search-planner.js +38 -9
  63. package/dist/semantic-snapshot.js +155 -0
  64. package/dist/semantic-trace.js +1497 -0
  65. package/dist/semantic-validator.js +3 -2
  66. package/dist/semantic_guard_test.js +2 -1
  67. package/dist/sessions.js +11 -0
  68. package/dist/ssg-validator.js +658 -20
  69. package/dist/svl-distribution.js +11 -0
  70. package/dist/terminal-status.js +11 -0
  71. package/dist/test_failure_corpus.js +3 -1
  72. package/dist/token-savings.js +11 -0
  73. package/dist/total-repairs.js +12 -0
  74. package/dist/unresolved-count.js +12 -0
  75. package/dist/valid-fingerprints.js +13 -0
  76. package/dist/validator.js +116 -60
  77. package/dist/verify-fps.js +11 -0
  78. package/dist/verify-ledgers.js +11 -0
  79. package/docs/whitepaper-style.css +77 -0
  80. package/docs/whitepaper-v2.1.md +609 -0
  81. package/docs/whitepaper-v2.2.md +1064 -0
  82. package/docs/whitepaper-v2.2.pdf +0 -0
  83. package/fly.toml +1 -1
  84. package/package.json +13 -4
  85. package/protocols.json +136 -0
  86. package/public/dashboard.html +119 -0
  87. package/readme.md +829 -0
  88. package/server/hub.js +84 -12
  89. package/test/replay-golden/sess_1780063202050_mgeld.json +9 -0
  90. package/test/replay-golden/sess_1780064032560_gocld.json +354 -0
  91. package/test/replay-golden/sess_1780064413331_s2709.json +606 -0
  92. package/test/replay-golden/sess_1780064792710_y3avo.json +614 -0
  93. package/test/replay-golden.ts +84 -0
  94. package/test_benchmark.js +165 -0
  95. package/test_comprehensive.mjs +638 -0
  96. package/test_concurrency.js +129 -0
  97. package/test_ir_robustness.js +85 -0
  98. package/test_semantic_contracts.js +269 -0
  99. package/test_ssg_stress.js +156 -0
  100. package/test_svl3.js +58 -0
  101. package/tsconfig.json +1 -1
  102. package/.env.example +0 -3
  103. package/.progmune_memory/fingerprints.json +0 -7
  104. package/.progmune_memory/opt_in.json +0 -4
  105. package/README.md +0 -118
  106. package/dist/mcp-server.js +0 -55
  107. package/immune_hub_data/2026-05-14.json +0 -50
package/dist/planner.js CHANGED
@@ -35,13 +35,16 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.plan = plan;
37
37
  const llm_1 = require("./llm");
38
+ const runtime_types_1 = require("./runtime-types");
38
39
  const action_runtime_1 = require("./action-runtime");
39
40
  const validator_1 = require("./validator");
40
41
  const semantic_validator_1 = require("./semantic-validator");
41
- const feedback_1 = require("./feedback");
42
42
  const utils_1 = require("./utils");
43
43
  const failure_corpus_1 = require("./failure-corpus");
44
44
  const memory_layer_1 = require("./memory-layer");
45
+ const ssg_validator_1 = require("./ssg-validator");
46
+ const protocol_registry_1 = require("./protocol-registry");
47
+ const semantic_snapshot_1 = require("./semantic-snapshot");
45
48
  const fs = __importStar(require("fs"));
46
49
  function enrichActions(actions, ir) {
47
50
  return actions.map(a => {
@@ -87,17 +90,536 @@ function determineConstraintType(svl) {
87
90
  case "SVL-4": return "protocol";
88
91
  }
89
92
  }
93
+ /** 构建紧凑函数列表(约节省 50% token) */
94
+ function buildCompactFuncList(funcs) {
95
+ return funcs.map((f) => {
96
+ const params = (f.params || []).map((p) => `${p.name}:${p.type}`).join(",");
97
+ return `${f.name}(${params})->${f.returnType || "any"}`;
98
+ }).join("\n");
99
+ }
100
+ const SYSTEM_PROMPT = `你是程序合成助手。只输出 JSON 数组,不输出解释。
101
+
102
+ 格式(紧凑一行):
103
+ [{"f":"函数名","to":"变量名","a":[{"n":"参数名","t":"类型","v":值}]},{"r":"变量名"}]
104
+
105
+ 规则:
106
+ - "f": 函数名,"to": 接收返回值的变量名(void 函数省略)
107
+ - "a": 参数数组,每个参数 n=名称 t=类型 v=值
108
+ - 字符串值直接写: "v":"hello",数字: "v":42
109
+ - 引用之前返回的变量: "v":"$变量名"($前缀表示这是变量引用)
110
+ - {"r":"变量名"} 表示返回该变量
111
+
112
+ 示例 — 提取 IR 后校验:
113
+ [{"f":"extractIR","to":"ir","a":[{"n":"root","t":"str","v":"."}]},{"f":"validateActionSequence","to":"ok","a":[{"n":"actions","t":"any","v":"$ir"}]},{"r":"ok"}]
114
+
115
+ 铁律:只输出 JSON 数组,双引号,不输出解释`;
116
+ const RETRY_HINT = `输出格式:紧凑 JSON 数组 [{"f":"函数名","to":"变量名","a":[...]}]`;
117
+ /** 构建重试 prompt:精简但包含必要的 IR 语法提示 */
118
+ /** 解析 LLM 输出的紧凑 JSON 为 Action[]。
119
+ * 格式: [{"f":"fn","to":"var","a":[{"n":"p","t":"str","v":"x"}]}, {"r":"var"}, ...]
120
+ * f=function(→call), to=assignTo, a=args, r=return, if=condition
121
+ */
122
+ function parseActionJSON(text) {
123
+ const clean = text.replace(/```(?:json|javascript)?\s*/gi, '').replace(/```\s*/g, '').trim();
124
+ try {
125
+ const arr = JSON.parse(clean);
126
+ if (!Array.isArray(arr))
127
+ return null;
128
+ const actions = [];
129
+ for (const item of arr) {
130
+ if (!item || typeof item !== 'object')
131
+ return null;
132
+ if (item.r !== undefined) {
133
+ actions.push({ kind: "return", value: item.r });
134
+ }
135
+ else if (item.f) {
136
+ const a = {
137
+ kind: "call",
138
+ function: item.f,
139
+ args: (item.a || []).map((p) => ({
140
+ name: p.n || "arg",
141
+ type: p.t || "any",
142
+ value: p.v ?? null,
143
+ })),
144
+ };
145
+ if (item.to)
146
+ a.assignTo = item.to;
147
+ actions.push(a);
148
+ }
149
+ else if (item.if) {
150
+ const a = {
151
+ kind: "if",
152
+ condition: item.if,
153
+ thenActions: item.then ? parseActionJSON(JSON.stringify(item.then)) || [] : [],
154
+ elseActions: item.else ? parseActionJSON(JSON.stringify(item.else)) || [] : [],
155
+ };
156
+ actions.push(a);
157
+ }
158
+ else if (item.kind) {
159
+ // 完整 Action 格式(后向兼容)
160
+ actions.push(item);
161
+ }
162
+ else {
163
+ return null;
164
+ }
165
+ }
166
+ return actions.length > 0 ? actions : null;
167
+ }
168
+ catch {
169
+ return null;
170
+ }
171
+ }
172
+ /** 模糊函数名纠正:当 LLM 生成不存在的函数名时,用 Jaccard 相似度找最接近的 IR 函数 */
173
+ function correctFunctionNames(actions, ir) {
174
+ const corrections = [];
175
+ const corrected = actions.map((a, i) => {
176
+ if (a.kind !== "call" || !a.function)
177
+ return a;
178
+ // 跳过已知存在的函数和白名单
179
+ if (ir.some((f) => f.name === a.function))
180
+ return a;
181
+ if (["if", "for", "assign", "return"].includes(a.function))
182
+ return a;
183
+ // 用 Jaccard 相似度找最佳匹配
184
+ let bestMatch = "";
185
+ let bestScore = 0;
186
+ const target = a.function.toLowerCase();
187
+ for (const fn of ir) {
188
+ const score = (0, utils_1.jaccardSimilarity)(target, fn.name.toLowerCase());
189
+ if (score > bestScore) {
190
+ bestScore = score;
191
+ bestMatch = fn.name;
192
+ }
193
+ }
194
+ if (bestMatch && bestScore >= 0.3) {
195
+ corrections.push(`action[${i}]: "${a.function}" → "${bestMatch}" (相似度 ${bestScore.toFixed(2)})`);
196
+ return { ...a, function: bestMatch };
197
+ }
198
+ return a;
199
+ });
200
+ return { actions: corrected, corrections };
201
+ }
202
+ /** 参数签名预检:确保 call action 的参数数量与 IR 函数签名一致 */
203
+ function fixParameterCounts(actions, ir) {
204
+ const fixes = [];
205
+ const corrected = actions.map((a, i) => {
206
+ if (a.kind !== "call" || !a.function)
207
+ return a;
208
+ const def = ir.find((f) => f.name === a.function);
209
+ if (!def || !def.params)
210
+ return a;
211
+ const expected = def.params.length;
212
+ const actual = a.args ? a.args.length : 0;
213
+ if (actual === expected)
214
+ return a;
215
+ if (actual < expected) {
216
+ // 参数太少:填充缺失参数
217
+ const padded = [...(a.args || [])];
218
+ for (let j = actual; j < expected; j++) {
219
+ padded.push({ name: def.params[j].name, type: def.params[j].type || "any", value: "" });
220
+ }
221
+ fixes.push(`action[${i}] ${a.function}: 参数 ${actual}→${expected} (填充 ${expected - actual} 个缺失参数)`);
222
+ return { ...a, args: padded };
223
+ }
224
+ else {
225
+ // 参数太多:截断多余参数
226
+ fixes.push(`action[${i}] ${a.function}: 参数 ${actual}→${expected} (截断 ${actual - expected} 个多余参数)`);
227
+ return { ...a, args: a.args.slice(0, expected) };
228
+ }
229
+ });
230
+ return { actions: corrected, fixes };
231
+ }
232
+ /** 构建协议链提示:为 LLM 显示协议状态机的合法调用顺序 */
233
+ function buildProtocolChainHint(protocols) {
234
+ if (protocols.length === 0)
235
+ return "";
236
+ // 按命名空间分组
237
+ const byNs = new Map();
238
+ for (const p of protocols) {
239
+ const ns = p.protocol.namespace || "_global";
240
+ if (!byNs.has(ns))
241
+ byNs.set(ns, []);
242
+ byNs.get(ns).push(p);
243
+ }
244
+ const lines = ["\n⚠️ 协议约束(必须严格遵循调用顺序):"];
245
+ for (const [ns, fns] of byNs) {
246
+ if (ns === "_global" || fns.length <= 1)
247
+ continue;
248
+ lines.push(` [${ns}] 合法调用链: ${fns.map(p => p.function).join(" → ")}`);
249
+ for (const p of fns) {
250
+ const pre = p.protocol.pre_states?.join(",") || "(无)";
251
+ const post = p.protocol.post_states?.join(",") || "(无)";
252
+ lines.push(` ${p.function}: 前置状态=[${pre}] → 后置状态=[${post}]`);
253
+ }
254
+ }
255
+ return lines.join("\n");
256
+ }
257
+ /** JSON schema pre-validation: structural checks before IR-aware validation.
258
+ * Catches malformed actions that parseActionJSON() accepted but are semantically invalid. */
259
+ function validateActionSchema(actions) {
260
+ const errors = [];
261
+ const assignedVars = new Set();
262
+ const validVarName = /^[a-zA-Z_]\w*$/;
263
+ for (let i = 0; i < actions.length; i++) {
264
+ const a = actions[i];
265
+ const pos = `action[${i}]`;
266
+ if (!a.kind) {
267
+ errors.push(`${pos}: missing "kind" field`);
268
+ continue;
269
+ }
270
+ switch (a.kind) {
271
+ case "call": {
272
+ if (!a.function || typeof a.function !== "string") {
273
+ errors.push(`${pos}: call action requires "function" (string)`);
274
+ }
275
+ if (!Array.isArray(a.args)) {
276
+ errors.push(`${pos}: call action requires "args" (array)`);
277
+ }
278
+ else {
279
+ for (let j = 0; j < a.args.length; j++) {
280
+ const arg = a.args[j];
281
+ if (!arg || typeof arg !== "object") {
282
+ errors.push(`${pos}: args[${j}] must be an object`);
283
+ }
284
+ else if (arg.name === undefined && arg.type === undefined && arg.value === undefined) {
285
+ errors.push(`${pos}: args[${j}] missing name/type/value`);
286
+ }
287
+ }
288
+ }
289
+ if (a.assignTo !== undefined) {
290
+ if (typeof a.assignTo !== "string" || !validVarName.test(a.assignTo)) {
291
+ errors.push(`${pos}: assignTo "${a.assignTo}" is not a valid variable name`);
292
+ }
293
+ else if (assignedVars.has(a.assignTo)) {
294
+ errors.push(`${pos}: duplicate assignTo "${a.assignTo}" (previously assigned at another action)`);
295
+ }
296
+ else {
297
+ assignedVars.add(a.assignTo);
298
+ }
299
+ }
300
+ break;
301
+ }
302
+ case "return": {
303
+ if (a.value === undefined) {
304
+ errors.push(`${pos}: return action requires "value"`);
305
+ }
306
+ if (i < actions.length - 1) {
307
+ errors.push(`${pos}: return should be the last action (actions after return are unreachable)`);
308
+ }
309
+ break;
310
+ }
311
+ case "assign": {
312
+ if (!a.target || typeof a.target !== "string" || !validVarName.test(a.target)) {
313
+ errors.push(`${pos}: assign requires valid "target" variable name`);
314
+ }
315
+ else if (assignedVars.has(a.target)) {
316
+ errors.push(`${pos}: duplicate assign target "${a.target}"`);
317
+ }
318
+ else {
319
+ assignedVars.add(a.target);
320
+ }
321
+ if (a.value === undefined) {
322
+ errors.push(`${pos}: assign action requires "value"`);
323
+ }
324
+ break;
325
+ }
326
+ case "if": {
327
+ if (!a.condition || typeof a.condition !== "string") {
328
+ errors.push(`${pos}: if action requires "condition" (string)`);
329
+ }
330
+ if (!Array.isArray(a.thenActions)) {
331
+ errors.push(`${pos}: if action requires "thenActions" (array)`);
332
+ }
333
+ else {
334
+ const thenCheck = validateActionSchema(a.thenActions);
335
+ errors.push(...thenCheck.errors.map(e => `${pos}.thenActions: ${e}`));
336
+ }
337
+ if (a.elseActions && !Array.isArray(a.elseActions)) {
338
+ errors.push(`${pos}: elseActions must be an array if present`);
339
+ }
340
+ else if (a.elseActions) {
341
+ const elseCheck = validateActionSchema(a.elseActions);
342
+ errors.push(...elseCheck.errors.map(e => `${pos}.elseActions: ${e}`));
343
+ }
344
+ break;
345
+ }
346
+ case "for": {
347
+ if (!a.variable || typeof a.variable !== "string" || !validVarName.test(a.variable)) {
348
+ errors.push(`${pos}: for action requires valid "variable" name`);
349
+ }
350
+ if (!a.iterable || typeof a.iterable !== "string") {
351
+ errors.push(`${pos}: for action requires "iterable" (string)`);
352
+ }
353
+ if (!Array.isArray(a.bodyActions)) {
354
+ errors.push(`${pos}: for action requires "bodyActions" (array)`);
355
+ }
356
+ else {
357
+ const bodyCheck = validateActionSchema(a.bodyActions);
358
+ errors.push(...bodyCheck.errors.map(e => `${pos}.bodyActions: ${e}`));
359
+ }
360
+ break;
361
+ }
362
+ default: {
363
+ errors.push(`${pos}: unknown action kind "${a.kind}"`);
364
+ }
365
+ }
366
+ }
367
+ return { valid: errors.length === 0, errors };
368
+ }
369
+ /** 构建重试 prompt:精简但包含必要的 IR 语法提示 */
370
+ /** 加载 IR 中所有带 protocol 的函数为协议规则,同时从 protocols.json 加载命名空间初始状态 */
371
+ function loadProtocols(ir) {
372
+ const irProtocols = ir
373
+ .filter((f) => f.protocol)
374
+ .map((f) => ({ function: f.name, protocol: f.protocol }));
375
+ // Phase 6C: use ProtocolRegistry for nsInit (single source of truth)
376
+ const namespaceInitialStates = (0, protocol_registry_1.getNsInit)();
377
+ // Parse protocol rules from JSON (rules themselves still need the JSON for definitions)
378
+ let jsonProtocols = [];
379
+ try {
380
+ const protoDef = JSON.parse(fs.readFileSync("protocols.json", "utf-8"));
381
+ jsonProtocols = (0, ssg_validator_1.parseProtocolsFromJSON)(protoDef);
382
+ }
383
+ catch { }
384
+ // Merge: IR @protocol takes priority, but inherits JSON namespace
385
+ const merged = new Map();
386
+ for (const p of jsonProtocols)
387
+ merged.set(p.function, p);
388
+ for (const p of irProtocols) {
389
+ const existing = merged.get(p.function);
390
+ if (existing && existing.protocol.namespace && !p.protocol.namespace) {
391
+ p.protocol.namespace = existing.protocol.namespace;
392
+ }
393
+ merged.set(p.function, p);
394
+ }
395
+ return { protocols: [...merged.values()], namespaceInitialStates };
396
+ }
397
+ /** 验证动作序列的协议合法性,使用 Semantic Ledger (Phase 3) 纯函数 */
398
+ function validateProtocolWithTransitions(actions, protocols, namespaceInitialStates) {
399
+ const rules = new Map();
400
+ for (const p of protocols)
401
+ rules.set(p.function, p.protocol);
402
+ const ruleHash = (0, ssg_validator_1.hashRules)(rules);
403
+ const ctx = {
404
+ ledger: [],
405
+ currentState: (0, ssg_validator_1.rebuildState)([], namespaceInitialStates),
406
+ };
407
+ const transitions = [];
408
+ for (let i = 0; i < actions.length; i++) {
409
+ const a = actions[i];
410
+ if (a.kind === "call" && a.function) {
411
+ const { valid, transition, rejection } = (0, ssg_validator_1.validateTransition)(ctx, a.function, i, rules, namespaceInitialStates, ruleHash);
412
+ transitions.push(transition);
413
+ if (!valid) {
414
+ const trace = transitions.map(t => ({
415
+ function: t.function,
416
+ statesBefore: t.statesBefore,
417
+ statesAfter: t.statesAfter,
418
+ }));
419
+ return { valid: false, rejection: rejection, index: i, trace, transitions, ruleHash };
420
+ }
421
+ // Incremental update — O(1) per step
422
+ ctx.ledger = transitions;
423
+ ctx.currentState = transition.statesAfter;
424
+ }
425
+ }
426
+ // Invariant check on full ledger
427
+ const consistency = (0, ssg_validator_1.checkLedgerConsistency)(transitions, namespaceInitialStates);
428
+ if (!consistency.consistent) {
429
+ console.error(`[Invariant] Ledger consistency violations: ${consistency.violations.length}`);
430
+ for (const v of consistency.violations) {
431
+ console.error(` [${v.invariant}] index=${v.index}: ${v.detail}`);
432
+ }
433
+ }
434
+ return { valid: true, transitions, ledgerConsistent: consistency.consistent, ruleHash };
435
+ }
436
+ /** SSG 确定性修复:当协议违规有已知修复路径时,自动插入缺失函数 */
437
+ function attemptSSGRepair(actions, rejection, ir, protocols, namespaceInitialStates) {
438
+ if (!rejection.fixPath || rejection.fixPath.length === 0)
439
+ return null;
440
+ // 找到被拦截函数在序列中的位置
441
+ const blockedIdx = actions.findIndex(a => a.kind === "call" && a.function === rejection.blocked);
442
+ if (blockedIdx === -1)
443
+ return null;
444
+ // 为修复路径中的每个函数创建合成 Action
445
+ const repairActions = [];
446
+ for (const fnName of rejection.fixPath) {
447
+ const def = ir.find((f) => f.name === fnName);
448
+ if (!def)
449
+ return null;
450
+ const args = (def.params || []).map((p, i) => ({
451
+ name: p.name || `p${i}`,
452
+ type: p.type || 'any',
453
+ value: "",
454
+ }));
455
+ const assignTo = def.returnType && def.returnType !== 'void' && def.returnType !== 'undefined'
456
+ ? `${fnName}_result` : undefined;
457
+ const action = { kind: 'call', function: fnName, args };
458
+ if (assignTo)
459
+ action.assignTo = assignTo;
460
+ repairActions.push(action);
461
+ }
462
+ // 在被拦截函数前插入修复函数
463
+ const repaired = [
464
+ ...actions.slice(0, blockedIdx),
465
+ ...repairActions,
466
+ ...actions.slice(blockedIdx),
467
+ ];
468
+ // 重新验证
469
+ const recheck = validateProtocolWithTransitions(repaired, protocols, namespaceInitialStates);
470
+ if (recheck.valid) {
471
+ console.error(`🔧 SSG 确定性修复: 自动插入 ${rejection.fixPath.join(' → ')} 以解决协议违规`);
472
+ return repaired;
473
+ }
474
+ // 单步修复不够,尝试递归修复
475
+ if (recheck.rejection && recheck.rejection.fixPath && recheck.rejection.fixPath.length > 0) {
476
+ const nested = attemptSSGRepair(repaired, recheck.rejection, ir, protocols, namespaceInitialStates);
477
+ if (nested)
478
+ return nested;
479
+ }
480
+ return null;
481
+ }
90
482
  async function plan(userIntent) {
91
483
  (0, llm_1.resetCallCount)();
92
- const ir = JSON.parse(fs.readFileSync("ir.json", "utf-8"));
93
- // ========== 语义模板快速通道 ==========
484
+ const irRaw = JSON.parse(fs.readFileSync("ir.json", "utf-8"));
485
+ // Support both old (array) and new ({typeMap, functions}) formats
486
+ const ir = Array.isArray(irRaw) ? irRaw : (irRaw.functions || []);
487
+ // Helper: wrap actions into PlanResult
488
+ let repairMetrics = { applied: false, count: 0, branchIds: [] };
489
+ const wrapResult = (actions, repair) => ({
490
+ actions,
491
+ sessionId: session?.sessionId || "",
492
+ ruleHash: session?.ruleHash,
493
+ repairApplied: repair?.applied ?? repairMetrics.applied,
494
+ repairCount: repair?.count ?? repairMetrics.count,
495
+ repairBranchIds: repair?.branchIds ?? repairMetrics.branchIds,
496
+ });
497
+ // 初始化执行会话和快照(需在抗体快速通道前创建,以便记录 antibody hits)
498
+ const sessionId = (0, runtime_types_1.generateSessionId)();
499
+ const session = {
500
+ sessionId,
501
+ intent: userIntent,
502
+ attempts: [],
503
+ resolved: false,
504
+ startedAt: Date.now(),
505
+ };
506
+ const snapshot = (0, semantic_snapshot_1.createSnapshot)(ir, userIntent);
507
+ const snapshotId = (0, semantic_snapshot_1.saveSnapshot)(snapshot);
508
+ // 语义模板快速通道
94
509
  const cachedTemplate = (0, memory_layer_1.findSemanticTemplate)(userIntent);
95
510
  if (cachedTemplate && cachedTemplate.successRate >= 0.8 && cachedTemplate.useCount >= 2) {
96
- console.log("⚡ 命中语义模板,直接复用已验证序列");
511
+ console.error("⚡ 命中语义模板,直接复用已验证序列");
97
512
  (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: cachedTemplate.actionSequence, success: true });
98
- return cachedTemplate.actionSequence;
513
+ return wrapResult(cachedTemplate.actionSequence);
514
+ }
515
+ // 提前加载协议(后续多处使用)
516
+ const { protocols, namespaceInitialStates } = loadProtocols(ir);
517
+ // 抗体免疫快速通道:查询高置信度抗体(ACL-3+),匹配则约束或跳过 LLM
518
+ const antibodies = (0, failure_corpus_1.queryAntibodies)(userIntent, "ACL-3");
519
+ let antibodyHint = "";
520
+ if (antibodies.length > 0) {
521
+ const top = antibodies[0];
522
+ const aclLabel = top.antibodyLevel;
523
+ console.error(`🛡️ 命中抗体: ${aclLabel} | 模式: ${top.signature} | 相似度: ${top._score.toFixed(2)}`);
524
+ console.error(` 修复路径: ${top.fixPath.join(" → ")}`);
525
+ // ACL-4: 全局稳定抗体 → 直接构建动作序列,跳过 LLM
526
+ if (aclLabel === "ACL-4" && top.fixPath.length > 0) {
527
+ const antibodyActions = top.fixPath.map((fnName) => {
528
+ const def = ir.find((f) => f.name === fnName);
529
+ const args = (def?.params || []).map((p, i) => ({
530
+ name: p.name || `p${i}`,
531
+ type: p.type || 'any',
532
+ value: "",
533
+ }));
534
+ const action = { kind: 'call', function: fnName, args };
535
+ if (def?.returnType && def.returnType !== 'void' && def.returnType !== 'undefined') {
536
+ action.assignTo = `${fnName}_result`;
537
+ }
538
+ return action;
539
+ });
540
+ const antibodyHit = {
541
+ level: aclLabel,
542
+ signature: top.signature,
543
+ fixPath: top.fixPath,
544
+ similarityScore: top._score,
545
+ action: "fast_path",
546
+ llmCallsSaved: 1,
547
+ estimatedTokensSaved: Math.ceil((0, llm_1.estimateTokens)(SYSTEM_PROMPT + userIntent) * 1.2),
548
+ };
549
+ // 验证抗体序列
550
+ const antibodyRuleHash = (() => {
551
+ const rules = new Map();
552
+ for (const p of protocols)
553
+ rules.set(p.function, p.protocol);
554
+ return (0, ssg_validator_1.hashRules)(rules);
555
+ })();
556
+ if (protocols.length > 0) {
557
+ const validation = validateProtocolWithTransitions(antibodyActions, protocols, namespaceInitialStates);
558
+ if (validation.valid) {
559
+ console.error(`⚡ ACL-4 抗体快速通道: 0 LLM 调用,节省 ~${Math.ceil((0, llm_1.estimateTokens)(SYSTEM_PROMPT + userIntent) * 1.2)} tokens (est.)`);
560
+ const antibodyAttempt = {
561
+ id: (0, runtime_types_1.generateAttemptId)(),
562
+ sessionId: session.sessionId,
563
+ attemptNumber: 1,
564
+ inputIntent: userIntent,
565
+ plannerSeed: (0, runtime_types_1.generatePlannerSeed)("antibody-acl4", "immune"),
566
+ constraintSnapshotId: snapshotId,
567
+ generatedActions: antibodyActions,
568
+ transitions: validation.transitions,
569
+ violations: [],
570
+ outcome: "success",
571
+ timestamp: Date.now(),
572
+ llmCallCount: 0,
573
+ durationMs: 0,
574
+ antibodyHit,
575
+ ruleHash: validation.ruleHash,
576
+ };
577
+ session.attempts.push(antibodyAttempt);
578
+ session.successfulAttempt = antibodyAttempt;
579
+ session.ruleHash = validation.ruleHash;
580
+ session.resolved = true;
581
+ session.snapshotId = snapshotId;
582
+ session.endedAt = Date.now();
583
+ (0, failure_corpus_1.recordSession)(session);
584
+ (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: antibodyActions, success: true });
585
+ return wrapResult(antibodyActions);
586
+ }
587
+ }
588
+ else {
589
+ // 无协议规则,直接信任抗体
590
+ console.error(`⚡ ACL-4 抗体快速通道: 0 LLM 调用(无协议约束),节省 ~${Math.ceil((0, llm_1.estimateTokens)(SYSTEM_PROMPT + userIntent) * 1.2)} tokens (est.)`);
591
+ const antibodyAttempt = {
592
+ id: (0, runtime_types_1.generateAttemptId)(),
593
+ sessionId: session.sessionId,
594
+ attemptNumber: 1,
595
+ inputIntent: userIntent,
596
+ plannerSeed: (0, runtime_types_1.generatePlannerSeed)("antibody-acl4", "immune"),
597
+ constraintSnapshotId: snapshotId,
598
+ generatedActions: antibodyActions,
599
+ transitions: [],
600
+ violations: [],
601
+ outcome: "success",
602
+ timestamp: Date.now(),
603
+ llmCallCount: 0,
604
+ durationMs: 0,
605
+ antibodyHit,
606
+ ruleHash: antibodyRuleHash,
607
+ };
608
+ session.attempts.push(antibodyAttempt);
609
+ session.successfulAttempt = antibodyAttempt;
610
+ session.ruleHash = antibodyRuleHash;
611
+ session.resolved = true;
612
+ session.snapshotId = snapshotId;
613
+ session.endedAt = Date.now();
614
+ (0, failure_corpus_1.recordSession)(session);
615
+ (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: antibodyActions, success: true });
616
+ return wrapResult(antibodyActions);
617
+ }
618
+ }
619
+ // ACL-3: 注入修复路径作为提示约束
620
+ antibodyHint = `\n已知正确调用顺序: ${top.fixPath.join(" → ")}。请遵循此顺序。`;
621
+ console.error(`💉 ACL-3 抗体注入提示: ${top.fixPath.join(" → ")}`);
99
622
  }
100
- // ====================================
101
623
  const keywords = (0, utils_1.extractKeywords)(userIntent);
102
624
  const scored = ir.map((f) => {
103
625
  let score = 0;
@@ -110,95 +632,333 @@ async function plan(userIntent) {
110
632
  });
111
633
  scored.sort((a, b) => b.score - a.score);
112
634
  const topFuncs = scored.slice(0, 15);
113
- const funcList = topFuncs.map((f) => {
114
- const rate = (0, feedback_1.getFunctionSuccessRate)(f.name);
115
- const star = rate > 0.8 ? "⭐" : rate > 0.5 ? "👍" : "⚠️";
116
- const params = f.params.map((p) => `${p.name}: ${p.type}`).join(", ");
117
- return `${star} ${f.name}(${params}) [${f.params.length}个参数] -> ${f.returnType} (成功率: ${(rate * 100).toFixed(0)}%)`;
118
- }).join("\n");
119
- const matchFunc = userIntent.match(/(?:实现|implement|编写|创建)\s*(\w+)\s*(?:函数|function)?/i);
635
+ const compactFuncList = buildCompactFuncList(topFuncs);
636
+ const userIntentPart = userIntent.match(/(?:实现|implement|编写|创建)\s*(\w+)\s*(?:函数|function)?/i);
120
637
  const forbiddenFuncs = [];
121
- if (matchFunc) {
122
- const targetName = matchFunc[1];
638
+ if (userIntentPart) {
639
+ const targetName = userIntentPart[1];
123
640
  if (ir.find((f) => f.name.toLowerCase() === targetName.toLowerCase())) {
124
641
  forbiddenFuncs.push(targetName);
125
642
  }
126
643
  }
127
- const exampleCode = `assign("query_key", "user:123")
128
- callAssign("cache_get", "cached_data", "query_key")
129
- ifElse("cached_data", () => {
130
- output("cached_data")
131
- }, () => {
132
- callAssign("query_data", "fresh_data", "query_key")
133
- call("cache_set", "query_key", "fresh_data")
134
- output("fresh_data")
135
- })`;
136
- const basePrompt = `你能使用的函数:
137
- ${funcList}
138
-
139
- 绝对禁止调用列表外函数。
140
-
141
- 示例(缓存查询,注意 assign 先于条件):
142
- ${exampleCode}
143
-
144
- 全局函数及用法规则:
145
- - 声明变量:assign("变量名", "值") 或 callAssign("函数", "变量名", ...)
146
- - 条件分支:ifElse("变量名", () => { ... }, () => { ... })
147
- - 简单分支:ifBlock("变量名", () => { ... })
148
- - 调用:call("函数", "arg1", ...)
149
- - 返回:output("值或变量名")
150
-
151
- 铁律:
152
- 1. 必须先 assign 或 callAssign 再使用变量。
153
- 2. 参数数量必须与函数声明一致。
154
- 3. 条件括号内只能是已声明的变量名。
644
+ const protocolChainHint = buildProtocolChainHint(protocols);
645
+ const userPrompt = `可用函数:
646
+ ${compactFuncList}${protocolChainHint}
155
647
 
156
- 需求:
157
- ${userIntent}
648
+ 需求:${userIntent}${antibodyHint}
158
649
 
159
- 只输出代码。`;
650
+ ${RETRY_HINT}
651
+ 只输出 JSON。`;
652
+ const estimatedTokens = (0, llm_1.estimateTokens)(SYSTEM_PROMPT + userPrompt);
653
+ console.error(`💰 估算 prompt token: ${estimatedTokens}`);
654
+ // ── 执行持久化:检查是否有未完成的 checkpoint ──
655
+ const cp = (0, failure_corpus_1.loadCheckpoint)(userIntent);
656
+ let startRetry = 0;
160
657
  let finalActions = [];
161
- let currentPrompt = basePrompt;
162
- for (let r = 0; r < 3; r++) {
658
+ let currentPrompt = userPrompt;
659
+ let useSystem = true;
660
+ if (cp) {
661
+ console.error(`📌 恢复 checkpoint: 已完成 ${cp.attemptIndex} 次尝试,从第 ${cp.attemptIndex + 1} 次继续`);
662
+ startRetry = cp.attemptIndex;
663
+ currentPrompt = cp.currentPrompt;
664
+ useSystem = cp.useSystem;
665
+ // 从 checkpoint 恢复已有的 session.attempts
666
+ if (cp.sessionAttempts) {
667
+ session.attempts = cp.sessionAttempts;
668
+ }
669
+ }
670
+ const sessionRuleHash = (() => {
671
+ const rules = new Map();
672
+ for (const p of protocols)
673
+ rules.set(p.function, p.protocol);
674
+ return (0, ssg_validator_1.hashRules)(rules);
675
+ })();
676
+ session.ruleHash = sessionRuleHash;
677
+ function getMaskedFuncList() {
678
+ if (protocols.length === 0)
679
+ return compactFuncList;
680
+ const rules = new Map();
681
+ for (const p of protocols)
682
+ rules.set(p.function, p.protocol);
683
+ const ctx = { ledger: [], currentState: (0, ssg_validator_1.rebuildState)([], namespaceInitialStates) };
684
+ const legalFuncs = topFuncs.filter((f) => {
685
+ const proto = protocols.find((p) => p.function === f.name);
686
+ if (!proto)
687
+ return true;
688
+ const { valid } = (0, ssg_validator_1.validateTransition)(ctx, f.name, 0, rules, namespaceInitialStates);
689
+ return valid;
690
+ });
691
+ if (legalFuncs.length === topFuncs.length)
692
+ return compactFuncList;
693
+ return buildCompactFuncList(legalFuncs);
694
+ }
695
+ const maxRetries = 3;
696
+ for (let r = startRetry; r < maxRetries; r++) {
163
697
  let text;
164
698
  try {
165
- text = await (0, llm_1.generate)(currentPrompt);
699
+ text = useSystem
700
+ ? await (0, llm_1.chat)(SYSTEM_PROMPT, currentPrompt)
701
+ : await (0, llm_1.generate)(`你是程序合成助手。\n\n${currentPrompt}`);
166
702
  }
167
703
  catch (e) {
168
704
  continue;
169
705
  }
170
706
  if (!text)
171
707
  continue;
172
- text = text.replace(/```javascript\s*/gi, '').replace(/```\s*/g, '').trim();
173
- console.log("📝 LLM 生成的代码:\n", text);
174
- const rawActions = (0, action_runtime_1.executeActionCode)(text);
175
- if (!rawActions || rawActions.length === 0) {
176
- console.log("⚠️ 代码执行失败,重试...");
177
- currentPrompt = basePrompt + "\n上一次代码无效,请严格模仿示例。";
708
+ text = text.replace(/```(?:json|javascript)?\s*/gi, '').replace(/```\s*/g, '').trim();
709
+ console.error("📝 LLM 输出:\n", text);
710
+ // 优先尝试 JSON 解析,失败则回退到 DSL 执行
711
+ let rawActions = parseActionJSON(text);
712
+ if (!rawActions || !Array.isArray(rawActions)) {
713
+ console.error("⚠️ JSON 解析失败,尝试 DSL 回退...");
714
+ rawActions = (0, action_runtime_1.executeActionCode)(text);
715
+ }
716
+ if (!rawActions || !Array.isArray(rawActions) || rawActions.length === 0) {
717
+ console.error("⚠️ 解析失败,重试...");
718
+ currentPrompt = `可用函数:\n${compactFuncList}${protocolChainHint}\n\n需求:${userIntent}\n\n上一次输出无效。请严格输出 JSON 数组。\n${RETRY_HINT}\n只输出 JSON。`;
719
+ useSystem = false;
178
720
  continue;
179
721
  }
722
+ // 🔧 SVL-1 修复: 模糊函数名纠正 — 把 LLM 编造的函数名映射到真实 IR 函数
723
+ const nameCorrection = correctFunctionNames(rawActions, ir);
724
+ if (nameCorrection.corrections.length > 0) {
725
+ console.error("🔧 [SVL-1 自动修复] 函数名纠正:");
726
+ for (const c of nameCorrection.corrections)
727
+ console.error(` ${c}`);
728
+ rawActions = nameCorrection.actions;
729
+ }
730
+ // 🔧 SVL-2 修复: 参数签名预检 — 自动调整 args 数量匹配 IR 签名
731
+ const paramFix = fixParameterCounts(rawActions, ir);
732
+ if (paramFix.fixes.length > 0) {
733
+ console.error("🔧 [SVL-2 自动修复] 参数数量修正:");
734
+ for (const f of paramFix.fixes)
735
+ console.error(` ${f}`);
736
+ rawActions = paramFix.actions;
737
+ }
738
+ // P2: JSON schema pre-validation — catch structural errors early
739
+ const schemaCheck = validateActionSchema(rawActions);
740
+ if (!schemaCheck.valid) {
741
+ console.error("⚠️ JSON schema 校验失败:", schemaCheck.errors.join("; "));
742
+ currentPrompt = `可用函数:\n${compactFuncList}${protocolChainHint}\n\n需求:${userIntent}\n\n输出格式错误:${schemaCheck.errors.join(";")}。请修正 JSON 结构。\n${RETRY_HINT}\n只输出 JSON。`;
743
+ useSystem = false;
744
+ const schemaViolation = {
745
+ svl: 1,
746
+ violatedConstraint: "schema",
747
+ actionIndex: 0,
748
+ description: schemaCheck.errors.join("; "),
749
+ };
750
+ const schemaAttempt = {
751
+ id: (0, runtime_types_1.generateAttemptId)(),
752
+ sessionId: session.sessionId,
753
+ attemptNumber: r + 1,
754
+ inputIntent: userIntent,
755
+ plannerSeed: (0, runtime_types_1.generatePlannerSeed)(currentPrompt, process.env.LLM_MODEL || "deepseek-chat"),
756
+ constraintSnapshotId: snapshotId,
757
+ generatedActions: rawActions,
758
+ transitions: [],
759
+ violations: [schemaViolation],
760
+ outcome: "constraint_violation",
761
+ timestamp: Date.now(),
762
+ llmCallCount: 0,
763
+ durationMs: 0,
764
+ ruleHash: sessionRuleHash,
765
+ };
766
+ session.attempts.push(schemaAttempt);
767
+ (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: rawActions, success: false, svlViolated: "SVL-1" });
768
+ (0, failure_corpus_1.saveCheckpoint)(userIntent, { attemptIndex: r + 1, sessionAttempts: session.attempts, currentPrompt, useSystem });
769
+ continue;
770
+ }
771
+ // 解析 $变量名 引用为实际变量名
772
+ rawActions = rawActions.map(a => {
773
+ if (a.kind === "call" && a.args) {
774
+ a.args = a.args.map(arg => {
775
+ if (typeof arg.value === 'string' && arg.value.startsWith('$')) {
776
+ return { ...arg, value: arg.value.slice(1) };
777
+ }
778
+ return arg;
779
+ });
780
+ }
781
+ return a;
782
+ });
180
783
  const enriched = enrichActions(rawActions, ir);
181
- const filtered = enriched.filter(a => !forbiddenFuncs.includes(a.function || ''));
784
+ const filtered = enriched.filter(a => !forbiddenFuncs.includes(a.kind === "call" ? a.function : ''));
785
+ let ssgTransitions = [];
786
+ // 1) 基础序列校验
182
787
  const seqResult = (0, validator_1.validateActionSequence)(filtered);
183
788
  if (!seqResult.valid) {
184
789
  const errorsFlat = seqResult.errors.flat();
185
- console.log("⚠️ 序列校验失败:", errorsFlat.join(", "));
186
- const svl = determineSVL(errorsFlat);
790
+ console.error("⚠️ 序列校验失败:", errorsFlat.join(", "));
791
+ // Use structured violations directly from validator
792
+ const violations = seqResult.violations.length > 0
793
+ ? seqResult.violations
794
+ : [{
795
+ svl: 1,
796
+ violatedConstraint: "symbol_existence",
797
+ actionIndex: 0,
798
+ description: errorsFlat.join("; "),
799
+ }];
800
+ const primarySvl = `SVL-${violations[0].svl}`;
801
+ const attempt = {
802
+ id: (0, runtime_types_1.generateAttemptId)(),
803
+ sessionId: session.sessionId,
804
+ attemptNumber: r + 1,
805
+ inputIntent: userIntent,
806
+ plannerSeed: (0, runtime_types_1.generatePlannerSeed)(currentPrompt, process.env.LLM_MODEL || "deepseek-chat"),
807
+ constraintSnapshotId: snapshotId,
808
+ generatedActions: filtered,
809
+ transitions: [],
810
+ violations,
811
+ outcome: "constraint_violation",
812
+ timestamp: Date.now(),
813
+ llmCallCount: 0,
814
+ durationMs: 0,
815
+ ruleHash: sessionRuleHash,
816
+ };
817
+ session.attempts.push(attempt);
187
818
  (0, failure_corpus_1.recordFailure)({
188
819
  intent: userIntent,
189
820
  projectFunctions: ir.map((f) => f.name),
190
- violatedSVL: svl,
191
- constraintType: determineConstraintType(svl),
821
+ violatedSVL: primarySvl,
822
+ constraintType: violations[0].violatedConstraint,
192
823
  actionSequence: filtered,
193
824
  errorDetail: errorsFlat.join("; "),
825
+ ssgMissingFunctions: violations[0].missingStates,
826
+ plannerAttempt: r + 1,
827
+ plannerRetryTotal: maxRetries,
194
828
  });
195
- (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: filtered, success: false, svlViolated: svl });
196
- currentPrompt = basePrompt + `\n错误:${errorsFlat.join(";")}。请修正。`;
829
+ (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: filtered, success: false, svlViolated: primarySvl });
830
+ currentPrompt = `可用函数:\n${compactFuncList}${protocolChainHint}\n\n需求:${userIntent}\n\n错误:${errorsFlat.join(";")}。请修正。\n${RETRY_HINT}\n只输出 JSON。`;
831
+ useSystem = false;
832
+ (0, failure_corpus_1.saveCheckpoint)(userIntent, { attemptIndex: r + 1, sessionAttempts: session.attempts, currentPrompt, useSystem });
197
833
  continue;
198
834
  }
835
+ // 2) 协议状态机校验 (SSG)
836
+ if (protocols.length > 0) {
837
+ const protoResult = validateProtocolWithTransitions(filtered, protocols, namespaceInitialStates);
838
+ if (!protoResult.valid && protoResult.rejection) {
839
+ const rej = protoResult.rejection;
840
+ const explain = (0, ssg_validator_1.explainRejection)(rej);
841
+ console.error(explain);
842
+ const violation = {
843
+ svl: 4,
844
+ violatedConstraint: "protocol",
845
+ actionIndex: protoResult.index || 0,
846
+ currentStates: rej.currentState,
847
+ requiredStates: rej.requiredState,
848
+ missingStates: rej.missingFunctions,
849
+ fixPath: rej.fixPath,
850
+ namespace: rej.namespace,
851
+ description: JSON.stringify((0, ssg_validator_1.rejectionToJSON)(rej)),
852
+ };
853
+ const attempt = {
854
+ id: (0, runtime_types_1.generateAttemptId)(),
855
+ sessionId: session.sessionId,
856
+ attemptNumber: r + 1,
857
+ inputIntent: userIntent,
858
+ plannerSeed: (0, runtime_types_1.generatePlannerSeed)(currentPrompt, process.env.LLM_MODEL || "deepseek-chat"),
859
+ constraintSnapshotId: snapshotId,
860
+ generatedActions: filtered,
861
+ transitions: protoResult.transitions,
862
+ violations: [violation],
863
+ outcome: "constraint_violation",
864
+ timestamp: Date.now(),
865
+ llmCallCount: 0,
866
+ durationMs: 0,
867
+ ruleHash: sessionRuleHash,
868
+ };
869
+ session.attempts.push(attempt);
870
+ (0, failure_corpus_1.recordFailure)({
871
+ intent: userIntent,
872
+ projectFunctions: ir.map((f) => f.name),
873
+ violatedSVL: "SVL-4",
874
+ constraintType: "protocol",
875
+ actionSequence: filtered,
876
+ errorDetail: JSON.stringify((0, ssg_validator_1.rejectionToJSON)(rej)),
877
+ ssgState: rej.currentState,
878
+ ssgTrace: protoResult.trace,
879
+ ssgFixPath: rej.fixPath,
880
+ ssgMissingFunctions: rej.missingFunctions,
881
+ plannerAttempt: r + 1,
882
+ plannerRetryTotal: maxRetries,
883
+ });
884
+ (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: filtered, success: false, svlViolated: "SVL-4" });
885
+ // 尝试确定性修复:用 SSG 的 fixPath 自动插入缺失函数
886
+ const repaired = attemptSSGRepair(filtered, rej, ir, protocols, namespaceInitialStates);
887
+ if (repaired) {
888
+ console.error("🔧 SSG 修复成功,跳过 LLM 重试");
889
+ // Phase 6: Repair → Branch — 保留原始序列作为证据
890
+ const { createRootBranch, createBranch } = require("./branch-ledger");
891
+ const rootBranch = createRootBranch(filtered);
892
+ rootBranch.outcome = "violation";
893
+ const repairBranch = createBranch(rootBranch, "repair_attempt", repaired);
894
+ repairBranch.outcome = "success";
895
+ session.branchTree = [rootBranch, repairBranch];
896
+ session.rootBranchId = rootBranch.id;
897
+ repairMetrics = {
898
+ applied: true,
899
+ count: rej.fixPath?.length || 0,
900
+ branchIds: [rootBranch.id, repairBranch.id],
901
+ };
902
+ // Phase 7: Record repair event for analytics
903
+ try {
904
+ const repairDir = ".progmune_corpus/repairs";
905
+ if (!fs.existsSync(repairDir))
906
+ fs.mkdirSync(repairDir, { recursive: true });
907
+ const repairRecord = {
908
+ sessionId: session.sessionId,
909
+ timestamp: Date.now(),
910
+ violation: "SVL-4",
911
+ constraint: "protocol",
912
+ blockedFunction: rej.blocked,
913
+ namespace: rej.namespace,
914
+ missingStates: rej.missingFunctions,
915
+ fixPath: rej.fixPath,
916
+ originalPlan: filtered.map((a) => a.kind === "call" ? a.function : a.kind),
917
+ repairPlan: repaired.map((a) => a.kind === "call" ? a.function : a.kind),
918
+ success: true,
919
+ };
920
+ fs.writeFileSync(`${repairDir}/repair_${session.sessionId}.json`, JSON.stringify(repairRecord, null, 2), "utf-8");
921
+ }
922
+ catch { }
923
+ finalActions = repaired;
924
+ break;
925
+ }
926
+ const maskedFuncList = getMaskedFuncList();
927
+ currentPrompt = `当前协议状态只允许以下函数:\n${maskedFuncList}${protocolChainHint}\n\n需求:${userIntent}\n\n协议违规:${explain.replace(/\n/g, ';')}。请修正。\n${RETRY_HINT}\n只输出 JSON。`;
928
+ useSystem = false;
929
+ (0, failure_corpus_1.saveCheckpoint)(userIntent, { attemptIndex: r + 1, sessionAttempts: session.attempts, currentPrompt, useSystem });
930
+ continue;
931
+ }
932
+ // SSG passed — capture transitions
933
+ ssgTransitions = protoResult.transitions;
934
+ }
935
+ // 3) 语义合约校验
199
936
  const semResult = (0, semantic_validator_1.checkSemantic)(userIntent, filtered);
200
937
  if (!semResult.valid) {
201
- console.log("⚠️ 语义校验失败:", semResult.errors.join(", "));
938
+ console.error("⚠️ 语义校验失败:", semResult.errors.join(", "));
939
+ const violation = {
940
+ svl: 4,
941
+ violatedConstraint: "semantic_contract",
942
+ actionIndex: 0,
943
+ description: semResult.errors.join("; "),
944
+ };
945
+ const attempt = {
946
+ id: (0, runtime_types_1.generateAttemptId)(),
947
+ sessionId: session.sessionId,
948
+ attemptNumber: r + 1,
949
+ inputIntent: userIntent,
950
+ plannerSeed: (0, runtime_types_1.generatePlannerSeed)(currentPrompt, process.env.LLM_MODEL || "deepseek-chat"),
951
+ constraintSnapshotId: snapshotId,
952
+ generatedActions: filtered,
953
+ transitions: ssgTransitions,
954
+ violations: [violation],
955
+ outcome: "constraint_violation",
956
+ timestamp: Date.now(),
957
+ llmCallCount: 0,
958
+ durationMs: 0,
959
+ ruleHash: sessionRuleHash,
960
+ };
961
+ session.attempts.push(attempt);
202
962
  (0, failure_corpus_1.recordFailure)({
203
963
  intent: userIntent,
204
964
  projectFunctions: ir.map((f) => f.name),
@@ -206,19 +966,127 @@ ${userIntent}
206
966
  constraintType: "protocol",
207
967
  actionSequence: filtered,
208
968
  errorDetail: semResult.errors.join("; "),
969
+ plannerAttempt: r + 1,
970
+ plannerRetryTotal: maxRetries,
209
971
  });
210
972
  (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: filtered, success: false, svlViolated: "SVL-4" });
211
- currentPrompt = basePrompt + `\n错误:${semResult.errors.join(";")}。请修正。`;
973
+ currentPrompt = `可用函数:\n${compactFuncList}${protocolChainHint}\n\n需求:${userIntent}\n\n语义错误:${semResult.errors.join(";")}。请修正。\n${RETRY_HINT}\n只输出 JSON。`;
974
+ useSystem = false;
975
+ (0, failure_corpus_1.saveCheckpoint)(userIntent, { attemptIndex: r + 1, sessionAttempts: session.attempts, currentPrompt, useSystem });
212
976
  continue;
213
977
  }
978
+ // 校验通过:构建成功 Attempt
979
+ const successAntibodyHit = antibodyHint
980
+ ? {
981
+ level: antibodies[0]?.antibodyLevel || "ACL-3",
982
+ signature: antibodies[0]?.signature || "",
983
+ fixPath: antibodies[0]?.fixPath || [],
984
+ similarityScore: antibodies[0]?._score || 0,
985
+ action: "injected_hint",
986
+ llmCallsSaved: 0,
987
+ estimatedTokensSaved: 0,
988
+ }
989
+ : undefined;
990
+ const successAttempt = {
991
+ id: (0, runtime_types_1.generateAttemptId)(),
992
+ sessionId: session.sessionId,
993
+ attemptNumber: r + 1,
994
+ inputIntent: userIntent,
995
+ plannerSeed: (0, runtime_types_1.generatePlannerSeed)(currentPrompt, process.env.LLM_MODEL || "deepseek-chat"),
996
+ constraintSnapshotId: snapshotId,
997
+ generatedActions: filtered,
998
+ transitions: ssgTransitions,
999
+ violations: [],
1000
+ outcome: "success",
1001
+ timestamp: Date.now(),
1002
+ llmCallCount: 1,
1003
+ durationMs: 0,
1004
+ antibodyHit: successAntibodyHit,
1005
+ ruleHash: sessionRuleHash,
1006
+ };
1007
+ session.attempts.push(successAttempt);
1008
+ session.successfulAttempt = successAttempt;
214
1009
  finalActions = filtered;
215
1010
  break;
216
1011
  }
217
1012
  if (finalActions.length > 0) {
218
1013
  (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: finalActions, success: true });
1014
+ session.resolved = true;
1015
+ session.snapshotId = snapshotId;
1016
+ session.endedAt = Date.now();
1017
+ (0, failure_corpus_1.recordSession)(session); // Phase 5 will update recordSession to accept ExecutionSession
1018
+ (0, failure_corpus_1.clearCheckpoint)(userIntent);
219
1019
  }
220
1020
  else {
1021
+ // LLM 3 次重试失败,尝试本地规则回退
1022
+ console.error("[降级] LLM 规划失败,尝试本地规则回退");
1023
+ const fallback = generateFallbackPlan(userIntent, ir);
1024
+ if (fallback.length > 0) {
1025
+ console.error(`[降级] 本地规则生成了 ${fallback.length} 个动作`);
1026
+ (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: fallback, success: true });
1027
+ const fallbackAttempt = {
1028
+ id: (0, runtime_types_1.generateAttemptId)(),
1029
+ sessionId: session.sessionId,
1030
+ attemptNumber: session.attempts.length + 1,
1031
+ inputIntent: userIntent,
1032
+ plannerSeed: (0, runtime_types_1.generatePlannerSeed)("fallback", "local-rule"),
1033
+ constraintSnapshotId: snapshotId,
1034
+ generatedActions: fallback,
1035
+ transitions: [],
1036
+ violations: [],
1037
+ outcome: "success",
1038
+ timestamp: Date.now(),
1039
+ llmCallCount: 0,
1040
+ durationMs: 0,
1041
+ ruleHash: sessionRuleHash,
1042
+ };
1043
+ session.attempts.push(fallbackAttempt);
1044
+ session.successfulAttempt = fallbackAttempt;
1045
+ session.resolved = true;
1046
+ session.snapshotId = snapshotId;
1047
+ session.endedAt = Date.now();
1048
+ (0, failure_corpus_1.recordSession)(session);
1049
+ (0, failure_corpus_1.clearCheckpoint)(userIntent);
1050
+ return wrapResult(fallback);
1051
+ }
221
1052
  (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: [], success: false });
1053
+ session.resolved = false;
1054
+ session.snapshotId = snapshotId;
1055
+ session.endedAt = Date.now();
1056
+ (0, failure_corpus_1.recordSession)(session);
1057
+ (0, failure_corpus_1.clearCheckpoint)(userIntent);
1058
+ }
1059
+ return wrapResult(finalActions);
1060
+ }
1061
+ /** 本地规则回退:当 LLM 不可用时,根据意图关键词生成简单动作序列 */
1062
+ function generateFallbackPlan(intent, ir) {
1063
+ const intentLower = intent.toLowerCase();
1064
+ const actions = [];
1065
+ const keywords = intentLower.split(/[\s,,、]+/).filter(k => k.length > 1);
1066
+ const matchedFuncs = [];
1067
+ for (const kw of keywords) {
1068
+ for (const fn of ir) {
1069
+ if (fn.name.toLowerCase().includes(kw) && !matchedFuncs.find((f) => f.name === fn.name)) {
1070
+ matchedFuncs.push(fn);
1071
+ }
1072
+ }
1073
+ }
1074
+ if (matchedFuncs.length === 0)
1075
+ return [];
1076
+ for (const fn of matchedFuncs) {
1077
+ const args = (fn.params || []).map((p, i) => ({
1078
+ name: p.name || `p${i}`,
1079
+ type: p.type || 'any',
1080
+ value: `{{${p.name || `p${i}`}}}`
1081
+ }));
1082
+ const assignTo = fn.returnType && fn.returnType !== 'void' && fn.returnType !== 'undefined'
1083
+ ? `${fn.name}_result` : undefined;
1084
+ if (assignTo) {
1085
+ actions.push({ kind: 'call', function: fn.name, args, assignTo });
1086
+ }
1087
+ else {
1088
+ actions.push({ kind: 'call', function: fn.name, args });
1089
+ }
222
1090
  }
223
- return finalActions;
1091
+ return actions;
224
1092
  }