progmune-runtime 2.0.4 → 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/.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/README.md +108 -54
- 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/action-runtime.js +1 -0
- package/dist/all-sessions.js +11 -0
- package/dist/antibody-stats.js +11 -0
- package/dist/audit.js +222 -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 +393 -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 +283 -0
- package/dist/emitter.js +143 -12
- package/dist/exec-metrics.js +11 -0
- package/dist/execute.js +251 -0
- package/dist/extract-ir.js +592 -5
- package/dist/failure-collector.js +163 -0
- package/dist/failure-corpus.js +507 -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 +10 -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 +34 -22
- package/dist/ir-utils.js +18 -0
- package/dist/learned.js +11 -0
- package/dist/ledger-registry.js +252 -0
- package/dist/llm.js +46 -2
- package/dist/load-benchmarks.js +47 -0
- package/dist/main.js +2 -1
- package/dist/mcp-server.mjs +445 -43
- package/dist/memory-layer.js +54 -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 +996 -80
- package/dist/protocol-registry.js +112 -0
- package/dist/recent-session.js +12 -0
- package/dist/repair-proposal.js +363 -0
- package/dist/runtime-invariants.js +170 -0
- package/dist/runtime-types.js +117 -0
- package/dist/runtime.js +1 -0
- package/dist/search-planner.js +39 -9
- package/dist/semantic-snapshot.js +157 -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/session-utils.js +19 -0
- package/dist/sessions.js +11 -0
- package/dist/ssg-validator.js +666 -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/utils.js +2 -0
- package/dist/valid-fingerprints.js +13 -0
- package/dist/validator.js +118 -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 +131 -11
- package/public/dashboard.html +119 -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/episodic.json +0 -186
- package/.progmune_memory/fingerprints.json +0 -7
- package/.progmune_memory/opt_in.json +0 -4
- package/immune_hub_data/2026-05-14.json +0 -50
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase 4: Deterministic Replay (P1)
|
|
4
|
+
*
|
|
5
|
+
* Verifies that replaying a session's ledger against current rules
|
|
6
|
+
* produces the same result as the stored execution certificate.
|
|
7
|
+
*
|
|
8
|
+
* Answers: "Would this execution produce the same result today?"
|
|
9
|
+
* Not just "read the log" — actively re-validates every transition.
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.replaySession = replaySession;
|
|
46
|
+
exports.replayLedger = replayLedger;
|
|
47
|
+
exports.replayWithDetail = replayWithDetail;
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
const ledger_registry_1 = require("./ledger-registry");
|
|
51
|
+
const protocol_registry_1 = require("./protocol-registry");
|
|
52
|
+
const ssg_validator_1 = require("./ssg-validator");
|
|
53
|
+
const branch_ledger_1 = require("./branch-ledger");
|
|
54
|
+
// ── Core Replay ──
|
|
55
|
+
/** Replay a session from disk, comparing against its stored fingerprint. */
|
|
56
|
+
/** Deterministically replay a session ledger and verify against stored fingerprint. */
|
|
57
|
+
/** @requires SESSION_DATA @produces REPLAY_RESULT */
|
|
58
|
+
function replaySession(sessionId, currentRules, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
|
|
59
|
+
// Load session
|
|
60
|
+
const sessionsDir = path.resolve(process.env.PROGMUNE_PROJECT_DIR || process.cwd(), ".progmune_corpus/sessions");
|
|
61
|
+
const sessionFile = path.join(sessionsDir, `${sessionId}.json`);
|
|
62
|
+
if (!fs.existsSync(sessionFile)) {
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
sessionId,
|
|
66
|
+
finalState: {},
|
|
67
|
+
replayedHash: "",
|
|
68
|
+
storedHash: "",
|
|
69
|
+
ruleHashMatch: false,
|
|
70
|
+
ledgerHashMatch: false,
|
|
71
|
+
totalTransitions: 0,
|
|
72
|
+
validTransitions: 0,
|
|
73
|
+
invalidTransitions: 0,
|
|
74
|
+
divergencePoint: 0,
|
|
75
|
+
divergenceDetail: `Session file not found: ${sessionFile}`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const session = JSON.parse(fs.readFileSync(sessionFile, "utf-8"));
|
|
79
|
+
// Collect all transitions
|
|
80
|
+
let transitions = [];
|
|
81
|
+
let sessionRuleHash = session.ruleHash || "";
|
|
82
|
+
// Handle branch tree if present (Phase 4)
|
|
83
|
+
if (session.branchTree && session.branchTree.length > 0) {
|
|
84
|
+
const branches = session.branchTree;
|
|
85
|
+
const map = (0, branch_ledger_1.buildBranchMap)(branches);
|
|
86
|
+
const root = (0, branch_ledger_1.findRootBranch)(branches);
|
|
87
|
+
if (root) {
|
|
88
|
+
transitions = (0, branch_ledger_1.flattenBranch)(root, map);
|
|
89
|
+
}
|
|
90
|
+
// Use first attempt's ruleHash as session hash
|
|
91
|
+
if (!sessionRuleHash && branches[0]?.transitions[0]?.ruleHash) {
|
|
92
|
+
sessionRuleHash = branches[0].transitions[0].ruleHash;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Legacy: collect from attempts
|
|
97
|
+
for (const attempt of (session.attempts || [])) {
|
|
98
|
+
if (attempt.transitions) {
|
|
99
|
+
transitions = transitions.concat(attempt.transitions);
|
|
100
|
+
}
|
|
101
|
+
if (!sessionRuleHash && (attempt.ruleHash || session.ruleHash)) {
|
|
102
|
+
sessionRuleHash = attempt.ruleHash || session.ruleHash;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Load fingerprint
|
|
107
|
+
const fingerprint = (0, ledger_registry_1.getFingerprint)(sessionId);
|
|
108
|
+
const storedHash = fingerprint?.ledgerHash || "";
|
|
109
|
+
// Compute current rule hash
|
|
110
|
+
let currentRuleHash = "";
|
|
111
|
+
if (currentRules && currentRules.size > 0) {
|
|
112
|
+
currentRuleHash = (0, ssg_validator_1.hashRules)(currentRules);
|
|
113
|
+
}
|
|
114
|
+
return replayLedger(sessionId, transitions, sessionRuleHash, storedHash, currentRuleHash, currentRules, namespaceInitialStates);
|
|
115
|
+
}
|
|
116
|
+
/** Core replay logic: replay transitions against (optional) current rules. */
|
|
117
|
+
/** Replay a ledger of transitions against current rules and verify consistency. */
|
|
118
|
+
/** @requires LEDGER_DATA @produces REPLAY_RESULT */
|
|
119
|
+
function replayLedger(sessionId, transitions, storedRuleHash, storedLedgerHash, currentRuleHash, currentRules, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
|
|
120
|
+
const replayedHash = transitions.length > 0 ? (0, ssg_validator_1.hashLedger)(transitions) : "";
|
|
121
|
+
const ruleHashMatch = !currentRuleHash || !storedRuleHash
|
|
122
|
+
? true // can't compare if either is missing
|
|
123
|
+
: currentRuleHash === storedRuleHash;
|
|
124
|
+
const ledgerHashMatch = !storedLedgerHash
|
|
125
|
+
? true // no fingerprint to compare against
|
|
126
|
+
: replayedHash === storedLedgerHash;
|
|
127
|
+
// Replay each transition if we have current rules
|
|
128
|
+
let validTransitions = 0;
|
|
129
|
+
let invalidTransitions = 0;
|
|
130
|
+
let divergencePoint;
|
|
131
|
+
let divergenceDetail;
|
|
132
|
+
if (currentRules && currentRules.size > 0) {
|
|
133
|
+
const ctx = {
|
|
134
|
+
ledger: [],
|
|
135
|
+
currentState: (0, ssg_validator_1.rebuildState)([], namespaceInitialStates),
|
|
136
|
+
};
|
|
137
|
+
for (let i = 0; i < transitions.length; i++) {
|
|
138
|
+
const t = transitions[i];
|
|
139
|
+
if (!t.valid) {
|
|
140
|
+
invalidTransitions++;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
// Re-validate against current rules
|
|
145
|
+
const { valid, transition } = (0, ssg_validator_1.validateTransition)(ctx, t.function, i, currentRules, namespaceInitialStates, currentRuleHash || "");
|
|
146
|
+
if (!valid && divergencePoint === undefined) {
|
|
147
|
+
divergencePoint = i;
|
|
148
|
+
divergenceDetail = `Transition[${i}] "${t.function}" is no longer valid under current rules.`;
|
|
149
|
+
}
|
|
150
|
+
// Check statesBefore matches rebuilt state
|
|
151
|
+
const rebuiltBefore = ctx.currentState;
|
|
152
|
+
const beforeMatch = deepEqualSnapshots(rebuiltBefore, t.statesBefore);
|
|
153
|
+
if (!beforeMatch && divergencePoint === undefined) {
|
|
154
|
+
divergencePoint = i;
|
|
155
|
+
divergenceDetail = `Transition[${i}] "${t.function}" statesBefore mismatch: rebuilt=${JSON.stringify(rebuiltBefore[t.namespace] || [])} recorded=${JSON.stringify(t.statesBefore[t.namespace] || [])}`;
|
|
156
|
+
}
|
|
157
|
+
if (valid) {
|
|
158
|
+
validTransitions++;
|
|
159
|
+
ctx.ledger.push(transition);
|
|
160
|
+
ctx.currentState = transition.statesAfter;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
invalidTransitions++;
|
|
164
|
+
ctx.ledger.push({ ...t, valid: false });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
invalidTransitions++;
|
|
169
|
+
if (divergencePoint === undefined) {
|
|
170
|
+
divergencePoint = i;
|
|
171
|
+
divergenceDetail = `Transition[${i}] "${t.function}" threw an error during replay. Rule may have changed.`;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// No current rules: just verify structural consistency
|
|
178
|
+
const consistency = (0, ssg_validator_1.checkLedgerConsistency)(transitions, namespaceInitialStates);
|
|
179
|
+
if (!consistency.consistent) {
|
|
180
|
+
divergencePoint = consistency.violations[0]?.index;
|
|
181
|
+
divergenceDetail = `Structural inconsistency: ${consistency.violations.length} violation(s). First: [${consistency.violations[0]?.invariant}] at index ${consistency.violations[0]?.index}`;
|
|
182
|
+
}
|
|
183
|
+
validTransitions = transitions.filter(t => t.valid).length;
|
|
184
|
+
invalidTransitions = transitions.filter(t => !t.valid).length;
|
|
185
|
+
}
|
|
186
|
+
// Compute final state
|
|
187
|
+
const finalState = transitions.length > 0
|
|
188
|
+
? (0, ssg_validator_1.rebuildState)(transitions.filter(t => t.valid), namespaceInitialStates)
|
|
189
|
+
: {};
|
|
190
|
+
const success = ruleHashMatch && ledgerHashMatch && divergencePoint === undefined;
|
|
191
|
+
return {
|
|
192
|
+
success,
|
|
193
|
+
sessionId,
|
|
194
|
+
finalState,
|
|
195
|
+
divergencePoint,
|
|
196
|
+
divergenceDetail,
|
|
197
|
+
replayedHash,
|
|
198
|
+
storedHash: storedLedgerHash,
|
|
199
|
+
ruleHashMatch,
|
|
200
|
+
ledgerHashMatch,
|
|
201
|
+
totalTransitions: transitions.length,
|
|
202
|
+
validTransitions,
|
|
203
|
+
invalidTransitions,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
/** Replay with per-transition detail — for debugging and UI. */
|
|
207
|
+
/** Replay transitions with per-step detail for debugging. */
|
|
208
|
+
/** @requires TRANSITIONS @produces DETAIL_RESULT */
|
|
209
|
+
function replayWithDetail(transitions, currentRules, namespaceInitialStates = (0, protocol_registry_1.getNsInit)()) {
|
|
210
|
+
if (!currentRules || currentRules.size === 0) {
|
|
211
|
+
return transitions.map(t => ({
|
|
212
|
+
index: t.actionIndex,
|
|
213
|
+
function: t.function,
|
|
214
|
+
namespace: t.namespace,
|
|
215
|
+
originalValid: t.valid,
|
|
216
|
+
replayValid: t.valid,
|
|
217
|
+
statesBeforeMatch: true,
|
|
218
|
+
statesAfterMatch: true,
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
const ctx = {
|
|
222
|
+
ledger: [],
|
|
223
|
+
currentState: (0, ssg_validator_1.rebuildState)([], namespaceInitialStates),
|
|
224
|
+
};
|
|
225
|
+
const currentRuleHash = (0, ssg_validator_1.hashRules)(currentRules);
|
|
226
|
+
const results = [];
|
|
227
|
+
for (let i = 0; i < transitions.length; i++) {
|
|
228
|
+
const t = transitions[i];
|
|
229
|
+
const result = {
|
|
230
|
+
index: t.actionIndex,
|
|
231
|
+
function: t.function,
|
|
232
|
+
namespace: t.namespace,
|
|
233
|
+
originalValid: t.valid,
|
|
234
|
+
replayValid: false,
|
|
235
|
+
statesBeforeMatch: false,
|
|
236
|
+
statesAfterMatch: false,
|
|
237
|
+
};
|
|
238
|
+
if (!t.valid) {
|
|
239
|
+
result.detail = "Originally invalid";
|
|
240
|
+
results.push(result);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const { valid, transition } = (0, ssg_validator_1.validateTransition)(ctx, t.function, i, currentRules, namespaceInitialStates, currentRuleHash);
|
|
245
|
+
result.replayValid = valid;
|
|
246
|
+
result.statesBeforeMatch = deepEqualSnapshots(ctx.currentState, t.statesBefore);
|
|
247
|
+
result.statesAfterMatch = valid
|
|
248
|
+
? deepEqualSnapshots(transition.statesAfter, t.statesAfter)
|
|
249
|
+
: false;
|
|
250
|
+
if (!result.statesBeforeMatch) {
|
|
251
|
+
result.detail = `statesBefore mismatch`;
|
|
252
|
+
}
|
|
253
|
+
else if (!result.statesAfterMatch) {
|
|
254
|
+
result.detail = `statesAfter mismatch`;
|
|
255
|
+
}
|
|
256
|
+
else if (!valid) {
|
|
257
|
+
result.detail = `No longer valid under current rules`;
|
|
258
|
+
}
|
|
259
|
+
ctx.ledger.push(valid ? transition : { ...t, valid: false });
|
|
260
|
+
ctx.currentState = valid ? transition.statesAfter : ctx.currentState;
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
result.replayValid = false;
|
|
264
|
+
result.detail = `Error: ${e.message?.slice(0, 80)}`;
|
|
265
|
+
}
|
|
266
|
+
results.push(result);
|
|
267
|
+
}
|
|
268
|
+
return results;
|
|
269
|
+
}
|
|
270
|
+
// ── Helpers ──
|
|
271
|
+
function deepEqualSnapshots(a, b) {
|
|
272
|
+
const keysA = Object.keys(a).sort();
|
|
273
|
+
const keysB = Object.keys(b).sort();
|
|
274
|
+
if (keysA.length !== keysB.length)
|
|
275
|
+
return false;
|
|
276
|
+
for (const k of keysA) {
|
|
277
|
+
if (!b[k])
|
|
278
|
+
return false;
|
|
279
|
+
if (a[k].join(",") !== b[k].join(","))
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
return true;
|
|
283
|
+
}
|
package/dist/emitter.js
CHANGED
|
@@ -39,11 +39,46 @@ const BASIC_TYPES = new Set([
|
|
|
39
39
|
"string", "number", "boolean", "any", "void", "undefined", "null",
|
|
40
40
|
"str", "int", "float", "bool", "list", "dict", "tuple", "bytes",
|
|
41
41
|
]);
|
|
42
|
+
/** Normalize a type string to an importable type name.
|
|
43
|
+
* Strips [], <...>, unions, and typeof expressions.
|
|
44
|
+
* e.g. "StateTransition[]" → "StateTransition"
|
|
45
|
+
* "Map<string, string>" → "Map"
|
|
46
|
+
* "Action | null" → "Action"
|
|
47
|
+
* "ReturnType<typeof fn>" → null (complex, skip) */
|
|
48
|
+
function normalizeTypeName(raw) {
|
|
49
|
+
let t = raw.trim();
|
|
50
|
+
// Skip function types and complex expressions
|
|
51
|
+
if (t.includes("=>") || t.includes("typeof"))
|
|
52
|
+
return null;
|
|
53
|
+
// Strip array brackets
|
|
54
|
+
t = t.replace(/\[\]/g, "");
|
|
55
|
+
// Strip generic parameters
|
|
56
|
+
t = t.replace(/<[^>]*>/g, "");
|
|
57
|
+
// Take first part of union/intersection
|
|
58
|
+
t = t.split("|")[0].split("&")[0].trim();
|
|
59
|
+
// Must be a simple identifier
|
|
60
|
+
if (/^[A-Z][a-zA-Z0-9_]*$/.test(t))
|
|
61
|
+
return t;
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
42
64
|
function getImportPath(file) {
|
|
43
|
-
|
|
65
|
+
// Normalize: strip common src/ prefix since generated code lives in src/
|
|
66
|
+
let clean = file.replace(/\.ts$|\.tsx$/, "");
|
|
67
|
+
if (clean.startsWith("src/"))
|
|
68
|
+
clean = clean.slice(4);
|
|
69
|
+
return "./" + clean;
|
|
44
70
|
}
|
|
45
|
-
|
|
46
|
-
|
|
71
|
+
/**
|
|
72
|
+
* 将动作序列编译为目标语言代码。
|
|
73
|
+
* @protocol namespace=dev_pipeline pre_states=["SEQUENCE_VALIDATED"] post_states=["CODE_EMITTED"] invalidate=["SEQUENCE_VALIDATED"]
|
|
74
|
+
*/
|
|
75
|
+
/** @requires ACTIONS @produces TYPESCRIPT_CODE */
|
|
76
|
+
/** @requires ACTIONS @produces TYPESCRIPT_CODE */
|
|
77
|
+
function emitCode(actions, meta) {
|
|
78
|
+
const irRaw = JSON.parse(fs.readFileSync("ir.json", "utf-8"));
|
|
79
|
+
// Support both old format (array) and new format ({typeMap, functions})
|
|
80
|
+
const typeMap = irRaw.typeMap || {};
|
|
81
|
+
const ir = irRaw.functions || irRaw;
|
|
47
82
|
const fnIndex = new Map();
|
|
48
83
|
const fnMeta = new Map();
|
|
49
84
|
for (const f of ir) {
|
|
@@ -56,16 +91,21 @@ function emitCode(actions) {
|
|
|
56
91
|
if (action.kind === "call" && action.function) {
|
|
57
92
|
const file = fnIndex.get(action.function);
|
|
58
93
|
const meta = fnMeta.get(action.function);
|
|
59
|
-
|
|
94
|
+
// Skip external functions — they are global/standard library (fs, path, etc.)
|
|
95
|
+
// Skip non-exported functions — can't import what isn't exported
|
|
96
|
+
if (file && file !== "(external)" && meta?.exported !== false) {
|
|
60
97
|
if (!imports.has(file))
|
|
61
98
|
imports.set(file, new Set());
|
|
62
99
|
imports.get(file).add(action.function);
|
|
63
100
|
if (meta?.params) {
|
|
64
101
|
for (const p of meta.params) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
102
|
+
const normalized = normalizeTypeName(p.type);
|
|
103
|
+
if (normalized && !BASIC_TYPES.has(normalized)) {
|
|
104
|
+
// Use typeMap to find the correct module for this type
|
|
105
|
+
const typeFile = typeMap[normalized] || file;
|
|
106
|
+
if (!typeImports.has(typeFile))
|
|
107
|
+
typeImports.set(typeFile, new Set());
|
|
108
|
+
typeImports.get(typeFile).add(normalized);
|
|
69
109
|
}
|
|
70
110
|
}
|
|
71
111
|
}
|
|
@@ -81,14 +121,63 @@ function emitCode(actions) {
|
|
|
81
121
|
};
|
|
82
122
|
for (const a of actions)
|
|
83
123
|
collect(a);
|
|
124
|
+
// ── Generation marker (provable usage) ──
|
|
84
125
|
let code = "";
|
|
126
|
+
if (meta?.sessionId) {
|
|
127
|
+
const ts = new Date().toISOString();
|
|
128
|
+
code += `// @progmune-generated session=${meta.sessionId} timestamp=${ts}`;
|
|
129
|
+
if (meta.ruleHash)
|
|
130
|
+
code += ` ruleHash=${meta.ruleHash}`;
|
|
131
|
+
code += `\n`;
|
|
132
|
+
const funcs = meta.irFunctionCount ?? ir.length;
|
|
133
|
+
const rules = meta.protocolRuleCount ?? 0;
|
|
134
|
+
code += `// Generated with IR constraint: ${funcs} functions`;
|
|
135
|
+
if (rules > 0)
|
|
136
|
+
code += `, ${rules} protocol rules`;
|
|
137
|
+
code += `\n`;
|
|
138
|
+
}
|
|
85
139
|
for (const [file, funcSet] of imports)
|
|
86
140
|
code += `import { ${[...funcSet].join(", ")} } from "${getImportPath(file)}";\n`;
|
|
87
141
|
for (const [file, typeSet] of typeImports)
|
|
88
142
|
code += `import type { ${[...typeSet].join(", ")} } from "${getImportPath(file)}";\n`;
|
|
89
|
-
|
|
143
|
+
// Pre-scan: find input variables (referenced but never assigned via call/assign)
|
|
90
144
|
const declared = new Set();
|
|
145
|
+
const referenced = new Set();
|
|
91
146
|
let counter = 0;
|
|
147
|
+
// First pass: collect declared and referenced variables
|
|
148
|
+
for (const action of actions) {
|
|
149
|
+
if (action.kind === "call" && action.assignTo) {
|
|
150
|
+
declared.add(action.assignTo);
|
|
151
|
+
}
|
|
152
|
+
else if (action.kind === "assign" && action.target) {
|
|
153
|
+
declared.add(action.target);
|
|
154
|
+
}
|
|
155
|
+
// Collect arg value references
|
|
156
|
+
if (action.kind === "call" && action.args) {
|
|
157
|
+
for (const arg of action.args) {
|
|
158
|
+
const v = typeof arg === "object" ? arg?.value : arg;
|
|
159
|
+
if (typeof v === "string" && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(v) && v !== "") {
|
|
160
|
+
referenced.add(v);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Inputs = referenced but not declared — add as typed parameters
|
|
166
|
+
const inputs = [...referenced].filter(v => !declared.has(v) && v !== "");
|
|
167
|
+
const inputTypes = new Map();
|
|
168
|
+
for (const action of actions) {
|
|
169
|
+
if (action.kind === "call" && action.args) {
|
|
170
|
+
for (const arg of action.args) {
|
|
171
|
+
const v = typeof arg === "object" ? arg?.value : arg;
|
|
172
|
+
const t = typeof arg === "object" ? arg?.type : "string";
|
|
173
|
+
if (typeof v === "string" && inputs.includes(v) && t && t !== "any") {
|
|
174
|
+
inputTypes.set(v, t);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const paramList = inputs.map(v => `${v}: ${inputTypes.get(v) || "string"}`).join(", ");
|
|
180
|
+
code += `\nexport function main(${paramList}) {\n`;
|
|
92
181
|
const convert = (action, indent = " ") => {
|
|
93
182
|
if (!action || !action.kind)
|
|
94
183
|
return "";
|
|
@@ -103,16 +192,37 @@ function emitCode(actions) {
|
|
|
103
192
|
const val = a?.value;
|
|
104
193
|
if (typeof val === "string" && declared.has(val))
|
|
105
194
|
return val;
|
|
195
|
+
// If value looks like a variable name (valid JS identifier), pass it through
|
|
196
|
+
if (typeof val === "string" && /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(val) && val !== "") {
|
|
197
|
+
declared.add(val);
|
|
198
|
+
return val;
|
|
199
|
+
}
|
|
106
200
|
const paramType = meta?.params?.[i]?.type || "any";
|
|
107
201
|
if (BASIC_TYPES.has(paramType)) {
|
|
108
202
|
if (paramType === "string" || paramType === "str")
|
|
109
|
-
return `"
|
|
203
|
+
return `""`;
|
|
110
204
|
if (paramType === "number" || paramType === "int" || paramType === "float")
|
|
111
205
|
return "0";
|
|
112
206
|
if (paramType === "boolean" || paramType === "bool")
|
|
113
207
|
return "false";
|
|
114
|
-
return `"
|
|
208
|
+
return `""`;
|
|
115
209
|
}
|
|
210
|
+
// String enum defaults — match param type to sensible value
|
|
211
|
+
const STRING_ENUMS = {
|
|
212
|
+
"SVL": '"SVL-4"', "RootCause": '"F01"', "BranchReason": '"repair_attempt"',
|
|
213
|
+
"RepairStrategy": '"insert"', "ConstraintType": '"protocol"',
|
|
214
|
+
"SVLString": '"SVL-4"', "BranchOutcome": '"success"',
|
|
215
|
+
};
|
|
216
|
+
if (STRING_ENUMS[paramType])
|
|
217
|
+
return STRING_ENUMS[paramType];
|
|
218
|
+
// Array types: use empty array
|
|
219
|
+
if (paramType.endsWith("[]"))
|
|
220
|
+
return "[]";
|
|
221
|
+
// Generic Map/Set types: use empty constructor
|
|
222
|
+
if (paramType.startsWith("Map<"))
|
|
223
|
+
return `new Map()`;
|
|
224
|
+
if (paramType.startsWith("Set<"))
|
|
225
|
+
return `new Set()`;
|
|
116
226
|
if (paramType === "UserPayload")
|
|
117
227
|
return `{ id: 1, role: "user" } as UserPayload`;
|
|
118
228
|
if (paramType === "PasswordHash")
|
|
@@ -161,6 +271,27 @@ function emitCode(actions) {
|
|
|
161
271
|
};
|
|
162
272
|
for (const a of actions)
|
|
163
273
|
code += convert(a) + "\n";
|
|
164
|
-
|
|
274
|
+
// Ensure last action is a return — inject one if LLM forgot
|
|
275
|
+
const lastAction = actions[actions.length - 1];
|
|
276
|
+
if (lastAction && lastAction.kind !== "return") {
|
|
277
|
+
const allCalls = actions.filter(a => a.kind === "call" && a.assignTo);
|
|
278
|
+
if (allCalls.length === 1) {
|
|
279
|
+
code += ` return ${allCalls[0].assignTo};\n`;
|
|
280
|
+
}
|
|
281
|
+
else if (allCalls.length > 1) {
|
|
282
|
+
// Multiple calls: return all results as an object
|
|
283
|
+
const vars = allCalls.map(c => c.assignTo).join(", ");
|
|
284
|
+
code += ` return { ${vars} };\n`;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
code += "}\n";
|
|
288
|
+
// Call main with params if it has inputs, otherwise no-arg call
|
|
289
|
+
if (inputs.length > 0) {
|
|
290
|
+
// Don't call — the user provides the arguments
|
|
291
|
+
// Just export the function for external use
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
code += "main();\n";
|
|
295
|
+
}
|
|
165
296
|
return code;
|
|
166
297
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.main = main;
|
|
4
|
+
// @progmune-generated session=sess_1780343438004_ux6bt timestamp=2026-06-01T19:50:39.415Z ruleHash=9dec68bc2995e92a
|
|
5
|
+
// Generated with IR constraint: 385 functions, 17 protocol rules
|
|
6
|
+
const execute_1 = require("./execute");
|
|
7
|
+
function main() {
|
|
8
|
+
const metrics = (0, execute_1.getExecutionMetrics)();
|
|
9
|
+
return metrics;
|
|
10
|
+
}
|
|
11
|
+
main();
|