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.
- package/.mcp.json +11 -0
- package/.progmune_allowlist +50 -0
- package/.test_report/test_report.md +87 -0
- package/Dockerfile +2 -10
- package/FAQ.md +167 -0
- package/demo-project/auth.ts +55 -0
- package/demo-project/tsconfig.json +8 -0
- package/dist/ab-stats.js +11 -0
- package/dist/acl-breakdown.js +13 -0
- package/dist/all-sessions.js +11 -0
- package/dist/antibody-stats.js +11 -0
- package/dist/audit.js +218 -0
- package/dist/benchmark-count.js +7 -0
- package/dist/benchmark-full.js +17 -0
- package/dist/benchmark-pass-rate.js +54 -0
- package/dist/benchmark-report.js +67 -0
- package/dist/benchmark-save.js +62 -0
- package/dist/benchmark-status.js +15 -0
- package/dist/branch-ledger.js +365 -0
- package/dist/branch-tree-count.js +14 -0
- package/dist/check.js +506 -0
- package/dist/common-fixpath.js +12 -0
- package/dist/constraint-types.js +12 -0
- package/dist/deterministic-replay.js +277 -0
- package/dist/emitter.js +112 -12
- package/dist/exec-metrics.js +11 -0
- package/dist/execute.js +242 -0
- package/dist/extract-ir.js +550 -5
- package/dist/failure-collector.js +143 -0
- package/dist/failure-corpus.js +481 -35
- package/dist/failure-report.js +11 -0
- package/dist/failures.js +11 -0
- package/dist/fast-path-hits.js +13 -0
- package/dist/feedback.js +6 -3
- package/dist/file-lock.js +82 -0
- package/dist/find-session.js +10 -0
- package/dist/fingerprint-list.js +15 -0
- package/dist/gen-history-log.js +13 -0
- package/dist/generate.js +2 -1
- package/dist/generate_500.js +4 -2
- package/dist/genome.js +11 -0
- package/dist/heatmap-data.js +11 -0
- package/dist/heatmap.js +11 -0
- package/dist/immune-reporter.js +137 -0
- package/dist/learned.js +11 -0
- package/dist/ledger-registry.js +241 -0
- package/dist/llm.js +43 -2
- package/dist/load-benchmarks.js +47 -0
- package/dist/main.js +2 -1
- package/dist/mcp-server.mjs +446 -34
- package/dist/memory-layer.js +51 -13
- package/dist/metrics.js +11 -0
- package/dist/obs-web.js +561 -0
- package/dist/p0_ssg_demo.js +255 -40
- package/dist/planner.js +933 -65
- package/dist/protocol-registry.js +105 -0
- package/dist/recent-session.js +12 -0
- package/dist/repair-proposal.js +353 -0
- package/dist/report.js +32 -0
- package/dist/runtime-invariants.js +161 -0
- package/dist/runtime-types.js +117 -0
- package/dist/search-planner.js +38 -9
- package/dist/semantic-snapshot.js +155 -0
- package/dist/semantic-trace.js +1497 -0
- package/dist/semantic-validator.js +3 -2
- package/dist/semantic_guard_test.js +2 -1
- package/dist/sessions.js +11 -0
- package/dist/ssg-validator.js +658 -20
- package/dist/svl-distribution.js +11 -0
- package/dist/terminal-status.js +11 -0
- package/dist/test_failure_corpus.js +3 -1
- package/dist/token-savings.js +11 -0
- package/dist/total-repairs.js +12 -0
- package/dist/unresolved-count.js +12 -0
- package/dist/valid-fingerprints.js +13 -0
- package/dist/validator.js +116 -60
- package/dist/verify-fps.js +11 -0
- package/dist/verify-ledgers.js +11 -0
- package/docs/whitepaper-style.css +77 -0
- package/docs/whitepaper-v2.1.md +609 -0
- package/docs/whitepaper-v2.2.md +1064 -0
- package/docs/whitepaper-v2.2.pdf +0 -0
- package/fly.toml +1 -1
- package/package.json +13 -4
- package/protocols.json +136 -0
- package/public/dashboard.html +119 -0
- package/readme.md +829 -0
- package/server/hub.js +84 -12
- package/test/replay-golden/sess_1780063202050_mgeld.json +9 -0
- package/test/replay-golden/sess_1780064032560_gocld.json +354 -0
- package/test/replay-golden/sess_1780064413331_s2709.json +606 -0
- package/test/replay-golden/sess_1780064792710_y3avo.json +614 -0
- package/test/replay-golden.ts +84 -0
- package/test_benchmark.js +165 -0
- package/test_comprehensive.mjs +638 -0
- package/test_concurrency.js +129 -0
- package/test_ir_robustness.js +85 -0
- package/test_semantic_contracts.js +269 -0
- package/test_ssg_stress.js +156 -0
- package/test_svl3.js +58 -0
- package/tsconfig.json +1 -1
- package/.env.example +0 -3
- package/.progmune_memory/fingerprints.json +0 -7
- package/.progmune_memory/opt_in.json +0 -4
- package/README.md +0 -118
- package/dist/mcp-server.js +0 -55
- package/immune_hub_data/2026-05-14.json +0 -50
package/dist/failure-corpus.js
CHANGED
|
@@ -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
|
|
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
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
74
|
-
for (const file of files) {
|
|
170
|
+
for (const file of fs.readdirSync(dirPath)) {
|
|
75
171
|
if (file.endsWith(".json")) {
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
88
|
-
const
|
|
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
|
-
|
|
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 [...
|
|
94
|
-
.map(([pattern,
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
109
|
-
|
|
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();
|
package/dist/failures.js
ADDED
|
@@ -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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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();
|