progmune-runtime 3.3.1 → 3.3.3

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.
@@ -10,12 +10,20 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextpro
10
10
  import * as fs from "fs";
11
11
  import * as path from "path";
12
12
  import { fileURLToPath } from "url";
13
+ import { createRequire } from "module";
13
14
  import { createLogger } from "./logger.js";
14
- // ── ESM-compatible __dirname (the compiled output is an .mjs module) ──
15
+ // ── ESM-compatible __dirname + require (the compiled output is an .mjs
16
+ // module, but the codebase uses lazy require() for circular-dependency
17
+ // handling — createRequire keeps those working in ESM scope) ──
15
18
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
- // ── Load .env ──
17
- const envPath = path.resolve(__dirname, "..", ".env");
18
- if (fs.existsSync(envPath)) {
19
+ const require = createRequire(import.meta.url);
20
+ // ── Load .env(包目录 + 项目目录,后者优先) ──
21
+ for (const envPath of [
22
+ path.resolve(process.env.PROGMUNE_PROJECT_DIR || process.cwd(), ".env"),
23
+ path.resolve(__dirname, "..", ".env"),
24
+ ]) {
25
+ if (!fs.existsSync(envPath))
26
+ continue;
19
27
  const envContent = fs.readFileSync(envPath, "utf-8");
20
28
  for (const line of envContent.split("\n")) {
21
29
  const trimmed = line.trim();
@@ -335,7 +343,7 @@ async function main() {
335
343
 
336
344
  Progmune needs an LLM API key to generate code. Configure via:
337
345
 
338
- 【.env file】Add to ${envPath}:
346
+ 【.env file】Add to ${path.resolve(process.env.PROGMUNE_PROJECT_DIR || process.cwd(), ".env")}:
339
347
  LLM_API_KEY=your-key
340
348
  LLM_BASE_URL=https://api.deepseek.com/v1
341
349
  LLM_MODEL=deepseek-chat
package/dist/planner.js CHANGED
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.validateProtocolWithTransitions = validateProtocolWithTransitions;
36
37
  exports.plan = plan;
37
38
  const llm_1 = require("./llm");
38
39
  const runtime_types_1 = require("./runtime-types");
@@ -393,22 +394,41 @@ function validateProtocolWithTransitions(actions, protocols, namespaceInitialSta
393
394
  // End-of-sequence check: held resources must be released (resource leak).
394
395
  // A state S is resource-holding when some rule REQUIRES S and INVALIDATES S
395
396
  // (acquire/release semantics — e.g. FILE_OPEN set by open_file, released by
396
- // close_file). UNAUTHENTICATED is not resource-holding (release rules for it
397
- // do not require it as a precondition).
397
+ // close_file). Only RESOURCE-LIFECYCLE namespaces apply: session/auth flows
398
+ // legitimately END with an active session (SESSION_ACTIVE is not a leak).
399
+ const RESOURCE_NS = /^(file|db|database|connection|conn|socket|stream|resource|io)/i;
398
400
  const heldStates = [];
399
401
  for (const p of protocols) {
400
402
  const ann = p.protocol;
401
403
  if (!ann)
402
404
  continue;
405
+ const ns = ann.namespace || "";
406
+ if (!RESOURCE_NS.test(ns))
407
+ continue;
403
408
  const inv = ann.invalidate || [];
404
409
  const pre = ann.pre_states || [];
405
410
  for (const s of inv) {
406
411
  if (pre.includes(s))
407
- heldStates.push({ state: s, releaseFn: p.function, namespace: ann.namespace || "" });
412
+ heldStates.push({ state: s, releaseFn: p.function, namespace: ns });
413
+ }
414
+ }
415
+ // 只检查"本序列中获取"的持有状态——继承自命名空间初始状态的
416
+ // (如 db 初始即 DB_CONNECTED)不算泄漏。
417
+ const acquiredStates = new Set();
418
+ for (const t of transitions) {
419
+ for (const ns of Object.keys(t.statesAfter || {})) {
420
+ const after = t.statesAfter[ns] || [];
421
+ const before = t.statesBefore?.[ns] || [];
422
+ for (const s of after) {
423
+ if (!before.includes(s))
424
+ acquiredStates.add(`${ns}::${s}`);
425
+ }
408
426
  }
409
427
  }
410
428
  for (const hs of heldStates) {
411
429
  const cur = ctx.currentState[hs.namespace] || [];
430
+ if (!acquiredStates.has(`${hs.namespace}::${hs.state}`))
431
+ continue;
412
432
  if (cur.includes(hs.state)) {
413
433
  const trace = transitions.map(t => ({
414
434
  function: t.function,
@@ -444,7 +464,11 @@ function validateProtocolWithTransitions(actions, protocols, namespaceInitialSta
444
464
  return { valid: true, transitions, ledgerConsistent: consistency.consistent, ruleHash };
445
465
  }
446
466
  /** SSG 确定性修复:当协议违规有已知修复路径时,自动插入缺失函数 */
447
- function attemptSSGRepair(actions, rejection, ir, protocols, namespaceInitialStates) {
467
+ function attemptSSGRepair(actions, rejection, ir, protocols, namespaceInitialStates, depth = 0) {
468
+ if (depth > 5) {
469
+ console.error(`[修复] 递归深度超限 (${depth}),放弃确定性修复`);
470
+ return null;
471
+ }
448
472
  if (!rejection.fixPath || rejection.fixPath.length === 0)
449
473
  return null;
450
474
  // 名称归一化:内置规则可能是下划线风格(generate_jwt),项目 IR 是
@@ -496,7 +520,8 @@ function attemptSSGRepair(actions, rejection, ir, protocols, namespaceInitialSta
496
520
  }
497
521
  // 单步修复不够,尝试递归修复
498
522
  if (recheck.rejection && recheck.rejection.fixPath && recheck.rejection.fixPath.length > 0) {
499
- const nested = attemptSSGRepair(repaired, recheck.rejection, ir, protocols, namespaceInitialStates);
523
+ console.error(`[修复] 重验仍失败 (blocked=${recheck.rejection.blocked}, fixPath=${recheck.rejection.fixPath.join(" ")}),递归深度 ${depth + 1}`);
524
+ const nested = attemptSSGRepair(repaired, recheck.rejection, ir, protocols, namespaceInitialStates, depth + 1);
500
525
  if (nested)
501
526
  return nested;
502
527
  }
@@ -1079,6 +1104,13 @@ ${planner_prompts_1.RETRY_HINT}
1079
1104
  const protoResult = validateProtocolWithTransitions(filtered, protocols, namespaceInitialStates);
1080
1105
  if (!protoResult.valid && protoResult.rejection) {
1081
1106
  const rej = protoResult.rejection;
1107
+ // P3:fixPath / missingFunctions 归一化到 IR 真实函数名——
1108
+ // 内置规则是下划线风格(generate_jwt),项目 IR 是 camelCase
1109
+ // (generateJwt)。提示与记录使用项目里真实存在的名字。
1110
+ const normIRName = (n) => ir.find((f) => f.name === n)
1111
+ || ir.find((f) => f.name.replace(/[_-]/g, "").toLowerCase() === n.replace(/[_-]/g, "").toLowerCase());
1112
+ rej.fixPath = (rej.fixPath || []).map(n => normIRName(n)?.name || n);
1113
+ rej.missingFunctions = (rej.missingFunctions || []).map(n => normIRName(n)?.name || n);
1082
1114
  const explain = (0, ssg_validator_1.explainRejection)(rej);
1083
1115
  console.error(explain);
1084
1116
  const violation = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "progmune-runtime",
3
- "version": "3.3.1",
3
+ "version": "3.3.3",
4
4
  "description": "Progmune — AI Trust Decision Engine. Verify AI-generated code before it reaches production. Outputs APPROVED / NEEDS_REVIEW / BLOCKED with evidence.",
5
5
  "files": [
6
6
  "dist/",