progmune-runtime 2.1.0 → 2.1.2
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 +75 -2
- 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/health-utils.js +40 -0
- package/dist/immune-reporter.js +1 -0
- package/dist/ir-utils.js +18 -0
- package/dist/ledger-registry.js +11 -0
- package/dist/ledger-utils.js +40 -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/stdlib.js +205 -0
- package/dist/utils.js +2 -0
- package/dist/validator.js +2 -0
- package/package.json +1 -1
- package/readme.md +0 -829
|
@@ -48,6 +48,9 @@ exports.formatFailureStats = formatFailureStats;
|
|
|
48
48
|
const fs = __importStar(require("fs"));
|
|
49
49
|
const CORPUS_DIR = "failure-corpus";
|
|
50
50
|
/** Classify a compile error string into a root cause. */
|
|
51
|
+
/** Classify a compile error into a root cause category. */
|
|
52
|
+
/** @requires ERROR_STRING @produces ROOT_CAUSE */
|
|
53
|
+
/** @requires ERROR_STRING @produces ROOT_CAUSE */
|
|
51
54
|
function classifyError(error) {
|
|
52
55
|
if (!error)
|
|
53
56
|
return "F10";
|
|
@@ -68,6 +71,9 @@ function classifyError(error) {
|
|
|
68
71
|
return "F10";
|
|
69
72
|
}
|
|
70
73
|
/** Classify a planning failure. */
|
|
74
|
+
/** Classify a planning failure into a root cause category. */
|
|
75
|
+
/** @requires ERROR_STRING @produces ROOT_CAUSE */
|
|
76
|
+
/** @requires ERROR_STRING @produces ROOT_CAUSE */
|
|
71
77
|
function classifyPlanError(error) {
|
|
72
78
|
if (!error)
|
|
73
79
|
return "F10";
|
|
@@ -78,6 +84,8 @@ function classifyPlanError(error) {
|
|
|
78
84
|
return "F07"; // most plan failures are parsing issues
|
|
79
85
|
}
|
|
80
86
|
/** Record a failure and save to disk. */
|
|
87
|
+
/** Record a generation failure to the failure corpus. */
|
|
88
|
+
/** @requires FAILURE_EVENT @produces FAILURE_ID */
|
|
81
89
|
function recordFailure(record) {
|
|
82
90
|
const id = `F-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
83
91
|
const entry = {
|
|
@@ -94,6 +102,10 @@ function recordFailure(record) {
|
|
|
94
102
|
return id;
|
|
95
103
|
}
|
|
96
104
|
/** Load all recorded failures. */
|
|
105
|
+
/** Load all recorded failures from the failure corpus. */
|
|
106
|
+
/** @requires FAILURE_CORPUS @produces FAILURE_LIST */
|
|
107
|
+
/** @requires FAILURE_CORPUS @produces FAILURE_LIST */
|
|
108
|
+
/** @requires FAILURE_CORPUS @produces FAILURE_LIST */
|
|
97
109
|
function loadFailures() {
|
|
98
110
|
if (!fs.existsSync(CORPUS_DIR))
|
|
99
111
|
return [];
|
|
@@ -109,6 +121,10 @@ function loadFailures() {
|
|
|
109
121
|
return failures.sort((a, b) => b.timestamp - a.timestamp);
|
|
110
122
|
}
|
|
111
123
|
/** Get failure statistics grouped by root cause. */
|
|
124
|
+
/** Get failure statistics grouped by root cause. */
|
|
125
|
+
/** @requires FAILURE_LIST @produces FAILURE_STATS */
|
|
126
|
+
/** @requires FAILURE_LIST @produces FAILURE_STATS */
|
|
127
|
+
/** @requires FAILURE_LIST @produces FAILURE_STATS */
|
|
112
128
|
function failureStats() {
|
|
113
129
|
const failures = loadFailures();
|
|
114
130
|
const byRootCause = {};
|
|
@@ -123,6 +139,10 @@ function failureStats() {
|
|
|
123
139
|
};
|
|
124
140
|
}
|
|
125
141
|
/** Format failure stats as readable text. */
|
|
142
|
+
/** Format failure statistics as a human-readable report. */
|
|
143
|
+
/** @requires FAILURE_STATS @produces FORMATTED_REPORT */
|
|
144
|
+
/** @requires FAILURE_STATS @produces FORMATTED_REPORT */
|
|
145
|
+
/** @requires FAILURE_STATS @produces FORMATTED_REPORT */
|
|
126
146
|
function formatFailureStats() {
|
|
127
147
|
const stats = failureStats();
|
|
128
148
|
if (stats.total === 0)
|
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") {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeHealthScore = computeHealthScore;
|
|
4
|
+
exports.formatHealthLevel = formatHealthLevel;
|
|
5
|
+
exports.countSessionLedgers = countSessionLedgers;
|
|
6
|
+
/** Compute overall immune health score from failure and antibody data.
|
|
7
|
+
* @requires FAILURE_GENOME @produces HEALTH_SCORE
|
|
8
|
+
* @tags health, score, immune
|
|
9
|
+
*/
|
|
10
|
+
function computeHealthScore(failureGenome, antibodyStats) {
|
|
11
|
+
const totalFailures = failureGenome?.totalFailures || 0;
|
|
12
|
+
const totalHits = antibodyStats?.totalHits || 0;
|
|
13
|
+
const base = 100;
|
|
14
|
+
const failurePenalty = Math.min(totalFailures * 2, 40);
|
|
15
|
+
const antibodyBonus = Math.min(totalHits * 3, 20);
|
|
16
|
+
return Math.max(0, Math.min(100, base - failurePenalty + antibodyBonus));
|
|
17
|
+
}
|
|
18
|
+
/** Format a health score as a status level.
|
|
19
|
+
* @requires HEALTH_SCORE @produces HEALTH_STATUS
|
|
20
|
+
* @tags health, format
|
|
21
|
+
*/
|
|
22
|
+
function formatHealthLevel(score) {
|
|
23
|
+
if (score >= 90)
|
|
24
|
+
return "Excellent";
|
|
25
|
+
if (score >= 70)
|
|
26
|
+
return "Good";
|
|
27
|
+
if (score >= 50)
|
|
28
|
+
return "Fair";
|
|
29
|
+
return "Poor";
|
|
30
|
+
}
|
|
31
|
+
/** Validate a ledger and return pass/fail counts.
|
|
32
|
+
* @requires SESSION_LIST @produces VALIDATION_COUNTS
|
|
33
|
+
* @tags ledger, validation, audit
|
|
34
|
+
*/
|
|
35
|
+
function countSessionLedgers(sessions) {
|
|
36
|
+
const withLedger = sessions.filter((s) => {
|
|
37
|
+
return s.attempts?.some((a) => a.transitions?.length > 0);
|
|
38
|
+
}).length;
|
|
39
|
+
return { total: sessions.length, withLedger };
|
|
40
|
+
}
|
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))
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.countTotalTransitions = countTotalTransitions;
|
|
4
|
+
exports.formatTransitionCount = formatTransitionCount;
|
|
5
|
+
exports.hasViolations = hasViolations;
|
|
6
|
+
exports.countSessionsWithViolations = countSessionsWithViolations;
|
|
7
|
+
/** Count total transitions across all session ledgers.
|
|
8
|
+
* @requires SESSION_LIST @produces TRANSITION_COUNT
|
|
9
|
+
* @tags ledger, count, statistics
|
|
10
|
+
*/
|
|
11
|
+
function countTotalTransitions(sessions) {
|
|
12
|
+
let count = 0;
|
|
13
|
+
for (const s of sessions) {
|
|
14
|
+
for (const a of (s.attempts || [])) {
|
|
15
|
+
count += (a.transitions || []).length;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return count;
|
|
19
|
+
}
|
|
20
|
+
/** Format a transition count as a summary string.
|
|
21
|
+
* @requires TRANSITION_COUNT @produces FORMATTED_COUNT
|
|
22
|
+
* @tags ledger, format
|
|
23
|
+
*/
|
|
24
|
+
function formatTransitionCount(count) {
|
|
25
|
+
return `${count} total transitions across all sessions`;
|
|
26
|
+
}
|
|
27
|
+
/** Check if a session has any protocol violations in its attempts.
|
|
28
|
+
* @requires SESSION_DATA @produces VIOLATION_CHECK
|
|
29
|
+
* @tags ledger, validation
|
|
30
|
+
*/
|
|
31
|
+
function hasViolations(session) {
|
|
32
|
+
return (session.attempts || []).some((a) => (a.violations || []).length > 0);
|
|
33
|
+
}
|
|
34
|
+
/** Count sessions that have violations.
|
|
35
|
+
* @requires SESSION_LIST @produces VIOLATION_COUNT
|
|
36
|
+
* @tags ledger, validation, statistics
|
|
37
|
+
*/
|
|
38
|
+
function countSessionsWithViolations(sessions) {
|
|
39
|
+
return sessions.filter(s => hasViolations(s)).length;
|
|
40
|
+
}
|
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
|
}
|