progmune-runtime 2.1.0 → 2.1.1
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/README.md +172 -0
- package/dist/action-runtime.js +1 -0
- package/dist/audit.js +4 -0
- package/dist/branch-ledger.js +28 -0
- package/dist/deterministic-replay.js +6 -0
- package/dist/emitter.js +31 -0
- package/dist/execute.js +9 -0
- package/dist/extract-ir.js +48 -6
- package/dist/failure-collector.js +20 -0
- package/dist/failure-corpus.js +26 -0
- package/dist/feedback.js +4 -0
- package/dist/immune-reporter.js +1 -0
- package/dist/ir-utils.js +18 -0
- package/dist/ledger-registry.js +11 -0
- package/dist/llm.js +3 -0
- package/dist/memory-layer.js +3 -0
- package/dist/planner.js +109 -18
- package/dist/protocol-registry.js +7 -0
- package/dist/repair-proposal.js +10 -0
- package/dist/runtime-invariants.js +9 -0
- package/dist/runtime.js +1 -0
- package/dist/search-planner.js +1 -0
- package/dist/semantic-snapshot.js +2 -0
- package/dist/session-utils.js +19 -0
- package/dist/ssg-validator.js +8 -0
- package/dist/utils.js +2 -0
- package/dist/validator.js +2 -0
- package/package.json +1 -1
- package/readme.md +0 -829
package/dist/failure-corpus.js
CHANGED
|
@@ -66,6 +66,7 @@ function checkpointPath(intent) {
|
|
|
66
66
|
const hash = Buffer.from(intent).toString("base64").replace(/[/+=]/g, "_").slice(0, 32);
|
|
67
67
|
return path.join(CHECKPOINT_DIR, `ckpt_${hash}.json`);
|
|
68
68
|
}
|
|
69
|
+
/** Save planner checkpoint for crash recovery. */
|
|
69
70
|
function saveCheckpoint(intent, data) {
|
|
70
71
|
ensureDir(CHECKPOINT_DIR);
|
|
71
72
|
const cp = {
|
|
@@ -75,6 +76,7 @@ function saveCheckpoint(intent, data) {
|
|
|
75
76
|
};
|
|
76
77
|
fs.writeFileSync(checkpointPath(intent), JSON.stringify(cp, null, 2));
|
|
77
78
|
}
|
|
79
|
+
/** Load a previously saved planner checkpoint. */
|
|
78
80
|
function loadCheckpoint(intent) {
|
|
79
81
|
try {
|
|
80
82
|
const raw = fs.readFileSync(checkpointPath(intent), "utf-8");
|
|
@@ -84,12 +86,14 @@ function loadCheckpoint(intent) {
|
|
|
84
86
|
return null;
|
|
85
87
|
}
|
|
86
88
|
}
|
|
89
|
+
/** Clear a saved planner checkpoint. */
|
|
87
90
|
function clearCheckpoint(intent) {
|
|
88
91
|
try {
|
|
89
92
|
fs.unlinkSync(checkpointPath(intent));
|
|
90
93
|
}
|
|
91
94
|
catch { }
|
|
92
95
|
}
|
|
96
|
+
/** Record a constraint violation to the failure corpus. */
|
|
93
97
|
function recordFailure(record) {
|
|
94
98
|
(0, file_lock_1.withLock)("failure-corpus", () => {
|
|
95
99
|
ensureDir(CORPUS_DIR);
|
|
@@ -122,6 +126,8 @@ function recordFailure(record) {
|
|
|
122
126
|
* 保存执行会话(含所有尝试、违规、状态转移)。
|
|
123
127
|
* @protocol namespace=dev_pipeline pre_states=["CODE_EMITTED"] post_states=["SESSION_RECORDED"] invalidate=["CODE_EMITTED"]
|
|
124
128
|
*/
|
|
129
|
+
/** @requires EXECUTION_DATA @produces SESSION_ID */
|
|
130
|
+
/** @requires EXECUTION_DATA @produces SESSION_ID */
|
|
125
131
|
function recordSession(session) {
|
|
126
132
|
ensureDir(SESSIONS_DIR);
|
|
127
133
|
const sessionId = session.sessionId || `sess_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
|
|
@@ -158,6 +164,7 @@ function recordSession(session) {
|
|
|
158
164
|
fs.writeFileSync(path.join(SESSIONS_DIR, `${sessionId}.json`), JSON.stringify(fullSession, null, 2));
|
|
159
165
|
return sessionId;
|
|
160
166
|
}
|
|
167
|
+
/** @requires FAILURE_CORPUS @produces FAILURE_LIST */
|
|
161
168
|
function getAllFailures() {
|
|
162
169
|
const records = [];
|
|
163
170
|
if (!fs.existsSync(CORPUS_DIR))
|
|
@@ -178,9 +185,11 @@ function getAllFailures() {
|
|
|
178
185
|
}
|
|
179
186
|
return records;
|
|
180
187
|
}
|
|
188
|
+
/** @requires FAILURE_LIST @produces FILTERED_FAILURES */
|
|
181
189
|
function getFailuresBySVL(level) {
|
|
182
190
|
return getAllFailures().filter(r => r.violatedSVL === level);
|
|
183
191
|
}
|
|
192
|
+
/** @requires FAILURE_LIST @produces FAILURE_PATTERNS */
|
|
184
193
|
function getTopFailurePatterns(limit = 5) {
|
|
185
194
|
const groups = new Map();
|
|
186
195
|
for (const r of getAllFailures()) {
|
|
@@ -200,6 +209,9 @@ function getTopFailurePatterns(limit = 5) {
|
|
|
200
209
|
/** Get failure genome statistics: total failures, SVL distribution, constraint types, top patterns.
|
|
201
210
|
* @tags failure, statistics, genome, audit
|
|
202
211
|
*/
|
|
212
|
+
/** Get failure genome statistics: total failures by SVL, constraint type, and fix path. */
|
|
213
|
+
/** @requires FAILURE_DATA @produces FAILURE_GENOME */
|
|
214
|
+
/** @requires FAILURE_DATA @produces FAILURE_GENOME */
|
|
203
215
|
function getFailureGenome() {
|
|
204
216
|
const sessions = getAllSessions();
|
|
205
217
|
const bySVL = { "SVL-1": 0, "SVL-2": 0, "SVL-3": 0, "SVL-4": 0 };
|
|
@@ -259,6 +271,10 @@ function getFailureGenome() {
|
|
|
259
271
|
/** Load all execution sessions from the corpus directory.
|
|
260
272
|
* @tags session, corpus, audit, history
|
|
261
273
|
*/
|
|
274
|
+
/** @requires SESSION_DATA @produces SESSION_LIST */
|
|
275
|
+
/** @requires SESSION_CORPUS @produces SESSION_LIST */
|
|
276
|
+
/** @requires SESSION_CORPUS @produces SESSION_LIST */
|
|
277
|
+
/** @requires SESSION_CORPUS @produces SESSION_LIST */
|
|
262
278
|
function getAllSessions() {
|
|
263
279
|
const sessions = [];
|
|
264
280
|
if (!fs.existsSync(SESSIONS_DIR))
|
|
@@ -349,6 +365,8 @@ function computeACL(count, distinctIntents, resolvedRate) {
|
|
|
349
365
|
return "ACL-2";
|
|
350
366
|
return "ACL-1";
|
|
351
367
|
}
|
|
368
|
+
/** Get antibody patterns learned from failure history. */
|
|
369
|
+
/** @requires FAILURE_HISTORY @produces LEARNED_PATTERNS */
|
|
352
370
|
function getLearnedPatterns() {
|
|
353
371
|
const sessions = getAllSessions();
|
|
354
372
|
const agg = new Map();
|
|
@@ -407,6 +425,8 @@ function getLearnedPatterns() {
|
|
|
407
425
|
return { failureToFix: patterns };
|
|
408
426
|
}
|
|
409
427
|
/** 查询匹配当前意图的高置信度抗体(ACL-3+),用于推理层免疫加速 */
|
|
428
|
+
/** Query antibody registry for matching repair patterns. */
|
|
429
|
+
/** @requires FAILURE_SIGNATURE @produces ANTIBODY_MATCH */
|
|
410
430
|
function queryAntibodies(intent, minACL = "ACL-3") {
|
|
411
431
|
const { failureToFix } = getLearnedPatterns();
|
|
412
432
|
const aclRank = { "ACL-1": 1, "ACL-2": 2, "ACL-3": 3, "ACL-4": 4 };
|
|
@@ -431,6 +451,9 @@ function queryAntibodies(intent, minACL = "ACL-3") {
|
|
|
431
451
|
.sort((a, b) => b._score - a._score);
|
|
432
452
|
}
|
|
433
453
|
/** 语义热力图:哪些协议/层最脆弱,约束如何聚类 */
|
|
454
|
+
/** Get semantic heatmap showing fragile protocols and SVL hotspots. */
|
|
455
|
+
/** @requires FAILURE_DATA @produces HEATMAP */
|
|
456
|
+
/** @requires FAILURE_HEATMAP @produces HEATMAP_DATA */
|
|
434
457
|
function getSemanticHeatmap() {
|
|
435
458
|
const sessions = getAllSessions();
|
|
436
459
|
// Count total violations from sessions
|
|
@@ -493,6 +516,8 @@ function getSemanticHeatmap() {
|
|
|
493
516
|
/** Get antibody efficacy statistics: hits by level, tokens saved, top signatures.
|
|
494
517
|
* @tags antibody, immune, statistics, efficiency
|
|
495
518
|
*/
|
|
519
|
+
/** @requires ANTIBODY_DATA @produces ANTIBODY_STATS */
|
|
520
|
+
/** @requires ANTIBODY_DATA @produces ANTIBODY_STATS */
|
|
496
521
|
function getAntibodyStats() {
|
|
497
522
|
const sessions = getAllSessions();
|
|
498
523
|
let totalHits = 0;
|
|
@@ -537,6 +562,7 @@ function getAntibodyStats() {
|
|
|
537
562
|
.slice(0, 10);
|
|
538
563
|
return { totalHits, fastPathHits, injectedHintHits, totalLLMCallsSaved, totalTokensSaved, byLevel, topSignatures };
|
|
539
564
|
}
|
|
565
|
+
/** Generate candidate immune rules from failure patterns. */
|
|
540
566
|
function generateCandidateRules() {
|
|
541
567
|
const genome = getFailureGenome();
|
|
542
568
|
const rules = [];
|
package/dist/feedback.js
CHANGED
|
@@ -41,11 +41,13 @@ const fs = __importStar(require("fs"));
|
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
42
|
const file_lock_1 = require("./file-lock");
|
|
43
43
|
const FEEDBACK_PATH = path.resolve(__dirname, "../feedback.json");
|
|
44
|
+
/** @requires CORPUS @produces FEEDBACK_DATA */
|
|
44
45
|
function loadFeedback() {
|
|
45
46
|
if (!fs.existsSync(FEEDBACK_PATH))
|
|
46
47
|
return [];
|
|
47
48
|
return JSON.parse(fs.readFileSync(FEEDBACK_PATH, "utf-8"));
|
|
48
49
|
}
|
|
50
|
+
/** @requires FEEDBACK_EVENT @produces FEEDBACK_ID */
|
|
49
51
|
function saveFeedback(record) {
|
|
50
52
|
(0, file_lock_1.withLock)("feedback.json", () => {
|
|
51
53
|
const data = loadFeedback();
|
|
@@ -53,6 +55,7 @@ function saveFeedback(record) {
|
|
|
53
55
|
fs.writeFileSync(FEEDBACK_PATH, JSON.stringify(data, null, 2));
|
|
54
56
|
});
|
|
55
57
|
}
|
|
58
|
+
/** @requires FUNCTION_NAME @produces SUCCESS_RATE */
|
|
56
59
|
function getFunctionSuccessRate(funcName) {
|
|
57
60
|
const records = loadFeedback();
|
|
58
61
|
const funcRecords = records.filter(r => r.functionName === funcName);
|
|
@@ -61,6 +64,7 @@ function getFunctionSuccessRate(funcName) {
|
|
|
61
64
|
const successCount = funcRecords.filter(r => r.success).length;
|
|
62
65
|
return successCount / funcRecords.length;
|
|
63
66
|
}
|
|
67
|
+
/** @requires EXECUTION_DATA @produces RUN_ID */
|
|
64
68
|
function recordRun(intent, actions, success, error) {
|
|
65
69
|
for (const action of actions) {
|
|
66
70
|
if (action.kind === "call") {
|
package/dist/immune-reporter.js
CHANGED
|
@@ -97,6 +97,7 @@ function extractFingerprints(cursor) {
|
|
|
97
97
|
}
|
|
98
98
|
return fingerprints;
|
|
99
99
|
}
|
|
100
|
+
/** @requires CORPUS @produces FINGERPRINT_REPORT */
|
|
100
101
|
async function reportFingerprints() {
|
|
101
102
|
const cursor = getReportCursor();
|
|
102
103
|
const fingerprints = extractFingerprints(cursor);
|
package/dist/ir-utils.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.countExported = countExported;
|
|
4
|
+
exports.mergeResults = mergeResults;
|
|
5
|
+
/** Count exported functions in an IR function list.
|
|
6
|
+
* @requires IR_FUNCTIONS @produces EXPORT_COUNT
|
|
7
|
+
* @tags ir, count, export
|
|
8
|
+
*/
|
|
9
|
+
function countExported(ir) {
|
|
10
|
+
return ir.filter((f) => f.exported).length;
|
|
11
|
+
}
|
|
12
|
+
/** Merge two results into a combined object.
|
|
13
|
+
* @requires RESULT_A @produces MERGED_RESULT
|
|
14
|
+
* @tags merge, combine
|
|
15
|
+
*/
|
|
16
|
+
function mergeResults(a, b) {
|
|
17
|
+
return { first: a, second: b };
|
|
18
|
+
}
|
package/dist/ledger-registry.js
CHANGED
|
@@ -62,6 +62,8 @@ function fingerprintPath(sessionId) {
|
|
|
62
62
|
// ── Core API ──
|
|
63
63
|
/** Register a ledger fingerprint (execution certificate).
|
|
64
64
|
* Called after a session is recorded — creates an immutable proof of the ledger state. */
|
|
65
|
+
/** Register a ledger fingerprint as an execution certificate. */
|
|
66
|
+
/** @requires LEDGER_DATA @produces FINGERPRINT */
|
|
65
67
|
function registerFingerprint(sessionId, transitions, ruleHash) {
|
|
66
68
|
const dir = fingerprintsDir();
|
|
67
69
|
if (!fs.existsSync(dir)) {
|
|
@@ -78,6 +80,8 @@ function registerFingerprint(sessionId, transitions, ruleHash) {
|
|
|
78
80
|
return fingerprint;
|
|
79
81
|
}
|
|
80
82
|
/** Get a single stored fingerprint by sessionId. Returns null if not registered. */
|
|
83
|
+
/** Get a stored ledger fingerprint by session ID. */
|
|
84
|
+
/** @requires SESSION_ID @produces FINGERPRINT */
|
|
81
85
|
function getFingerprint(sessionId) {
|
|
82
86
|
const fpPath = fingerprintPath(sessionId);
|
|
83
87
|
if (!fs.existsSync(fpPath))
|
|
@@ -90,6 +94,7 @@ function getFingerprint(sessionId) {
|
|
|
90
94
|
}
|
|
91
95
|
}
|
|
92
96
|
/** List all registered fingerprints, sorted by timestamp (oldest first). */
|
|
97
|
+
/** List all registered ledger fingerprints. */
|
|
93
98
|
function getFingerprintRegistry() {
|
|
94
99
|
const dir = fingerprintsDir();
|
|
95
100
|
if (!fs.existsSync(dir))
|
|
@@ -112,6 +117,7 @@ function getFingerprintRegistry() {
|
|
|
112
117
|
}
|
|
113
118
|
/** Verify a single session's fingerprint.
|
|
114
119
|
* Requires the session's transitions to re-hash and compare. */
|
|
120
|
+
/** Verify a single ledger fingerprint against current data. */
|
|
115
121
|
function verifyFingerprint(sessionId, transitions, currentRuleHash) {
|
|
116
122
|
const stored = getFingerprint(sessionId);
|
|
117
123
|
if (!stored) {
|
|
@@ -144,6 +150,9 @@ function verifyFingerprint(sessionId, transitions, currentRuleHash) {
|
|
|
144
150
|
}
|
|
145
151
|
/** Verify all registered fingerprints.
|
|
146
152
|
* Loads each session to re-hash and compare against the stored fingerprint. */
|
|
153
|
+
/** Verify all registered ledger fingerprints and return tampered status. */
|
|
154
|
+
/** @requires FINGERPRINT_DATA @produces VERIFICATION_RESULT */
|
|
155
|
+
/** @requires FINGERPRINT_DATA @produces VERIFICATION_RESULT */
|
|
147
156
|
function verifyAllFingerprints(currentRuleHash) {
|
|
148
157
|
const fingerprints = getFingerprintRegistry();
|
|
149
158
|
const results = [];
|
|
@@ -199,6 +208,8 @@ function verifyAllFingerprints(currentRuleHash) {
|
|
|
199
208
|
}
|
|
200
209
|
/** Register fingerprints for all sessions that don't yet have one.
|
|
201
210
|
* Called during `npm run check` to ensure all sessions are fingerprinted. */
|
|
211
|
+
/** Register fingerprints for all sessions that lack them. */
|
|
212
|
+
/** @requires SESSION_DATA @produces FINGERPRINT_DATA */
|
|
202
213
|
function registerAllMissingFingerprints() {
|
|
203
214
|
const sessionsDir = path.resolve(process.env.PROGMUNE_PROJECT_DIR || process.cwd(), ".progmune_corpus/sessions");
|
|
204
215
|
if (!fs.existsSync(sessionsDir))
|
package/dist/llm.js
CHANGED
|
@@ -36,11 +36,13 @@ const client = new openai_1.default({ apiKey, baseURL });
|
|
|
36
36
|
exports.callCount = 0;
|
|
37
37
|
function resetCallCount() { exports.callCount = 0; }
|
|
38
38
|
/** 粗略 token 估算:CJK 字符 ~1.5 token/字,其余 ~0.4 token/字符 */
|
|
39
|
+
/** @requires TEXT @produces TOKEN_COUNT */
|
|
39
40
|
function estimateTokens(text) {
|
|
40
41
|
const cjk = (text.match(/[一-鿿㐀-䶿]/g) || []).length;
|
|
41
42
|
const other = text.length - cjk;
|
|
42
43
|
return Math.ceil(cjk * 1.5 + other * 0.4);
|
|
43
44
|
}
|
|
45
|
+
/** @requires PROMPT @produces LLM_RESPONSE */
|
|
44
46
|
async function generate(prompt) {
|
|
45
47
|
exports.callCount++;
|
|
46
48
|
const resp = await client.chat.completions.create({
|
|
@@ -51,6 +53,7 @@ async function generate(prompt) {
|
|
|
51
53
|
return resp.choices[0]?.message?.content || "";
|
|
52
54
|
}
|
|
53
55
|
/** 带 system prompt 的调用:静态规则放 system,动态内容放 user,语义分离便于未来对接各平台缓存策略 */
|
|
56
|
+
/** @requires SYSTEM_PROMPT @produces LLM_RESPONSE */
|
|
54
57
|
async function chat(systemPrompt, userPrompt) {
|
|
55
58
|
exports.callCount++;
|
|
56
59
|
const resp = await client.chat.completions.create({
|
package/dist/memory-layer.js
CHANGED
|
@@ -91,6 +91,7 @@ function saveEpisodes(episodes) {
|
|
|
91
91
|
fs.writeFileSync(EPISODIC_FILE, JSON.stringify(fresh.slice(0, MAX_EPISODES), null, 2));
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
+
/** @requires EXECUTION_DATA @produces MEMORY_ID */
|
|
94
95
|
function recordEpisode(episode) {
|
|
95
96
|
const episodes = loadEpisodes();
|
|
96
97
|
const newEpisode = {
|
|
@@ -104,6 +105,7 @@ function recordEpisode(episode) {
|
|
|
104
105
|
}
|
|
105
106
|
saveEpisodes(episodes);
|
|
106
107
|
}
|
|
108
|
+
/** @requires LIMIT @produces EPISODE_LIST */
|
|
107
109
|
function getRecentEpisodes(limit = 10) {
|
|
108
110
|
return loadEpisodes().slice(0, limit);
|
|
109
111
|
}
|
|
@@ -172,6 +174,7 @@ function consolidateSemantic(minOccurrences = 3) {
|
|
|
172
174
|
saveSemantic(templates);
|
|
173
175
|
console.error(`[语义记忆] 巩固完成,模板数量: ${templates.length}`);
|
|
174
176
|
}
|
|
177
|
+
/** @requires INTENT @produces TEMPLATE */
|
|
175
178
|
function findSemanticTemplate(intent) {
|
|
176
179
|
const templates = loadSemantic();
|
|
177
180
|
if (templates.length === 0)
|
package/dist/planner.js
CHANGED
|
@@ -90,29 +90,72 @@ function determineConstraintType(svl) {
|
|
|
90
90
|
case "SVL-4": return "protocol";
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
/**
|
|
94
|
-
function buildCompactFuncList(funcs) {
|
|
93
|
+
/** 构建紧凑函数列表 — 包含能力元数据帮助 LLM 理解函数语义 */
|
|
94
|
+
function buildCompactFuncList(funcs, allFuncs) {
|
|
95
|
+
// Known string enums with example values
|
|
96
|
+
const ENUM_DEFAULTS = {
|
|
97
|
+
"SVL": '"SVL-4"', "RootCause": '"F01"', "BranchReason": '"repair_attempt"',
|
|
98
|
+
"RepairStrategy": '"insert"', "ConstraintType": '"protocol"',
|
|
99
|
+
};
|
|
95
100
|
return funcs.map((f) => {
|
|
96
|
-
const params = (f.params || []).map((p) =>
|
|
97
|
-
|
|
101
|
+
const params = (f.params || []).map((p) => {
|
|
102
|
+
const t = (p.type || "any").replace(/\[\]$/, "");
|
|
103
|
+
const def = ENUM_DEFAULTS[t];
|
|
104
|
+
return def ? `${p.name}: ${def}` : `${p.name}: ${p.type}`;
|
|
105
|
+
}).join(",");
|
|
106
|
+
let line = `${f.name}(${params})->${f.returnType || "any"}`;
|
|
107
|
+
// Add capability metadata
|
|
108
|
+
const meta = [];
|
|
109
|
+
if (f.purpose)
|
|
110
|
+
meta.push(f.purpose.slice(0, 60));
|
|
111
|
+
if (f.produces && f.produces.length > 0)
|
|
112
|
+
meta.push(`→${f.produces.join(",")}`);
|
|
113
|
+
if (meta.length > 0)
|
|
114
|
+
line += ` // ${meta.join(" | ")}`;
|
|
115
|
+
return line;
|
|
98
116
|
}).join("\n");
|
|
99
117
|
}
|
|
118
|
+
/** Build capability chain hints from IR: producer→consumer relationships.
|
|
119
|
+
* e.g. "failureStats → formatFailureStats (FAILURE_STATS)" */
|
|
120
|
+
function buildChainHints(funcs) {
|
|
121
|
+
const chains = [];
|
|
122
|
+
for (const f of funcs) {
|
|
123
|
+
if (!f.produces)
|
|
124
|
+
continue;
|
|
125
|
+
for (const p of f.produces) {
|
|
126
|
+
const consumers = funcs.filter((x) => x.requires?.includes(p) && x.name !== f.name);
|
|
127
|
+
for (const c of consumers) {
|
|
128
|
+
chains.push(`${f.name}()→${c.name}() // ${p}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (chains.length === 0)
|
|
133
|
+
return "";
|
|
134
|
+
return "\n推荐调用链(先调生产者,用 $变量名 传给消费者):\n" + chains.map(c => ` ${c}`).join("\n");
|
|
135
|
+
}
|
|
100
136
|
const SYSTEM_PROMPT = `你是程序合成助手。只输出 JSON 数组,不输出解释。
|
|
101
137
|
|
|
102
|
-
|
|
103
|
-
[{"f":"函数名","to":"变量名","a":[{"n":"参数名","t":"类型","v":值}]},{"r":"变量名"}]
|
|
138
|
+
格式:[{"f":"函数名","to":"变量名","a":[{"n":"参数名","t":"类型","v":值}]},{"r":"变量名"}]
|
|
104
139
|
|
|
105
140
|
规则:
|
|
106
|
-
-
|
|
107
|
-
- "a":
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
141
|
+
- 函数名从可用列表中选择,优先选注释中 purpose 匹配需求的函数
|
|
142
|
+
- 0参数函数直接用 "a":[]:{"f":"getAllSessions","to":"s","a":[]}
|
|
143
|
+
- 参数值规则(重要!):
|
|
144
|
+
- 字符串: "v":""(空串)或 "v":"SVL-4"(已知枚举值)
|
|
145
|
+
- 数字: "v":0 或 "v":1
|
|
146
|
+
- 布尔: "v":false
|
|
147
|
+
- 对象/数组: "v":{} as Type
|
|
148
|
+
- 上一个函数返回值: "v":"$变量名"($前缀引用)
|
|
149
|
+
- 返回值: {"r":"变量名"} — 必须返回,不能只调用不返回
|
|
150
|
+
- 链式调用:看到推荐调用链时,用 $变量名 把生产者输出传给消费者
|
|
111
151
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
152
|
+
铁律:
|
|
153
|
+
- 函数签名中带引号的参数(如 "SVL-4")是字符串值,直接 v 中
|
|
154
|
+
- 字符串枚举(SVL等)用带引号的值,禁止 {} as Type
|
|
155
|
+
- 每个call的返回值用"to"命名变量,下一个call通过"$变量名"引用
|
|
156
|
+
- 最后一个action必须是{"r":"变量名"},不能以call结尾
|
|
157
|
+
- 如果你调了函数,必须return它的结果
|
|
158
|
+
- 只输出JSON`;
|
|
116
159
|
const RETRY_HINT = `输出格式:紧凑 JSON 数组 [{"f":"函数名","to":"变量名","a":[...]}]`;
|
|
117
160
|
/** 构建重试 prompt:精简但包含必要的 IR 语法提示 */
|
|
118
161
|
/** 解析 LLM 输出的紧凑 JSON 为 Action[]。
|
|
@@ -479,6 +522,7 @@ function attemptSSGRepair(actions, rejection, ir, protocols, namespaceInitialSta
|
|
|
479
522
|
}
|
|
480
523
|
return null;
|
|
481
524
|
}
|
|
525
|
+
/** @requires INTENT @produces ACTION_PLAN */
|
|
482
526
|
async function plan(userIntent) {
|
|
483
527
|
(0, llm_1.resetCallCount)();
|
|
484
528
|
const irRaw = JSON.parse(fs.readFileSync("ir.json", "utf-8"));
|
|
@@ -621,18 +665,65 @@ async function plan(userIntent) {
|
|
|
621
665
|
console.error(`💉 ACL-3 抗体注入提示: ${top.fixPath.join(" → ")}`);
|
|
622
666
|
}
|
|
623
667
|
const keywords = (0, utils_1.extractKeywords)(userIntent);
|
|
668
|
+
const intentLower = userIntent.toLowerCase();
|
|
624
669
|
const scored = ir.map((f) => {
|
|
625
670
|
let score = 0;
|
|
671
|
+
// Name match (existing)
|
|
626
672
|
for (const kw of keywords) {
|
|
627
673
|
score += (0, utils_1.jaccardSimilarity)(f.name.toLowerCase(), kw);
|
|
628
674
|
if (f.name.toLowerCase().includes(kw))
|
|
629
675
|
score += 0.5;
|
|
630
676
|
}
|
|
677
|
+
// Capability Graph: purpose match
|
|
678
|
+
if (f.purpose) {
|
|
679
|
+
const purposeLower = f.purpose.toLowerCase();
|
|
680
|
+
for (const kw of keywords) {
|
|
681
|
+
if (purposeLower.includes(kw))
|
|
682
|
+
score += 1.0; // strong signal
|
|
683
|
+
}
|
|
684
|
+
// Full intent overlap with purpose
|
|
685
|
+
const intentWords = intentLower.split(/[\s,,]+/);
|
|
686
|
+
for (const w of intentWords) {
|
|
687
|
+
if (w.length > 2 && purposeLower.includes(w))
|
|
688
|
+
score += 0.3;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
// Capability Graph: requires/produces capability matching
|
|
692
|
+
if (f.produces) {
|
|
693
|
+
for (const p of f.produces) {
|
|
694
|
+
if (intentLower.includes(p.toLowerCase().replace(/_/g, " ")))
|
|
695
|
+
score += 1.5;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (f.requires) {
|
|
699
|
+
for (const r of f.requires) {
|
|
700
|
+
if (intentLower.includes(r.toLowerCase().replace(/_/g, " ")))
|
|
701
|
+
score += 0.5;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
// Capability Graph: tag match
|
|
705
|
+
if (f.tags) {
|
|
706
|
+
for (const tag of f.tags) {
|
|
707
|
+
if (intentLower.includes(tag.toLowerCase()))
|
|
708
|
+
score += 0.8;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
631
711
|
return { ...f, score };
|
|
632
712
|
});
|
|
633
713
|
scored.sort((a, b) => b.score - a.score);
|
|
634
714
|
const topFuncs = scored.slice(0, 15);
|
|
635
|
-
const compactFuncList = buildCompactFuncList(topFuncs);
|
|
715
|
+
const compactFuncList = buildCompactFuncList(topFuncs, ir);
|
|
716
|
+
const chainHints = buildChainHints(topFuncs);
|
|
717
|
+
// Known string-enum types: tell LLM these are strings, not objects
|
|
718
|
+
const STRING_ENUMS = {
|
|
719
|
+
"SVL": '"SVL-1"|"SVL-2"|"SVL-3"|"SVL-4"',
|
|
720
|
+
"RootCause": '"F01"|"F02"|...|"F10"',
|
|
721
|
+
"BranchReason": '"root"|"repair_attempt"|"alternative"',
|
|
722
|
+
"RepairStrategy": '"insert"|"replace"|"reorder"',
|
|
723
|
+
};
|
|
724
|
+
const typeHints = Object.keys(STRING_ENUMS).length > 0
|
|
725
|
+
? `\n类型速查:${Object.entries(STRING_ENUMS).map(([k, v]) => `${k}=${v}`).join(",")}。这些类型传字符串值。`
|
|
726
|
+
: "";
|
|
636
727
|
const userIntentPart = userIntent.match(/(?:实现|implement|编写|创建)\s*(\w+)\s*(?:函数|function)?/i);
|
|
637
728
|
const forbiddenFuncs = [];
|
|
638
729
|
if (userIntentPart) {
|
|
@@ -643,7 +734,7 @@ async function plan(userIntent) {
|
|
|
643
734
|
}
|
|
644
735
|
const protocolChainHint = buildProtocolChainHint(protocols);
|
|
645
736
|
const userPrompt = `可用函数:
|
|
646
|
-
${compactFuncList}${protocolChainHint}
|
|
737
|
+
${compactFuncList}${protocolChainHint}${chainHints}${typeHints}
|
|
647
738
|
|
|
648
739
|
需求:${userIntent}${antibodyHint}
|
|
649
740
|
|
|
@@ -690,7 +781,7 @@ ${RETRY_HINT}
|
|
|
690
781
|
});
|
|
691
782
|
if (legalFuncs.length === topFuncs.length)
|
|
692
783
|
return compactFuncList;
|
|
693
|
-
return buildCompactFuncList(legalFuncs);
|
|
784
|
+
return buildCompactFuncList(legalFuncs, ir);
|
|
694
785
|
}
|
|
695
786
|
const maxRetries = 3;
|
|
696
787
|
for (let r = startRetry; r < maxRetries; r++) {
|
|
@@ -53,11 +53,14 @@ const ssg_validator_1 = require("./ssg-validator");
|
|
|
53
53
|
// ── Singleton cache ──
|
|
54
54
|
let cached = null;
|
|
55
55
|
/** Invalidate the cache (call after protocols.json changes). */
|
|
56
|
+
/** Invalidate cached protocol configuration for reload. */
|
|
56
57
|
function invalidateProtocolCache() {
|
|
57
58
|
cached = null;
|
|
58
59
|
}
|
|
59
60
|
/** Get the authoritative protocol configuration.
|
|
60
61
|
* Cached after first call; call invalidateProtocolCache() to force reload. */
|
|
62
|
+
/** Get the authoritative protocol configuration from the single source of truth. */
|
|
63
|
+
/** @requires PROJECT_CONFIG @produces PROTOCOL_CONFIG */
|
|
61
64
|
function getProtocolConfig() {
|
|
62
65
|
if (cached)
|
|
63
66
|
return cached;
|
|
@@ -96,10 +99,14 @@ function getProtocolConfig() {
|
|
|
96
99
|
}
|
|
97
100
|
// ── Convenience re-exports ──
|
|
98
101
|
/** Get namespace initial states only (most common need). */
|
|
102
|
+
/** Get namespace initial states from protocol configuration. */
|
|
103
|
+
/** @requires PROJECT_CONFIG @produces NAMESPACE_STATES */
|
|
99
104
|
function getNsInit() {
|
|
100
105
|
return new Map(getProtocolConfig().nsInit);
|
|
101
106
|
}
|
|
102
107
|
/** Get current rule hash without loading full config. */
|
|
108
|
+
/** Get the current rule set hash. */
|
|
109
|
+
/** @requires PROTOCOL_CONFIG @produces RULE_HASH */
|
|
103
110
|
function getRuleHash() {
|
|
104
111
|
return getProtocolConfig().ruleHash;
|
|
105
112
|
}
|
package/dist/repair-proposal.js
CHANGED
|
@@ -27,6 +27,8 @@ const protocol_registry_1 = require("./protocol-registry");
|
|
|
27
27
|
const branch_ledger_1 = require("./branch-ledger");
|
|
28
28
|
// ── Proposal Generation ──
|
|
29
29
|
/** Generate repair proposals for all detected violations in a ledger. */
|
|
30
|
+
/** Generate repair proposals for all detected violations. */
|
|
31
|
+
/** @requires VIOLATIONS @produces REPAIR_PROPOSALS */
|
|
30
32
|
function suggestRepairs(violations, ir, protocols) {
|
|
31
33
|
const proposals = [];
|
|
32
34
|
for (const v of violations) {
|
|
@@ -43,6 +45,7 @@ function suggestRepairs(violations, ir, protocols) {
|
|
|
43
45
|
return proposals;
|
|
44
46
|
}
|
|
45
47
|
/** Protocol violation repair: use SSG fixPath to suggest insertions. */
|
|
48
|
+
/** Generate repair proposals for SSG protocol violations. */
|
|
46
49
|
function suggestProtocolRepair(rejection, ir) {
|
|
47
50
|
const proposals = [];
|
|
48
51
|
if (rejection.fixPath && rejection.fixPath.length > 0) {
|
|
@@ -94,6 +97,7 @@ function suggestProtocolRepair(rejection, ir) {
|
|
|
94
97
|
return proposals;
|
|
95
98
|
}
|
|
96
99
|
/** Invariant violation repair: use rebuildState to compute correct transition data. */
|
|
100
|
+
/** Generate repair proposals for invariant consistency violations. */
|
|
97
101
|
function suggestInvariantRepair(violation, ledger, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
|
|
98
102
|
const proposals = [];
|
|
99
103
|
if (violation.invariant === "before-consistency") {
|
|
@@ -223,6 +227,7 @@ function suggestGenericRepair(violation, ir) {
|
|
|
223
227
|
/** Convert an accepted repair proposal into a new Branch.
|
|
224
228
|
* Creates a child branch with the proposed fix applied.
|
|
225
229
|
* The original ledger is never modified. */
|
|
230
|
+
/** Convert an accepted repair proposal into a new branch. */
|
|
226
231
|
function applyProposalAsBranch(proposal, parentBranch, currentLedger, ir) {
|
|
227
232
|
const branch = (0, branch_ledger_1.createBranch)(parentBranch, "repair_attempt");
|
|
228
233
|
switch (proposal.strategy) {
|
|
@@ -275,6 +280,7 @@ function applyProposalAsBranch(proposal, parentBranch, currentLedger, ir) {
|
|
|
275
280
|
// ── Validation ──
|
|
276
281
|
/** Validate a repair proposal: does applying it fix the violation?
|
|
277
282
|
* Returns true if a re-check passes after applying the proposal. */
|
|
283
|
+
/** Validate whether a repair proposal fixes the violation. */
|
|
278
284
|
function validateProposal(proposal, currentLedger, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
|
|
279
285
|
let proposedLedger;
|
|
280
286
|
switch (proposal.strategy) {
|
|
@@ -298,6 +304,8 @@ function validateProposal(proposal, currentLedger, namespaceInitialStates = (0,
|
|
|
298
304
|
}
|
|
299
305
|
// ── Summary ──
|
|
300
306
|
/** Generate a comprehensive repair summary from a ledger and IR context. */
|
|
307
|
+
/** Generate a comprehensive repair summary with minimal fix set. */
|
|
308
|
+
/** @requires LEDGER_DATA @produces REPAIR_SUMMARY */
|
|
301
309
|
function generateRepairSummary(ledger, ir, protocols, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
|
|
302
310
|
const consistency = (0, ssg_validator_1.checkLedgerConsistency)(ledger, namespaceInitialStates);
|
|
303
311
|
const allProposals = [];
|
|
@@ -319,6 +327,8 @@ function generateRepairSummary(ledger, ir, protocols, namespaceInitialStates = (
|
|
|
319
327
|
* This is the authoritative "minimal fix set" — applying these proposals in order
|
|
320
328
|
* should resolve all detected violations without redundant fixes.
|
|
321
329
|
*/
|
|
330
|
+
/** Get the minimal set of repair proposals by deduplication. */
|
|
331
|
+
/** @requires REPAIR_PROPOSALS @produces MINIMAL_FIX_SET */
|
|
322
332
|
function getMinimalFixSet(proposals) {
|
|
323
333
|
const seen = new Map();
|
|
324
334
|
for (const p of proposals) {
|
|
@@ -25,6 +25,8 @@ const isStrict = () => STRICT;
|
|
|
25
25
|
// ── Assertions ──
|
|
26
26
|
/** Assert full ledger passes Invariant-0 + Invariant-1.
|
|
27
27
|
* Throws InvariantViolationError with the first violation's details. */
|
|
28
|
+
/** Assert a ledger passes all invariant checks. */
|
|
29
|
+
/** @requires LEDGER_DATA @produces CONSISTENCY_CHECK */
|
|
28
30
|
function assertLedgerConsistency(ledger, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
|
|
29
31
|
if (ledger.length === 0)
|
|
30
32
|
return;
|
|
@@ -51,6 +53,8 @@ function assertLedgerConsistency(ledger, namespaceInitialStates = (0, protocol_r
|
|
|
51
53
|
}
|
|
52
54
|
/** Assert a single transition's delta consistency.
|
|
53
55
|
* Checks that applying acquire/invalidate to statesBefore produces statesAfter. */
|
|
56
|
+
/** Assert a single transition has consistent state deltas. */
|
|
57
|
+
/** @requires TRANSITION @produces DELTA_CHECK */
|
|
54
58
|
function assertDeltaConsistency(transition) {
|
|
55
59
|
if (!transition.valid)
|
|
56
60
|
return;
|
|
@@ -111,6 +115,8 @@ function assertDeltaConsistency(transition) {
|
|
|
111
115
|
}
|
|
112
116
|
}
|
|
113
117
|
/** Assert rule hashes match — detects when validation rules changed under a ledger. */
|
|
118
|
+
/** Assert rule hashes match to detect rule changes. */
|
|
119
|
+
/** @requires EXPECTED_HASH @produces HASH_MATCH_RESULT */
|
|
114
120
|
function assertRuleHashMatch(expected, actual, context) {
|
|
115
121
|
if (expected === actual)
|
|
116
122
|
return;
|
|
@@ -126,6 +132,7 @@ function assertRuleHashMatch(expected, actual, context) {
|
|
|
126
132
|
});
|
|
127
133
|
}
|
|
128
134
|
/** Assert transition indices are strictly monotonic (no duplicates, non-decreasing). */
|
|
135
|
+
/** Assert transition indices are strictly monotonic. */
|
|
129
136
|
function assertTransitionOrder(ledger) {
|
|
130
137
|
if (ledger.length <= 1)
|
|
131
138
|
return;
|
|
@@ -144,6 +151,8 @@ function assertTransitionOrder(ledger) {
|
|
|
144
151
|
}
|
|
145
152
|
}
|
|
146
153
|
/** Convenience: run all invariant checks on a ledger. Does not throw if all pass. */
|
|
154
|
+
/** Run all invariant checks on a ledger. */
|
|
155
|
+
/** @requires LEDGER_DATA @produces INVARIANT_RESULT */
|
|
147
156
|
function assertLedgerInvariants(ledger, namespaceInitialStates = (0, protocol_registry_1.getNsInit)(), expectedRuleHash) {
|
|
148
157
|
assertTransitionOrder(ledger);
|
|
149
158
|
assertLedgerConsistency(ledger, namespaceInitialStates);
|
package/dist/runtime.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.runAndCheck = runAndCheck;
|
|
|
37
37
|
const child_process_1 = require("child_process");
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
|
+
/** @requires COMMAND @produces EXECUTION_RESULT */
|
|
40
41
|
function runAndCheck(code) {
|
|
41
42
|
// 把临时文件写入 test-login 目录,使用它的 tsconfig 编译
|
|
42
43
|
const tmpDir = path.resolve("test-login");
|
package/dist/search-planner.js
CHANGED
|
@@ -127,6 +127,7 @@ async function batchScoreFuncs(funcs, goal) {
|
|
|
127
127
|
}
|
|
128
128
|
return result;
|
|
129
129
|
}
|
|
130
|
+
/** @requires INTENT @produces ACTION_PLAN */
|
|
130
131
|
async function searchPlan(intent, beamWidth = 2, maxDepth = 6) {
|
|
131
132
|
(0, llm_1.resetCallCount)();
|
|
132
133
|
staticScoreCache.clear();
|
|
@@ -50,6 +50,7 @@ function ensureDir(dir) {
|
|
|
50
50
|
fs.mkdirSync(dir, { recursive: true });
|
|
51
51
|
}
|
|
52
52
|
/** 从 IR 数据创建快照 */
|
|
53
|
+
/** @requires IR_DATA @produces SNAPSHOT */
|
|
53
54
|
function createSnapshot(ir, intent, sessionId) {
|
|
54
55
|
const functions = ir.map((f) => ({
|
|
55
56
|
name: f.name,
|
|
@@ -69,6 +70,7 @@ function createSnapshot(ir, intent, sessionId) {
|
|
|
69
70
|
};
|
|
70
71
|
}
|
|
71
72
|
/** 持久化快照 */
|
|
73
|
+
/** @requires SNAPSHOT @produces SNAPSHOT_ID */
|
|
72
74
|
function saveSnapshot(snapshot) {
|
|
73
75
|
ensureDir(SNAPSHOT_DIR);
|
|
74
76
|
fs.writeFileSync(path.join(SNAPSHOT_DIR, `${snapshot.id}.json`), JSON.stringify(snapshot, null, 2));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.countResolved = countResolved;
|
|
4
|
+
exports.formatSessionCounts = formatSessionCounts;
|
|
5
|
+
/** Count resolved vs unresolved sessions in a session list.
|
|
6
|
+
* @requires SESSION_LIST @produces RESOLVED_COUNT
|
|
7
|
+
* @tags session, count, statistics
|
|
8
|
+
*/
|
|
9
|
+
function countResolved(sessions) {
|
|
10
|
+
const resolved = sessions.filter((s) => s.resolved).length;
|
|
11
|
+
return { resolved, unresolved: sessions.length - resolved, total: sessions.length };
|
|
12
|
+
}
|
|
13
|
+
/** Get a summary of session counts as a formatted string.
|
|
14
|
+
* @requires RESOLVED_COUNT @produces FORMATTED_COUNT
|
|
15
|
+
* @tags session, format, report
|
|
16
|
+
*/
|
|
17
|
+
function formatSessionCounts(counts) {
|
|
18
|
+
return `${counts.resolved}/${counts.total} resolved, ${counts.unresolved} unresolved`;
|
|
19
|
+
}
|