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
@@ -33,48 +33,146 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.saveCheckpoint = saveCheckpoint;
37
+ exports.loadCheckpoint = loadCheckpoint;
38
+ exports.clearCheckpoint = clearCheckpoint;
36
39
  exports.recordFailure = recordFailure;
40
+ exports.recordSession = recordSession;
37
41
  exports.getAllFailures = getAllFailures;
38
42
  exports.getFailuresBySVL = getFailuresBySVL;
39
43
  exports.getTopFailurePatterns = getTopFailurePatterns;
44
+ exports.getFailureGenome = getFailureGenome;
45
+ exports.getAllSessions = getAllSessions;
46
+ exports.getLearnedPatterns = getLearnedPatterns;
47
+ exports.queryAntibodies = queryAntibodies;
48
+ exports.getSemanticHeatmap = getSemanticHeatmap;
49
+ exports.getAntibodyStats = getAntibodyStats;
40
50
  exports.generateCandidateRules = generateCandidateRules;
41
51
  const fs = __importStar(require("fs"));
42
52
  const path = __importStar(require("path"));
43
- const CORPUS_DIR = path.resolve(__dirname, "../failure_corpus");
53
+ const file_lock_1 = require("./file-lock");
54
+ // 优先使用 PROGMUNE_PROJECT_DIR(由 MCP 服务器在调用时设置),确保多项目隔离
55
+ const projectDir = process.env.PROGMUNE_PROJECT_DIR || process.cwd();
56
+ const CORPUS_DIR = process.env.PROGMUNE_CORPUS_DIR
57
+ || path.resolve(projectDir, ".progmune_corpus");
58
+ const SESSIONS_DIR = path.join(CORPUS_DIR, "sessions");
59
+ const CHECKPOINT_DIR = path.join(CORPUS_DIR, "checkpoints");
44
60
  function ensureDir(dir) {
45
61
  if (!fs.existsSync(dir))
46
62
  fs.mkdirSync(dir, { recursive: true });
47
63
  }
48
- function recordFailure(record) {
49
- ensureDir(CORPUS_DIR);
50
- const date = new Date().toISOString().slice(0, 10);
51
- const dateDir = path.join(CORPUS_DIR, date);
52
- ensureDir(dateDir);
53
- const id = `fail_${Date.now()}`;
54
- const fullRecord = {
55
- ...record,
56
- id,
64
+ function checkpointPath(intent) {
65
+ // 用 intent 的稳定 hash 作为文件名,避免特殊字符
66
+ const hash = Buffer.from(intent).toString("base64").replace(/[/+=]/g, "_").slice(0, 32);
67
+ return path.join(CHECKPOINT_DIR, `ckpt_${hash}.json`);
68
+ }
69
+ function saveCheckpoint(intent, data) {
70
+ ensureDir(CHECKPOINT_DIR);
71
+ const cp = {
72
+ ...data,
73
+ intent,
57
74
  timestamp: new Date().toISOString(),
58
75
  };
59
- const filename = `${id}.json`;
60
- const filepath = path.join(dateDir, filename);
61
- fs.writeFileSync(filepath, JSON.stringify(fullRecord, null, 2));
62
- console.log(`[FailureCorpus] 记录失败案例: ${id} [${record.violatedSVL}]`);
76
+ fs.writeFileSync(checkpointPath(intent), JSON.stringify(cp, null, 2));
77
+ }
78
+ function loadCheckpoint(intent) {
79
+ try {
80
+ const raw = fs.readFileSync(checkpointPath(intent), "utf-8");
81
+ return JSON.parse(raw);
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }
87
+ function clearCheckpoint(intent) {
88
+ try {
89
+ fs.unlinkSync(checkpointPath(intent));
90
+ }
91
+ catch { }
92
+ }
93
+ function recordFailure(record) {
94
+ (0, file_lock_1.withLock)("failure-corpus", () => {
95
+ ensureDir(CORPUS_DIR);
96
+ const date = new Date().toISOString().slice(0, 10);
97
+ const dateDir = path.join(CORPUS_DIR, date);
98
+ ensureDir(dateDir);
99
+ const seqFile = path.join(CORPUS_DIR, ".seq");
100
+ let seq = 0;
101
+ try {
102
+ seq = parseInt(fs.readFileSync(seqFile, 'utf-8'), 10);
103
+ }
104
+ catch { }
105
+ seq++;
106
+ fs.writeFileSync(seqFile, String(seq));
107
+ const id = `fail_${Date.now()}_${seq}`;
108
+ const fullRecord = {
109
+ ...record,
110
+ id,
111
+ timestamp: new Date().toISOString(),
112
+ plannerAttempt: record.plannerAttempt || 1,
113
+ plannerRetryTotal: record.plannerRetryTotal || 1,
114
+ };
115
+ const filename = `${id}.json`;
116
+ fs.writeFileSync(path.join(dateDir, filename), JSON.stringify(fullRecord, null, 2));
117
+ console.error(`[FailureCorpus] ${record.violatedSVL} | 尝试 ${record.plannerAttempt}/${record.plannerRetryTotal} | ${id}`);
118
+ });
119
+ }
120
+ /** 记录一个完整的意图会话(支持 ExecutionSession 和旧 IntentSession) */
121
+ /**
122
+ * 保存执行会话(含所有尝试、违规、状态转移)。
123
+ * @protocol namespace=dev_pipeline pre_states=["CODE_EMITTED"] post_states=["SESSION_RECORDED"] invalidate=["CODE_EMITTED"]
124
+ */
125
+ function recordSession(session) {
126
+ ensureDir(SESSIONS_DIR);
127
+ const sessionId = session.sessionId || `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
128
+ // 检测新格式:ExecutionSession has .attempts[0]?.violations
129
+ const isExecutionSession = session.attempts?.length > 0 && session.attempts[0].violations;
130
+ if (isExecutionSession) {
131
+ // 新格式:直接保存 ExecutionSession
132
+ const fullSession = { ...session, sessionId };
133
+ fs.writeFileSync(path.join(SESSIONS_DIR, `${sessionId}.json`), JSON.stringify(fullSession, null, 2));
134
+ // 为每个违规的 attempt 记录到 failure corpus(向后兼容)
135
+ for (const attempt of fullSession.attempts) {
136
+ for (const violation of attempt.violations) {
137
+ const svlStr = `SVL-${violation.svl}`;
138
+ const blockingFn = attempt.generatedActions[violation.actionIndex];
139
+ recordFailure({
140
+ intent: fullSession.intent,
141
+ projectFunctions: [],
142
+ violatedSVL: svlStr,
143
+ constraintType: violation.violatedConstraint,
144
+ actionSequence: attempt.generatedActions,
145
+ errorDetail: violation.description,
146
+ ssgState: violation.currentStates,
147
+ ssgFixPath: violation.fixPath,
148
+ ssgMissingFunctions: violation.missingStates,
149
+ plannerAttempt: attempt.attemptNumber,
150
+ plannerRetryTotal: fullSession.attempts.length,
151
+ });
152
+ }
153
+ }
154
+ return sessionId;
155
+ }
156
+ // 旧格式:兼容保存
157
+ const fullSession = { ...session, sessionId };
158
+ fs.writeFileSync(path.join(SESSIONS_DIR, `${sessionId}.json`), JSON.stringify(fullSession, null, 2));
159
+ return sessionId;
63
160
  }
64
161
  function getAllFailures() {
65
162
  const records = [];
66
163
  if (!fs.existsSync(CORPUS_DIR))
67
164
  return records;
68
- const dirs = fs.readdirSync(CORPUS_DIR);
165
+ const dirs = fs.readdirSync(CORPUS_DIR).filter(d => d !== 'sessions');
69
166
  for (const dir of dirs) {
70
167
  const dirPath = path.join(CORPUS_DIR, dir);
71
168
  if (!fs.statSync(dirPath).isDirectory())
72
169
  continue;
73
- const files = fs.readdirSync(dirPath);
74
- for (const file of files) {
170
+ for (const file of fs.readdirSync(dirPath)) {
75
171
  if (file.endsWith(".json")) {
76
- const content = fs.readFileSync(path.join(dirPath, file), "utf-8");
77
- records.push(JSON.parse(content));
172
+ try {
173
+ records.push(JSON.parse(fs.readFileSync(path.join(dirPath, file), "utf-8")));
174
+ }
175
+ catch { }
78
176
  }
79
177
  }
80
178
  }
@@ -84,30 +182,378 @@ function getFailuresBySVL(level) {
84
182
  return getAllFailures().filter(r => r.violatedSVL === level);
85
183
  }
86
184
  function getTopFailurePatterns(limit = 5) {
87
- const counts = new Map();
88
- const all = getAllFailures();
89
- for (const r of all) {
185
+ const groups = new Map();
186
+ for (const r of getAllFailures()) {
90
187
  const key = `${r.violatedSVL}:${r.constraintType}`;
91
- counts.set(key, (counts.get(key) || 0) + 1);
188
+ const entry = groups.get(key) || { count: 0, examples: [] };
189
+ entry.count++;
190
+ if (entry.examples.length < 3)
191
+ entry.examples.push(r.intent);
192
+ groups.set(key, entry);
92
193
  }
93
- return [...counts.entries()]
94
- .map(([pattern, count]) => ({ pattern, count }))
194
+ return [...groups.entries()]
195
+ .map(([pattern, entry]) => ({ pattern, count: entry.count, examples: entry.examples }))
95
196
  .sort((a, b) => b.count - a.count)
96
197
  .slice(0, limit);
97
198
  }
98
- function generateCandidateRules() {
99
- const patterns = getTopFailurePatterns(3);
100
- const rules = [];
101
- for (const p of patterns) {
102
- if (p.pattern === "SVL-4:protocol") {
103
- rules.push("建议:为相关函数添加 SSG 协议约束,检查前置状态。");
199
+ /** 构建 AI 失败基因组 —— 按失效模式分组的完整画像 */
200
+ /** Get failure genome statistics: total failures, SVL distribution, constraint types, top patterns.
201
+ * @tags failure, statistics, genome, audit
202
+ */
203
+ function getFailureGenome() {
204
+ const sessions = getAllSessions();
205
+ const bySVL = { "SVL-1": 0, "SVL-2": 0, "SVL-3": 0, "SVL-4": 0 };
206
+ const byConstraint = {};
207
+ const patternCounts = new Map();
208
+ const fixPathCounts = new Map();
209
+ let totalViolations = 0;
210
+ let totalRetries = 0;
211
+ let resolvedSessions = 0;
212
+ for (const s of sessions) {
213
+ const sessionRetries = s.attempts.filter(a => a.outcome !== "success").length;
214
+ totalRetries += sessionRetries;
215
+ if (s.resolved)
216
+ resolvedSessions++;
217
+ for (const a of s.attempts) {
218
+ for (const v of a.violations) {
219
+ totalViolations++;
220
+ const svlKey = `SVL-${v.svl}`;
221
+ bySVL[svlKey] = (bySVL[svlKey] || 0) + 1;
222
+ const ct = v.violatedConstraint || "unknown";
223
+ byConstraint[ct] = (byConstraint[ct] || 0) + 1;
224
+ // Pattern grouping
225
+ const patternKey = `${svlKey}:${ct}`;
226
+ const entry = patternCounts.get(patternKey) || { count: 0, examples: [] };
227
+ entry.count++;
228
+ if (entry.examples.length < 3)
229
+ entry.examples.push(s.intent);
230
+ patternCounts.set(patternKey, entry);
231
+ if (v.fixPath && v.fixPath.length > 0) {
232
+ const key = `${svlKey}:${v.fixPath.join("→")}`;
233
+ fixPathCounts.set(key, (fixPathCounts.get(key) || 0) + 1);
234
+ }
235
+ }
236
+ }
237
+ }
238
+ const topPatterns = [...patternCounts.entries()]
239
+ .map(([pattern, entry]) => ({ pattern, count: entry.count, examples: entry.examples }))
240
+ .sort((a, b) => b.count - a.count)
241
+ .slice(0, 5);
242
+ const commonFixPaths = [...fixPathCounts.entries()]
243
+ .map(([key, count]) => {
244
+ const [violation, ...rest] = key.split(":");
245
+ return { violation, fixPath: rest.join(":").split("→"), count };
246
+ })
247
+ .sort((a, b) => b.count - a.count)
248
+ .slice(0, 10);
249
+ return {
250
+ totalFailures: totalViolations,
251
+ bySVL,
252
+ byConstraintType: byConstraint,
253
+ topPatterns,
254
+ commonFixPaths,
255
+ averageRetriesToSuccess: totalViolations > 0 ? Math.round((totalRetries / totalViolations) * 10) / 10 : 0,
256
+ };
257
+ }
258
+ /** 获取所有意图会话(归一化为 ExecutionSession[]) */
259
+ /** Load all execution sessions from the corpus directory.
260
+ * @tags session, corpus, audit, history
261
+ */
262
+ function getAllSessions() {
263
+ const sessions = [];
264
+ if (!fs.existsSync(SESSIONS_DIR))
265
+ return sessions;
266
+ for (const file of fs.readdirSync(SESSIONS_DIR)) {
267
+ if (file.endsWith(".json")) {
268
+ try {
269
+ const raw = JSON.parse(fs.readFileSync(path.join(SESSIONS_DIR, file), "utf-8"));
270
+ sessions.push(normalizeSession(raw));
271
+ }
272
+ catch { }
273
+ }
274
+ }
275
+ return sessions;
276
+ }
277
+ /** 检测并归一化新旧两种会话格式 */
278
+ function normalizeSession(raw) {
279
+ // 新格式:attempts[0] 有 violations 字段
280
+ if (raw.attempts?.length > 0 && raw.attempts[0].violations) {
281
+ return raw;
282
+ }
283
+ // 旧 IntentSession 格式 → 上转
284
+ return convertOldSession(raw);
285
+ }
286
+ /** 将旧 IntentSession 转换为 ExecutionSession */
287
+ function convertOldSession(old) {
288
+ const attempts = (old.attempts || []).map((sa, i) => ({
289
+ id: `${old.sessionId}_att_${i + 1}`,
290
+ sessionId: old.sessionId,
291
+ attemptNumber: sa.plannerAttempt || i + 1,
292
+ inputIntent: old.intent,
293
+ plannerSeed: old.sessionId,
294
+ constraintSnapshotId: old.snapshotId || "",
295
+ generatedActions: sa.actionSequence || [],
296
+ transitions: [],
297
+ violations: [{
298
+ svl: parseInt((sa.violatedSVL || "SVL-1").split("-")[1]),
299
+ violatedConstraint: sa.constraintType || "unknown",
300
+ actionIndex: 0,
301
+ currentStates: sa.ssgState,
302
+ missingStates: sa.ssgMissingFunctions,
303
+ fixPath: sa.ssgFixPath,
304
+ description: sa.errorDetail || "",
305
+ }],
306
+ outcome: "constraint_violation",
307
+ timestamp: new Date(old.timestamp).getTime(),
308
+ llmCallCount: 1,
309
+ durationMs: 0,
310
+ }));
311
+ return {
312
+ sessionId: old.sessionId,
313
+ intent: old.intent,
314
+ attempts,
315
+ successfulAttempt: old.resolved && old.successfulAlternative
316
+ ? {
317
+ id: `${old.sessionId}_success`,
318
+ sessionId: old.sessionId,
319
+ attemptNumber: old.totalRetries + 1,
320
+ inputIntent: old.intent,
321
+ plannerSeed: old.sessionId,
322
+ constraintSnapshotId: old.snapshotId || "",
323
+ generatedActions: old.successfulAlternative,
324
+ transitions: [],
325
+ violations: [],
326
+ outcome: "success",
327
+ timestamp: new Date(old.timestamp).getTime(),
328
+ llmCallCount: old.totalRetries,
329
+ durationMs: 0,
330
+ }
331
+ : undefined,
332
+ resolved: old.resolved,
333
+ snapshotId: old.snapshotId,
334
+ startedAt: new Date(old.timestamp).getTime(),
335
+ endedAt: new Date(old.timestamp).getTime(),
336
+ };
337
+ }
338
+ function computeACL(count, distinctIntents, resolvedRate) {
339
+ const acl4Count = parseInt(process.env.PROGMUNE_ACL4_COUNT || "10", 10);
340
+ const acl4Intents = parseInt(process.env.PROGMUNE_ACL4_INTENTS || "5", 10);
341
+ const acl3Count = parseInt(process.env.PROGMUNE_ACL3_COUNT || "4", 10);
342
+ const acl3Intents = parseInt(process.env.PROGMUNE_ACL3_INTENTS || "3", 10);
343
+ const acl2Count = parseInt(process.env.PROGMUNE_ACL2_COUNT || "2", 10);
344
+ if (count >= acl4Count && distinctIntents >= acl4Intents)
345
+ return "ACL-4";
346
+ if (count >= acl3Count || distinctIntents >= acl3Intents)
347
+ return "ACL-3";
348
+ if (count >= acl2Count)
349
+ return "ACL-2";
350
+ return "ACL-1";
351
+ }
352
+ function getLearnedPatterns() {
353
+ const sessions = getAllSessions();
354
+ const agg = new Map();
355
+ for (const s of sessions) {
356
+ for (const a of s.attempts) {
357
+ // Extract SSG violations from Attempt.violations
358
+ for (const v of a.violations) {
359
+ if (!v.fixPath || v.fixPath.length === 0)
360
+ continue;
361
+ if (v.svl !== 4)
362
+ continue; // only SVL-4 has fix paths
363
+ const violatedLabel = `SVL-${v.svl}`;
364
+ const missingKey = (v.missingStates || ["unknown"]).join(",");
365
+ const signature = `${violatedLabel}:${missingKey}`;
366
+ const existing = agg.get(signature);
367
+ const ts = new Date(s.startedAt).toISOString();
368
+ if (existing) {
369
+ existing.count++;
370
+ existing.intents.add(s.intent);
371
+ if (s.resolved)
372
+ existing.resolvedCount++;
373
+ if (ts > existing.lastSeen)
374
+ existing.lastSeen = ts;
375
+ }
376
+ else {
377
+ agg.set(signature, {
378
+ violation: `${violatedLabel}: ${(v.missingStates || ["unknown"]).join(", ")}`,
379
+ fixPath: v.fixPath,
380
+ count: 1,
381
+ intents: new Set([s.intent]),
382
+ resolvedCount: s.resolved ? 1 : 0,
383
+ firstSeen: ts,
384
+ lastSeen: ts,
385
+ });
386
+ }
387
+ }
104
388
  }
105
- else if (p.pattern === "SVL-1:symbol_existence") {
106
- rules.push("建议:检查项目 IR 是否缺少必要的函数定义。");
389
+ }
390
+ const patterns = [];
391
+ for (const [signature, data] of agg) {
392
+ const distinctCount = data.intents.size;
393
+ const resolvedRate = data.count > 0 ? Math.round((data.resolvedCount / data.count) * 100) / 100 : 0;
394
+ patterns.push({
395
+ signature,
396
+ violation: data.violation,
397
+ fixPath: data.fixPath,
398
+ occurrenceCount: data.count,
399
+ distinctIntents: [...data.intents],
400
+ resolvedRate,
401
+ antibodyLevel: computeACL(data.count, distinctCount, resolvedRate),
402
+ firstSeen: data.firstSeen,
403
+ lastSeen: data.lastSeen,
404
+ });
405
+ }
406
+ patterns.sort((a, b) => b.occurrenceCount - a.occurrenceCount);
407
+ return { failureToFix: patterns };
408
+ }
409
+ /** 查询匹配当前意图的高置信度抗体(ACL-3+),用于推理层免疫加速 */
410
+ function queryAntibodies(intent, minACL = "ACL-3") {
411
+ const { failureToFix } = getLearnedPatterns();
412
+ const aclRank = { "ACL-1": 1, "ACL-2": 2, "ACL-3": 3, "ACL-4": 4 };
413
+ const minRank = aclRank[minACL];
414
+ const intentLower = intent.toLowerCase();
415
+ const intentWords = new Set(intentLower.split(/[\s,,、]+|(?<=[一-鿿])(?=[一-鿿])/).filter(w => w.length > 0));
416
+ return failureToFix
417
+ .filter(p => aclRank[p.antibodyLevel] >= minRank)
418
+ .filter(p => p.fixPath && p.fixPath.length > 0)
419
+ .map(p => {
420
+ // 计算意图相似度
421
+ let overlapScore = 0;
422
+ for (const di of p.distinctIntents) {
423
+ const diWords = new Set(di.toLowerCase().split(/[\s,,、]+|(?<=[一-鿿])(?=[一-鿿])/).filter(w => w.length > 0));
424
+ const intersection = [...intentWords].filter(w => diWords.has(w)).length;
425
+ const union = new Set([...intentWords, ...diWords]).size;
426
+ overlapScore = Math.max(overlapScore, union > 0 ? intersection / union : 0);
107
427
  }
108
- else if (p.pattern === "SVL-3:dataflow") {
109
- rules.push("建议:强化变量声明检查,确保变量使用前已初始化。");
428
+ return { ...p, _score: overlapScore };
429
+ })
430
+ .filter(p => p._score >= 0.2) // 至少 20% 的 Jaccard 相似度
431
+ .sort((a, b) => b._score - a._score);
432
+ }
433
+ /** 语义热力图:哪些协议/层最脆弱,约束如何聚类 */
434
+ function getSemanticHeatmap() {
435
+ const sessions = getAllSessions();
436
+ // Count total violations from sessions
437
+ let totalViolations = 0;
438
+ const svlCounts = {};
439
+ const funcCounts = new Map();
440
+ for (const s of sessions) {
441
+ for (const a of s.attempts) {
442
+ for (const v of a.violations) {
443
+ totalViolations++;
444
+ const svlKey = `SVL-${v.svl}`;
445
+ svlCounts[svlKey] = (svlCounts[svlKey] || 0) + 1;
446
+ // Fragile protocols: extract blocked function from description
447
+ const blockedMatch = v.description.match(/(\w+)\s*(要求|requires|blocked|不允许|不合法)/);
448
+ const fn = blockedMatch ? blockedMatch[1] : (a.generatedActions.find(x => x.kind === "call")?.function || "unknown");
449
+ const existing = funcCounts.get(fn);
450
+ if (existing) {
451
+ existing.count++;
452
+ }
453
+ else {
454
+ funcCounts.set(fn, { count: 1, svl: svlKey });
455
+ }
456
+ }
110
457
  }
111
458
  }
459
+ const total = totalViolations || 1;
460
+ const fragileProtocols = [...funcCounts.entries()]
461
+ .map(([fn, data]) => ({ function: fn, violationCount: data.count, svl: data.svl }))
462
+ .sort((a, b) => b.violationCount - a.violationCount)
463
+ .slice(0, 10);
464
+ // SVL hotspots
465
+ const svlHotspots = [];
466
+ for (const [svl, count] of Object.entries(svlCounts)) {
467
+ svlHotspots.push({ svl, count, percentage: Math.round((count / total) * 100) });
468
+ }
469
+ svlHotspots.sort((a, b) => b.count - a.count);
470
+ // Constraint clusters: which anomaly types co-occur in the same session
471
+ const constraintClusters = [];
472
+ for (const s of sessions) {
473
+ if (s.attempts.length < 2)
474
+ continue;
475
+ const types = [...new Set(s.attempts.flatMap(a => a.violations.map(v => v.violatedConstraint)))].filter(Boolean).sort();
476
+ if (types.length >= 2) {
477
+ constraintClusters.push({ constraints: types, count: s.attempts.length, intent: s.intent });
478
+ }
479
+ }
480
+ constraintClusters.sort((a, b) => b.count - a.count);
481
+ // High friction intents: which tasks require the most adaptations
482
+ const highFrictionIntents = sessions
483
+ .map(s => ({
484
+ intent: s.intent,
485
+ adaptationCount: s.attempts.filter(a => a.outcome !== "success").length,
486
+ anomalyTypes: [...new Set(s.attempts.flatMap(a => a.violations.map(v => v.violatedConstraint)))].filter(Boolean),
487
+ }))
488
+ .sort((a, b) => b.adaptationCount - a.adaptationCount)
489
+ .slice(0, 8);
490
+ return { fragileProtocols, svlHotspots, constraintClusters, highFrictionIntents };
491
+ }
492
+ /** 抗体效能统计:量化免疫加速节省的 LLM 调用和 token */
493
+ /** Get antibody efficacy statistics: hits by level, tokens saved, top signatures.
494
+ * @tags antibody, immune, statistics, efficiency
495
+ */
496
+ function getAntibodyStats() {
497
+ const sessions = getAllSessions();
498
+ let totalHits = 0;
499
+ let fastPathHits = 0;
500
+ let injectedHintHits = 0;
501
+ let totalLLMCallsSaved = 0;
502
+ let totalTokensSaved = 0;
503
+ const byLevel = {};
504
+ const sigMap = new Map();
505
+ for (const s of sessions) {
506
+ for (const a of s.attempts) {
507
+ const hit = a.antibodyHit;
508
+ if (!hit)
509
+ continue;
510
+ totalHits++;
511
+ if (hit.action === "fast_path")
512
+ fastPathHits++;
513
+ else if (hit.action === "injected_hint")
514
+ injectedHintHits++;
515
+ totalLLMCallsSaved += hit.llmCallsSaved || 0;
516
+ totalTokensSaved += hit.estimatedTokensSaved || 0;
517
+ const level = hit.level || "ACL-1";
518
+ const lb = byLevel[level] || { hits: 0, llmSaved: 0, tokensSaved: 0 };
519
+ lb.hits++;
520
+ lb.llmSaved += hit.llmCallsSaved || 0;
521
+ lb.tokensSaved += hit.estimatedTokensSaved || 0;
522
+ byLevel[level] = lb;
523
+ const sig = hit.signature || "unknown";
524
+ const se = sigMap.get(sig) || { hits: 0, similarities: [] };
525
+ se.hits++;
526
+ se.similarities.push(hit.similarityScore || 0);
527
+ sigMap.set(sig, se);
528
+ }
529
+ }
530
+ const topSignatures = [...sigMap.entries()]
531
+ .map(([signature, d]) => ({
532
+ signature,
533
+ hits: d.hits,
534
+ avgSimilarity: Math.round((d.similarities.reduce((a, b) => a + b, 0) / d.similarities.length) * 100) / 100,
535
+ }))
536
+ .sort((a, b) => b.hits - a.hits)
537
+ .slice(0, 10);
538
+ return { totalHits, fastPathHits, injectedHintHits, totalLLMCallsSaved, totalTokensSaved, byLevel, topSignatures };
539
+ }
540
+ function generateCandidateRules() {
541
+ const genome = getFailureGenome();
542
+ const rules = [];
543
+ // SVL-4 修复路径
544
+ const ssgFixes = genome.commonFixPaths.filter(f => f.violation === 'SVL-4');
545
+ if (ssgFixes.length > 0) {
546
+ const top = ssgFixes[0];
547
+ rules.push(`SSG 修复规则: 当检测到协议违规时,优先尝试调用 ${top.fixPath.join(' → ')}。`);
548
+ }
549
+ if (genome.bySVL["SVL-1"] > genome.bySVL["SVL-4"]) {
550
+ rules.push("SVL-1 是最高频失效模式——建议强化 IR 提取的符号覆盖度。");
551
+ }
552
+ if (genome.bySVL["SVL-4"] > 0) {
553
+ rules.push("SVL-4 协议违规建议:为关键函数添加 @protocol JSDoc 注解。");
554
+ }
555
+ if (genome.averageRetriesToSuccess > 2) {
556
+ rules.push(`平均需要 ${genome.averageRetriesToSuccess} 次重试才能成功——考虑优化 Planner 路径排序。`);
557
+ }
112
558
  return rules;
113
559
  }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.main = main;
4
+ // @progmune-generated session=sess_1780343206982_0y8gk timestamp=2026-06-01T19:46:48.561Z ruleHash=9dec68bc2995e92a
5
+ // Generated with IR constraint: 382 functions, 17 protocol rules
6
+ const failure_corpus_1 = require("./failure-corpus");
7
+ function main() {
8
+ const genome = (0, failure_corpus_1.getFailureGenome)();
9
+ return genome;
10
+ }
11
+ main();
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.main = main;
4
+ // @progmune-generated session=sess_1780345916092_sn5tt timestamp=2026-06-01T20:31:57.612Z ruleHash=9dec68bc2995e92a
5
+ // Generated with IR constraint: 390 functions, 17 protocol rules
6
+ const failure_corpus_1 = require("./failure-corpus");
7
+ function main() {
8
+ const failures = (0, failure_corpus_1.getAllFailures)();
9
+ return failures;
10
+ }
11
+ main();
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.main = main;
4
+ // @progmune-generated session=sess_1780343852770_0srir timestamp=2026-06-01T19:57:34.249Z ruleHash=9dec68bc2995e92a
5
+ // Generated with IR constraint: 395 functions, 17 protocol rules
6
+ const failure_corpus_1 = require("./failure-corpus");
7
+ function main() {
8
+ const stats = (0, failure_corpus_1.getAntibodyStats)();
9
+ const body = getBody();
10
+ const methods = getMethods();
11
+ return stats;
12
+ }
13
+ main();
package/dist/feedback.js CHANGED
@@ -39,6 +39,7 @@ exports.getFunctionSuccessRate = getFunctionSuccessRate;
39
39
  exports.recordRun = recordRun;
40
40
  const fs = __importStar(require("fs"));
41
41
  const path = __importStar(require("path"));
42
+ const file_lock_1 = require("./file-lock");
42
43
  const FEEDBACK_PATH = path.resolve(__dirname, "../feedback.json");
43
44
  function loadFeedback() {
44
45
  if (!fs.existsSync(FEEDBACK_PATH))
@@ -46,9 +47,11 @@ function loadFeedback() {
46
47
  return JSON.parse(fs.readFileSync(FEEDBACK_PATH, "utf-8"));
47
48
  }
48
49
  function saveFeedback(record) {
49
- const data = loadFeedback();
50
- data.push(record);
51
- fs.writeFileSync(FEEDBACK_PATH, JSON.stringify(data, null, 2));
50
+ (0, file_lock_1.withLock)("feedback.json", () => {
51
+ const data = loadFeedback();
52
+ data.push(record);
53
+ fs.writeFileSync(FEEDBACK_PATH, JSON.stringify(data, null, 2));
54
+ });
52
55
  }
53
56
  function getFunctionSuccessRate(funcName) {
54
57
  const records = loadFeedback();