@kenhkl/agent-moss 0.12.8 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -137,6 +137,7 @@ __export(config_exports, {
137
137
  getRetryConfig: () => getRetryConfig,
138
138
  getTimeoutConfig: () => getTimeoutConfig,
139
139
  isBrainEnabled: () => isBrainEnabled,
140
+ isFormalObserveMode: () => isFormalObserveMode,
140
141
  isHeuristicEnabled: () => isHeuristicEnabled,
141
142
  isLLMAvailable: () => isLLMAvailable,
142
143
  isLogicRulesEnabled: () => isLogicRulesEnabled,
@@ -252,6 +253,7 @@ function getCustomRules() {
252
253
  }
253
254
  function isLLMAvailable() {
254
255
  if (process.env.AGENT_MOSS_DISABLE_LLM === "1") return false;
256
+ if (!layerEnabledOrDefault("L3_llm_analysis")) return false;
255
257
  return llmConfig.apiKey.length > 0;
256
258
  }
257
259
  async function probeLLMConnection(timeoutMs = 8e3) {
@@ -316,13 +318,26 @@ function getProviderHeaders(provider) {
316
318
  return PROVIDER_HEADERS[provider] || {};
317
319
  }
318
320
  function isHeuristicEnabled() {
319
- return process.env.AGENT_MOSS_DISABLE_HEURISTIC !== "1";
321
+ if (process.env.AGENT_MOSS_DISABLE_HEURISTIC === "1") return false;
322
+ return layerEnabledOrDefault("L1_heuristic");
323
+ }
324
+ function layerEnabledOrDefault(key) {
325
+ try {
326
+ const { isLayerEnabled: isLayerEnabled2 } = __require("../infra/runtime-config");
327
+ return isLayerEnabled2(key);
328
+ } catch {
329
+ return true;
330
+ }
320
331
  }
321
332
  function isBrainEnabled() {
322
333
  return process.env.AGENT_MOSS_BRAIN_ENABLED === "1";
323
334
  }
324
335
  function isLogicRulesEnabled() {
325
- return process.env.AGENT_MOSS_DISABLE_LOGIC_RULES !== "1";
336
+ if (process.env.AGENT_MOSS_DISABLE_LOGIC_RULES === "1") return false;
337
+ return layerEnabledOrDefault("L2_logic_rules");
338
+ }
339
+ function isFormalObserveMode() {
340
+ return process.env.AGENT_MOSS_FORMAL_OBSERVE === "1";
326
341
  }
327
342
  var timeoutConfig, retryConfig, llmConfig, __configModuleDir, USER_RULES_PATHS, customRules, PROVIDER_HEADERS;
328
343
  var init_config = __esm({
@@ -580,8 +595,12 @@ var init_inline_analyzer = __esm({
580
595
 
581
596
  // src/infra/rules-loader.ts
582
597
  import { readFileSync as readFileSync2, existsSync } from "fs";
583
- import { resolve, dirname } from "path";
598
+ import { resolve, dirname, join as join2 } from "path";
584
599
  import { fileURLToPath as fileURLToPath3 } from "url";
600
+ import { homedir as homedir2 } from "os";
601
+ function dataRootDir() {
602
+ return process.env.AGENT_MOSS_DATA_DIR || join2(homedir2(), ".config", "agentmoss");
603
+ }
585
604
  function findRulesFile(name) {
586
605
  for (const dir of RULES_DIR_CANDS) {
587
606
  const p = resolve(dir, name);
@@ -607,6 +626,24 @@ function loadL2() {
607
626
  function loadL3() {
608
627
  return loadJson("l3_rules.json");
609
628
  }
629
+ function loadMC() {
630
+ const mc = loadJson("mc_rules.json");
631
+ const rules = mc.rules || [];
632
+ try {
633
+ const rt = (init_runtime_config(), __toCommonJS(runtime_config_exports));
634
+ if (rules.length > 0 && typeof rt.hasRuntimeConfig === "function" && rt.hasRuntimeConfig()) {
635
+ const rtRules = rt.loadRuntimeConfig()?.L2_4?.ltl_rules?.rules || [];
636
+ const enabledById = {};
637
+ for (const r of rtRules) if (r?.id) enabledById[r.id] = r.enabled !== false;
638
+ for (const r of rules) if (r?.id && r.id in enabledById) r.enabled = enabledById[r.id];
639
+ }
640
+ } catch {
641
+ }
642
+ return mc;
643
+ }
644
+ function loadToolRegistry() {
645
+ return loadJson("tool_registry.json");
646
+ }
610
647
  var __filename2, __dirname2, RULES_DIR_CANDS;
611
648
  var init_rules_loader = __esm({
612
649
  "src/infra/rules-loader.ts"() {
@@ -616,6 +653,10 @@ var init_rules_loader = __esm({
616
653
  __dirname2 = dirname(__filename2);
617
654
  RULES_DIR_CANDS = (() => {
618
655
  const cands = [
656
+ // 用户数据目录覆盖优先(与 Python rules_loader._load_json 的
657
+ // <data_root>/rules/<name> 优先逻辑对齐)。用户安装 pip/npm 包后把自定义
658
+ // mc_rules.json 放到 <data_root>/rules/ 即可覆盖内置默认,无需改源码。
659
+ resolve(dataRootDir(), "rules"),
619
660
  // dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
620
661
  resolve(__dirname2, "../rules"),
621
662
  resolve(__dirname2, "rules"),
@@ -1353,6 +1394,7 @@ var init_skill_loader = __esm({
1353
1394
  // src/infra/runtime-config.ts
1354
1395
  var runtime_config_exports = {};
1355
1396
  __export(runtime_config_exports, {
1397
+ DEFAULT_LAYERS: () => DEFAULT_LAYERS,
1356
1398
  DENY_MODE_LABELS: () => DENY_MODE_LABELS,
1357
1399
  addAgentRule: () => addAgentRule,
1358
1400
  addCustomRule: () => addCustomRule,
@@ -1404,7 +1446,7 @@ __export(runtime_config_exports, {
1404
1446
  updateSkillEnabled: () => updateSkillEnabled
1405
1447
  });
1406
1448
  import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
1407
- import { join as join2 } from "path";
1449
+ import { join as join3 } from "path";
1408
1450
  import { fileURLToPath as fileURLToPath5 } from "url";
1409
1451
  import { dirname as dirname3 } from "path";
1410
1452
  function runtimeDir() {
@@ -1428,6 +1470,7 @@ function emptyRuntimeConfig() {
1428
1470
  L1_rules: {},
1429
1471
  L2_rules: {},
1430
1472
  L3_skills: {},
1473
+ L2_4: {},
1431
1474
  agent_rules: {},
1432
1475
  scenario_rules: {}
1433
1476
  };
@@ -1455,6 +1498,7 @@ function loadRuntimeConfig() {
1455
1498
  L1_rules: merged.L1_rules || {},
1456
1499
  L2_rules: merged.L2_rules || {},
1457
1500
  L3_skills: merged.L3_skills || {},
1501
+ L2_4: merged.L2_4 || {},
1458
1502
  agent_rules: merged.agent_rules || {},
1459
1503
  scenario_rules: merged.scenario_rules || {}
1460
1504
  };
@@ -1702,7 +1746,7 @@ function _scenarioRulesToDefaults() {
1702
1746
  return out;
1703
1747
  }
1704
1748
  for (const scenarioId of entries) {
1705
- const rulesFile = join2(scenDir, scenarioId, "rules.json");
1749
+ const rulesFile = join3(scenDir, scenarioId, "rules.json");
1706
1750
  if (!existsSync4(rulesFile)) continue;
1707
1751
  let data;
1708
1752
  try {
@@ -1751,7 +1795,7 @@ function _agentRulesToDefaults() {
1751
1795
  return out;
1752
1796
  }
1753
1797
  for (const agentId of entries) {
1754
- const rulesFile = join2(agentsDir, agentId, "rules.json");
1798
+ const rulesFile = join3(agentsDir, agentId, "rules.json");
1755
1799
  if (!existsSync4(rulesFile)) continue;
1756
1800
  let data;
1757
1801
  try {
@@ -1792,7 +1836,7 @@ function _agentRulesToDefaults() {
1792
1836
  function generateSourceDefaults() {
1793
1837
  return {
1794
1838
  version: 1,
1795
- layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
1839
+ layers: { ...DEFAULT_LAYERS },
1796
1840
  L1_rules: {
1797
1841
  dangerous_commands: {
1798
1842
  category_enabled: true,
@@ -1831,10 +1875,45 @@ function generateSourceDefaults() {
1831
1875
  user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
1832
1876
  },
1833
1877
  L3_skills: _l3SkillsToRules(),
1878
+ L2_4: _ltlRulesToRuntime(),
1834
1879
  agent_rules: _agentRulesToDefaults(),
1835
1880
  scenario_rules: _scenarioRulesToDefaults()
1836
1881
  };
1837
1882
  }
1883
+ function _ltlRulesToRuntime() {
1884
+ const cands = [
1885
+ join3(__dirname4, "..", "rules"),
1886
+ join3(__dirname4, "..", "..", "..", "agent_moss", "rules"),
1887
+ join3(process.cwd(), "agent_moss", "rules")
1888
+ ];
1889
+ let mc = {};
1890
+ for (const dir of cands) {
1891
+ const p = join3(dir, "mc_rules.json");
1892
+ if (existsSync4(p)) {
1893
+ try {
1894
+ mc = JSON.parse(readFileSync4(p, "utf-8")) || {};
1895
+ } catch {
1896
+ mc = {};
1897
+ }
1898
+ break;
1899
+ }
1900
+ }
1901
+ const rules = mc.rules || [];
1902
+ return {
1903
+ ltl_rules: {
1904
+ category_enabled: true,
1905
+ category_desc: "LTL \u653B\u51FB\u6A21\u5F0F\u89C4\u5219\uFF08\u65F6\u5E8F\u653B\u51FB\u68C0\u6D4B\uFF0C\u5148 A \u540E B\uFF09",
1906
+ rules: rules.map((r) => ({
1907
+ id: r.id,
1908
+ reason: r.reason || "",
1909
+ risk_level: r.risk_level || "high",
1910
+ action: r.action || "block",
1911
+ enabled: true,
1912
+ builtin: true
1913
+ }))
1914
+ }
1915
+ };
1916
+ }
1838
1917
  function normalizePatternForFingerprint(pattern) {
1839
1918
  return pattern.replace(/\\\//g, "/");
1840
1919
  }
@@ -1984,6 +2063,7 @@ function mergeConfig(userCfg, srcDefaults) {
1984
2063
  );
1985
2064
  _mergeRuleCategories(cfg, srcDefaults, "L1_rules");
1986
2065
  _mergeRuleCategories(cfg, srcDefaults, "L2_rules");
2066
+ _mergeRuleCategories(cfg, srcDefaults, "L2_4");
1987
2067
  _mergeSkillCategories(cfg, srcDefaults, "L3_skills");
1988
2068
  _mergeAgentRules(cfg, srcDefaults);
1989
2069
  _mergeAgentRules(cfg, srcDefaults, "scenario_rules");
@@ -2176,12 +2256,12 @@ function deleteCustomRule(layer, category, ruleId) {
2176
2256
  function addCustomSkill(skillId, category, keywords, content) {
2177
2257
  _validateSkillId(skillId);
2178
2258
  const runtime = loadRuntimeConfig();
2179
- const skillsDir = join2(RUNTIME_CONFIG_DIR, "agent_moss_skills");
2259
+ const skillsDir = join3(RUNTIME_CONFIG_DIR, "agent_moss_skills");
2180
2260
  try {
2181
2261
  mkdirSync(skillsDir, { recursive: true });
2182
2262
  } catch {
2183
2263
  }
2184
- writeFileSync(join2(skillsDir, `${skillId}.md`), content, "utf-8");
2264
+ writeFileSync(join3(skillsDir, `${skillId}.md`), content, "utf-8");
2185
2265
  const l3 = runtime.L3_skills || (runtime.L3_skills = {});
2186
2266
  const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
2187
2267
  (cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
@@ -2196,7 +2276,7 @@ function deleteCustomSkill(skillId, category) {
2196
2276
  if (idx >= 0) {
2197
2277
  if (skills[idx].builtin) return null;
2198
2278
  skills.splice(idx, 1);
2199
- const skillPath = join2(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
2279
+ const skillPath = join3(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
2200
2280
  try {
2201
2281
  if (existsSync4(skillPath)) unlinkSync(skillPath);
2202
2282
  } catch {
@@ -2390,7 +2470,7 @@ function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
2390
2470
  saveRuntimeConfig(runtime);
2391
2471
  return runtime;
2392
2472
  }
2393
- var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
2473
+ var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
2394
2474
  var init_runtime_config = __esm({
2395
2475
  "src/infra/runtime-config.ts"() {
2396
2476
  "use strict";
@@ -2402,21 +2482,28 @@ var init_runtime_config = __esm({
2402
2482
  __dirname4 = dirname3(fileURLToPath5(import.meta.url));
2403
2483
  ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
2404
2484
  RUNTIME_CONFIG_DIR = runtimeDir();
2405
- RUNTIME_CONFIG_PATH = join2(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
2485
+ RUNTIME_CONFIG_PATH = join3(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
2406
2486
  LAYER_DISABLE_ENV = {
2407
2487
  L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
2408
2488
  L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
2409
2489
  L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
2410
2490
  };
2491
+ DEFAULT_LAYERS = {
2492
+ L1_heuristic: true,
2493
+ L2_logic_rules: true,
2494
+ L3_llm_analysis: true,
2495
+ agent_rules_enabled: true,
2496
+ scenario_rules_enabled: true
2497
+ };
2411
2498
  DENY_MODE_LABELS = {
2412
2499
  deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
2413
2500
  deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
2414
2501
  deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
2415
2502
  };
2416
- _AGENTS_DIR_PROD = join2(__dirname4, "agents");
2417
- _AGENTS_DIR_DEV = join2(__dirname4, "..", "..", "..", "agent_moss", "agents");
2418
- _SCENARIOS_DIR_PROD = join2(__dirname4, "scenarios");
2419
- _SCENARIOS_DIR_DEV = join2(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
2503
+ _AGENTS_DIR_PROD = join3(__dirname4, "agents");
2504
+ _AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
2505
+ _SCENARIOS_DIR_PROD = join3(__dirname4, "scenarios");
2506
+ _SCENARIOS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
2420
2507
  SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
2421
2508
  }
2422
2509
  });
@@ -2554,11 +2641,21 @@ function getScenarioExtensions(scenarioId) {
2554
2641
  );
2555
2642
  return ext;
2556
2643
  }
2644
+ function getScenarioExtensionsMulti(scenarioSpec) {
2645
+ if (!scenarioSpec) return null;
2646
+ const ids = scenarioSpec.split(",").map((s) => s.trim()).filter(Boolean);
2647
+ if (ids.length === 0) return null;
2648
+ const exts = ids.map((id) => getScenarioExtensions(id));
2649
+ const nonNull = exts.filter((e) => e !== null);
2650
+ if (nonNull.length === 0) return null;
2651
+ return mergeAgentExtensions(...nonNull);
2652
+ }
2557
2653
  var __filename5, __dirname6, SCENARIOS_DIR_PROD, SCENARIOS_DIR_DEV_FALLBACK, cache2;
2558
2654
  var init_scenarios = __esm({
2559
2655
  "src/scenarios/index.ts"() {
2560
2656
  "use strict";
2561
2657
  init_esm_shims();
2658
+ init_agents();
2562
2659
  __filename5 = fileURLToPath7(import.meta.url);
2563
2660
  __dirname6 = dirname5(__filename5);
2564
2661
  SCENARIOS_DIR_PROD = resolve4(__dirname6, "scenarios");
@@ -2607,8 +2704,8 @@ function runHeuristicCheck(input) {
2607
2704
  }
2608
2705
  highestRiskType = topRiskType || highestRiskType;
2609
2706
  const agentExt = mergeAgentExtensions(
2610
- getAgentExtensions(input.agentId),
2611
- getScenarioExtensions(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "")
2707
+ isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
2708
+ isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
2612
2709
  );
2613
2710
  if (agentExt?.extra_command_patterns) {
2614
2711
  for (const rule of agentExt.extra_command_patterns) {
@@ -3053,6 +3150,7 @@ var init_heuristic = __esm({
3053
3150
  init_config();
3054
3151
  init_agents();
3055
3152
  init_scenarios();
3153
+ init_runtime_config();
3056
3154
  init_rules_loader();
3057
3155
  init_logic_rules();
3058
3156
  INJECTION_KEYWORDS = (() => {
@@ -3252,6 +3350,112 @@ var init_token_stats = __esm({
3252
3350
  }
3253
3351
  });
3254
3352
 
3353
+ // src/engine/formal-engine/decode.ts
3354
+ var decode_exports = {};
3355
+ __export(decode_exports, {
3356
+ allCandidates: () => allCandidates,
3357
+ decodeBase64Literals: () => decodeBase64Literals,
3358
+ decodeUrlAndNumeric: () => decodeUrlAndNumeric,
3359
+ matchStageAny: () => matchStageAny
3360
+ });
3361
+ function isValidBase64Token(tok) {
3362
+ if (tok.length < 8) return false;
3363
+ if (!(tok.length % 4 === 0 || tok.endsWith("==") || tok.endsWith("="))) return false;
3364
+ return true;
3365
+ }
3366
+ function isPrintable(s) {
3367
+ for (let i = 0; i < s.length; i++) {
3368
+ const c = s.charCodeAt(i);
3369
+ if (c < 32 || c > 126) return false;
3370
+ }
3371
+ return true;
3372
+ }
3373
+ function decodeBase64Literals(text) {
3374
+ const out = [];
3375
+ const seen = /* @__PURE__ */ new Set();
3376
+ for (const m of text.matchAll(B64_TOKEN_RE)) {
3377
+ const tok = m[0];
3378
+ if (!isValidBase64Token(tok)) continue;
3379
+ if (!/[A-Z]/.test(tok) || !/[a-z]/.test(tok)) continue;
3380
+ try {
3381
+ const padded = tok + "=".repeat((4 - tok.length % 4) % 4);
3382
+ const bin = Buffer.from(padded, "base64");
3383
+ const s = bin.toString("utf8");
3384
+ if (s && isPrintable(s) && !seen.has(s)) {
3385
+ seen.add(s);
3386
+ out.push(s);
3387
+ }
3388
+ } catch {
3389
+ }
3390
+ }
3391
+ return out;
3392
+ }
3393
+ function decodeUrlAndNumeric(text) {
3394
+ const out = [];
3395
+ let s = text;
3396
+ for (let i = 0; i < 3; i++) {
3397
+ let nxt = s;
3398
+ try {
3399
+ nxt = decodeURIComponent(s);
3400
+ } catch {
3401
+ break;
3402
+ }
3403
+ if (nxt === s) break;
3404
+ s = nxt;
3405
+ }
3406
+ if (s !== text) out.push(s);
3407
+ if (/\\[0-7]{3}/.test(text)) {
3408
+ let expanded = "";
3409
+ try {
3410
+ expanded = text.replace(/\\[0-7]{3}/g, (m) => String.fromCharCode(parseInt(m.slice(1), 8)));
3411
+ } catch {
3412
+ }
3413
+ if (expanded && expanded !== text) out.push(expanded);
3414
+ }
3415
+ if (/\\x[0-9a-fA-F]{2}/.test(text)) {
3416
+ let expanded = "";
3417
+ try {
3418
+ expanded = text.replace(/\\x[0-9a-fA-F]{2}/g, (m) => String.fromCharCode(parseInt(m.slice(2), 16)));
3419
+ } catch {
3420
+ }
3421
+ if (expanded && expanded !== text) out.push(expanded);
3422
+ }
3423
+ return out;
3424
+ }
3425
+ function allCandidates(detail, max = 8) {
3426
+ const cands = [detail];
3427
+ cands.push(...decodeBase64Literals(detail));
3428
+ cands.push(...decodeUrlAndNumeric(detail));
3429
+ const seen = /* @__PURE__ */ new Set();
3430
+ const out = [];
3431
+ for (const c of cands) {
3432
+ if (seen.has(c) || c.length > 4096) continue;
3433
+ seen.add(c);
3434
+ out.push(c);
3435
+ if (out.length >= max) break;
3436
+ }
3437
+ return out;
3438
+ }
3439
+ function matchStageAny(detail, stages) {
3440
+ for (const cand of allCandidates(detail)) {
3441
+ for (const [name, pats] of Object.entries(stages)) {
3442
+ for (const p of pats) {
3443
+ p.lastIndex = 0;
3444
+ if (p.test(cand)) return name;
3445
+ }
3446
+ }
3447
+ }
3448
+ return "";
3449
+ }
3450
+ var B64_TOKEN_RE;
3451
+ var init_decode = __esm({
3452
+ "src/engine/formal-engine/decode.ts"() {
3453
+ "use strict";
3454
+ init_esm_shims();
3455
+ B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
3456
+ }
3457
+ });
3458
+
3255
3459
  // node_modules/better-sqlite3/lib/util.js
3256
3460
  var require_util = __commonJS({
3257
3461
  "node_modules/better-sqlite3/lib/util.js"(exports) {
@@ -3335,7 +3539,7 @@ var require_bindings = __commonJS({
3335
3539
  var fs7 = __require("fs");
3336
3540
  var path6 = __require("path");
3337
3541
  var fileURLToPath10 = require_file_uri_to_path();
3338
- var join10 = path6.join;
3542
+ var join11 = path6.join;
3339
3543
  var dirname11 = path6.dirname;
3340
3544
  var exists = fs7.accessSync && function(path7) {
3341
3545
  try {
@@ -3395,7 +3599,7 @@ var require_bindings = __commonJS({
3395
3599
  var requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
3396
3600
  var tries = [], i = 0, l = opts.try.length, n, b, err;
3397
3601
  for (; i < l; i++) {
3398
- n = join10.apply(
3602
+ n = join11.apply(
3399
3603
  null,
3400
3604
  opts.try[i].map(function(p) {
3401
3605
  return opts[p] || p;
@@ -3456,7 +3660,7 @@ var require_bindings = __commonJS({
3456
3660
  if (dir === ".") {
3457
3661
  dir = process.cwd();
3458
3662
  }
3459
- if (exists(join10(dir, "package.json")) || exists(join10(dir, "node_modules"))) {
3663
+ if (exists(join11(dir, "package.json")) || exists(join11(dir, "node_modules"))) {
3460
3664
  return dir;
3461
3665
  }
3462
3666
  if (prev === dir) {
@@ -3465,7 +3669,7 @@ var require_bindings = __commonJS({
3465
3669
  );
3466
3670
  }
3467
3671
  prev = dir;
3468
- dir = join10(dir, "..");
3672
+ dir = join11(dir, "..");
3469
3673
  }
3470
3674
  };
3471
3675
  }
@@ -4171,7 +4375,7 @@ function loadTemplates() {
4171
4375
  // src/engine/script-analyzer.ts
4172
4376
  init_esm_shims();
4173
4377
  import { readFileSync as readFileSync8 } from "fs";
4174
- import { resolve as resolve6, join as join3 } from "path";
4378
+ import { resolve as resolve6, join as join4 } from "path";
4175
4379
  var SUSPICIOUS_PATTERNS = [
4176
4380
  // Network exfiltration (curl/wget POST in scripts is almost never legitimate)
4177
4381
  { name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
@@ -4304,7 +4508,7 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
4304
4508
  }
4305
4509
  function readScriptContent(scriptPath, maxLines = 500) {
4306
4510
  try {
4307
- const expandedPath = scriptPath.startsWith("~") ? join3(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve6(scriptPath);
4511
+ const expandedPath = scriptPath.startsWith("~") ? join4(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve6(scriptPath);
4308
4512
  const raw = readFileSync8(expandedPath, "utf-8");
4309
4513
  const lines = raw.split("\n").slice(0, maxLines);
4310
4514
  return { content: lines.join("\n"), lineCount: lines.length, error: "" };
@@ -4360,8 +4564,8 @@ init_esm_shims();
4360
4564
  init_heuristic();
4361
4565
  init_logic_rules();
4362
4566
  import { statSync as statSync2, existsSync as existsSync9 } from "fs";
4363
- import { resolve as resolve8, join as join5, isAbsolute as isAbsolute2 } from "path";
4364
- import { homedir as homedir3 } from "os";
4567
+ import { resolve as resolve8, join as join6, isAbsolute as isAbsolute2 } from "path";
4568
+ import { homedir as homedir4 } from "os";
4365
4569
 
4366
4570
  // src/engine/script-engine/tracker.ts
4367
4571
  init_esm_shims();
@@ -6006,7 +6210,7 @@ function extractInlineScriptPath(actionDetail) {
6006
6210
  function resolvePath2(p) {
6007
6211
  let expanded = p;
6008
6212
  if (expanded.startsWith("~/") || expanded === "~") {
6009
- expanded = join5(homedir3(), expanded.slice(1));
6213
+ expanded = join6(homedir4(), expanded.slice(1));
6010
6214
  }
6011
6215
  return resolve8(expanded);
6012
6216
  }
@@ -6087,7 +6291,7 @@ function analyzeScriptContent2(input) {
6087
6291
  isInline = true;
6088
6292
  }
6089
6293
  if (resolvedPath && !isAbsolute2(resolvedPath) && !resolvedPath.startsWith("~/")) {
6090
- resolvedPath = join5(input.cwd || process.cwd(), resolvedPath);
6294
+ resolvedPath = join6(input.cwd || process.cwd(), resolvedPath);
6091
6295
  }
6092
6296
  const l1Adapter = (content) => {
6093
6297
  const virtualInput = {
@@ -6398,6 +6602,7 @@ async function analyzeWithLLM(prompt, config) {
6398
6602
 
6399
6603
  // src/engine/coordinator.ts
6400
6604
  init_config();
6605
+ init_runtime_config();
6401
6606
  init_agents();
6402
6607
  init_scenarios();
6403
6608
 
@@ -6407,9 +6612,9 @@ init_esm_shims();
6407
6612
  // src/brain/rule-store.ts
6408
6613
  init_esm_shims();
6409
6614
  import { existsSync as existsSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
6410
- import { join as join6 } from "path";
6411
- import { homedir as homedir4 } from "os";
6412
- var DEFAULT_RULES_PATH = join6(homedir4(), ".agentmoss", "brain", "rules.json");
6615
+ import { join as join7 } from "path";
6616
+ import { homedir as homedir5 } from "os";
6617
+ var DEFAULT_RULES_PATH = join7(homedir5(), ".agentmoss", "brain", "rules.json");
6413
6618
  var ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
6414
6619
  var STATUS_DRAFT = "draft";
6415
6620
  var STATUS_APPROVED = "approved";
@@ -6935,98 +7140,132 @@ init_esm_shims();
6935
7140
  // src/engine/formal-engine/builder.ts
6936
7141
  init_esm_shims();
6937
7142
  init_rules_loader();
7143
+ init_decode();
6938
7144
 
6939
- // src/engine/formal-engine/decode.ts
7145
+ // src/engine/formal-engine/ltl.ts
6940
7146
  init_esm_shims();
6941
- var B64_TOKEN_RE = /[A-Za-z0-9+/]+={0,2}/g;
6942
- function isValidBase64Token(tok) {
6943
- if (tok.length < 8) return false;
6944
- if (!(tok.length % 4 === 0 || tok.endsWith("==") || tok.endsWith("="))) return false;
6945
- return true;
6946
- }
6947
- function isPrintable(s) {
6948
- for (let i = 0; i < s.length; i++) {
6949
- const c = s.charCodeAt(i);
6950
- if (c < 32 || c > 126) return false;
6951
- }
6952
- return true;
7147
+ init_rules_loader();
7148
+
7149
+ // src/engine/formal-engine/ir.ts
7150
+ init_esm_shims();
7151
+ function allNodes(mg) {
7152
+ const out = [];
7153
+ for (const s of mg.sessions) out.push(...s.nodes);
7154
+ return out;
6953
7155
  }
6954
- function decodeBase64Literals(text) {
7156
+ function allEdges(mg) {
6955
7157
  const out = [];
6956
- const seen = /* @__PURE__ */ new Set();
6957
- for (const m of text.matchAll(B64_TOKEN_RE)) {
6958
- const tok = m[0];
6959
- if (!isValidBase64Token(tok)) continue;
6960
- if (!/[A-Z]/.test(tok) || !/[a-z]/.test(tok)) continue;
6961
- try {
6962
- const padded = tok + "=".repeat((4 - tok.length % 4) % 4);
6963
- const bin = Buffer.from(padded, "base64");
6964
- const s = bin.toString("utf8");
6965
- if (s && isPrintable(s) && !seen.has(s)) {
6966
- seen.add(s);
6967
- out.push(s);
7158
+ for (const s of mg.sessions) out.push(...s.edges);
7159
+ out.push(...mg.crossEdges);
7160
+ return out;
7161
+ }
7162
+ function emptyVerificationResult() {
7163
+ return { verified: true, violations: [] };
7164
+ }
7165
+
7166
+ // src/engine/formal-engine/ltl.ts
7167
+ var AP_RE = null;
7168
+ function compileAps(apPatterns) {
7169
+ if (AP_RE) return;
7170
+ AP_RE = {};
7171
+ for (const [name, pats] of Object.entries(apPatterns || {})) {
7172
+ AP_RE[name] = pats.map((p) => new RegExp(p, "i"));
7173
+ }
7174
+ }
7175
+ function labelEvent(actionDetail) {
7176
+ if (!AP_RE) compileAps(loadMC().ap_patterns || {});
7177
+ let candidates = [actionDetail];
7178
+ try {
7179
+ candidates = (init_decode(), __toCommonJS(decode_exports)).allCandidates(actionDetail);
7180
+ } catch {
7181
+ }
7182
+ const hits = /* @__PURE__ */ new Set();
7183
+ for (const [name, pats] of Object.entries(AP_RE || {})) {
7184
+ for (const c of candidates) {
7185
+ const m = pats.some((p) => p.test(c));
7186
+ if (m) {
7187
+ hits.add(name);
7188
+ break;
6968
7189
  }
6969
- } catch {
6970
7190
  }
6971
7191
  }
6972
- return out;
7192
+ return hits;
6973
7193
  }
6974
- function decodeUrlAndNumeric(text) {
6975
- const out = [];
6976
- let s = text;
6977
- for (let i = 0; i < 3; i++) {
6978
- let nxt = s;
6979
- try {
6980
- nxt = decodeURIComponent(s);
6981
- } catch {
6982
- break;
7194
+ function evaluateLTL(formula, trace, pos) {
7195
+ const op = formula[0];
7196
+ switch (op) {
7197
+ case "ap":
7198
+ return trace[pos].has(formula[1]);
7199
+ case "!":
7200
+ return !evaluateLTL(formula[1], trace, pos);
7201
+ case "&":
7202
+ return evaluateLTL(formula[1], trace, pos) && evaluateLTL(formula[2], trace, pos);
7203
+ case "|":
7204
+ return evaluateLTL(formula[1], trace, pos) || evaluateLTL(formula[2], trace, pos);
7205
+ case "->":
7206
+ return !evaluateLTL(formula[1], trace, pos) || evaluateLTL(formula[2], trace, pos);
7207
+ case "X":
7208
+ return pos + 1 < trace.length && evaluateLTL(formula[1], trace, pos + 1);
7209
+ case "G": {
7210
+ for (let j = pos; j < trace.length; j++) {
7211
+ if (!evaluateLTL(formula[1], trace, j)) return false;
7212
+ }
7213
+ return true;
6983
7214
  }
6984
- if (nxt === s) break;
6985
- s = nxt;
6986
- }
6987
- if (s !== text) out.push(s);
6988
- if (/\\[0-7]{3}/.test(text)) {
6989
- let expanded = "";
6990
- try {
6991
- expanded = text.replace(/\\[0-7]{3}/g, (m) => String.fromCharCode(parseInt(m.slice(1), 8)));
6992
- } catch {
7215
+ case "F": {
7216
+ for (let j = pos; j < trace.length; j++) {
7217
+ if (evaluateLTL(formula[1], trace, j)) return true;
7218
+ }
7219
+ return false;
6993
7220
  }
6994
- if (expanded && expanded !== text) out.push(expanded);
6995
- }
6996
- if (/\\x[0-9a-fA-F]{2}/.test(text)) {
6997
- let expanded = "";
6998
- try {
6999
- expanded = text.replace(/\\x[0-9a-fA-F]{2}/g, (m) => String.fromCharCode(parseInt(m.slice(2), 16)));
7000
- } catch {
7221
+ case "U": {
7222
+ for (let j = pos; j < trace.length; j++) {
7223
+ if (evaluateLTL(formula[2], trace, j)) {
7224
+ for (let k = pos; k < j; k++) {
7225
+ if (!evaluateLTL(formula[1], trace, k)) return false;
7226
+ }
7227
+ return true;
7228
+ }
7229
+ }
7230
+ return false;
7001
7231
  }
7002
- if (expanded && expanded !== text) out.push(expanded);
7232
+ default:
7233
+ return false;
7003
7234
  }
7004
- return out;
7005
7235
  }
7006
- function allCandidates(detail, max = 8) {
7007
- const cands = [detail];
7008
- cands.push(...decodeBase64Literals(detail));
7009
- cands.push(...decodeUrlAndNumeric(detail));
7010
- const seen = /* @__PURE__ */ new Set();
7011
- const out = [];
7012
- for (const c of cands) {
7013
- if (seen.has(c) || c.length > 4096) continue;
7014
- seen.add(c);
7015
- out.push(c);
7016
- if (out.length >= max) break;
7236
+ function ruleViolates(rule, traceWith, witnessMode) {
7237
+ const formula = rule?.formula;
7238
+ if (!Array.isArray(formula) || formula.length === 0) return false;
7239
+ const satWith = evaluateLTL(formula, traceWith, 0);
7240
+ if (!satWith) return false;
7241
+ if (witnessMode && traceWith.length > 1) {
7242
+ const traceWithout = traceWith.slice(0, -1);
7243
+ if (evaluateLTL(formula, traceWithout, 0)) return false;
7017
7244
  }
7018
- return out;
7245
+ return true;
7019
7246
  }
7020
- function matchStageAny(detail, stages) {
7021
- for (const cand of allCandidates(detail)) {
7022
- for (const [name, pats] of Object.entries(stages)) {
7023
- for (const p of pats) {
7024
- p.lastIndex = 0;
7025
- if (p.test(cand)) return name;
7026
- }
7247
+ function checkMCRules(graph) {
7248
+ const violations = [];
7249
+ const nodes = allNodes(graph);
7250
+ if (nodes.length === 0) return violations;
7251
+ const mc = loadMC();
7252
+ const rules = mc.rules || [];
7253
+ if (rules.length === 0) return violations;
7254
+ const trace = nodes.map((n) => labelEvent(n.actionDetail));
7255
+ const witnessMode = graph.witnessIdx !== void 0;
7256
+ for (const rule of rules) {
7257
+ if (rule?.type !== "ltl" || !(rule.enabled !== false)) continue;
7258
+ if (ruleViolates(rule, trace, witnessMode)) {
7259
+ violations.push({
7260
+ propertyId: String(rule.id || "mc-ltl"),
7261
+ reason: String(rule.reason || "L2.2 \u6A21\u578B\u68C0\u6D4B\u89C4\u5219\u547D\u4E2D"),
7262
+ riskLevel: String(rule.risk_level || "high"),
7263
+ path: nodes.filter((n) => labelEvent(n.actionDetail).size > 0).slice(-3).map((n) => ({ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }))
7264
+ });
7265
+ if (witnessMode) return violations;
7027
7266
  }
7028
7267
  }
7029
- return "";
7268
+ return violations;
7030
7269
  }
7031
7270
 
7032
7271
  // src/engine/formal-engine/builder.ts
@@ -7057,6 +7296,26 @@ function extractResources(actionDetail) {
7057
7296
  function markStage(actionDetail) {
7058
7297
  return matchStageAny(actionDetail, loadStages());
7059
7298
  }
7299
+ function markIntegrity(actionDetail) {
7300
+ const aps = labelEvent(actionDetail);
7301
+ if (aps.has("download_file") || aps.has("prompt_injection")) {
7302
+ return { integrity: "low", isLowSource: true };
7303
+ }
7304
+ return { integrity: "medium", isLowSource: false };
7305
+ }
7306
+ function initNode(sessionId, seq, actionType, actionDetail) {
7307
+ const { integrity, isLowSource } = markIntegrity(actionDetail);
7308
+ return {
7309
+ sessionId,
7310
+ seq,
7311
+ actionType,
7312
+ actionDetail,
7313
+ stage: markStage(actionDetail),
7314
+ resources: extractResources(actionDetail),
7315
+ integrity,
7316
+ isLowSource
7317
+ };
7318
+ }
7060
7319
  function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
7061
7320
  const nodes = [];
7062
7321
  const edges = [];
@@ -7070,17 +7329,9 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
7070
7329
  details.push({ actionType: String(aNext.actionType || ""), actionDetail: String(aNext.actionDetail || "") });
7071
7330
  }
7072
7331
  details.forEach((d, seq) => {
7073
- const stage = markStage(d.actionDetail);
7074
- const node = {
7075
- sessionId,
7076
- seq,
7077
- actionType: d.actionType,
7078
- actionDetail: d.actionDetail,
7079
- stage,
7080
- resources: extractResources(d.actionDetail)
7081
- };
7332
+ const node = initNode(sessionId, seq, d.actionType, d.actionDetail);
7082
7333
  nodes.push(node);
7083
- if (stage) stageFlags.add(stage);
7334
+ if (node.stage) stageFlags.add(node.stage);
7084
7335
  });
7085
7336
  for (let i = 0; i < nodes.length - 1; i++) {
7086
7337
  edges.push({ src: i, dst: i + 1, kind: "next" });
@@ -7100,15 +7351,12 @@ function buildMultiSessionGraph(sessions, aNext) {
7100
7351
  if (aNext) {
7101
7352
  const last = mg.sessions[mg.sessions.length - 1];
7102
7353
  if (last.sessionId === "current") {
7103
- const stage = markStage(String(aNext.actionDetail || ""));
7104
- last.nodes.push({
7105
- sessionId: last.sessionId,
7106
- seq: last.nodes.length,
7107
- actionType: String(aNext.actionType || ""),
7108
- actionDetail: String(aNext.actionDetail || ""),
7109
- stage,
7110
- resources: extractResources(String(aNext.actionDetail || ""))
7111
- });
7354
+ last.nodes.push(initNode(
7355
+ last.sessionId,
7356
+ last.nodes.length,
7357
+ String(aNext.actionType || ""),
7358
+ String(aNext.actionDetail || "")
7359
+ ));
7112
7360
  if (last.nodes.length > 1) {
7113
7361
  last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
7114
7362
  }
@@ -7122,11 +7370,25 @@ function buildMultiSessionGraph(sessions, aNext) {
7122
7370
  function addCrossDataEdges(mg) {
7123
7371
  const nodes = allNodesFlat(mg);
7124
7372
  if (nodes.length < 2) return;
7125
- for (let i = 0; i < nodes.length; i++) {
7126
- for (let j = i + 1; j < nodes.length; j++) {
7127
- if (nodes[i].sessionId === nodes[j].sessionId) continue;
7128
- const shared = intersect(nodes[i].resources, nodes[j].resources);
7129
- if (shared.size > 0) {
7373
+ const resIndex = /* @__PURE__ */ new Map();
7374
+ nodes.forEach((n, i) => {
7375
+ for (const r of n.resources) {
7376
+ const list = resIndex.get(r);
7377
+ if (list) list.push(i);
7378
+ else resIndex.set(r, [i]);
7379
+ }
7380
+ });
7381
+ const seen = /* @__PURE__ */ new Set();
7382
+ for (const idxs of resIndex.values()) {
7383
+ if (idxs.length < 2) continue;
7384
+ for (let a = 0; a < idxs.length; a++) {
7385
+ const i = idxs[a];
7386
+ for (let b = a + 1; b < idxs.length; b++) {
7387
+ const j = idxs[b];
7388
+ if (nodes[i].sessionId === nodes[j].sessionId) continue;
7389
+ const key = `${i}:${j}`;
7390
+ if (seen.has(key)) continue;
7391
+ seen.add(key);
7130
7392
  mg.crossEdges.push({ src: i, dst: j, kind: "data" });
7131
7393
  }
7132
7394
  }
@@ -7137,28 +7399,6 @@ function allNodesFlat(mg) {
7137
7399
  for (const s of mg.sessions) out.push(...s.nodes);
7138
7400
  return out;
7139
7401
  }
7140
- function intersect(a, b) {
7141
- const out = /* @__PURE__ */ new Set();
7142
- for (const v of a) if (b.has(v)) out.add(v);
7143
- return out;
7144
- }
7145
-
7146
- // src/engine/formal-engine/ir.ts
7147
- init_esm_shims();
7148
- function allNodes(mg) {
7149
- const out = [];
7150
- for (const s of mg.sessions) out.push(...s.nodes);
7151
- return out;
7152
- }
7153
- function allEdges(mg) {
7154
- const out = [];
7155
- for (const s of mg.sessions) out.push(...s.edges);
7156
- out.push(...mg.crossEdges);
7157
- return out;
7158
- }
7159
- function emptyVerificationResult() {
7160
- return { verified: true, violations: [] };
7161
- }
7162
7402
 
7163
7403
  // src/engine/formal-engine/properties.ts
7164
7404
  init_esm_shims();
@@ -7181,27 +7421,50 @@ function checkCrossSessionChain(graph) {
7181
7421
  const violations = [];
7182
7422
  const nodes = allNodes(graph);
7183
7423
  if (nodes.length === 0) return violations;
7424
+ const witness = graph.witnessIdx;
7184
7425
  for (const chain of loadCrossSessionChains()) {
7185
7426
  const stages = chain.stages;
7186
7427
  if (!stages.length) continue;
7187
- let matched = 0;
7188
- let stageIdx = 0;
7189
- const path6 = [];
7190
- for (const n of nodes) {
7191
- if (stageIdx < stages.length && n.stage === stages[stageIdx]) {
7192
- matched += 1;
7193
- path6.push({ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) });
7194
- stageIdx += 1;
7195
- if (matched >= stages.length) break;
7428
+ if (witness !== void 0) {
7429
+ if (nodes[witness].stage !== stages[stages.length - 1]) continue;
7430
+ let matched = 0;
7431
+ const path6 = [];
7432
+ for (let k = 0; k < witness; k++) {
7433
+ const n = nodes[k];
7434
+ if (matched < stages.length - 1 && n.stage === stages[matched]) {
7435
+ matched += 1;
7436
+ path6.push({ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) });
7437
+ }
7196
7438
  }
7197
- }
7198
- if (matched >= stages.length) {
7439
+ if (matched < stages.length - 1) continue;
7440
+ path6.push({ sessionId: nodes[witness].sessionId, seq: nodes[witness].seq, detail: nodes[witness].actionDetail.slice(0, 60) });
7199
7441
  violations.push({
7200
7442
  propertyId: "cross-" + chain.id,
7201
7443
  reason: chain.reason,
7202
7444
  riskLevel: chain.riskLevel,
7203
7445
  path: path6
7204
7446
  });
7447
+ return violations;
7448
+ } else {
7449
+ let matched = 0;
7450
+ let stageIdx = 0;
7451
+ const path6 = [];
7452
+ for (const n of nodes) {
7453
+ if (stageIdx < stages.length && n.stage === stages[stageIdx]) {
7454
+ matched += 1;
7455
+ path6.push({ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) });
7456
+ stageIdx += 1;
7457
+ if (matched >= stages.length) break;
7458
+ }
7459
+ }
7460
+ if (matched >= stages.length) {
7461
+ violations.push({
7462
+ propertyId: "cross-" + chain.id,
7463
+ reason: chain.reason,
7464
+ riskLevel: chain.riskLevel,
7465
+ path: path6
7466
+ });
7467
+ }
7205
7468
  }
7206
7469
  }
7207
7470
  return violations;
@@ -7236,7 +7499,10 @@ function checkSensitiveDataFlow(graph) {
7236
7499
  }
7237
7500
  }
7238
7501
  }
7239
- for (const [i, n] of nodes.entries()) {
7502
+ const witness = graph.witnessIdx;
7503
+ const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
7504
+ for (const i of candidates) {
7505
+ const n = nodes[i];
7240
7506
  if (tainted.has(i) && EXFIL_RE.test(n.actionDetail)) {
7241
7507
  violations.push({
7242
7508
  propertyId: "sensitive-data-exfil",
@@ -7244,6 +7510,7 @@ function checkSensitiveDataFlow(graph) {
7244
7510
  riskLevel: "critical",
7245
7511
  path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
7246
7512
  });
7513
+ if (witness !== void 0) return violations;
7247
7514
  }
7248
7515
  }
7249
7516
  return violations;
@@ -7274,6 +7541,32 @@ function checkNeverExfilAfterPrivesc(graph) {
7274
7541
  const violations = [];
7275
7542
  const nodes = allNodes(graph);
7276
7543
  if (nodes.length < 2) return violations;
7544
+ const witness = graph.witnessIdx;
7545
+ if (witness !== void 0) {
7546
+ const evIdx = [];
7547
+ nodes.forEach((n, i) => {
7548
+ if ((i === witness || n.sessionId === "ebpf-evidence") && isExfilNode(n)) evIdx.push(i);
7549
+ });
7550
+ if (evIdx.length === 0) return violations;
7551
+ const firstEv = Math.min(...evIdx);
7552
+ let pre = null;
7553
+ for (let i = 0; i < firstEv; i++) {
7554
+ if (isPrivescNode(nodes[i])) pre = nodes[i];
7555
+ }
7556
+ if (pre) {
7557
+ const post = nodes[firstEv];
7558
+ return [{
7559
+ propertyId: "no-privesc-then-exfil",
7560
+ reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\xAC\u25C7EXFIL)",
7561
+ riskLevel: "critical",
7562
+ path: [
7563
+ { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
7564
+ { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
7565
+ ]
7566
+ }];
7567
+ }
7568
+ return violations;
7569
+ }
7277
7570
  for (let i = 0; i < nodes.length; i++) {
7278
7571
  if (!isPrivescNode(nodes[i])) continue;
7279
7572
  for (let j = i + 1; j < nodes.length; j++) {
@@ -7297,6 +7590,32 @@ function checkNeverExfilAfterCred(graph) {
7297
7590
  const violations = [];
7298
7591
  const nodes = allNodes(graph);
7299
7592
  if (nodes.length < 2) return violations;
7593
+ const witness = graph.witnessIdx;
7594
+ if (witness !== void 0) {
7595
+ const evIdx = [];
7596
+ nodes.forEach((n, i) => {
7597
+ if ((i === witness || n.sessionId === "ebpf-evidence") && isExfilNode(n)) evIdx.push(i);
7598
+ });
7599
+ if (evIdx.length === 0) return violations;
7600
+ const firstEv = Math.min(...evIdx);
7601
+ let pre = null;
7602
+ for (let i = 0; i < firstEv; i++) {
7603
+ if (isCredNode(nodes[i])) pre = nodes[i];
7604
+ }
7605
+ if (pre) {
7606
+ const post = nodes[firstEv];
7607
+ return [{
7608
+ propertyId: "no-cred-then-exfil",
7609
+ reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
7610
+ riskLevel: "critical",
7611
+ path: [
7612
+ { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
7613
+ { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
7614
+ ]
7615
+ }];
7616
+ }
7617
+ return violations;
7618
+ }
7300
7619
  for (let i = 0; i < nodes.length; i++) {
7301
7620
  if (!isCredNode(nodes[i])) continue;
7302
7621
  for (let j = i + 1; j < nodes.length; j++) {
@@ -7316,11 +7635,172 @@ function checkNeverExfilAfterCred(graph) {
7316
7635
  }
7317
7636
  return violations;
7318
7637
  }
7638
+ var TOOL_REGISTRY = null;
7639
+ function getToolRegistry() {
7640
+ if (TOOL_REGISTRY) return TOOL_REGISTRY;
7641
+ TOOL_REGISTRY = loadToolRegistry().tools || {};
7642
+ return TOOL_REGISTRY;
7643
+ }
7644
+ function lookupToolClass(actionType) {
7645
+ const entry = getToolRegistry()[actionType] || {};
7646
+ return entry.actionClass;
7647
+ }
7648
+ var COMPUTE_RE = /subprocess|os\.system|shell\s*=\s*True|\beval\(|\bexec\(|bash\s+[^\s]|sh\s+-c|python3?\s+|node\s+|\btimeout\s+|nohup\s+|\b\.\/[\w./-]+/i;
7649
+ var STRONG_COMPUTE_TYPES = /* @__PURE__ */ new Set(["python", "node", "exec", "terminal", "cmd"]);
7650
+ function isComputeNode(n) {
7651
+ if (STRONG_COMPUTE_TYPES.has(n.actionType.toLowerCase())) return true;
7652
+ const cls = lookupToolClass(n.actionType);
7653
+ if (cls === "read" || cls === "write" || cls === "network_egress" || cls === "message_egress") return false;
7654
+ return COMPUTE_RE.test(n.actionDetail);
7655
+ }
7656
+ function checkLowIntegrityCommandExec(graph) {
7657
+ const violations = [];
7658
+ const nodes = allNodes(graph);
7659
+ if (nodes.length === 0) return violations;
7660
+ const witness = graph.witnessIdx;
7661
+ const lowResources = /* @__PURE__ */ new Set();
7662
+ const lowAt = /* @__PURE__ */ new Map();
7663
+ nodes.forEach((n, i) => {
7664
+ if (n.isLowSource) {
7665
+ lowAt.set(i, n.actionDetail.slice(0, 60));
7666
+ for (const r of n.resources) lowResources.add(r);
7667
+ }
7668
+ });
7669
+ let changed = true;
7670
+ while (changed) {
7671
+ changed = false;
7672
+ for (let i = 0; i < nodes.length; i++) {
7673
+ if (lowAt.has(i)) continue;
7674
+ let shared = "";
7675
+ for (const r of nodes[i].resources) {
7676
+ if (lowResources.has(r)) {
7677
+ shared = r;
7678
+ break;
7679
+ }
7680
+ }
7681
+ if (shared) {
7682
+ lowAt.set(i, `\u7EE7\u627F\u81EA ${shared}`);
7683
+ for (const r of nodes[i].resources) lowResources.add(r);
7684
+ changed = true;
7685
+ }
7686
+ }
7687
+ }
7688
+ if (lowAt.size === 0) return violations;
7689
+ const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
7690
+ for (const i of candidates) {
7691
+ const n = nodes[i];
7692
+ if (lowAt.has(i) && isComputeNode(n)) {
7693
+ violations.push({
7694
+ propertyId: "low-integrity-controls-command-exec",
7695
+ reason: `\u4F4E\u5B8C\u6574\u6027\u6570\u636E\u63A7\u5236\u547D\u4EE4\u6267\u884C\uFF1A${lowAt.get(i)} \u2192 ${n.sessionId}#${n.seq} \u6267\u884C\u4E0D\u53EF\u4FE1\u5185\u5BB9`,
7696
+ riskLevel: "critical",
7697
+ path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
7698
+ });
7699
+ if (witness !== void 0) return violations;
7700
+ }
7701
+ }
7702
+ return violations;
7703
+ }
7704
+ var DL_OR_LOW_RE = /curl\s+.*-o\b|wget\s+.*\s+-O\b|curl\s+.*\|\s*(bash|sh|python)|wget\s+.*\|\s*(bash|sh|python)|git\s+clone|docker\s+pull|pip\s+install|npm\s+install|base64\s+-d/i;
7705
+ function checkCrossSessionReachability(graph) {
7706
+ const violations = [];
7707
+ const nodes = allNodes(graph);
7708
+ if (nodes.length === 0) return violations;
7709
+ const witness = graph.witnessIdx;
7710
+ const sources = /* @__PURE__ */ new Set();
7711
+ nodes.forEach((n, i) => {
7712
+ if (n.isLowSource || DL_OR_LOW_RE.test(n.actionDetail)) sources.add(i);
7713
+ });
7714
+ if (sources.size === 0) return violations;
7715
+ const reached = new Set(sources);
7716
+ let changed = true;
7717
+ while (changed) {
7718
+ changed = false;
7719
+ for (const e of allEdges(graph)) {
7720
+ if (reached.has(e.src) && !reached.has(e.dst)) {
7721
+ const srcRes = nodes[e.src].resources;
7722
+ const dstRes = nodes[e.dst].resources;
7723
+ for (const r of srcRes) {
7724
+ if (dstRes.has(r)) {
7725
+ reached.add(e.dst);
7726
+ changed = true;
7727
+ break;
7728
+ }
7729
+ }
7730
+ }
7731
+ }
7732
+ }
7733
+ const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
7734
+ for (const i of candidates) {
7735
+ const n = nodes[i];
7736
+ if (sources.has(i)) continue;
7737
+ if (reached.has(i)) {
7738
+ const srcIdx = Math.min(...sources);
7739
+ const srcDesc = nodes[srcIdx].actionDetail.slice(0, 40);
7740
+ violations.push({
7741
+ propertyId: "cross-session-reachability",
7742
+ reason: `\u4F4E\u5B8C\u6574\u6027\u6E90\u7ECF\u8DE8 session \u6570\u636E\u94FE\u591A\u8DF3\u5230\u8FBE\u5F53\u524D\u6267\u884C\uFF08\u6E90: ${srcDesc} \u2192 ${n.sessionId}#${n.seq}\uFF09`,
7743
+ riskLevel: "high",
7744
+ path: nodes.slice(-3).map((m) => ({ sessionId: m.sessionId, seq: m.seq, detail: m.actionDetail.slice(0, 60) }))
7745
+ });
7746
+ if (witness !== void 0) return violations;
7747
+ }
7748
+ }
7749
+ return violations;
7750
+ }
7751
+ function checkSensitiveDataAggregation(graph) {
7752
+ const violations = [];
7753
+ const nodes = allNodes(graph);
7754
+ if (nodes.length < 2) return violations;
7755
+ const witness = graph.witnessIdx;
7756
+ const resIndex = /* @__PURE__ */ new Map();
7757
+ nodes.forEach((n, i) => {
7758
+ for (const r of n.resources) {
7759
+ const list = resIndex.get(r);
7760
+ if (list) list.push(i);
7761
+ else resIndex.set(r, [i]);
7762
+ }
7763
+ });
7764
+ const sourcesFor = (exfilIdx) => {
7765
+ const srcs = /* @__PURE__ */ new Set();
7766
+ const n = nodes[exfilIdx];
7767
+ for (const r of n.resources) {
7768
+ for (const j of resIndex.get(r) || []) {
7769
+ if (j === exfilIdx) continue;
7770
+ srcs.add(`${nodes[j].sessionId}#${nodes[j].seq}`);
7771
+ }
7772
+ }
7773
+ return srcs;
7774
+ };
7775
+ const candidates = witness !== void 0 ? [witness] : nodes.map((_, i) => i);
7776
+ for (const i of candidates) {
7777
+ const n = nodes[i];
7778
+ if (!isExfilNode(n)) continue;
7779
+ const srcs = sourcesFor(i);
7780
+ if (srcs.size >= 2) {
7781
+ violations.push({
7782
+ propertyId: "sensitive-data-aggregation",
7783
+ reason: `\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20\uFF1Aegress \u805A\u5408 ${srcs.size} \u4E2A\u4E8B\u4EF6\u6E90\u6570\u636E\uFF08Rule 12\uFF09`,
7784
+ riskLevel: "critical",
7785
+ path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
7786
+ });
7787
+ if (witness !== void 0) return violations;
7788
+ }
7789
+ }
7790
+ return violations;
7791
+ }
7319
7792
  var ALL_PROPERTIES = [
7320
7793
  { id: "cross-session-chain", check: checkCrossSessionChain },
7321
7794
  { id: "sensitive-data-flow", check: checkSensitiveDataFlow },
7795
+ { id: "cross-session-reachability", check: checkCrossSessionReachability },
7322
7796
  { id: "no-privesc-then-exfil", check: checkNeverExfilAfterPrivesc },
7323
- { id: "no-cred-then-exfil", check: checkNeverExfilAfterCred }
7797
+ { id: "no-cred-then-exfil", check: checkNeverExfilAfterCred },
7798
+ // L2.2 模型检测(通用 LTL 求值器,mc_rules.json 单一事实源;witness 语义见 ltl.ts)
7799
+ { id: "mc-ltl-rules", check: checkMCRules },
7800
+ // Rule 5 低完整性控制命令执行(integrity 维度,站点 L2.1 最有区分度的检测之一)
7801
+ { id: "low-integrity-command-exec", check: checkLowIntegrityCommandExec },
7802
+ // Rule 12 敏感数据聚合外传(egress 聚合 ≥2 事件源)
7803
+ { id: "sensitive-data-aggregation", check: checkSensitiveDataAggregation }
7324
7804
  ];
7325
7805
 
7326
7806
  // src/engine/formal-engine/engine.ts
@@ -7352,6 +7832,9 @@ var FormalEngine = class {
7352
7832
  });
7353
7833
  }
7354
7834
  const graph = buildMultiSessionGraph(all, aNext);
7835
+ if (aNext) {
7836
+ graph.witnessIdx = allNodes(graph).length - 1;
7837
+ }
7355
7838
  const result = emptyVerificationResult();
7356
7839
  for (const prop of this.properties) {
7357
7840
  let violations;
@@ -7380,6 +7863,7 @@ var FormalEngine = class {
7380
7863
  // src/engine/formal-engine/stateful.ts
7381
7864
  init_esm_shims();
7382
7865
  init_rules_loader();
7866
+ init_decode();
7383
7867
  var DESTROY_RE = /\b(?:pkill|kill)\b|systemctl\s+stop|service\s+stop|crontab\s+-r|rm\s+-[rf]?\s+/i;
7384
7868
  var NEVER_ACTIVATED_RE = /\b(?:not\s+found|no\s+such\s+file|command\s+not\s+found|refused|failed\s+to\s+(?:start|bind|listen)|address\s+already\s+in\s+use)\b/i;
7385
7869
  var CRED_FILE_RE2 = /(known_hosts|\.ssh|id_rsa|\/etc\/shadow|\/etc\/passwd|credentials)/i;
@@ -7892,6 +8376,7 @@ function getBrainStore() {
7892
8376
  }
7893
8377
  var formalEngine = new FormalEngine();
7894
8378
  async function runAnalysis(input) {
8379
+ const observedFormalLayers = [];
7895
8380
  const heuristicResult = isHeuristicEnabled() ? runHeuristicCheck(input) : null;
7896
8381
  const isInlineScriptCmd = isInlineScriptCommand(input.aNext.actionDetail.toLowerCase());
7897
8382
  const isLiteralTextCmd = isLiteralTextCommand(input.aNext.actionDetail.toLowerCase());
@@ -7946,8 +8431,8 @@ async function runAnalysis(input) {
7946
8431
  const rawDetail = extractRawCommand(input.aNext.actionDetail);
7947
8432
  const lowerDetail = rawDetail.toLowerCase();
7948
8433
  const agentExt = mergeAgentExtensions(
7949
- getAgentExtensions(input.agentId),
7950
- getScenarioExtensions(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "")
8434
+ isLayerEnabled("agent_rules_enabled") ? getAgentExtensions(input.agentId) : null,
8435
+ isLayerEnabled("scenario_rules_enabled") ? getScenarioExtensionsMulti(input.scenarioId || process.env.AGENT_MOSS_SCENARIO || "") : null
7951
8436
  );
7952
8437
  if (FULLY_SAFE_TOOLS.has(actionType)) {
7953
8438
  return {
@@ -8064,15 +8549,22 @@ async function runAnalysis(input) {
8064
8549
  const formalResult = formalEngine.verify(formalSessions, input.aNext);
8065
8550
  if (!formalResult.verified) {
8066
8551
  const v = formalResult.violations[0];
8067
- return {
8068
- allowed: false,
8069
- reason: v ? v.reason : "\u5F62\u5F0F\u5316\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF08\u884C\u4E3A\u6027\u8D28\u8FDD\u53CD\uFF09",
8070
- riskLevel: v ? v.riskLevel : "high",
8071
- riskType: "formal_verification",
8072
- confidence: 90,
8073
- source: "formal_engine",
8074
- violatedLayers: ["2.4"]
8075
- };
8552
+ if (isFormalObserveMode()) {
8553
+ console.warn(
8554
+ `[AgentMoss] \u5F62\u5F0F\u5316\u9A8C\u8BC1 observe\uFF08\u53EA\u8BB0\u5F55\u4E0D\u62E6\u622A\uFF09: property=${v?.propertyId ?? "?"} risk=${v?.riskLevel ?? "?"} path_len=${v?.path?.length ?? 0} reason=${(v?.reason ?? "?").slice(0, 200)}`
8555
+ );
8556
+ observedFormalLayers.push("2.4");
8557
+ } else {
8558
+ return {
8559
+ allowed: false,
8560
+ reason: v ? v.reason : "\u5F62\u5F0F\u5316\u9A8C\u8BC1\u672A\u901A\u8FC7\uFF08\u884C\u4E3A\u6027\u8D28\u8FDD\u53CD\uFF09",
8561
+ riskLevel: v ? v.riskLevel : "high",
8562
+ riskType: "formal_verification",
8563
+ confidence: 90,
8564
+ source: "formal_engine",
8565
+ violatedLayers: ["2.4"]
8566
+ };
8567
+ }
8076
8568
  }
8077
8569
  }
8078
8570
  if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {
@@ -8119,7 +8611,7 @@ async function runAnalysis(input) {
8119
8611
  riskType: "",
8120
8612
  confidence: 85,
8121
8613
  source: "heuristic_and_logic_only",
8122
- violatedLayers: []
8614
+ violatedLayers: observedFormalLayers.length > 0 ? [...observedFormalLayers] : []
8123
8615
  };
8124
8616
  }
8125
8617
  let skipL3Hints = "\u65E0\uFF08\u5F53\u524D\u65E0\u7981\u7528\u89C4\u5219\u547D\u4E2D\uFF09";
@@ -8164,7 +8656,7 @@ async function runAnalysis(input) {
8164
8656
  riskType: "",
8165
8657
  confidence: 50,
8166
8658
  source: "llm_failure_fallback",
8167
- violatedLayers: []
8659
+ violatedLayers: observedFormalLayers.length > 0 ? [...observedFormalLayers] : []
8168
8660
  };
8169
8661
  }
8170
8662
  return {
@@ -8191,6 +8683,9 @@ async function runAnalysis(input) {
8191
8683
  } catch {
8192
8684
  }
8193
8685
  }
8686
+ if (observedFormalLayers.length > 0 && llmResult.allowed) {
8687
+ llmResult.violatedLayers = Array.from(/* @__PURE__ */ new Set([...observedFormalLayers, ...llmResult.violatedLayers || []]));
8688
+ }
8194
8689
  return llmResult;
8195
8690
  }
8196
8691
 
@@ -8682,12 +9177,13 @@ init_esm_shims();
8682
9177
  init_esm_shims();
8683
9178
  init_runtime_config();
8684
9179
  init_token_stats();
9180
+ init_rules_loader();
8685
9181
  import { Hono } from "hono";
8686
9182
  import { readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
8687
- import { join as join9, dirname as dirname10 } from "path";
9183
+ import { join as join10, dirname as dirname10 } from "path";
8688
9184
  import { fileURLToPath as fileURLToPath9 } from "url";
8689
9185
  var __dirname8 = dirname10(fileURLToPath9(import.meta.url));
8690
- var STATIC_DIR = join9(__dirname8, "static");
9186
+ var STATIC_DIR = join10(__dirname8, "static");
8691
9187
  var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
8692
9188
  function getConsoleToken() {
8693
9189
  return process.env[CONSOLE_TOKEN_ENV] || "";
@@ -8711,7 +9207,7 @@ function createConsoleRouter() {
8711
9207
  const console2 = new Hono();
8712
9208
  console2.use("/api/*", authMiddleware());
8713
9209
  const serveIndex = (c) => {
8714
- const indexFile = join9(STATIC_DIR, "index.html");
9210
+ const indexFile = join10(STATIC_DIR, "index.html");
8715
9211
  if (existsSync14(indexFile)) {
8716
9212
  return c.html(readFileSync15(indexFile, "utf-8"));
8717
9213
  }
@@ -8728,24 +9224,89 @@ function createConsoleRouter() {
8728
9224
  const rc = updateLayerEnabled(body.layer_key, body.enabled);
8729
9225
  return c.json({ layers: rc.layers, env_overrides: getEnvOverrides() });
8730
9226
  });
9227
+ function getL24View() {
9228
+ const ltlHuman = {
9229
+ "mc-ltl-001": "\u5148\u8BFB\u53D6\u4E86\u51ED\u636E/\u5BC6\u94A5\uFF0C\u4E4B\u540E\u51FA\u73B0\u7F51\u7EDC\u5916\u4F20",
9230
+ "mc-ltl-002": "\u5148\u4E0B\u8F7D/\u83B7\u53D6\u4E86\u6587\u4EF6\uFF0C\u4E4B\u540E\u6267\u884C\u4E86\u6587\u4EF6",
9231
+ "mc-ltl-003": "\u5148\u7981\u7528\u4E86\u5B89\u5168\u9632\u62A4\uFF0C\u4E4B\u540E\u51FA\u73B0\u7834\u574F\u6027\u64CD\u4F5C\uFF08\u6267\u884C/\u5220\u9664/\u63D0\u6743\uFF09",
9232
+ "mc-ltl-004": "\u5148\u51FA\u73B0\u63D0\u793A\u8BCD\u6CE8\u5165\uFF0C\u4E4B\u540E\u51FA\u73B0\u654F\u611F\u64CD\u4F5C\uFF08\u5916\u4F20/\u5199\u6587\u4EF6/\u6267\u884C\uFF09",
9233
+ "mc-ltl-005": "\u5148\u8BFB\u53D6\u51ED\u636E\uFF0C\u4E4B\u540E\u51FA\u73B0\u6A2A\u5411\u79FB\u52A8\uFF08ssh/scp \u5230\u8FDC\u7A0B\uFF09",
9234
+ "mc-ltl-006": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E\uFF0C\u4E4B\u540E\u6E05\u9664\u65E5\u5FD7",
9235
+ "mc-ltl-007": "\u5148\u63D0\u6743\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
9236
+ "mc-ltl-008": "\u5148\u4E0B\u8F7D\u6587\u4EF6\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
9237
+ "mc-ltl-009": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E \u2192 \u6253\u5305\u538B\u7F29 \u2192 \u7F51\u7EDC\u5916\u4F20",
9238
+ "mc-ltl-010": "\u5B89\u88C5\u4E86\u6301\u4E45\u5316\u673A\u5236\uFF08crontab/systemd/.bashrc \u7B49\uFF09\uFF0C\u9700\u5BA1\u6279",
9239
+ "mc-ctl-001": "\u4EFB\u4F55\u60C5\u51B5\u4E0B\u4E0D\u5141\u8BB8\u6E05\u9664\u65E5\u5FD7\uFF08\u5B89\u5168\u5E95\u7EBF\uFF09"
9240
+ };
9241
+ const mc = loadMC();
9242
+ const rules = mc.rules || [];
9243
+ return {
9244
+ enabled_via: "L2_logic_rules",
9245
+ description: "\u65F6\u5E8F\u4E0E\u6570\u636E\u6D41\u653B\u51FB\u68C0\u6D4B\uFF08L2.2 \u6A21\u578B\u68C0\u6D4B\uFF09",
9246
+ ltl_rules: rules.map((r) => ({
9247
+ id: r.id,
9248
+ type: r.type,
9249
+ formula: Array.isArray(r.formula) ? "<\u516C\u5F0F>" : r.formula,
9250
+ reason: r.reason,
9251
+ risk_level: r.risk_level,
9252
+ action: r.action,
9253
+ enabled: r.enabled !== false,
9254
+ human: ltlHuman[r.id] || r.reason || ""
9255
+ }))
9256
+ };
9257
+ }
9258
+ function withL24View(rc) {
9259
+ if (rc && rc.L2_4 && typeof rc.L2_4 === "object" && !Array.isArray(rc.L2_4)) {
9260
+ const cat = rc.L2_4.ltl_rules;
9261
+ const rls = cat && cat.rules || [];
9262
+ const ltlHumanLocal = {
9263
+ "mc-ltl-001": "\u5148\u8BFB\u53D6\u4E86\u51ED\u636E/\u5BC6\u94A5\uFF0C\u4E4B\u540E\u51FA\u73B0\u7F51\u7EDC\u5916\u4F20",
9264
+ "mc-ltl-002": "\u5148\u4E0B\u8F7D/\u83B7\u53D6\u4E86\u6587\u4EF6\uFF0C\u4E4B\u540E\u6267\u884C\u4E86\u6587\u4EF6",
9265
+ "mc-ltl-003": "\u5148\u7981\u7528\u4E86\u5B89\u5168\u9632\u62A4\uFF0C\u4E4B\u540E\u51FA\u73B0\u7834\u574F\u6027\u64CD\u4F5C\uFF08\u6267\u884C/\u5220\u9664/\u63D0\u6743\uFF09",
9266
+ "mc-ltl-004": "\u5148\u51FA\u73B0\u63D0\u793A\u8BCD\u6CE8\u5165\uFF0C\u4E4B\u540E\u51FA\u73B0\u654F\u611F\u64CD\u4F5C\uFF08\u5916\u4F20/\u5199\u6587\u4EF6/\u6267\u884C\uFF09",
9267
+ "mc-ltl-005": "\u5148\u8BFB\u53D6\u51ED\u636E\uFF0C\u4E4B\u540E\u51FA\u73B0\u6A2A\u5411\u79FB\u52A8\uFF08ssh/scp \u5230\u8FDC\u7A0B\uFF09",
9268
+ "mc-ltl-006": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E\uFF0C\u4E4B\u540E\u6E05\u9664\u65E5\u5FD7",
9269
+ "mc-ltl-007": "\u5148\u63D0\u6743\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
9270
+ "mc-ltl-008": "\u5148\u4E0B\u8F7D\u6587\u4EF6\uFF0C\u4E4B\u540E\u5B89\u88C5\u6301\u4E45\u5316\u540E\u95E8",
9271
+ "mc-ltl-009": "\u5148\u63A5\u89E6\u654F\u611F\u6570\u636E \u2192 \u6253\u5305\u538B\u7F29 \u2192 \u7F51\u7EDC\u5916\u4F20",
9272
+ "mc-ltl-010": "\u5B89\u88C5\u4E86\u6301\u4E45\u5316\u673A\u5236\uFF08crontab/systemd/.bashrc \u7B49\uFF09\uFF0C\u9700\u5BA1\u6279",
9273
+ "mc-ctl-001": "\u4EFB\u4F55\u60C5\u51B5\u4E0B\u4E0D\u5141\u8BB8\u6E05\u9664\u65E5\u5FD7\uFF08\u5B89\u5168\u5E95\u7EBF\uFF09"
9274
+ };
9275
+ rc.L2_4 = {
9276
+ enabled_via: "L2_logic_rules",
9277
+ description: "\u65F6\u5E8F\u4E0E\u6570\u636E\u6D41\u653B\u51FB\u68C0\u6D4B\uFF08L2.2 \u6A21\u578B\u68C0\u6D4B\uFF09",
9278
+ ltl_rules: rls.map((r) => ({
9279
+ id: r.id,
9280
+ type: "ltl",
9281
+ formula: "<\u516C\u5F0F>",
9282
+ reason: r.reason || "",
9283
+ risk_level: r.risk_level || "high",
9284
+ action: r.action || "block",
9285
+ enabled: r.enabled !== false,
9286
+ human: ltlHumanLocal[r.id] || r.reason || ""
9287
+ }))
9288
+ };
9289
+ }
9290
+ return rc;
9291
+ }
8731
9292
  console2.get("/api/rules", (c) => {
8732
9293
  const rc = loadRuntimeConfig();
8733
- return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules });
9294
+ return c.json({ L1_rules: rc.L1_rules, L2_rules: rc.L2_rules, L2_4: getL24View() });
8734
9295
  });
8735
9296
  console2.put("/api/rules/enabled", async (c) => {
8736
9297
  const b = await c.req.json();
8737
9298
  const rc = updateRuleEnabled(b.layer, b.category, b.rule_id, b.enabled);
8738
- return c.json(rc);
9299
+ return c.json(withL24View(rc));
8739
9300
  });
8740
9301
  console2.put("/api/rules/deny_mode", async (c) => {
8741
9302
  const b = await c.req.json();
8742
9303
  const rc = updateRuleDenyMode(b.layer, b.category, b.rule_id, b.deny_mode);
8743
- return c.json(rc);
9304
+ return c.json(withL24View(rc));
8744
9305
  });
8745
9306
  console2.put("/api/rules/skip_l3", async (c) => {
8746
9307
  const b = await c.req.json();
8747
9308
  const rc = updateRuleSkipL3(b.layer, b.category, b.rule_id, b.skip_l3_on_disabled);
8748
- return c.json(rc);
9309
+ return c.json(withL24View(rc));
8749
9310
  });
8750
9311
  console2.put("/api/categories/enabled", async (c) => {
8751
9312
  const b = await c.req.json();
@@ -8804,9 +9365,9 @@ function createConsoleRouter() {
8804
9365
  if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
8805
9366
  return c.json({ error: "invalid skill_id" }, 400);
8806
9367
  }
8807
- const skillPath = join9(dirname10(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
9368
+ const skillPath = join10(dirname10(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
8808
9369
  if (!existsSync14(skillPath)) {
8809
- const builtinPath = join9(__dirname8, "..", "..", "agent_moss", "skills", `${skillId}.md`);
9370
+ const builtinPath = join10(__dirname8, "..", "..", "agent_moss", "skills", `${skillId}.md`);
8810
9371
  if (existsSync14(builtinPath)) return c.text(readFileSync15(builtinPath, "utf-8"));
8811
9372
  return c.json({ error: "skill not found" }, 404);
8812
9373
  }
@@ -8895,7 +9456,7 @@ function createConsoleRouter() {
8895
9456
  });
8896
9457
  console2.get("/api/config", (c) => {
8897
9458
  const rc = loadRuntimeConfig();
8898
- return c.json(rc);
9459
+ return c.json({ ...rc, L2_4: getL24View() });
8899
9460
  });
8900
9461
  console2.get("/api/env-overrides", (c) => {
8901
9462
  return c.json({ overrides: getEnvOverrides() });
@@ -8972,4 +9533,7 @@ export {
8972
9533
  findFreePort,
8973
9534
  startServer
8974
9535
  };
9536
+ //! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
9537
+ //! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
9538
+ //! properties.py ALL_PROPERTIES 保持一致并双端测试确认。
8975
9539
  //# sourceMappingURL=server.js.map