progmune-runtime 2.0.4 → 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 (106) 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 +33 -22
  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 +445 -43
  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 +905 -80
  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/runtime-invariants.js +161 -0
  60. package/dist/runtime-types.js +117 -0
  61. package/dist/search-planner.js +38 -9
  62. package/dist/semantic-snapshot.js +155 -0
  63. package/dist/semantic-trace.js +1497 -0
  64. package/dist/semantic-validator.js +3 -2
  65. package/dist/semantic_guard_test.js +2 -1
  66. package/dist/sessions.js +11 -0
  67. package/dist/ssg-validator.js +658 -20
  68. package/dist/svl-distribution.js +11 -0
  69. package/dist/terminal-status.js +11 -0
  70. package/dist/test_failure_corpus.js +3 -1
  71. package/dist/token-savings.js +11 -0
  72. package/dist/total-repairs.js +12 -0
  73. package/dist/unresolved-count.js +12 -0
  74. package/dist/valid-fingerprints.js +13 -0
  75. package/dist/validator.js +116 -60
  76. package/dist/verify-fps.js +11 -0
  77. package/dist/verify-ledgers.js +11 -0
  78. package/docs/whitepaper-style.css +77 -0
  79. package/docs/whitepaper-v2.1.md +609 -0
  80. package/docs/whitepaper-v2.2.md +1064 -0
  81. package/docs/whitepaper-v2.2.pdf +0 -0
  82. package/fly.toml +1 -1
  83. package/package.json +13 -4
  84. package/protocols.json +131 -11
  85. package/public/dashboard.html +119 -0
  86. package/readme.md +829 -0
  87. package/server/hub.js +84 -12
  88. package/test/replay-golden/sess_1780063202050_mgeld.json +9 -0
  89. package/test/replay-golden/sess_1780064032560_gocld.json +354 -0
  90. package/test/replay-golden/sess_1780064413331_s2709.json +606 -0
  91. package/test/replay-golden/sess_1780064792710_y3avo.json +614 -0
  92. package/test/replay-golden.ts +84 -0
  93. package/test_benchmark.js +165 -0
  94. package/test_comprehensive.mjs +638 -0
  95. package/test_concurrency.js +129 -0
  96. package/test_ir_robustness.js +85 -0
  97. package/test_semantic_contracts.js +269 -0
  98. package/test_ssg_stress.js +156 -0
  99. package/test_svl3.js +58 -0
  100. package/tsconfig.json +1 -1
  101. package/.env.example +0 -3
  102. package/.progmune_memory/episodic.json +0 -186
  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/immune_hub_data/2026-05-14.json +0 -50
package/dist/planner.js CHANGED
@@ -35,14 +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
45
  const ssg_validator_1 = require("./ssg-validator");
46
+ const protocol_registry_1 = require("./protocol-registry");
47
+ const semantic_snapshot_1 = require("./semantic-snapshot");
46
48
  const fs = __importStar(require("fs"));
47
49
  function enrichActions(actions, ir) {
48
50
  return actions.map(a => {
@@ -88,35 +90,535 @@ function determineConstraintType(svl) {
88
90
  case "SVL-4": return "protocol";
89
91
  }
90
92
  }
91
- /** 加载 IR 中所有带 protocol 的函数为协议规则 */
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 加载命名空间初始状态 */
92
371
  function loadProtocols(ir) {
93
- return ir
372
+ const irProtocols = ir
94
373
  .filter((f) => f.protocol)
95
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 };
96
396
  }
97
- /** 验证动作序列的协议合法性 */
98
- function validateProtocol(actions, protocols, initialState) {
99
- const ssv = new ssg_validator_1.StateMachineValidator(protocols, initialState);
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 = [];
100
408
  for (let i = 0; i < actions.length; i++) {
101
409
  const a = actions[i];
102
410
  if (a.kind === "call" && a.function) {
103
- const result = ssv.apply(a.function);
104
- if (!result.valid) {
105
- return { valid: false, error: result.error, index: i };
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 };
106
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}`);
107
432
  }
108
433
  }
109
- return { valid: true };
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;
110
481
  }
111
482
  async function plan(userIntent) {
112
483
  (0, llm_1.resetCallCount)();
113
- const ir = JSON.parse(fs.readFileSync("ir.json", "utf-8"));
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);
114
508
  // 语义模板快速通道
115
509
  const cachedTemplate = (0, memory_layer_1.findSemanticTemplate)(userIntent);
116
510
  if (cachedTemplate && cachedTemplate.successRate >= 0.8 && cachedTemplate.useCount >= 2) {
117
- console.log("⚡ 命中语义模板,直接复用已验证序列");
511
+ console.error("⚡ 命中语义模板,直接复用已验证序列");
118
512
  (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: cachedTemplate.actionSequence, success: true });
119
- 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(" → ")}`);
120
622
  }
121
623
  const keywords = (0, utils_1.extractKeywords)(userIntent);
122
624
  const scored = ir.map((f) => {
@@ -130,118 +632,333 @@ async function plan(userIntent) {
130
632
  });
131
633
  scored.sort((a, b) => b.score - a.score);
132
634
  const topFuncs = scored.slice(0, 15);
133
- const funcList = topFuncs.map((f) => {
134
- const rate = (0, feedback_1.getFunctionSuccessRate)(f.name);
135
- const star = rate > 0.8 ? "⭐" : rate > 0.5 ? "👍" : "⚠️";
136
- const params = f.params.map((p) => `${p.name}: ${p.type}`).join(", ");
137
- return `${star} ${f.name}(${params}) [${f.params.length}个参数] -> ${f.returnType} (成功率: ${(rate * 100).toFixed(0)}%)`;
138
- }).join("\n");
139
- 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);
140
637
  const forbiddenFuncs = [];
141
- if (matchFunc) {
142
- const targetName = matchFunc[1];
638
+ if (userIntentPart) {
639
+ const targetName = userIntentPart[1];
143
640
  if (ir.find((f) => f.name.toLowerCase() === targetName.toLowerCase())) {
144
641
  forbiddenFuncs.push(targetName);
145
642
  }
146
643
  }
147
- const exampleCode = `assign("query_key", "user:123")
148
- callAssign("cache_get", "cached_data", "query_key")
149
- ifElse("cached_data", () => {
150
- output("cached_data")
151
- }, () => {
152
- callAssign("query_data", "fresh_data", "query_key")
153
- call("cache_set", "query_key", "fresh_data")
154
- output("fresh_data")
155
- })`;
156
- const basePrompt = `你能使用的函数:
157
- ${funcList}
158
-
159
- 绝对禁止调用列表外函数。
160
-
161
- 示例(缓存查询,注意 assign 先于条件):
162
- ${exampleCode}
163
-
164
- 全局函数及用法规则:
165
- - 声明变量:assign("变量名", "值") 或 callAssign("函数", "变量名", ...)
166
- - 条件分支:ifElse("变量名", () => { ... }, () => { ... })
167
- - 简单分支:ifBlock("变量名", () => { ... })
168
- - 调用:call("函数", "arg1", ...)
169
- - 返回:output("值或变量名")
644
+ const protocolChainHint = buildProtocolChainHint(protocols);
645
+ const userPrompt = `可用函数:
646
+ ${compactFuncList}${protocolChainHint}
170
647
 
171
- 铁律:
172
- 1. 必须先 assign 或 callAssign 再使用变量。
173
- 2. 参数数量必须与函数声明一致。
174
- 3. 条件括号内只能是已声明的变量名。
648
+ 需求:${userIntent}${antibodyHint}
175
649
 
176
- 需求:
177
- ${userIntent}
178
-
179
- 只输出代码。`;
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;
180
657
  let finalActions = [];
181
- let currentPrompt = basePrompt;
182
- // 加载协议规则,设定初始状态(例如未认证场景)
183
- const protocols = loadProtocols(ir);
184
- 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++) {
185
697
  let text;
186
698
  try {
187
- 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}`);
188
702
  }
189
703
  catch (e) {
190
704
  continue;
191
705
  }
192
706
  if (!text)
193
707
  continue;
194
- text = text.replace(/```javascript\s*/gi, '').replace(/```\s*/g, '').trim();
195
- console.log("📝 LLM 生成的代码:\n", text);
196
- const rawActions = (0, action_runtime_1.executeActionCode)(text);
197
- if (!rawActions || rawActions.length === 0) {
198
- console.log("⚠️ 代码执行失败,重试...");
199
- 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;
200
720
  continue;
201
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
+ });
202
783
  const enriched = enrichActions(rawActions, ir);
203
- 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 = [];
204
786
  // 1) 基础序列校验
205
787
  const seqResult = (0, validator_1.validateActionSequence)(filtered);
206
788
  if (!seqResult.valid) {
207
789
  const errorsFlat = seqResult.errors.flat();
208
- console.log("⚠️ 序列校验失败:", errorsFlat.join(", "));
209
- 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);
210
818
  (0, failure_corpus_1.recordFailure)({
211
819
  intent: userIntent,
212
820
  projectFunctions: ir.map((f) => f.name),
213
- violatedSVL: svl,
214
- constraintType: determineConstraintType(svl),
821
+ violatedSVL: primarySvl,
822
+ constraintType: violations[0].violatedConstraint,
215
823
  actionSequence: filtered,
216
824
  errorDetail: errorsFlat.join("; "),
825
+ ssgMissingFunctions: violations[0].missingStates,
826
+ plannerAttempt: r + 1,
827
+ plannerRetryTotal: maxRetries,
217
828
  });
218
- (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: filtered, success: false, svlViolated: svl });
219
- 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 });
220
833
  continue;
221
834
  }
222
835
  // 2) 协议状态机校验 (SSG)
223
836
  if (protocols.length > 0) {
224
- const protoResult = validateProtocol(filtered, protocols, "UNAUTHENTICATED");
225
- if (!protoResult.valid) {
226
- console.log("🛡️ SSG 协议违规:", protoResult.error);
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);
227
870
  (0, failure_corpus_1.recordFailure)({
228
871
  intent: userIntent,
229
872
  projectFunctions: ir.map((f) => f.name),
230
873
  violatedSVL: "SVL-4",
231
874
  constraintType: "protocol",
232
875
  actionSequence: filtered,
233
- errorDetail: protoResult.error,
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,
234
883
  });
235
884
  (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: filtered, success: false, svlViolated: "SVL-4" });
236
- currentPrompt = basePrompt + `\n协议错误:${protoResult.error}。请按照正确的业务顺序重新生成,确保先通过认证再签发令牌。`;
237
- continue;
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 });
238
930
  continue;
239
931
  }
932
+ // SSG passed — capture transitions
933
+ ssgTransitions = protoResult.transitions;
240
934
  }
241
935
  // 3) 语义合约校验
242
936
  const semResult = (0, semantic_validator_1.checkSemantic)(userIntent, filtered);
243
937
  if (!semResult.valid) {
244
- 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);
245
962
  (0, failure_corpus_1.recordFailure)({
246
963
  intent: userIntent,
247
964
  projectFunctions: ir.map((f) => f.name),
@@ -249,19 +966,127 @@ ${userIntent}
249
966
  constraintType: "protocol",
250
967
  actionSequence: filtered,
251
968
  errorDetail: semResult.errors.join("; "),
969
+ plannerAttempt: r + 1,
970
+ plannerRetryTotal: maxRetries,
252
971
  });
253
972
  (0, memory_layer_1.recordEpisode)({ intent: userIntent, actions: filtered, success: false, svlViolated: "SVL-4" });
254
- 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 });
255
976
  continue;
256
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;
257
1009
  finalActions = filtered;
258
1010
  break;
259
1011
  }
260
1012
  if (finalActions.length > 0) {
261
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);
262
1019
  }
263
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
+ }
264
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
+ }
265
1090
  }
266
- return finalActions;
1091
+ return actions;
267
1092
  }