@kenhkl/agent-moss 0.16.9 → 0.16.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/cli.d.ts +19 -1
- package/dist/cli.js +2605 -478
- package/dist/data/agent_moss.service +4 -0
- package/dist/data/rules_template/README.md +133 -0
- package/dist/data/rules_template/extra.json +14 -0
- package/dist/data/rules_template/override.json +15 -0
- package/dist/engine/coordinator.js +917 -208
- package/dist/server.d.ts +18 -1
- package/dist/server.js +1809 -312
- package/dist/static/index.html +26 -1
- package/package.json +2 -1
package/dist/server.js
CHANGED
|
@@ -394,6 +394,52 @@ var init_runtime_config_bridge = __esm({
|
|
|
394
394
|
}
|
|
395
395
|
});
|
|
396
396
|
|
|
397
|
+
// src/infra/rules-mode.ts
|
|
398
|
+
function truthy(name) {
|
|
399
|
+
return TRUTHY.has((process.env[name] || "").trim().toLowerCase());
|
|
400
|
+
}
|
|
401
|
+
function getRulesMode() {
|
|
402
|
+
if (cached !== null) return cached;
|
|
403
|
+
const raw = (process.env[ENV_RULES_MODE] || "").trim().toLowerCase();
|
|
404
|
+
if (raw) {
|
|
405
|
+
if (VALID_MODES.has(raw)) {
|
|
406
|
+
cached = raw;
|
|
407
|
+
} else {
|
|
408
|
+
console.error(
|
|
409
|
+
`[AgentMoss] ${ENV_RULES_MODE}=${JSON.stringify(raw)} \u975E\u6CD5\uFF08\u53EA\u652F\u6301 hot / cold / builtin\uFF09\uFF1B\u4E3A\u5B89\u5168\u8D77\u89C1\u6309 builtin\uFF08\u4EC5\u5185\u7F6E\u89C4\u5219\uFF09\u5904\u7406`
|
|
410
|
+
);
|
|
411
|
+
cached = "builtin";
|
|
412
|
+
}
|
|
413
|
+
return cached;
|
|
414
|
+
}
|
|
415
|
+
if (truthy(ENV_RULES_BUILTIN_ONLY)) cached = "builtin";
|
|
416
|
+
else if (truthy(ENV_DISABLE_RULE_HOT_RELOAD)) cached = "cold";
|
|
417
|
+
else cached = "hot";
|
|
418
|
+
return cached;
|
|
419
|
+
}
|
|
420
|
+
function isHotReloadEnabled() {
|
|
421
|
+
return getRulesMode() === "hot";
|
|
422
|
+
}
|
|
423
|
+
function isRuntimeConfigDisabled() {
|
|
424
|
+
return getRulesMode() !== "hot";
|
|
425
|
+
}
|
|
426
|
+
function isBuiltinOnly() {
|
|
427
|
+
return getRulesMode() === "builtin";
|
|
428
|
+
}
|
|
429
|
+
var ENV_RULES_MODE, ENV_RULES_BUILTIN_ONLY, ENV_DISABLE_RULE_HOT_RELOAD, TRUTHY, VALID_MODES, cached;
|
|
430
|
+
var init_rules_mode = __esm({
|
|
431
|
+
"src/infra/rules-mode.ts"() {
|
|
432
|
+
"use strict";
|
|
433
|
+
init_esm_shims();
|
|
434
|
+
ENV_RULES_MODE = "AGENT_MOSS_RULES_MODE";
|
|
435
|
+
ENV_RULES_BUILTIN_ONLY = "AGENT_MOSS_RULES_BUILTIN_ONLY";
|
|
436
|
+
ENV_DISABLE_RULE_HOT_RELOAD = "AGENT_MOSS_DISABLE_RULE_HOT_RELOAD";
|
|
437
|
+
TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
438
|
+
VALID_MODES = /* @__PURE__ */ new Set(["hot", "cold", "builtin"]);
|
|
439
|
+
cached = null;
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
|
|
397
443
|
// src/infra/rules-loader.ts
|
|
398
444
|
import { readFileSync as readFileSync2, existsSync, readdirSync, statSync } from "fs";
|
|
399
445
|
import { resolve, dirname, join as join2 } from "path";
|
|
@@ -406,71 +452,64 @@ function parseDirEnv(envName) {
|
|
|
406
452
|
if (!raw) return [];
|
|
407
453
|
return raw.split(/[,;]/).map((s) => s.trim()).filter(Boolean).map((s) => resolve(s));
|
|
408
454
|
}
|
|
409
|
-
function
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
console.warn(
|
|
416
|
-
`[AgentMoss] \u89C4\u5219\u6587\u4EF6\u88AB\u8986\u76D6\u76EE\u5F55\u8986\u76D6: ${path6}\uFF08\u5185\u7F6E\u9ED8\u8BA4\u88AB\u5FFD\u7565\uFF09\u3002\u8BE5\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4 \u2014\u2014 **\u5185\u7F6E\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762**\uFF1B\u5220\u9664\u8BE5\u6587\u4EF6\uFF08\u6216\u6E05\u7A7A AGENT_MOSS_OVERRIDE_RULES_DIR\uFF09\u5373\u6062\u590D\u5185\u7F6E\u89C4\u5219\u3002\u82E5\u53EA\u60F3\u9010\u6761\u542F\u505C/\u589E\u5220\uFF0C\u8BF7\u6539\u7528 runtime config\uFF08Console\uFF09\u3002`
|
|
417
|
-
);
|
|
418
|
-
const missing = (EXPECTED_KEYS[name] || []).filter((k) => !(k in data));
|
|
419
|
-
if (missing.length > 0) {
|
|
420
|
-
console.warn(
|
|
421
|
-
`\u26A0\uFE0F [AgentMoss] \u8986\u76D6\u6587\u4EF6 ${path6} \u7F3A\u5C11\u9876\u5C42\u952E [${missing.join(", ")}] \u2014\u2014 \u5BF9\u5E94\u7C7B\u522B\u4F1A**\u4E00\u6761\u89C4\u5219\u90FD\u4E0D\u8DD1**\uFF08\u9759\u9ED8\u5931\u6548\uFF09\u3002\u82E5\u53EA\u60F3\u6539\u5176\u4E2D\u4E00\u7C7B\uFF0C\u8BF7\u5148\u62F7\u8D1D\u5B8C\u6574\u7684\u5185\u7F6E\u6587\u4EF6\u518D\u6539\u3002`
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
function overriddenRuleFiles() {
|
|
426
|
-
const out = [];
|
|
427
|
-
for (const name of Object.keys(EXPECTED_KEYS)) {
|
|
428
|
-
for (const dir of overrideRulesDirs()) {
|
|
429
|
-
const p = resolve(dir, name);
|
|
430
|
-
if (existsSync(p)) {
|
|
431
|
-
out.push(p);
|
|
432
|
-
break;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
return out;
|
|
455
|
+
function rulesDirs() {
|
|
456
|
+
if (isBuiltinOnly()) return [];
|
|
457
|
+
const dirs = [];
|
|
458
|
+
for (const env of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env));
|
|
459
|
+
dirs.push(resolve(dataRootDir(), "rules"));
|
|
460
|
+
return [...new Set(dirs)];
|
|
437
461
|
}
|
|
438
462
|
function findRulesFile(name) {
|
|
439
|
-
for (const dir of overrideRulesDirs()) {
|
|
440
|
-
const p = resolve(dir, name);
|
|
441
|
-
if (existsSync(p)) return p;
|
|
442
|
-
}
|
|
443
463
|
for (const dir of RULES_DIR_CANDS) {
|
|
444
464
|
const p = resolve(dir, name);
|
|
445
465
|
if (existsSync(p)) return p;
|
|
446
466
|
}
|
|
447
467
|
return null;
|
|
448
468
|
}
|
|
449
|
-
function isOverridePath(name, p) {
|
|
450
|
-
return overrideRulesDirs().some((dir) => resolve(dir, name) === p);
|
|
451
|
-
}
|
|
452
469
|
function loadJson(name) {
|
|
470
|
+
if (!isHotReloadEnabled()) {
|
|
471
|
+
const cached2 = frozenJsonCache.get(name);
|
|
472
|
+
if (cached2 !== void 0) return cached2;
|
|
473
|
+
}
|
|
453
474
|
const p = findRulesFile(name);
|
|
454
475
|
if (!p) return {};
|
|
455
|
-
const isOverride = isOverridePath(name, p);
|
|
456
|
-
let data;
|
|
457
476
|
try {
|
|
458
|
-
|
|
477
|
+
const parsed = JSON.parse(readFileSync2(p, "utf-8")) || {};
|
|
478
|
+
if (!isHotReloadEnabled()) frozenJsonCache.set(name, parsed);
|
|
479
|
+
return parsed;
|
|
459
480
|
} catch {
|
|
460
|
-
if (isOverride) warnOverride(name, p, {});
|
|
461
481
|
return {};
|
|
462
482
|
}
|
|
463
|
-
if (isOverride) warnOverride(name, p, data);
|
|
464
|
-
return data;
|
|
465
483
|
}
|
|
466
484
|
function resolveScenarioRulesFile(scenarioId) {
|
|
467
485
|
const rel = join2("scenarios", scenarioId, "rules.json");
|
|
468
|
-
for (const dir of
|
|
486
|
+
for (const dir of rulesDirs()) {
|
|
469
487
|
const p = resolve(dir, rel);
|
|
470
|
-
if (existsSync(p))
|
|
488
|
+
if (!existsSync(p)) continue;
|
|
489
|
+
let reason = "";
|
|
490
|
+
try {
|
|
491
|
+
reason = invalidScenarioRulesReason(JSON.parse(readFileSync2(p, "utf-8")) || {});
|
|
492
|
+
} catch (err) {
|
|
493
|
+
reason = `\u8BFB\u53D6/\u89E3\u6790\u5931\u8D25: ${String(err)}`;
|
|
494
|
+
}
|
|
495
|
+
if (reason) {
|
|
496
|
+
console.error(
|
|
497
|
+
`[AgentMoss] \u274C \u573A\u666F\u89C4\u5219\u8986\u76D6\u6587\u4EF6\u7ED3\u6784\u4E0D\u53EF\u7528\uFF0C**\u5DF2\u5FFD\u7565\u5E76\u56DE\u9000\u5185\u7F6E**: ${p}
|
|
498
|
+
\u539F\u56E0: ${reason}`
|
|
499
|
+
);
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
console.warn(`[AgentMoss] \u26A0\uFE0F \u573A\u666F ${scenarioId} \u89C4\u5219\u88AB\u8986\u76D6: ${p}\uFF08\u5185\u7F6E\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762\uFF09`);
|
|
503
|
+
return p;
|
|
471
504
|
}
|
|
472
505
|
return null;
|
|
473
506
|
}
|
|
507
|
+
function invalidScenarioRulesReason(data) {
|
|
508
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
509
|
+
return `\u9876\u5C42\u5FC5\u987B\u662F JSON \u5BF9\u8C61\uFF0C\u5B9E\u9645\u662F ${Array.isArray(data) ? "array" : typeof data}`;
|
|
510
|
+
}
|
|
511
|
+
return SCENARIO_LIST_KEYS.filter((k) => k in data && !Array.isArray(data[k])).map((k) => `"${k}" \u5E94\u4E3A\u6570\u7EC4\uFF08\u5BF9\u8C61\u6570\u7EC4\uFF09\uFF0C\u5B9E\u9645\u662F ${typeof data[k]}`).join("\uFF1B");
|
|
512
|
+
}
|
|
474
513
|
function loadL1() {
|
|
475
514
|
return loadJson("l1_rules.json");
|
|
476
515
|
}
|
|
@@ -478,10 +517,40 @@ function loadL2() {
|
|
|
478
517
|
return loadJson("l2_rules.json");
|
|
479
518
|
}
|
|
480
519
|
function loadL3() {
|
|
481
|
-
|
|
520
|
+
const data = { ...loadJson("l3_rules.json") };
|
|
521
|
+
try {
|
|
522
|
+
const ov = loadOverrideRules().skills;
|
|
523
|
+
const ex = loadExtraRules().skills || {};
|
|
524
|
+
if (ov && typeof ov === "object") {
|
|
525
|
+
for (const [cat, skills] of Object.entries(ov)) data[cat] = { ...skills };
|
|
526
|
+
}
|
|
527
|
+
if (ex && typeof ex === "object") {
|
|
528
|
+
for (const [cat, skills] of Object.entries(ex)) data[cat] = { ...data[cat] || {}, ...skills };
|
|
529
|
+
}
|
|
530
|
+
} catch {
|
|
531
|
+
}
|
|
532
|
+
return data;
|
|
533
|
+
}
|
|
534
|
+
function loadMCRaw() {
|
|
535
|
+
const mc = { ...loadJson("mc_rules.json") };
|
|
536
|
+
let rules = [...mc.rules || []];
|
|
537
|
+
try {
|
|
538
|
+
const ov = loadOverrideRules().ltl_rules;
|
|
539
|
+
const ex = loadExtraRules().ltl_rules || [];
|
|
540
|
+
if (Array.isArray(ov)) rules = [...ov];
|
|
541
|
+
if (Array.isArray(ex) && ex.length > 0) {
|
|
542
|
+
const byId = /* @__PURE__ */ new Map();
|
|
543
|
+
for (const r of rules) if (r && r.id) byId.set(r.id, r);
|
|
544
|
+
for (const r of ex) if (r && r.id) byId.set(r.id, r);
|
|
545
|
+
rules = [...byId.values()];
|
|
546
|
+
}
|
|
547
|
+
} catch {
|
|
548
|
+
}
|
|
549
|
+
mc.rules = rules;
|
|
550
|
+
return mc;
|
|
482
551
|
}
|
|
483
552
|
function loadMC() {
|
|
484
|
-
const mc =
|
|
553
|
+
const mc = loadMCRaw();
|
|
485
554
|
const rules = mc.rules || [];
|
|
486
555
|
try {
|
|
487
556
|
const rt = runtimeConfigBridge.current;
|
|
@@ -501,166 +570,303 @@ function loadMC() {
|
|
|
501
570
|
return mc;
|
|
502
571
|
}
|
|
503
572
|
function loadToolRegistry() {
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
function extraRulesDirs() {
|
|
507
|
-
return parseDirEnv("AGENT_MOSS_EXTRA_RULES_DIR");
|
|
508
|
-
}
|
|
509
|
-
function listJsonFilesDeep(dir) {
|
|
510
|
-
const out = [];
|
|
511
|
-
let entries;
|
|
573
|
+
const data = { ...loadJson("tool_registry.json") };
|
|
574
|
+
let tools = { ...data.tools || {} };
|
|
512
575
|
try {
|
|
513
|
-
|
|
576
|
+
const ov = loadOverrideRules().tools;
|
|
577
|
+
const ex = loadExtraRules().tools || {};
|
|
578
|
+
if (ov && typeof ov === "object") tools = { ...ov };
|
|
579
|
+
if (ex && typeof ex === "object") tools = { ...tools, ...ex };
|
|
514
580
|
} catch {
|
|
515
|
-
return out;
|
|
516
581
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
582
|
+
data.tools = tools;
|
|
583
|
+
return data;
|
|
584
|
+
}
|
|
585
|
+
function scanStructEntry(cat, value, path6) {
|
|
586
|
+
if (cat === "skills") {
|
|
587
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
588
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 skills \u5E94\u4E3A\u5BF9\u8C61\uFF08{\u7C7B\u522B: {\u6280\u80FD\u540D: [\u5173\u952E\u8BCD]}}\uFF09\u2014\u2014 \u5DF2\u8DF3\u8FC7\u3002`);
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
const out = {};
|
|
592
|
+
for (const [skCat, skills] of Object.entries(value)) {
|
|
593
|
+
if (!skills || typeof skills !== "object" || Array.isArray(skills)) {
|
|
594
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 skills.${skCat} \u5E94\u4E3A\u5BF9\u8C61\uFF08\u6280\u80FD\u540D \u2192 \u5173\u952E\u8BCD\u6570\u7EC4\uFF09\u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u7C7B\u522B\u3002`);
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
const keep = {};
|
|
598
|
+
for (const [name, kws] of Object.entries(skills)) {
|
|
599
|
+
if (Array.isArray(kws)) keep[name] = kws;
|
|
600
|
+
else console.warn(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 skills.${skCat}.${name} \u5173\u952E\u8BCD\u4E0D\u662F\u6570\u7EC4 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u6761\u3002`);
|
|
601
|
+
}
|
|
602
|
+
if (Object.keys(keep).length > 0) out[skCat] = keep;
|
|
524
603
|
}
|
|
525
|
-
|
|
526
|
-
else if (name.toLowerCase().endsWith(".json")) out.push(p);
|
|
604
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
527
605
|
}
|
|
528
|
-
|
|
606
|
+
if (cat === "ltl_rules") {
|
|
607
|
+
if (!Array.isArray(value)) {
|
|
608
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 ltl_rules \u5E94\u4E3A\u5BF9\u8C61\u6570\u7EC4 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u3002`);
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
const keep = value.filter((r) => r && typeof r === "object" && r.id);
|
|
612
|
+
if (keep.length !== value.length) {
|
|
613
|
+
console.warn(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 ltl_rules \u6709\u6761\u76EE\u7F3A id \u6216\u4E0D\u662F\u5BF9\u8C61 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u6761\u3002`);
|
|
614
|
+
}
|
|
615
|
+
return keep.length > 0 ? keep : null;
|
|
616
|
+
}
|
|
617
|
+
if (cat === "tools") {
|
|
618
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
619
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 tools \u5E94\u4E3A\u5BF9\u8C61\uFF08{\u5DE5\u5177\u540D: {actionClass, ...}}\uFF09\u2014\u2014 \u5DF2\u8DF3\u8FC7\u3002`);
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
const keep = {};
|
|
623
|
+
for (const [k, v] of Object.entries(value)) {
|
|
624
|
+
if (v && typeof v === "object" && !Array.isArray(v)) keep[k] = v;
|
|
625
|
+
else console.warn(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684 tools.${k} \u4E0D\u662F\u5BF9\u8C61 \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u6761\u3002`);
|
|
626
|
+
}
|
|
627
|
+
return Object.keys(keep).length > 0 ? keep : null;
|
|
628
|
+
}
|
|
629
|
+
return null;
|
|
529
630
|
}
|
|
530
|
-
function
|
|
531
|
-
const
|
|
532
|
-
const
|
|
533
|
-
|
|
631
|
+
function scanRulesFile(path6, kind) {
|
|
632
|
+
const out = {};
|
|
633
|
+
const label = kind === "override" ? "\u8986\u76D6" : "\u8FFD\u52A0";
|
|
634
|
+
let parsed;
|
|
635
|
+
try {
|
|
636
|
+
parsed = JSON.parse(readFileSync2(path6, "utf-8"));
|
|
637
|
+
} catch (e) {
|
|
638
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u635F\u574F/\u4E0D\u53EF\u8BFB\uFF0C\u5DF2\u8DF3\u8FC7\uFF08${kind} \u89C4\u5219\u4E0D\u751F\u6548\uFF09: ${e.message}`);
|
|
639
|
+
return out;
|
|
640
|
+
}
|
|
534
641
|
const classifyFlat = (r) => {
|
|
535
642
|
for (const [keys, cat] of EXTRA_FLAT_FIELD) {
|
|
536
643
|
if (r && typeof r === "object" && keys.every((k) => r[k])) return cat;
|
|
537
644
|
}
|
|
538
645
|
return null;
|
|
539
646
|
};
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
647
|
+
let pairs = [];
|
|
648
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
649
|
+
pairs = Object.entries(parsed);
|
|
650
|
+
} else if (Array.isArray(parsed)) {
|
|
651
|
+
let flatBad = 0;
|
|
652
|
+
for (const r of parsed) {
|
|
653
|
+
const cat = classifyFlat(r);
|
|
654
|
+
if (cat === null) {
|
|
655
|
+
flatBad += 1;
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
pairs.push([cat, [r]]);
|
|
659
|
+
}
|
|
660
|
+
if (flatBad > 0) {
|
|
661
|
+
console.warn(
|
|
662
|
+
`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u6709 ${flatBad} \u6761\u5E73\u94FA\u6761\u76EE\u65E0\u6CD5\u5F52\u7C7B\uFF08\u88F8 pattern \u4E0D\u5E26 risk_level/risk_type \u65F6\u533A\u5206\u4E0D\u4E86 L1 \u5371\u9669\u547D\u4EE4\u4E0E L2 \u6388\u6743\u7C7B\uFF09\u2014\u2014 \u8BF7\u6539\u7528\u5BF9\u8C61\u5F62\u5F0F\uFF1A{"\u7C7B\u522B\u952E": [\u6761\u76EE...]}\u3002`
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
} else {
|
|
666
|
+
console.error(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u9876\u5C42\u5E94\u662F\u5BF9\u8C61\uFF08\u7C7B\u522B\u952E\u2192\u6761\u76EE\u6570\u7EC4\uFF09\u6216\u6570\u7EC4\uFF0C\u5DF2\u8DF3\u8FC7\u3002`);
|
|
667
|
+
return out;
|
|
668
|
+
}
|
|
669
|
+
for (const [cat, rules] of pairs) {
|
|
670
|
+
if (String(cat).startsWith("_")) continue;
|
|
671
|
+
if (STRUCT_KEYS.includes(cat)) {
|
|
672
|
+
const got = scanStructEntry(cat, rules, path6);
|
|
673
|
+
if (got !== null) out[cat] = got;
|
|
546
674
|
continue;
|
|
547
675
|
}
|
|
548
|
-
|
|
549
|
-
if (
|
|
550
|
-
pairs = Object.entries(parsed);
|
|
551
|
-
} else if (Array.isArray(parsed)) {
|
|
552
|
-
let flatBad = 0;
|
|
553
|
-
for (const r of parsed) {
|
|
554
|
-
const cat = classifyFlat(r);
|
|
555
|
-
if (cat === null) {
|
|
556
|
-
flatBad += 1;
|
|
557
|
-
continue;
|
|
558
|
-
}
|
|
559
|
-
pairs.push([cat, [r]]);
|
|
560
|
-
}
|
|
561
|
-
if (flatBad > 0) {
|
|
562
|
-
console.warn(
|
|
563
|
-
`[AgentMoss] \u8FFD\u52A0\u89C4\u5219\u6587\u4EF6 ${p} \u6709 ${flatBad} \u6761\u5E73\u94FA\u6761\u76EE\u65E0\u6CD5\u5F52\u7C7B\uFF08\u88F8 pattern \u4E0D\u5E26 risk_level/risk_type \u65F6\u533A\u5206\u4E0D\u4E86 L1 \u5371\u9669\u547D\u4EE4\u4E0E L2 \u6388\u6743\u7C7B\uFF09\u2014\u2014 \u8BF7\u6539\u7528\u5BF9\u8C61\u5F62\u5F0F\uFF1A{"\u7C7B\u522B\u952E": [\u6761\u76EE...]}\u3002`
|
|
564
|
-
);
|
|
565
|
-
}
|
|
566
|
-
} else {
|
|
676
|
+
const required = EXTRA_CATEGORIES[cat];
|
|
677
|
+
if (!required) {
|
|
567
678
|
console.warn(
|
|
568
|
-
`[AgentMoss] \
|
|
679
|
+
`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u542B\u672A\u77E5\u7C7B\u522B\u952E "${cat}"\uFF08\u53EF\u7528\uFF1A${Object.keys(EXTRA_CATEGORIES).join(", ")}\uFF09\u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u7C7B\u522B\u3002`
|
|
680
|
+
);
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
if (rules && typeof rules === "object" && !Array.isArray(rules)) {
|
|
684
|
+
console.error(
|
|
685
|
+
`[AgentMoss] \u274C \u89C4\u5219\u6587\u4EF6 ${path6} \u7684\u7C7B\u522B "${cat}" \u662F**\u5BF9\u8C61**\uFF0C\u5E94\u4E3A**\u5BF9\u8C61\u6570\u7EC4** \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u7C7B\u522B\u3002
|
|
686
|
+
\u6B63\u786E\u5199\u6CD5: {"${cat}": [{"pattern": "...", "risk_level": "high", ...}]}
|
|
687
|
+
\u4F60\u5199\u7684\u50CF\u662F runtime config \u7684\u683C\u5F0F\uFF08{"category_enabled": ..., "rules": [...]}\uFF09\u2014\u2014\u90A3\u662F ~/.config/agentmoss/agent_moss_runtime.json \u7684\u683C\u5F0F\uFF0C\u4E0D\u80FD\u76F4\u63A5\u5F53\u89C4\u5219\u6587\u4EF6\u7528\u3002`
|
|
569
688
|
);
|
|
570
689
|
continue;
|
|
571
690
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
691
|
+
if (!Array.isArray(rules)) {
|
|
692
|
+
console.warn(`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7684\u7C7B\u522B "${cat}" \u5E94\u4E3A\u6570\u7EC4\uFF0C\u5B9E\u9645\u662F ${typeof rules} \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u7C7B\u522B\u3002`);
|
|
693
|
+
continue;
|
|
694
|
+
}
|
|
695
|
+
const items = [];
|
|
696
|
+
for (const r of rules) {
|
|
697
|
+
if (!r || typeof r !== "object" || !required.every((k) => r[k])) {
|
|
576
698
|
console.warn(
|
|
577
|
-
`[AgentMoss] \
|
|
699
|
+
`[AgentMoss] \u89C4\u5219\u6587\u4EF6 ${path6} \u7C7B\u522B ${cat} \u6709\u6761\u76EE\u7F3A\u5FC5\u9700\u5B57\u6BB5 [${required.join(", ")}] \u2014\u2014 \u5DF2\u8DF3\u8FC7\u8BE5\u6761\u3002`
|
|
578
700
|
);
|
|
579
701
|
continue;
|
|
580
702
|
}
|
|
581
|
-
|
|
582
|
-
|
|
703
|
+
items.push(r);
|
|
704
|
+
}
|
|
705
|
+
if (items.length > 0) out[cat] = (out[cat] || []).concat(items);
|
|
706
|
+
}
|
|
707
|
+
if (Object.keys(out).length > 0) {
|
|
708
|
+
console.info(
|
|
709
|
+
`[AgentMoss] \u5DF2\u52A0\u8F7D${label}\u89C4\u5219\u6587\u4EF6 ${path6}\uFF1A` + Object.entries(out).map(([k, v]) => `${k}\xD7${v.length}`).join(", ")
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
return out;
|
|
713
|
+
}
|
|
714
|
+
function scanRuleDirs() {
|
|
715
|
+
const override = {};
|
|
716
|
+
const extra = {};
|
|
717
|
+
const loaded = [];
|
|
718
|
+
const ignored = [];
|
|
719
|
+
for (const d of rulesDirs()) {
|
|
720
|
+
if (!existsSync(d)) continue;
|
|
721
|
+
let entries = [];
|
|
722
|
+
try {
|
|
723
|
+
entries = readdirSync(d).sort();
|
|
724
|
+
} catch {
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
for (const name of entries) {
|
|
728
|
+
const f = resolve(d, name);
|
|
729
|
+
try {
|
|
730
|
+
if (statSync(f).isDirectory()) continue;
|
|
731
|
+
} catch {
|
|
583
732
|
continue;
|
|
584
733
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
)
|
|
590
|
-
|
|
734
|
+
if (name === OVERRIDE_FILENAME) {
|
|
735
|
+
const got = scanRulesFile(f, "override");
|
|
736
|
+
if (Object.keys(got).length > 0) loaded.push(f);
|
|
737
|
+
for (const [cat, items] of Object.entries(got)) {
|
|
738
|
+
if (Array.isArray(items)) {
|
|
739
|
+
override[cat] = (override[cat] || []).concat(items);
|
|
740
|
+
} else {
|
|
741
|
+
override[cat] = { ...override[cat] || {}, ...items };
|
|
742
|
+
}
|
|
591
743
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
);
|
|
598
|
-
|
|
744
|
+
} else if (name === EXTRA_FILENAME) {
|
|
745
|
+
const got = scanRulesFile(f, "extra");
|
|
746
|
+
if (Object.values(got).some((v) => v.length > 0)) loaded.push(f);
|
|
747
|
+
for (const [cat, items] of Object.entries(got)) {
|
|
748
|
+
if (Array.isArray(items)) {
|
|
749
|
+
extra[cat] = (extra[cat] || []).concat(items);
|
|
750
|
+
} else {
|
|
751
|
+
extra[cat] = { ...extra[cat] || {}, ...items };
|
|
599
752
|
}
|
|
600
|
-
seenIds.set(rid, p);
|
|
601
753
|
}
|
|
602
|
-
|
|
603
|
-
|
|
754
|
+
} else if (name.toLowerCase().endsWith(".json")) {
|
|
755
|
+
ignored.push(f);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
if (Object.keys(override).length > 0 && identicalToBuiltin(override)) {
|
|
760
|
+
const key = "override-identical::" + Object.entries(override).map(([c, v]) => `${c}=${v.length}`).sort().join("|");
|
|
761
|
+
if (!warnedIgnored.has(key)) {
|
|
762
|
+
warnedIgnored.add(key);
|
|
763
|
+
console.warn(
|
|
764
|
+
`[AgentMoss] \u2139\uFE0F ${OVERRIDE_FILENAME} \u4E0E**\u5185\u7F6E\u89C4\u5219\u5B8C\u5168\u4E00\u81F4**\uFF08` + Object.entries(override).sort().map(([c, v]) => `${c}\xD7${v.length}`).join(", ") + `\uFF09\u2014\u2014 \u770B\u8D77\u6765\u662F\u6A21\u677F\u539F\u6837\u62F7\u8FDB\u6765\u6CA1\u6539\u3002
|
|
765
|
+
\u5B83\u73B0\u5728\u7684\u6548\u679C = \u5185\u7F6E\u89C4\u5219\uFF1B\u4F46**\u4EE5\u540E\u5347\u7EA7\u5185\u7F6E\u89C4\u5219\u4E0D\u4F1A\u53CD\u6620\u5230\u8FD9\u4EFD\u4E0A**\uFF08\u88AB\u9489\u4F4F\u4E86\uFF09\u3002
|
|
766
|
+
\u53EA\u60F3\u8FFD\u52A0\u89C4\u5219\uFF1A\u5220\u6389 ${OVERRIDE_FILENAME}\uFF0C\u6539\u7528 ${EXTRA_FILENAME}\u3002`
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
if (Object.keys(override).length > 0) {
|
|
771
|
+
const key = "override-summary::" + Object.entries(override).map(([c, v]) => `${c}=${v.length}`).sort().join("|");
|
|
772
|
+
if (!warnedIgnored.has(key)) {
|
|
773
|
+
warnedIgnored.add(key);
|
|
774
|
+
const l1 = loadL1();
|
|
775
|
+
const l2 = loadL2();
|
|
776
|
+
const builtinCounts = {
|
|
777
|
+
dangerous_commands: (l1.dangerous_commands || []).length,
|
|
778
|
+
injection_detection: (l1.injection_detection || []).length,
|
|
779
|
+
sensitive_path_access: (l1.sensitive_path_access || []).length,
|
|
780
|
+
intent_consistency: (l2.intent_consistency || []).length,
|
|
781
|
+
password_modify_consent: (l2.password_modify_consent || []).length,
|
|
782
|
+
user_deletion_consent: (l2.user_deletion_consent || []).length
|
|
783
|
+
};
|
|
784
|
+
console.warn(`[AgentMoss] \u26A0\uFE0F ${OVERRIDE_FILENAME} \u751F\u6548\uFF08**\u6574\u4EFD\u66FF\u6362**\uFF0C\u672A\u5217\u51FA\u7684\u5185\u7F6E\u89C4\u5219\u4E0D\u518D\u751F\u6548\uFF09\uFF1A`);
|
|
785
|
+
for (const [cat, items] of Object.entries(override).sort()) {
|
|
786
|
+
console.warn(` \u7C7B\u522B ${cat}\uFF1A\u5185\u7F6E ${builtinCounts[cat] ?? 0} \u6761 \u2192 \u4F60\u7684 ${items.length} \u6761`);
|
|
604
787
|
}
|
|
788
|
+
console.warn(` \u53EA\u60F3**\u8FFD\u52A0**\u89C4\u5219\u8BF7\u6539\u7528 ${EXTRA_FILENAME}\uFF1B\u8981\u6539\u67D0\u4E00\u6761\u5185\u7F6E\u89C4\u5219\uFF0C\u8BF7\u5148\u62F7\u8D1D\u5185\u7F6E\u6587\u4EF6\u518D\u6539\u3002`);
|
|
605
789
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
790
|
+
}
|
|
791
|
+
if (ignored.length > 0) {
|
|
792
|
+
const key = "ignored::" + ignored.join("|");
|
|
793
|
+
if (!warnedIgnored.has(key)) {
|
|
794
|
+
warnedIgnored.add(key);
|
|
795
|
+
console.warn(
|
|
796
|
+
`[AgentMoss] \u26A0\uFE0F \u89C4\u5219\u76EE\u5F55\u91CC\u6709 ${ignored.length} \u4E2A json **\u4E0D\u4F1A\u88AB\u8BFB\u53D6**\uFF08\u53EA\u6709 ${OVERRIDE_FILENAME} \u4E0E ${EXTRA_FILENAME} \u4F1A\u88AB\u8BFB\uFF09: ${ignored.join(", ")}
|
|
797
|
+
\u8981\u8986\u76D6\u5185\u7F6E\u89C4\u5219 \u2192 \u5185\u5BB9\u653E\u8FDB ${OVERRIDE_FILENAME}\uFF1B\u8981\u8FFD\u52A0 \u2192 \u653E\u8FDB ${EXTRA_FILENAME}\u3002`
|
|
798
|
+
);
|
|
609
799
|
}
|
|
610
800
|
}
|
|
611
|
-
return {
|
|
801
|
+
return { override, extra, loaded, ignored };
|
|
612
802
|
}
|
|
613
|
-
function
|
|
614
|
-
|
|
615
|
-
const dirs =
|
|
616
|
-
|
|
803
|
+
function rulesCached() {
|
|
804
|
+
if (!isHotReloadEnabled() && _RULES_CACHE) return _RULES_CACHE;
|
|
805
|
+
const dirs = rulesDirs();
|
|
806
|
+
const sig = RULES_DIR_ENVS.map((e) => `${e}=${process.env[e] || ""}`).join("|") + "|" + dirs.join("|");
|
|
807
|
+
const files = [];
|
|
617
808
|
for (const d of dirs) {
|
|
618
|
-
if (existsSync(d))
|
|
619
|
-
|
|
809
|
+
if (!existsSync(d)) continue;
|
|
810
|
+
try {
|
|
811
|
+
for (const name of readdirSync(d).sort()) {
|
|
812
|
+
if (name.toLowerCase().endsWith(".json")) files.push(resolve(d, name));
|
|
813
|
+
}
|
|
814
|
+
} catch {
|
|
815
|
+
}
|
|
620
816
|
}
|
|
621
|
-
|
|
622
|
-
const fp = files.map((p) => {
|
|
817
|
+
const fp = JSON.stringify(files.map((p) => {
|
|
623
818
|
try {
|
|
624
819
|
const st = statSync(p);
|
|
625
820
|
return [p, st.mtimeMs, st.size];
|
|
626
821
|
} catch {
|
|
627
822
|
return [p, -1, -1];
|
|
628
823
|
}
|
|
629
|
-
});
|
|
630
|
-
if (
|
|
631
|
-
|
|
824
|
+
}));
|
|
825
|
+
if (_RULES_CACHE && _RULES_CACHE.sig === sig && _RULES_CACHE.fp === fp) return _RULES_CACHE;
|
|
826
|
+
_RULES_CACHE = { sig, fp, ...scanRuleDirs() };
|
|
827
|
+
return _RULES_CACHE;
|
|
828
|
+
}
|
|
829
|
+
function identicalToBuiltin(override) {
|
|
830
|
+
const l1 = loadL1();
|
|
831
|
+
const l2 = loadL2();
|
|
832
|
+
for (const [cat, items] of Object.entries(override)) {
|
|
833
|
+
const builtin = l1[cat] ?? l2[cat];
|
|
834
|
+
if (!Array.isArray(builtin) || builtin.length !== items.length) return false;
|
|
835
|
+
if (JSON.stringify(builtin) !== JSON.stringify(items)) return false;
|
|
632
836
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
837
|
+
return Object.keys(override).length > 0;
|
|
838
|
+
}
|
|
839
|
+
function loadOverrideRules() {
|
|
840
|
+
if (isBuiltinOnly()) return {};
|
|
841
|
+
return rulesCached().override;
|
|
636
842
|
}
|
|
637
|
-
function
|
|
638
|
-
|
|
639
|
-
return
|
|
843
|
+
function loadExtraRules() {
|
|
844
|
+
if (isBuiltinOnly()) return {};
|
|
845
|
+
return rulesCached().extra;
|
|
846
|
+
}
|
|
847
|
+
function ruleFilesLoaded() {
|
|
848
|
+
return rulesCached().loaded;
|
|
640
849
|
}
|
|
641
|
-
|
|
850
|
+
function ignoredRuleFiles() {
|
|
851
|
+
return rulesCached().ignored;
|
|
852
|
+
}
|
|
853
|
+
var __filename2, __dirname2, OVERRIDE_FILENAME, EXTRA_FILENAME, RULES_DIR_ENVS, RULES_DIR_CANDS, frozenJsonCache, SCENARIO_LIST_KEYS, EXTRA_CATEGORIES, STRUCT_KEYS, EXTRA_FLAT_FIELD, _RULES_CACHE, warnedIgnored;
|
|
642
854
|
var init_rules_loader = __esm({
|
|
643
855
|
"src/infra/rules-loader.ts"() {
|
|
644
856
|
"use strict";
|
|
645
857
|
init_esm_shims();
|
|
646
858
|
init_paths();
|
|
859
|
+
init_rules_mode();
|
|
647
860
|
init_runtime_config_bridge();
|
|
648
861
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
649
862
|
__dirname2 = dirname(__filename2);
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
"l3_rules.json": [],
|
|
654
|
-
"mc_rules.json": ["rules"],
|
|
655
|
-
"tool_registry.json": ["tools"]
|
|
656
|
-
};
|
|
657
|
-
warnedOverride = /* @__PURE__ */ new Set();
|
|
863
|
+
OVERRIDE_FILENAME = "override.json";
|
|
864
|
+
EXTRA_FILENAME = "extra.json";
|
|
865
|
+
RULES_DIR_ENVS = ["AGENT_MOSS_EXTRA_RULES_DIR", "AGENT_MOSS_OVERRIDE_RULES_DIR"];
|
|
658
866
|
RULES_DIR_CANDS = (() => {
|
|
659
867
|
const cands = [
|
|
660
|
-
//
|
|
661
|
-
//
|
|
662
|
-
// mc_rules.json 放到 <data_root>/rules/ 即可覆盖内置默认,无需改源码。
|
|
663
|
-
resolve(dataRootDir(), "rules"),
|
|
868
|
+
// 注:`<data_root>/rules/` 不再是"按内置文件名覆盖"的查找位 —— 2026-10 起
|
|
869
|
+
// 规则目录只认 override.json / extra.json(见 rulesDirs())。
|
|
664
870
|
// dist: __dirname=dist/ → dist/rules;__dirname=dist/engine/ → dist/engine/../rules=dist/rules
|
|
665
871
|
resolve(__dirname2, "../rules"),
|
|
666
872
|
resolve(__dirname2, "rules"),
|
|
@@ -673,6 +879,8 @@ var init_rules_loader = __esm({
|
|
|
673
879
|
];
|
|
674
880
|
return cands.filter((c) => existsSync(c));
|
|
675
881
|
})();
|
|
882
|
+
frozenJsonCache = /* @__PURE__ */ new Map();
|
|
883
|
+
SCENARIO_LIST_KEYS = ["extra_command_patterns", "extra_injection_keywords", "extra_sensitive_paths"];
|
|
676
884
|
EXTRA_CATEGORIES = {
|
|
677
885
|
dangerous_commands: ["pattern"],
|
|
678
886
|
injection_detection: ["keyword", "lang"],
|
|
@@ -681,6 +889,7 @@ var init_rules_loader = __esm({
|
|
|
681
889
|
password_modify_consent: ["pattern"],
|
|
682
890
|
user_deletion_consent: ["pattern"]
|
|
683
891
|
};
|
|
892
|
+
STRUCT_KEYS = ["skills", "ltl_rules", "tools"];
|
|
684
893
|
EXTRA_FLAT_FIELD = [
|
|
685
894
|
[["path"], "sensitive_path_access"],
|
|
686
895
|
[["keyword", "lang"], "injection_detection"],
|
|
@@ -688,7 +897,8 @@ var init_rules_loader = __esm({
|
|
|
688
897
|
[["pattern", "risk_level"], "dangerous_commands"],
|
|
689
898
|
[["pattern", "risk_type"], "dangerous_commands"]
|
|
690
899
|
];
|
|
691
|
-
|
|
900
|
+
_RULES_CACHE = null;
|
|
901
|
+
warnedIgnored = /* @__PURE__ */ new Set();
|
|
692
902
|
}
|
|
693
903
|
});
|
|
694
904
|
|
|
@@ -1752,6 +1962,7 @@ function userSkillsDir() {
|
|
|
1752
1962
|
return resolve3(getDataRoot(), "agent_moss_skills");
|
|
1753
1963
|
}
|
|
1754
1964
|
function runtimeCustomSkillKeywords() {
|
|
1965
|
+
if (isRuntimeConfigDisabled()) return {};
|
|
1755
1966
|
try {
|
|
1756
1967
|
const rt = runtimeConfigBridge.current?.loadRuntimeConfig?.();
|
|
1757
1968
|
if (!rt) return {};
|
|
@@ -1769,6 +1980,7 @@ function runtimeCustomSkillKeywords() {
|
|
|
1769
1980
|
}
|
|
1770
1981
|
}
|
|
1771
1982
|
function loadUserSkill(name) {
|
|
1983
|
+
if (isRuntimeConfigDisabled()) return "";
|
|
1772
1984
|
if (userSkillCache[name] !== void 0) return userSkillCache[name];
|
|
1773
1985
|
try {
|
|
1774
1986
|
const p = resolve3(userSkillsDir(), `${name}.md`);
|
|
@@ -1837,6 +2049,7 @@ var init_skill_loader = __esm({
|
|
|
1837
2049
|
init_esm_shims();
|
|
1838
2050
|
init_rules_loader();
|
|
1839
2051
|
init_paths();
|
|
2052
|
+
init_rules_mode();
|
|
1840
2053
|
init_asset_dir();
|
|
1841
2054
|
init_runtime_config_bridge();
|
|
1842
2055
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
@@ -1897,6 +2110,7 @@ __export(runtime_config_exports, {
|
|
|
1897
2110
|
getEnabledScenarioExtensions: () => getEnabledScenarioExtensions,
|
|
1898
2111
|
getEnvOverrides: () => getEnvOverrides,
|
|
1899
2112
|
getProfileOrDetect: () => getProfileOrDetect,
|
|
2113
|
+
getRuleSourceFingerprint: () => getRuleSourceFingerprint,
|
|
1900
2114
|
getRuntimeConfigPath: () => getRuntimeConfigPath,
|
|
1901
2115
|
getScenarioRules: () => getScenarioRules,
|
|
1902
2116
|
hasRuntimeConfig: () => hasRuntimeConfig,
|
|
@@ -1931,7 +2145,22 @@ function getRuntimeConfigPath() {
|
|
|
1931
2145
|
if (envPath) return envPath;
|
|
1932
2146
|
return RUNTIME_CONFIG_PATH;
|
|
1933
2147
|
}
|
|
2148
|
+
function getRuleSourceFingerprint() {
|
|
2149
|
+
if (isRuntimeConfigDisabled()) return `mode:${getRulesMode()}`;
|
|
2150
|
+
const p = getRuntimeConfigPath();
|
|
2151
|
+
try {
|
|
2152
|
+
const st = statSync2(p);
|
|
2153
|
+
return `${p}|${st.mtimeMs}|${st.size}`;
|
|
2154
|
+
} catch {
|
|
2155
|
+
return `${p}|absent`;
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
1934
2158
|
function saveRuntimeConfig(config) {
|
|
2159
|
+
if (isRuntimeConfigDisabled()) {
|
|
2160
|
+
throw new Error(
|
|
2161
|
+
`\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF1A\u4FEE\u6539\u89C4\u5219\u4E0D\u4F1A\u751F\u6548\uFF0C\u8BF7\u5207\u6362\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u5E76\u91CD\u542F\u670D\u52A1\u3002`
|
|
2162
|
+
);
|
|
2163
|
+
}
|
|
1935
2164
|
const p = getRuntimeConfigPath();
|
|
1936
2165
|
const dir = p.substring(0, p.lastIndexOf("/"));
|
|
1937
2166
|
if (dir && !existsSync5(dir)) mkdirSync(dir, { recursive: true });
|
|
@@ -1955,6 +2184,18 @@ function invalidateRuntimeConfigCache() {
|
|
|
1955
2184
|
hasRuntimeConfigCache.clear();
|
|
1956
2185
|
}
|
|
1957
2186
|
function loadRuntimeConfig() {
|
|
2187
|
+
if (isRuntimeConfigDisabled()) {
|
|
2188
|
+
return {
|
|
2189
|
+
version: 1,
|
|
2190
|
+
layers: { ...DEFAULT_LAYERS },
|
|
2191
|
+
L1_rules: {},
|
|
2192
|
+
L2_rules: {},
|
|
2193
|
+
L3_skills: {},
|
|
2194
|
+
L2_2: {},
|
|
2195
|
+
agent_rules: {},
|
|
2196
|
+
scenario_rules: {}
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
1958
2199
|
const p = getRuntimeConfigPath();
|
|
1959
2200
|
if (!existsSync5(p)) {
|
|
1960
2201
|
const seeded = mergeConfig({}, generateSourceDefaults());
|
|
@@ -2001,6 +2242,7 @@ function loadRuntimeConfig() {
|
|
|
2001
2242
|
function isLayerEnabled(layerKey) {
|
|
2002
2243
|
const disableKey = LAYER_DISABLE_ENV[layerKey];
|
|
2003
2244
|
if (disableKey && process.env[disableKey] === "1") return false;
|
|
2245
|
+
if (isRuntimeConfigDisabled()) return true;
|
|
2004
2246
|
const runtime = loadRuntimeConfig();
|
|
2005
2247
|
const layers = runtime.layers || {};
|
|
2006
2248
|
if (layerKey in layers) return !!layers[layerKey];
|
|
@@ -2046,18 +2288,34 @@ function _mergeUserCustom(primary, runtime, layers, key) {
|
|
|
2046
2288
|
const extra = _userCustomRules(runtime, layers, key);
|
|
2047
2289
|
return extra.length > 0 ? [...primary, ...extra] : [...primary];
|
|
2048
2290
|
}
|
|
2049
|
-
function
|
|
2291
|
+
function _dedup(rules, category) {
|
|
2292
|
+
const keys = _DEDUP_KEYS[category];
|
|
2293
|
+
if (!keys) return rules;
|
|
2294
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2295
|
+
const out = [];
|
|
2296
|
+
for (const r of rules) {
|
|
2297
|
+
const k = keys.map((x) => String(r?.[x] ?? "")).join("\0");
|
|
2298
|
+
if (seen.has(k)) continue;
|
|
2299
|
+
seen.add(k);
|
|
2300
|
+
out.push(r);
|
|
2301
|
+
}
|
|
2302
|
+
return out;
|
|
2303
|
+
}
|
|
2304
|
+
function _mergeRulesFromDirs(primary, category) {
|
|
2305
|
+
let base = [...primary];
|
|
2050
2306
|
try {
|
|
2307
|
+
const override = loadOverrideRules()[category];
|
|
2308
|
+
if (override !== void 0 && override !== null) base = [...override];
|
|
2051
2309
|
const extra = loadExtraRules()[category] || [];
|
|
2052
|
-
return
|
|
2310
|
+
return _dedup([...base, ...extra], category);
|
|
2053
2311
|
} catch {
|
|
2054
|
-
return
|
|
2312
|
+
return base;
|
|
2055
2313
|
}
|
|
2056
2314
|
}
|
|
2057
2315
|
function getEnabledL1CommandPatterns(runtime, osType) {
|
|
2058
2316
|
const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
|
|
2059
2317
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2060
|
-
rules =
|
|
2318
|
+
rules = _mergeRulesFromDirs(rules, "dangerous_commands");
|
|
2061
2319
|
rules = _mergeUserCustom(rules, runtime, "L1_rules", "pattern");
|
|
2062
2320
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2063
2321
|
return rules.map((r) => ({
|
|
@@ -2073,7 +2331,7 @@ function getEnabledL2SensitivePaths(runtime, osType) {
|
|
|
2073
2331
|
cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
|
|
2074
2332
|
}
|
|
2075
2333
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2076
|
-
rules =
|
|
2334
|
+
rules = _mergeRulesFromDirs(rules, "sensitive_path_access");
|
|
2077
2335
|
rules = _mergeUserCustom(rules, runtime, ["L1_rules", "L2_rules"], "path");
|
|
2078
2336
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2079
2337
|
return rules.map((r) => {
|
|
@@ -2085,6 +2343,7 @@ function getEnabledL2SensitivePaths(runtime, osType) {
|
|
|
2085
2343
|
});
|
|
2086
2344
|
}
|
|
2087
2345
|
function hasRuntimeConfig() {
|
|
2346
|
+
if (isRuntimeConfigDisabled()) return false;
|
|
2088
2347
|
const p = getRuntimeConfigPath();
|
|
2089
2348
|
let key;
|
|
2090
2349
|
try {
|
|
@@ -2093,8 +2352,8 @@ function hasRuntimeConfig() {
|
|
|
2093
2352
|
} catch {
|
|
2094
2353
|
return false;
|
|
2095
2354
|
}
|
|
2096
|
-
const
|
|
2097
|
-
if (
|
|
2355
|
+
const cached2 = hasRuntimeConfigCache.get(key);
|
|
2356
|
+
if (cached2 !== void 0) return cached2;
|
|
2098
2357
|
let ok = false;
|
|
2099
2358
|
try {
|
|
2100
2359
|
JSON.parse(readFileSync4(p, "utf-8"));
|
|
@@ -2458,25 +2717,11 @@ function generateSourceDefaults() {
|
|
|
2458
2717
|
};
|
|
2459
2718
|
}
|
|
2460
2719
|
function _ltlRulesToRuntime() {
|
|
2461
|
-
const cands = [
|
|
2462
|
-
getRuntimeConfigDir() && join3(getRuntimeConfigDir(), "rules"),
|
|
2463
|
-
join3(__dirname4, "../rules"),
|
|
2464
|
-
join3(__dirname4, "rules"),
|
|
2465
|
-
join3(__dirname4, "../../../agent_moss/rules"),
|
|
2466
|
-
join3(__dirname4, "../../../../agent_moss/rules"),
|
|
2467
|
-
join3(process.cwd(), "agent_moss/rules")
|
|
2468
|
-
].filter(Boolean);
|
|
2469
2720
|
let mc = {};
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
mc = JSON.parse(readFileSync4(p, "utf-8")) || {};
|
|
2475
|
-
} catch {
|
|
2476
|
-
mc = {};
|
|
2477
|
-
}
|
|
2478
|
-
break;
|
|
2479
|
-
}
|
|
2721
|
+
try {
|
|
2722
|
+
mc = loadMCRaw();
|
|
2723
|
+
} catch {
|
|
2724
|
+
mc = {};
|
|
2480
2725
|
}
|
|
2481
2726
|
const rules = mc.rules || [];
|
|
2482
2727
|
return {
|
|
@@ -2705,7 +2950,7 @@ function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
|
|
|
2705
2950
|
function getEnabledL1InjectionKeywords(runtime, osType) {
|
|
2706
2951
|
const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
|
|
2707
2952
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2708
|
-
rules =
|
|
2953
|
+
rules = _mergeRulesFromDirs(rules, "injection_detection");
|
|
2709
2954
|
rules = _mergeUserCustom(rules, runtime, "L1_rules", "keyword");
|
|
2710
2955
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2711
2956
|
return rules.map((r) => ({
|
|
@@ -2717,7 +2962,7 @@ function getEnabledL1InjectionKeywords(runtime, osType) {
|
|
|
2717
2962
|
function getEnabledL2IntentPatterns(runtime, osType) {
|
|
2718
2963
|
const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
|
|
2719
2964
|
let rules = filterEnabledRules(cat.rules || [], cat.category_enabled);
|
|
2720
|
-
rules =
|
|
2965
|
+
rules = _mergeRulesFromDirs(rules, "intent_consistency");
|
|
2721
2966
|
rules = _mergeUserCustom(rules, runtime, "L2_rules", "intent_keywords");
|
|
2722
2967
|
if (osType) rules = _filterRulesByOs(rules, osType);
|
|
2723
2968
|
return rules.map((r) => ({
|
|
@@ -2729,7 +2974,7 @@ function getEnabledL2IntentPatterns(runtime, osType) {
|
|
|
2729
2974
|
function getEnabledL2PasswordPatterns(runtime) {
|
|
2730
2975
|
const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
|
|
2731
2976
|
const rules = _mergeUserCustom(
|
|
2732
|
-
|
|
2977
|
+
_mergeRulesFromDirs(filterEnabledRules(cat.rules || [], cat.category_enabled), "password_modify_consent"),
|
|
2733
2978
|
runtime,
|
|
2734
2979
|
"L2_rules",
|
|
2735
2980
|
"pattern"
|
|
@@ -2739,7 +2984,7 @@ function getEnabledL2PasswordPatterns(runtime) {
|
|
|
2739
2984
|
function getEnabledL2UserDeletionPatterns(runtime) {
|
|
2740
2985
|
const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
|
|
2741
2986
|
const rules = _mergeUserCustom(
|
|
2742
|
-
|
|
2987
|
+
_mergeRulesFromDirs(filterEnabledRules(cat.rules || [], cat.category_enabled), "user_deletion_consent"),
|
|
2743
2988
|
runtime,
|
|
2744
2989
|
"L2_rules",
|
|
2745
2990
|
"pattern"
|
|
@@ -2776,7 +3021,7 @@ function getEnabledL1CommandPatternsOrDefault(runtime, osType) {
|
|
|
2776
3021
|
return getEnabledL1CommandPatterns(runtime, osType);
|
|
2777
3022
|
}
|
|
2778
3023
|
return _mergeUserCustom(
|
|
2779
|
-
|
|
3024
|
+
_mergeRulesFromDirs(_filterDefaultsByOs(_l1CommandPatternsToRules(), osType), "dangerous_commands"),
|
|
2780
3025
|
runtime,
|
|
2781
3026
|
"L1_rules",
|
|
2782
3027
|
"pattern"
|
|
@@ -2788,7 +3033,7 @@ function getEnabledL1InjectionKeywordsOrDefault(runtime, osType) {
|
|
|
2788
3033
|
return getEnabledL1InjectionKeywords(runtime, osType);
|
|
2789
3034
|
}
|
|
2790
3035
|
return _mergeUserCustom(
|
|
2791
|
-
|
|
3036
|
+
_mergeRulesFromDirs(_l1InjectionKeywordsToRules(), "injection_detection"),
|
|
2792
3037
|
runtime,
|
|
2793
3038
|
"L1_rules",
|
|
2794
3039
|
"keyword"
|
|
@@ -2800,7 +3045,7 @@ function getEnabledL2SensitivePathsOrDefault(runtime, osType) {
|
|
|
2800
3045
|
return getEnabledL2SensitivePaths(runtime, osType);
|
|
2801
3046
|
}
|
|
2802
3047
|
return _mergeUserCustom(
|
|
2803
|
-
|
|
3048
|
+
_mergeRulesFromDirs(_filterDefaultsByOs(_l2SensitivePathsToRules(), osType), "sensitive_path_access"),
|
|
2804
3049
|
runtime,
|
|
2805
3050
|
["L1_rules", "L2_rules"],
|
|
2806
3051
|
"path"
|
|
@@ -2812,7 +3057,7 @@ function getEnabledL2IntentPatternsOrDefault(runtime, osType) {
|
|
|
2812
3057
|
return getEnabledL2IntentPatterns(runtime, osType);
|
|
2813
3058
|
}
|
|
2814
3059
|
return _mergeUserCustom(
|
|
2815
|
-
|
|
3060
|
+
_mergeRulesFromDirs(_filterDefaultsByOs(_l2IntentPatternsToRules(), osType), "intent_consistency"),
|
|
2816
3061
|
runtime,
|
|
2817
3062
|
"L2_rules",
|
|
2818
3063
|
"intent_keywords"
|
|
@@ -2988,6 +3233,11 @@ function deleteCustomRule(layer, category, ruleId) {
|
|
|
2988
3233
|
}
|
|
2989
3234
|
function addCustomSkill(skillId, category, keywords, content) {
|
|
2990
3235
|
_validateSkillId(skillId);
|
|
3236
|
+
if (isRuntimeConfigDisabled()) {
|
|
3237
|
+
throw new Error(
|
|
3238
|
+
`\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF0C\u4E0D\u5141\u8BB8\u65B0\u589E\u81EA\u5B9A\u4E49 skill\u3002`
|
|
3239
|
+
);
|
|
3240
|
+
}
|
|
2991
3241
|
const runtime = loadRuntimeConfig();
|
|
2992
3242
|
const skillsDir = join3(getRuntimeConfigDir(), "agent_moss_skills");
|
|
2993
3243
|
try {
|
|
@@ -3203,7 +3453,7 @@ function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
|
|
|
3203
3453
|
saveRuntimeConfig(runtime);
|
|
3204
3454
|
return runtime;
|
|
3205
3455
|
}
|
|
3206
|
-
var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, runtimeConfigCache, hasRuntimeConfigCache, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, _effectiveCache, SKILL_ID_RE;
|
|
3456
|
+
var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, runtimeConfigCache, _DEDUP_KEYS, hasRuntimeConfigCache, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, _effectiveCache, SKILL_ID_RE;
|
|
3207
3457
|
var init_runtime_config = __esm({
|
|
3208
3458
|
"src/infra/runtime-config.ts"() {
|
|
3209
3459
|
"use strict";
|
|
@@ -3214,6 +3464,7 @@ var init_runtime_config = __esm({
|
|
|
3214
3464
|
init_skill_loader();
|
|
3215
3465
|
init_rules_loader();
|
|
3216
3466
|
init_runtime_config_bridge();
|
|
3467
|
+
init_rules_mode();
|
|
3217
3468
|
__dirname4 = dirname3(fileURLToPath4(import.meta.url));
|
|
3218
3469
|
ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
|
|
3219
3470
|
RUNTIME_CONFIG_DIR = runtimeDir();
|
|
@@ -3236,6 +3487,14 @@ var init_runtime_config = __esm({
|
|
|
3236
3487
|
deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
|
|
3237
3488
|
};
|
|
3238
3489
|
runtimeConfigCache = null;
|
|
3490
|
+
_DEDUP_KEYS = {
|
|
3491
|
+
dangerous_commands: ["pattern"],
|
|
3492
|
+
password_modify_consent: ["pattern"],
|
|
3493
|
+
user_deletion_consent: ["pattern"],
|
|
3494
|
+
sensitive_path_access: ["path"],
|
|
3495
|
+
injection_detection: ["keyword", "lang"],
|
|
3496
|
+
intent_consistency: ["intent_keywords", "reason"]
|
|
3497
|
+
};
|
|
3239
3498
|
hasRuntimeConfigCache = /* @__PURE__ */ new Map();
|
|
3240
3499
|
_AGENTS_DIR_PROD = join3(__dirname4, "agents");
|
|
3241
3500
|
_AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
|
|
@@ -3261,6 +3520,8 @@ var init_runtime_config = __esm({
|
|
|
3261
3520
|
var config_exports = {};
|
|
3262
3521
|
__export(config_exports, {
|
|
3263
3522
|
detectProvider: () => detectProvider,
|
|
3523
|
+
getAgentMossSetting: () => getAgentMossSetting,
|
|
3524
|
+
getCacheOptions: () => getCacheOptions,
|
|
3264
3525
|
getCustomRules: () => getCustomRules,
|
|
3265
3526
|
getLLMConfig: () => getLLMConfig,
|
|
3266
3527
|
getLLMFailMode: () => getLLMFailMode,
|
|
@@ -3272,7 +3533,10 @@ __export(config_exports, {
|
|
|
3272
3533
|
isHeuristicEnabled: () => isHeuristicEnabled,
|
|
3273
3534
|
isLLMAvailable: () => isLLMAvailable,
|
|
3274
3535
|
isLogicRulesEnabled: () => isLogicRulesEnabled,
|
|
3536
|
+
isPolicyGenEnabled: () => isPolicyGenEnabled,
|
|
3537
|
+
isPolicySaveEnabled: () => isPolicySaveEnabled,
|
|
3275
3538
|
probeLLMConnection: () => probeLLMConnection,
|
|
3539
|
+
reloadCustomRules: () => reloadCustomRules,
|
|
3276
3540
|
updateLLMConfig: () => updateLLMConfig,
|
|
3277
3541
|
userRuleVariantPattern: () => userRuleVariantPattern,
|
|
3278
3542
|
wildcardToRegExp: () => wildcardToRegExp
|
|
@@ -3294,8 +3558,10 @@ function resolveConfigPath() {
|
|
|
3294
3558
|
const userConfig = path2.join(getConfigDir(), "config.json");
|
|
3295
3559
|
if (fs2.existsSync(userConfig)) return userConfig;
|
|
3296
3560
|
const defaultCandidates = [
|
|
3297
|
-
path2.join(
|
|
3298
|
-
path2.join(
|
|
3561
|
+
path2.join(__configModuleDir, "config.json.example"),
|
|
3562
|
+
path2.join(__configModuleDir, "..", "config.json.example"),
|
|
3563
|
+
path2.join(__configModuleDir, "..", "..", "agent_moss", "infra", "config.json.example"),
|
|
3564
|
+
path2.join(__configModuleDir, "..", "..", "..", "agent_moss", "infra", "config.json.example")
|
|
3299
3565
|
];
|
|
3300
3566
|
const defaultConfig = defaultCandidates.find((p) => fs2.existsSync(p));
|
|
3301
3567
|
if (defaultConfig) {
|
|
@@ -3304,8 +3570,8 @@ function resolveConfigPath() {
|
|
|
3304
3570
|
const pkgConfig = loadConfigFile(defaultConfig);
|
|
3305
3571
|
const safeConfig = pkgConfig ? { ...pkgConfig, llm: { ...pkgConfig.llm, api_key: "" } } : {};
|
|
3306
3572
|
fs2.writeFileSync(userConfig, JSON.stringify(safeConfig, null, 4), "utf-8");
|
|
3307
|
-
console.
|
|
3308
|
-
console.
|
|
3573
|
+
console.error(`[AgentMoss] \u5DF2\u521B\u5EFA\u7528\u6237\u914D\u7F6E\u6587\u4EF6: ${userConfig}`);
|
|
3574
|
+
console.error(`[AgentMoss] \u8BF7\u7F16\u8F91\u8BE5\u6587\u4EF6\u914D\u7F6E\u4F60\u7684 LLM API Key`);
|
|
3309
3575
|
}
|
|
3310
3576
|
return userConfig;
|
|
3311
3577
|
}
|
|
@@ -3351,17 +3617,19 @@ function wildcardToRegExp(pattern) {
|
|
|
3351
3617
|
}
|
|
3352
3618
|
function loadCustomRules() {
|
|
3353
3619
|
const rules = [];
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3620
|
+
if (!isBuiltinOnly()) {
|
|
3621
|
+
try {
|
|
3622
|
+
const raw = process.env.AGENT_MOSS_CUSTOM_RULES;
|
|
3623
|
+
if (raw) {
|
|
3624
|
+
const parsed = JSON.parse(raw);
|
|
3625
|
+
if (Array.isArray(parsed)) {
|
|
3626
|
+
for (const r of parsed) {
|
|
3627
|
+
if (r && r.pattern && r.action) rules.push({ pattern: r.pattern, action: r.action, severity: r.severity || "high" });
|
|
3628
|
+
}
|
|
3361
3629
|
}
|
|
3362
3630
|
}
|
|
3631
|
+
} catch {
|
|
3363
3632
|
}
|
|
3364
|
-
} catch {
|
|
3365
3633
|
}
|
|
3366
3634
|
for (const rulesPath of USER_RULES_PATHS) {
|
|
3367
3635
|
try {
|
|
@@ -3390,6 +3658,9 @@ function userRuleVariantPattern(pattern) {
|
|
|
3390
3658
|
}
|
|
3391
3659
|
return null;
|
|
3392
3660
|
}
|
|
3661
|
+
function reloadCustomRules() {
|
|
3662
|
+
customRules = loadCustomRules();
|
|
3663
|
+
}
|
|
3393
3664
|
function getLLMConfig() {
|
|
3394
3665
|
return llmConfig;
|
|
3395
3666
|
}
|
|
@@ -3477,7 +3748,33 @@ function layerEnabledOrDefault(key) {
|
|
|
3477
3748
|
}
|
|
3478
3749
|
}
|
|
3479
3750
|
function isBrainEnabled() {
|
|
3480
|
-
|
|
3751
|
+
if (isRuntimeConfigDisabled()) return false;
|
|
3752
|
+
return getAgentMossSetting("AGENT_MOSS_BRAIN_ENABLED", "") === "1";
|
|
3753
|
+
}
|
|
3754
|
+
function getAgentMossSetting(key, def = "") {
|
|
3755
|
+
const envValue = process.env[key];
|
|
3756
|
+
if (envValue !== void 0) return envValue;
|
|
3757
|
+
const cfg = loadConfigFile(resolveConfigPath());
|
|
3758
|
+
if (cfg && key in cfg) return String(cfg[key]);
|
|
3759
|
+
return def;
|
|
3760
|
+
}
|
|
3761
|
+
function isPolicyGenEnabled() {
|
|
3762
|
+
return getAgentMossSetting("AGENT_MOSS_ENABLE_POLICY_GEN", "") === "1";
|
|
3763
|
+
}
|
|
3764
|
+
function getCacheOptions() {
|
|
3765
|
+
let enabled = true;
|
|
3766
|
+
let maxSize = 1e3;
|
|
3767
|
+
const cfg = loadConfigFile(resolveConfigPath());
|
|
3768
|
+
const cache3 = cfg?.cache;
|
|
3769
|
+
if (cache3 && typeof cache3 === "object") {
|
|
3770
|
+
if (typeof cache3.enabled === "boolean") enabled = cache3.enabled;
|
|
3771
|
+
if (typeof cache3.max_size === "number" && cache3.max_size > 0) maxSize = cache3.max_size;
|
|
3772
|
+
}
|
|
3773
|
+
if (process.env.AGENT_MOSS_DISABLE_CACHE === "1") enabled = false;
|
|
3774
|
+
return { enabled, maxSize };
|
|
3775
|
+
}
|
|
3776
|
+
function isPolicySaveEnabled() {
|
|
3777
|
+
return getAgentMossSetting("AGENT_MOSS_SAVE_POLICY", "1") !== "0";
|
|
3481
3778
|
}
|
|
3482
3779
|
function isLogicRulesEnabled() {
|
|
3483
3780
|
if (process.env.AGENT_MOSS_DISABLE_LOGIC_RULES === "1") return false;
|
|
@@ -3486,13 +3783,21 @@ function isLogicRulesEnabled() {
|
|
|
3486
3783
|
function isFormalObserveMode() {
|
|
3487
3784
|
return process.env.AGENT_MOSS_FORMAL_OBSERVE === "1";
|
|
3488
3785
|
}
|
|
3489
|
-
var timeoutConfig, retryConfig, llmConfig,
|
|
3786
|
+
var __configModuleDir, timeoutConfig, retryConfig, llmConfig, USER_RULES_PATHS, customRules, PROVIDER_HEADERS;
|
|
3490
3787
|
var init_config = __esm({
|
|
3491
3788
|
"src/config.ts"() {
|
|
3492
3789
|
"use strict";
|
|
3493
3790
|
init_esm_shims();
|
|
3494
3791
|
init_paths();
|
|
3495
3792
|
init_runtime_config();
|
|
3793
|
+
init_rules_mode();
|
|
3794
|
+
__configModuleDir = (() => {
|
|
3795
|
+
try {
|
|
3796
|
+
return path2.dirname(fileURLToPath5(import.meta.url));
|
|
3797
|
+
} catch {
|
|
3798
|
+
return typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
3799
|
+
}
|
|
3800
|
+
})();
|
|
3496
3801
|
llmConfig = (() => {
|
|
3497
3802
|
const configPath = resolveConfigPath();
|
|
3498
3803
|
const fileConfig = fs2.existsSync(configPath) ? loadLLMConfigFromFile(configPath) : {};
|
|
@@ -3510,13 +3815,6 @@ var init_config = __esm({
|
|
|
3510
3815
|
timeout: parseInt(process.env.AGENT_MOSS_LLM_TIMEOUT || String(fileConfig.timeout ?? String(tr.timeout.prompt1Timeout)), 10)
|
|
3511
3816
|
};
|
|
3512
3817
|
})();
|
|
3513
|
-
__configModuleDir = (() => {
|
|
3514
|
-
try {
|
|
3515
|
-
return path2.dirname(fileURLToPath5(import.meta.url));
|
|
3516
|
-
} catch {
|
|
3517
|
-
return typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
3518
|
-
}
|
|
3519
|
-
})();
|
|
3520
3818
|
USER_RULES_PATHS = [
|
|
3521
3819
|
path2.join(__configModuleDir, "rules", "user_rules.json"),
|
|
3522
3820
|
// 源在 ts/src/rules(一般不存在)
|
|
@@ -3549,7 +3847,9 @@ function resolveAgentsDir() {
|
|
|
3549
3847
|
}
|
|
3550
3848
|
function getAgentExtensions(agentId) {
|
|
3551
3849
|
if (!agentId) return null;
|
|
3552
|
-
|
|
3850
|
+
const fp = getRuleSourceFingerprint();
|
|
3851
|
+
const cached2 = cache.get(agentId);
|
|
3852
|
+
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
3553
3853
|
let ext = null;
|
|
3554
3854
|
try {
|
|
3555
3855
|
if (hasRuntimeConfig()) {
|
|
@@ -3561,7 +3861,7 @@ function getAgentExtensions(agentId) {
|
|
|
3561
3861
|
const agentsDir = resolveAgentsDir();
|
|
3562
3862
|
const rulesFile = resolve4(agentsDir, agentId, "rules.json");
|
|
3563
3863
|
if (!existsSync6(rulesFile)) {
|
|
3564
|
-
cache.set(agentId, null);
|
|
3864
|
+
cache.set(agentId, { fp, ext: null });
|
|
3565
3865
|
return null;
|
|
3566
3866
|
}
|
|
3567
3867
|
try {
|
|
@@ -3569,16 +3869,16 @@ function getAgentExtensions(agentId) {
|
|
|
3569
3869
|
ext = JSON.parse(raw);
|
|
3570
3870
|
if (!ext.agent_id) {
|
|
3571
3871
|
console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
|
|
3572
|
-
cache.set(agentId, null);
|
|
3872
|
+
cache.set(agentId, { fp, ext: null });
|
|
3573
3873
|
return null;
|
|
3574
3874
|
}
|
|
3575
3875
|
} catch (e) {
|
|
3576
3876
|
console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
|
|
3577
|
-
cache.set(agentId, null);
|
|
3877
|
+
cache.set(agentId, { fp, ext: null });
|
|
3578
3878
|
return null;
|
|
3579
3879
|
}
|
|
3580
3880
|
}
|
|
3581
|
-
cache.set(agentId, ext);
|
|
3881
|
+
cache.set(agentId, { fp, ext });
|
|
3582
3882
|
console.error(
|
|
3583
3883
|
`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths, ${ext.builtin_safe_skills?.length ?? 0} builtin safe skills, ${ext.builtin_safe_action_types?.length ?? 0} builtin safe action types`
|
|
3584
3884
|
);
|
|
@@ -3624,7 +3924,9 @@ function resolveScenariosDir() {
|
|
|
3624
3924
|
}
|
|
3625
3925
|
function getScenarioExtensions(scenarioId) {
|
|
3626
3926
|
if (!scenarioId) return null;
|
|
3627
|
-
|
|
3927
|
+
const fp = getRuleSourceFingerprint();
|
|
3928
|
+
const cached2 = cache2.get(scenarioId);
|
|
3929
|
+
if (cached2 && cached2.fp === fp) return cached2.ext;
|
|
3628
3930
|
let ext = null;
|
|
3629
3931
|
let overrideHit = null;
|
|
3630
3932
|
try {
|
|
@@ -3648,7 +3950,7 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3648
3950
|
);
|
|
3649
3951
|
}
|
|
3650
3952
|
if (!existsSync7(rulesFile)) {
|
|
3651
|
-
cache2.set(scenarioId, null);
|
|
3953
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3652
3954
|
return null;
|
|
3653
3955
|
}
|
|
3654
3956
|
try {
|
|
@@ -3657,7 +3959,7 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3657
3959
|
const id = data.scenario_id || data.agent_id || "";
|
|
3658
3960
|
if (!id) {
|
|
3659
3961
|
console.warn(`[AgentMoss scenarios] rules.json \u7F3A\u5C11 scenario_id \u5B57\u6BB5: ${rulesFile}`);
|
|
3660
|
-
cache2.set(scenarioId, null);
|
|
3962
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3661
3963
|
return null;
|
|
3662
3964
|
}
|
|
3663
3965
|
ext = {
|
|
@@ -3672,11 +3974,11 @@ function getScenarioExtensions(scenarioId) {
|
|
|
3672
3974
|
};
|
|
3673
3975
|
} catch (e) {
|
|
3674
3976
|
console.error(`[AgentMoss scenarios] \u52A0\u8F7D\u573A\u666F ${scenarioId} rules.json \u5931\u8D25:`, e.message);
|
|
3675
|
-
cache2.set(scenarioId, null);
|
|
3977
|
+
cache2.set(scenarioId, { fp, ext: null });
|
|
3676
3978
|
return null;
|
|
3677
3979
|
}
|
|
3678
3980
|
}
|
|
3679
|
-
cache2.set(scenarioId, ext);
|
|
3981
|
+
cache2.set(scenarioId, { fp, ext });
|
|
3680
3982
|
console.error(
|
|
3681
3983
|
`[AgentMoss scenarios] \u52A0\u8F7D\u573A\u666F ${scenarioId} \u5B9A\u5236\u89C4\u5219: ${ext.extra_command_patterns?.length ?? 0} cmd patterns, ${ext.extra_injection_keywords?.length ?? 0} injection keywords, ${ext.extra_sensitive_paths?.length ?? 0} sensitive paths`
|
|
3682
3984
|
);
|
|
@@ -7030,10 +7332,10 @@ async function runLLMAnalysis(input, previous) {
|
|
|
7030
7332
|
const config = input.llmConfig?.apiKey ? { ...globalConfig, ...input.llmConfig } : globalConfig;
|
|
7031
7333
|
const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
|
|
7032
7334
|
const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
|
|
7033
|
-
const
|
|
7034
|
-
if (
|
|
7335
|
+
const cached2 = llmCache.get(cacheKey);
|
|
7336
|
+
if (cached2) {
|
|
7035
7337
|
console.log("[AgentMoss] LLM cache hit");
|
|
7036
|
-
return
|
|
7338
|
+
return cached2;
|
|
7037
7339
|
}
|
|
7038
7340
|
const matchedSkills = matchSkills(input.aNext.actionType, input.aNext.actionDetail, input.reason);
|
|
7039
7341
|
const templates = loadTemplates();
|
|
@@ -7222,7 +7524,7 @@ async function analyzeWithLLM(prompt, config) {
|
|
|
7222
7524
|
}
|
|
7223
7525
|
if (attempt < maxRetries) {
|
|
7224
7526
|
console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
|
|
7225
|
-
await new Promise((
|
|
7527
|
+
await new Promise((resolve11) => setTimeout(resolve11, retryCfg.retryInterval * 1e3));
|
|
7226
7528
|
} else {
|
|
7227
7529
|
clearTimeout(totalTimeoutTimer);
|
|
7228
7530
|
console.error("[AgentMoss] LLM call failed after all retries:", err.message);
|
|
@@ -7345,6 +7647,56 @@ var init_rule_store = __esm({
|
|
|
7345
7647
|
getRule(ruleId) {
|
|
7346
7648
|
return this.data.rules.find((r) => r.id === ruleId);
|
|
7347
7649
|
}
|
|
7650
|
+
/** 质疑:active → disputed,并把 disputed_count +1(镜像 Python dispute_rule)。 */
|
|
7651
|
+
disputeRule(ruleId, _reason = "") {
|
|
7652
|
+
const rule = this.getRule(ruleId);
|
|
7653
|
+
if (!rule) throw new Error(`\u89C4\u5219\u4E0D\u5B58\u5728: ${ruleId}`);
|
|
7654
|
+
if (rule.status !== STATUS_ACTIVE) throw new Error(`\u53EA\u6709 active \u72B6\u6001\u53EF\u8D28\u7591\uFF0C\u5F53\u524D: ${rule.status}`);
|
|
7655
|
+
rule.status = STATUS_DISPUTED;
|
|
7656
|
+
rule.disputedCount += 1;
|
|
7657
|
+
this.save();
|
|
7658
|
+
}
|
|
7659
|
+
// ==================== 生命周期(curator 用,镜像 Python rule_store) ====================
|
|
7660
|
+
/** 长期未触发的 active/disputed 规则 → stale。 */
|
|
7661
|
+
markStale(staleDays = 30) {
|
|
7662
|
+
const cutoff = Date.now() - staleDays * 864e5;
|
|
7663
|
+
for (const r of this.data.rules) {
|
|
7664
|
+
if ((r.status === STATUS_ACTIVE || r.status === STATUS_DISPUTED) && r.lastTriggeredAt > 0 && r.lastTriggeredAt < cutoff) {
|
|
7665
|
+
r.status = STATUS_STALE;
|
|
7666
|
+
}
|
|
7667
|
+
}
|
|
7668
|
+
this.save();
|
|
7669
|
+
}
|
|
7670
|
+
/** 长期 stale 的规则 → archived。 */
|
|
7671
|
+
markArchived(archiveDays = 90) {
|
|
7672
|
+
const cutoff = Date.now() - archiveDays * 864e5;
|
|
7673
|
+
for (const r of this.data.rules) {
|
|
7674
|
+
if (r.status === STATUS_STALE && r.lastTriggeredAt > 0 && r.lastTriggeredAt < cutoff) {
|
|
7675
|
+
r.status = STATUS_ARCHIVED;
|
|
7676
|
+
}
|
|
7677
|
+
}
|
|
7678
|
+
this.save();
|
|
7679
|
+
}
|
|
7680
|
+
/** disputed 次数达阈值 → 自动降级 stale(自动纠错闭环)。 */
|
|
7681
|
+
autoDemoteDisputed(disputeThreshold = 3) {
|
|
7682
|
+
for (const r of this.data.rules) {
|
|
7683
|
+
if (r.status === STATUS_DISPUTED && r.disputedCount >= disputeThreshold) {
|
|
7684
|
+
r.status = STATUS_STALE;
|
|
7685
|
+
}
|
|
7686
|
+
}
|
|
7687
|
+
this.save();
|
|
7688
|
+
}
|
|
7689
|
+
// ==================== 中心化预留(镜像 Python) ====================
|
|
7690
|
+
/** 导出本地新增(非 central 来源)的 active 规则。 */
|
|
7691
|
+
getRulesForExport() {
|
|
7692
|
+
return this.data.rules.filter((r) => r.source !== "central" && r.status === STATUS_ACTIVE);
|
|
7693
|
+
}
|
|
7694
|
+
/** 导入中心下发规则(强制标 central 来源,避免被再次回流)。 */
|
|
7695
|
+
importRules(rules) {
|
|
7696
|
+
for (const rule of rules) {
|
|
7697
|
+
this.addRule({ ...rule, source: "central", originNode: "central" });
|
|
7698
|
+
}
|
|
7699
|
+
}
|
|
7348
7700
|
getActiveRules(layer) {
|
|
7349
7701
|
return this.data.rules.filter((r) => r.status === STATUS_ACTIVE && (layer === void 0 || r.layer === layer));
|
|
7350
7702
|
}
|
|
@@ -7452,12 +7804,14 @@ var init_pattern_extractor = __esm({
|
|
|
7452
7804
|
minOccurrences;
|
|
7453
7805
|
builtinPatterns;
|
|
7454
7806
|
reviewFn;
|
|
7807
|
+
skillManager;
|
|
7455
7808
|
constructor(ruleStore, opts) {
|
|
7456
7809
|
this.ruleStore = ruleStore;
|
|
7457
7810
|
this.minConfidence = opts?.minConfidence ?? 0.9;
|
|
7458
7811
|
this.minOccurrences = opts?.minOccurrences ?? 3;
|
|
7459
7812
|
this.builtinPatterns = opts?.builtinPatterns ?? [];
|
|
7460
7813
|
this.reviewFn = opts?.review ?? null;
|
|
7814
|
+
this.skillManager = opts?.skillManager ?? null;
|
|
7461
7815
|
}
|
|
7462
7816
|
async extract(event, llmResult) {
|
|
7463
7817
|
const result = { rulesCreated: 0, patternsRecorded: 0, skippedReason: "", deduped: [] };
|
|
@@ -7546,8 +7900,96 @@ var init_pattern_extractor = __esm({
|
|
|
7546
7900
|
result.rulesCreated += 1;
|
|
7547
7901
|
}
|
|
7548
7902
|
}
|
|
7903
|
+
if (this.skillManager) {
|
|
7904
|
+
const skill = this.buildSkill(actionDetail, reason, riskType, eventId);
|
|
7905
|
+
if (skill) {
|
|
7906
|
+
const id = this.skillManager.addSkill(skill);
|
|
7907
|
+
if (id) {
|
|
7908
|
+
result.skillsCreated = (result.skillsCreated ?? 0) + 1;
|
|
7909
|
+
}
|
|
7910
|
+
}
|
|
7911
|
+
}
|
|
7549
7912
|
return result;
|
|
7550
7913
|
}
|
|
7914
|
+
/**
|
|
7915
|
+
* 生成候选 Skill;不值得沉淀时返回 null(镜像 Python `_extract_skill` 的三道价值评估)。
|
|
7916
|
+
*
|
|
7917
|
+
* 评估 1:reason 太短(<50 字)说明没有可复用的判断逻辑,不沉淀;
|
|
7918
|
+
* 评估 2:同名 skill 已存在 → 跳过;
|
|
7919
|
+
* 评估 3:与已有 active skill 关键词重叠 >= 2 → 跳过(防同类案例重复沉淀,
|
|
7920
|
+
* 如 cat /etc/shadow 与 cat /etc/sudoers 生成两个近乎重复的 skill)。
|
|
7921
|
+
*/
|
|
7922
|
+
buildSkill(actionDetail, reason, riskType, eventId) {
|
|
7923
|
+
if (!reason || reason.length < 50) return null;
|
|
7924
|
+
if (!riskType) return null;
|
|
7925
|
+
const detailWords = (actionDetail.toLowerCase().match(/[a-z]+/g) || []).slice(0, 3);
|
|
7926
|
+
const name = detailWords.length > 0 ? `brain_${riskType}_${detailWords.join("_")}` : `brain_${riskType}`;
|
|
7927
|
+
if (this.skillManager.getSkillMeta(name)) return null;
|
|
7928
|
+
const keywords = this.extractSkillKeywords(actionDetail, reason, riskType);
|
|
7929
|
+
if (this.isCoveredByExistingSkill(keywords)) return null;
|
|
7930
|
+
return {
|
|
7931
|
+
name,
|
|
7932
|
+
category: this.categorizeRisk(riskType),
|
|
7933
|
+
content: this.generateSkillContent(name, actionDetail, reason, riskType),
|
|
7934
|
+
keywords,
|
|
7935
|
+
sourceEventIds: eventId ? [eventId] : [],
|
|
7936
|
+
status: "active"
|
|
7937
|
+
};
|
|
7938
|
+
}
|
|
7939
|
+
/** 关键词提取(镜像 Python `_extract_keywords`)。 */
|
|
7940
|
+
extractSkillKeywords(actionDetail, reason, riskType) {
|
|
7941
|
+
const keywords = /* @__PURE__ */ new Set();
|
|
7942
|
+
if (riskType) keywords.add(riskType);
|
|
7943
|
+
for (const w of (actionDetail.toLowerCase().match(/[a-z_]{3,}/g) || []).slice(0, 10)) keywords.add(w);
|
|
7944
|
+
for (const w of (reason.toLowerCase().match(/[a-z_]{3,}/g) || []).slice(0, 5)) keywords.add(w);
|
|
7945
|
+
return [...keywords];
|
|
7946
|
+
}
|
|
7947
|
+
/** 与已有 active skill 关键词重叠 >= 2 → 视为已覆盖(镜像 `_is_covered_by_existing_skill`)。 */
|
|
7948
|
+
isCoveredByExistingSkill(keywords) {
|
|
7949
|
+
if (keywords.length === 0) return false;
|
|
7950
|
+
let existing = [];
|
|
7951
|
+
try {
|
|
7952
|
+
existing = this.skillManager.getActiveSkills();
|
|
7953
|
+
} catch {
|
|
7954
|
+
return false;
|
|
7955
|
+
}
|
|
7956
|
+
const cand = new Set(keywords.map((k) => k.toLowerCase()));
|
|
7957
|
+
for (const meta of existing) {
|
|
7958
|
+
const metaKeywords = (meta.keywords || []).map((k) => String(k).toLowerCase());
|
|
7959
|
+
if (metaKeywords.length === 0) continue;
|
|
7960
|
+
const overlap = metaKeywords.filter((k) => cand.has(k)).length;
|
|
7961
|
+
if (overlap >= 2) return true;
|
|
7962
|
+
}
|
|
7963
|
+
return false;
|
|
7964
|
+
}
|
|
7965
|
+
/** 风险类型 → skill 分类(镜像 Python `_categorize_risk`)。 */
|
|
7966
|
+
categorizeRisk(riskType) {
|
|
7967
|
+
if (!riskType) return "edge_case";
|
|
7968
|
+
if (["inject", "backdoor", "persist"].some((k) => riskType.includes(k))) return "attack_pattern";
|
|
7969
|
+
if (["exfil", "leak", "export"].some((k) => riskType.includes(k))) return "attack_pattern";
|
|
7970
|
+
return "defense_strategy";
|
|
7971
|
+
}
|
|
7972
|
+
/** skill 正文(与 Python `_generate_skill_content` 同结构,便于双端对拍)。 */
|
|
7973
|
+
generateSkillContent(name, actionDetail, reason, riskType) {
|
|
7974
|
+
return `# ${name}
|
|
7975
|
+
|
|
7976
|
+
## \u6765\u6E90
|
|
7977
|
+
Brain \u81EA\u52A8\u63D0\u53D6\uFF08LLM \u5206\u6790\u6C89\u6DC0\uFF09
|
|
7978
|
+
|
|
7979
|
+
## \u573A\u666F
|
|
7980
|
+
\u5F53 Agent \u6267\u884C\u4EE5\u4E0B\u7C7B\u578B\u64CD\u4F5C\u65F6\u89E6\u53D1\uFF1A
|
|
7981
|
+
- \u98CE\u9669\u7C7B\u578B: ${riskType}
|
|
7982
|
+
- \u64CD\u4F5C\u7279\u5F81: ${actionDetail.slice(0, 200)}
|
|
7983
|
+
|
|
7984
|
+
## \u5224\u65AD\u4F9D\u636E
|
|
7985
|
+
${reason.slice(0, 500)}
|
|
7986
|
+
|
|
7987
|
+
## \u5904\u7406\u5EFA\u8BAE
|
|
7988
|
+
- \u4F18\u5148\u68C0\u67E5\u64CD\u4F5C\u7684\u4E0A\u4E0B\u6587\u548C\u610F\u56FE
|
|
7989
|
+
- \u7ED3\u5408 action_history \u5224\u65AD\u662F\u5426\u5B58\u5728\u6076\u610F\u94FE
|
|
7990
|
+
- \u5FC5\u8981\u65F6 Deny \u5E76\u8BB0\u5F55\u539F\u56E0
|
|
7991
|
+
`;
|
|
7992
|
+
}
|
|
7551
7993
|
extractPatterns(actionDetail, reason, riskType) {
|
|
7552
7994
|
const patterns = [];
|
|
7553
7995
|
const detailLower = actionDetail.toLowerCase();
|
|
@@ -7743,11 +8185,234 @@ var init_reviewer = __esm({
|
|
|
7743
8185
|
}
|
|
7744
8186
|
});
|
|
7745
8187
|
|
|
7746
|
-
// src/brain/
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
8188
|
+
// src/brain/skill-manager.ts
|
|
8189
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync13, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
8190
|
+
import { homedir as homedir5 } from "os";
|
|
8191
|
+
import { join as join8 } from "path";
|
|
8192
|
+
import { randomUUID } from "crypto";
|
|
8193
|
+
function getMetaPath(override) {
|
|
8194
|
+
return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
|
|
8195
|
+
}
|
|
8196
|
+
var DEFAULT_SKILL_META_PATH, ENV_SKILL_META_PATH, BrainSkillManager;
|
|
8197
|
+
var init_skill_manager = __esm({
|
|
8198
|
+
"src/brain/skill-manager.ts"() {
|
|
8199
|
+
"use strict";
|
|
8200
|
+
init_esm_shims();
|
|
8201
|
+
DEFAULT_SKILL_META_PATH = join8(homedir5(), ".agentmoss", "brain", "skill_meta.json");
|
|
8202
|
+
ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
|
|
8203
|
+
BrainSkillManager = class {
|
|
8204
|
+
constructor(skillsDir, metaPath) {
|
|
8205
|
+
this.skillsDir = skillsDir;
|
|
8206
|
+
this.metaPath = getMetaPath(metaPath);
|
|
8207
|
+
try {
|
|
8208
|
+
mkdirSync4(join8(this.metaPath, ".."), { recursive: true });
|
|
8209
|
+
} catch {
|
|
8210
|
+
}
|
|
8211
|
+
this.load();
|
|
8212
|
+
this.cleanupGhostSkills();
|
|
8213
|
+
}
|
|
8214
|
+
skillsDir;
|
|
8215
|
+
skills = [];
|
|
8216
|
+
metaPath;
|
|
8217
|
+
load() {
|
|
8218
|
+
if (!existsSync13(this.metaPath)) return;
|
|
8219
|
+
try {
|
|
8220
|
+
const data = JSON.parse(readFileSync13(this.metaPath, "utf-8"));
|
|
8221
|
+
this.skills = data.skills || [];
|
|
8222
|
+
} catch {
|
|
8223
|
+
this.skills = [];
|
|
8224
|
+
}
|
|
8225
|
+
}
|
|
8226
|
+
save() {
|
|
8227
|
+
const out = { version: 1, skills: this.skills };
|
|
8228
|
+
const tmp = `${this.metaPath}.tmp`;
|
|
8229
|
+
try {
|
|
8230
|
+
writeFileSync4(tmp, JSON.stringify(out, null, 2), "utf-8");
|
|
8231
|
+
renameSync(tmp, this.metaPath);
|
|
8232
|
+
} catch {
|
|
8233
|
+
}
|
|
8234
|
+
}
|
|
8235
|
+
/**
|
|
8236
|
+
* 清理"幽灵 skill":meta 记 active 但磁盘 .md 已不存在。
|
|
8237
|
+
* (沿用 Python 的处理:置 stale,避免污染关键词表并阻塞同名新 skill 生成。)
|
|
8238
|
+
*/
|
|
8239
|
+
cleanupGhostSkills() {
|
|
8240
|
+
let changed = false;
|
|
8241
|
+
for (const sk of this.skills) {
|
|
8242
|
+
if (sk.status !== "active") continue;
|
|
8243
|
+
if (!existsSync13(join8(this.skillsDir, `${sk.name}.md`))) {
|
|
8244
|
+
sk.status = "stale";
|
|
8245
|
+
changed = true;
|
|
8246
|
+
}
|
|
8247
|
+
}
|
|
8248
|
+
if (changed) this.save();
|
|
8249
|
+
}
|
|
8250
|
+
/** 新增 Skill:写 .md + 记元数据。目录缺失或同名已存在时跳过(返回 "")。 */
|
|
8251
|
+
addSkill(skill) {
|
|
8252
|
+
const id = skill.id || randomUUID();
|
|
8253
|
+
const name = skill.name || `brain_${id.slice(0, 8)}`;
|
|
8254
|
+
if (!existsSync13(this.skillsDir)) return "";
|
|
8255
|
+
const mdPath = join8(this.skillsDir, `${name}.md`);
|
|
8256
|
+
if (existsSync13(mdPath)) return "";
|
|
8257
|
+
try {
|
|
8258
|
+
writeFileSync4(mdPath, skill.content, "utf-8");
|
|
8259
|
+
} catch {
|
|
8260
|
+
return "";
|
|
8261
|
+
}
|
|
8262
|
+
this.skills.push({
|
|
8263
|
+
id,
|
|
8264
|
+
name,
|
|
8265
|
+
category: skill.category || "",
|
|
8266
|
+
keywords: skill.keywords || [],
|
|
8267
|
+
source_event_ids: skill.sourceEventIds || [],
|
|
8268
|
+
status: skill.status || "draft",
|
|
8269
|
+
created_at: skill.createdAt || Date.now() / 1e3,
|
|
8270
|
+
usage_count: skill.usageCount || 0,
|
|
8271
|
+
origin_node: skill.originNode || "local"
|
|
8272
|
+
});
|
|
8273
|
+
this.save();
|
|
8274
|
+
return id;
|
|
8275
|
+
}
|
|
8276
|
+
getSkillMeta(name) {
|
|
8277
|
+
return this.skills.find((s) => s.name === name);
|
|
8278
|
+
}
|
|
8279
|
+
getActiveSkills() {
|
|
8280
|
+
return this.skills.filter((s) => s.status === "active").sort((a, b) => (b.created_at || 0) - (a.created_at || 0));
|
|
8281
|
+
}
|
|
8282
|
+
recordUsage(name) {
|
|
8283
|
+
const sk = this.getSkillMeta(name);
|
|
8284
|
+
if (!sk) return;
|
|
8285
|
+
sk.usage_count = (sk.usage_count || 0) + 1;
|
|
8286
|
+
this.save();
|
|
8287
|
+
}
|
|
8288
|
+
/** 长期未使用(usage_count=0)且创建超过 staleDays 的 active skill → stale。 */
|
|
8289
|
+
markStale(staleDays = 60) {
|
|
8290
|
+
const cutoff = Date.now() / 1e3 - staleDays * 86400;
|
|
8291
|
+
let changed = false;
|
|
8292
|
+
for (const sk of this.skills) {
|
|
8293
|
+
if (sk.status === "active" && (sk.usage_count || 0) === 0 && (sk.created_at || 0) < cutoff) {
|
|
8294
|
+
sk.status = "stale";
|
|
8295
|
+
changed = true;
|
|
8296
|
+
}
|
|
8297
|
+
}
|
|
8298
|
+
if (changed) this.save();
|
|
8299
|
+
}
|
|
8300
|
+
/** 导出本地 active skill(含正文),供回流中心。 */
|
|
8301
|
+
exportSkills() {
|
|
8302
|
+
const out = [];
|
|
8303
|
+
for (const sk of this.skills) {
|
|
8304
|
+
if (sk.origin_node !== "local" || sk.status !== "active") continue;
|
|
8305
|
+
const mdPath = join8(this.skillsDir, `${sk.name}.md`);
|
|
8306
|
+
if (!existsSync13(mdPath)) continue;
|
|
8307
|
+
out.push({
|
|
8308
|
+
name: sk.name,
|
|
8309
|
+
category: sk.category || "",
|
|
8310
|
+
content: readFileSync13(mdPath, "utf-8"),
|
|
8311
|
+
keywords: sk.keywords || []
|
|
8312
|
+
});
|
|
8313
|
+
}
|
|
8314
|
+
return out;
|
|
8315
|
+
}
|
|
8316
|
+
/** 导入中心下发 skill(标 central,避免被再次回流)。 */
|
|
8317
|
+
importSkills(skills) {
|
|
8318
|
+
for (const s of skills) {
|
|
8319
|
+
this.addSkill({
|
|
8320
|
+
name: s.name,
|
|
8321
|
+
category: s.category || "",
|
|
8322
|
+
content: s.content,
|
|
8323
|
+
keywords: s.keywords || [],
|
|
8324
|
+
status: "active",
|
|
8325
|
+
originNode: "central"
|
|
8326
|
+
});
|
|
8327
|
+
}
|
|
8328
|
+
}
|
|
8329
|
+
/** 保留 close 签名(JSON 落盘已逐次持久化)。 */
|
|
8330
|
+
close() {
|
|
8331
|
+
this.save();
|
|
8332
|
+
}
|
|
8333
|
+
};
|
|
8334
|
+
}
|
|
8335
|
+
});
|
|
8336
|
+
|
|
8337
|
+
// src/brain/curator.ts
|
|
8338
|
+
var BrainCurator;
|
|
8339
|
+
var init_curator = __esm({
|
|
8340
|
+
"src/brain/curator.ts"() {
|
|
8341
|
+
"use strict";
|
|
8342
|
+
init_esm_shims();
|
|
8343
|
+
BrainCurator = class {
|
|
8344
|
+
constructor(ruleStore, skillManager) {
|
|
8345
|
+
this.ruleStore = ruleStore;
|
|
8346
|
+
this.skillManager = skillManager;
|
|
8347
|
+
}
|
|
8348
|
+
ruleStore;
|
|
8349
|
+
skillManager;
|
|
8350
|
+
/**
|
|
8351
|
+
* 执行一次维护,返回报告。
|
|
8352
|
+
*
|
|
8353
|
+
* autoApprove 默认 true:brain 提取的规则达阈值后自动生效(学习闭环);
|
|
8354
|
+
* 需人工审核时设 false(由 Console 手动 approve)。
|
|
8355
|
+
*/
|
|
8356
|
+
runMaintenance(opts = {}) {
|
|
8357
|
+
const {
|
|
8358
|
+
staleRuleDays = 30,
|
|
8359
|
+
archiveRuleDays = 90,
|
|
8360
|
+
staleSkillDays = 60,
|
|
8361
|
+
disputeThreshold = 3,
|
|
8362
|
+
autoApprove = true
|
|
8363
|
+
} = opts;
|
|
8364
|
+
const autoApproved = autoApprove ? this.autoApproveDrafts() : [];
|
|
8365
|
+
this.ruleStore.markStale(staleRuleDays);
|
|
8366
|
+
this.ruleStore.markArchived(archiveRuleDays);
|
|
8367
|
+
this.ruleStore.autoDemoteDisputed(disputeThreshold);
|
|
8368
|
+
this.skillManager.markStale(staleSkillDays);
|
|
8369
|
+
return {
|
|
8370
|
+
rule_stats: this.ruleStore.getStats(),
|
|
8371
|
+
auto_approved: autoApproved
|
|
8372
|
+
};
|
|
8373
|
+
}
|
|
8374
|
+
/** draft → active(仅未 disputed 的 draft)。返回被激活规则的 pattern 前缀。 */
|
|
8375
|
+
autoApproveDrafts() {
|
|
8376
|
+
const approved = [];
|
|
8377
|
+
let drafts;
|
|
8378
|
+
try {
|
|
8379
|
+
drafts = this.ruleStore.getDraftRules();
|
|
8380
|
+
} catch {
|
|
8381
|
+
return approved;
|
|
8382
|
+
}
|
|
8383
|
+
for (const rule of drafts) {
|
|
8384
|
+
try {
|
|
8385
|
+
this.ruleStore.approveRule(rule.id);
|
|
8386
|
+
approved.push(rule.pattern.slice(0, 60));
|
|
8387
|
+
} catch {
|
|
8388
|
+
}
|
|
8389
|
+
}
|
|
8390
|
+
return approved;
|
|
8391
|
+
}
|
|
8392
|
+
};
|
|
8393
|
+
}
|
|
8394
|
+
});
|
|
8395
|
+
|
|
8396
|
+
// src/brain/contributor.ts
|
|
8397
|
+
import { dirname as dirname9 } from "path";
|
|
8398
|
+
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
8399
|
+
var __dirname8;
|
|
8400
|
+
var init_contributor = __esm({
|
|
8401
|
+
"src/brain/contributor.ts"() {
|
|
8402
|
+
"use strict";
|
|
8403
|
+
init_esm_shims();
|
|
8404
|
+
init_rule_store();
|
|
8405
|
+
init_skill_manager();
|
|
8406
|
+
init_asset_dir();
|
|
8407
|
+
__dirname8 = dirname9(fileURLToPath9(import.meta.url));
|
|
8408
|
+
}
|
|
8409
|
+
});
|
|
8410
|
+
|
|
8411
|
+
// src/brain/index.ts
|
|
8412
|
+
function checkBrainRules(store, opts) {
|
|
8413
|
+
if (!store) return null;
|
|
8414
|
+
const rules = store.getActiveRules(opts.layer);
|
|
8415
|
+
if (rules.length === 0) return null;
|
|
7751
8416
|
const combined = `${opts.actionType} ${opts.actionDetail}`;
|
|
7752
8417
|
for (const rule of rules) {
|
|
7753
8418
|
try {
|
|
@@ -7795,6 +8460,9 @@ var init_brain = __esm({
|
|
|
7795
8460
|
init_rule_store();
|
|
7796
8461
|
init_pattern_extractor();
|
|
7797
8462
|
init_reviewer();
|
|
8463
|
+
init_skill_manager();
|
|
8464
|
+
init_curator();
|
|
8465
|
+
init_contributor();
|
|
7798
8466
|
}
|
|
7799
8467
|
});
|
|
7800
8468
|
|
|
@@ -9324,19 +9992,64 @@ function extractRawCommand(detail) {
|
|
|
9324
9992
|
function matchesAny(cmd, patterns) {
|
|
9325
9993
|
return patterns.some((p) => p.test(cmd));
|
|
9326
9994
|
}
|
|
9327
|
-
function getBrainStore() {
|
|
9995
|
+
function getBrainStore(osType) {
|
|
9328
9996
|
if (!isBrainEnabled()) return null;
|
|
9329
9997
|
if (!_brainInitialized) {
|
|
9330
9998
|
_brainStore = new BrainRuleStore();
|
|
9331
|
-
|
|
9999
|
+
const skillsDir = resolveAssetDir(__dirname, "skills");
|
|
10000
|
+
_brainSkillManager = skillsDir ? new BrainSkillManager(skillsDir) : null;
|
|
10001
|
+
_brainExtractor = new PatternExtractor(_brainStore, {
|
|
10002
|
+
review: createBrainReviewer(),
|
|
10003
|
+
// 内置规则去重集(对齐 Python coordinator._ensure_brain 收集的 builtin_patterns):
|
|
10004
|
+
// 危险命令正则 + 敏感路径(deny_* 或 credential)。
|
|
10005
|
+
// 历史 bug:TS 这里只传 review,不传 builtinPatterns → 提取器拿不到内置规则,
|
|
10006
|
+
// `isCoveredByBuiltin` 恒 False → 把 L1/L2 本来就能拦的案例重复沉淀成 Brain 规则。
|
|
10007
|
+
builtinPatterns: collectBrainBuiltinPatterns(osType),
|
|
10008
|
+
skillManager: _brainSkillManager
|
|
10009
|
+
});
|
|
10010
|
+
if (_brainSkillManager) {
|
|
10011
|
+
_brainCurator = new BrainCurator(_brainStore, _brainSkillManager);
|
|
10012
|
+
}
|
|
9332
10013
|
_brainInitialized = true;
|
|
9333
10014
|
}
|
|
10015
|
+
const now = Date.now();
|
|
10016
|
+
if (_brainCurator && now - _brainLastMaintenance >= BRAIN_MAINT_INTERVAL_MS) {
|
|
10017
|
+
try {
|
|
10018
|
+
_brainCurator.runMaintenance({ autoApprove: true });
|
|
10019
|
+
} catch {
|
|
10020
|
+
}
|
|
10021
|
+
_brainLastMaintenance = now;
|
|
10022
|
+
}
|
|
9334
10023
|
return _brainStore;
|
|
9335
10024
|
}
|
|
10025
|
+
function collectBrainBuiltinPatterns(osType) {
|
|
10026
|
+
const bridge = runtimeConfigBridge.current;
|
|
10027
|
+
if (!bridge) return [];
|
|
10028
|
+
const out = [];
|
|
10029
|
+
try {
|
|
10030
|
+
for (const cp of bridge.effectiveDangerousPatterns(osType)) {
|
|
10031
|
+
if (cp?.pattern?.source) out.push(cp.pattern.source);
|
|
10032
|
+
}
|
|
10033
|
+
} catch {
|
|
10034
|
+
}
|
|
10035
|
+
try {
|
|
10036
|
+
for (const sp of bridge.effectiveSensitivePaths(osType)) {
|
|
10037
|
+
const denyMode = String(sp?.denyMode || "");
|
|
10038
|
+
if (["deny_both", "deny_write", "deny_read"].includes(denyMode) || sp?.credential) {
|
|
10039
|
+
if (sp?.path) out.push(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
10040
|
+
}
|
|
10041
|
+
}
|
|
10042
|
+
} catch {
|
|
10043
|
+
}
|
|
10044
|
+
return out.filter(Boolean);
|
|
10045
|
+
}
|
|
9336
10046
|
function _resetBrainForTest() {
|
|
9337
10047
|
_brainStore = null;
|
|
9338
10048
|
_brainExtractor = null;
|
|
10049
|
+
_brainSkillManager = null;
|
|
10050
|
+
_brainCurator = null;
|
|
9339
10051
|
_brainInitialized = false;
|
|
10052
|
+
_brainLastMaintenance = 0;
|
|
9340
10053
|
}
|
|
9341
10054
|
async function runAnalysis(input) {
|
|
9342
10055
|
const observedFormalLayers = [];
|
|
@@ -9388,7 +10101,7 @@ async function runAnalysis(input) {
|
|
|
9388
10101
|
}
|
|
9389
10102
|
}
|
|
9390
10103
|
}
|
|
9391
|
-
const brainStore = getBrainStore();
|
|
10104
|
+
const brainStore = getBrainStore(input.osType);
|
|
9392
10105
|
if (brainStore) {
|
|
9393
10106
|
const brainL1 = checkBrainRules(brainStore, {
|
|
9394
10107
|
layer: 1,
|
|
@@ -9685,7 +10398,7 @@ async function runAnalysis(input) {
|
|
|
9685
10398
|
}
|
|
9686
10399
|
return llmResult;
|
|
9687
10400
|
}
|
|
9688
|
-
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS, _brainStore, _brainExtractor, _brainInitialized, formalEngine;
|
|
10401
|
+
var FULLY_SAFE_TOOLS, TRUSTED_SKILLS_BASE, READONLY_SENSITIVE_TOOLS, _brainStore, _brainExtractor, _brainSkillManager, _brainCurator, _brainInitialized, _brainLastMaintenance, BRAIN_MAINT_INTERVAL_MS, formalEngine;
|
|
9689
10402
|
var init_coordinator = __esm({
|
|
9690
10403
|
"src/engine/coordinator.ts"() {
|
|
9691
10404
|
"use strict";
|
|
@@ -9702,7 +10415,11 @@ var init_coordinator = __esm({
|
|
|
9702
10415
|
init_agents();
|
|
9703
10416
|
init_scenarios();
|
|
9704
10417
|
init_brain();
|
|
10418
|
+
init_skill_manager();
|
|
10419
|
+
init_curator();
|
|
9705
10420
|
init_reviewer();
|
|
10421
|
+
init_asset_dir();
|
|
10422
|
+
init_runtime_config_bridge();
|
|
9706
10423
|
init_formal_engine();
|
|
9707
10424
|
FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
9708
10425
|
"ask_user_question",
|
|
@@ -9725,7 +10442,11 @@ var init_coordinator = __esm({
|
|
|
9725
10442
|
]);
|
|
9726
10443
|
_brainStore = null;
|
|
9727
10444
|
_brainExtractor = null;
|
|
10445
|
+
_brainSkillManager = null;
|
|
10446
|
+
_brainCurator = null;
|
|
9728
10447
|
_brainInitialized = false;
|
|
10448
|
+
_brainLastMaintenance = 0;
|
|
10449
|
+
BRAIN_MAINT_INTERVAL_MS = 3e5;
|
|
9729
10450
|
formalEngine = new FormalEngine();
|
|
9730
10451
|
}
|
|
9731
10452
|
});
|
|
@@ -9736,6 +10457,7 @@ import { Hono as Hono2 } from "hono";
|
|
|
9736
10457
|
import { cors } from "hono/cors";
|
|
9737
10458
|
import { logger } from "hono/logger";
|
|
9738
10459
|
import net from "net";
|
|
10460
|
+
import { existsSync as existsSync19 } from "fs";
|
|
9739
10461
|
|
|
9740
10462
|
// src/routes.ts
|
|
9741
10463
|
init_esm_shims();
|
|
@@ -9822,8 +10544,438 @@ var ReviewResponseSchema = z.object({
|
|
|
9822
10544
|
discovered_patterns: z.array(z.record(z.unknown())).default([]).describe("\u6570\u636E\u9A71\u52A8\u53D1\u73B0\uFF1A\u8DE8 session \u9891\u7E41\u5371\u9669\u7EC4\u5408\uFF08\u9884\u7F6E\u94FE\u76F2\u533A\uFF0CPatternMiner\uFF09")
|
|
9823
10545
|
});
|
|
9824
10546
|
|
|
9825
|
-
// src/
|
|
10547
|
+
// src/engine/analyzer.ts
|
|
10548
|
+
init_esm_shims();
|
|
9826
10549
|
init_coordinator();
|
|
10550
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync16, writeFileSync as writeFileSync7 } from "fs";
|
|
10551
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
10552
|
+
import { join as join11, resolve as resolve10 } from "path";
|
|
10553
|
+
|
|
10554
|
+
// src/infra/policy-cache.ts
|
|
10555
|
+
init_esm_shims();
|
|
10556
|
+
init_config();
|
|
10557
|
+
import { createHash as createHash2 } from "crypto";
|
|
10558
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync6, readdirSync as readdirSync5, readFileSync as readFileSync15, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
10559
|
+
import { tmpdir } from "os";
|
|
10560
|
+
import { join as join10 } from "path";
|
|
10561
|
+
var CACHE_DIR = join10(tmpdir(), "agent_moss_policy_cache");
|
|
10562
|
+
var cacheDirOverride = null;
|
|
10563
|
+
function cacheDir() {
|
|
10564
|
+
return cacheDirOverride ?? CACHE_DIR;
|
|
10565
|
+
}
|
|
10566
|
+
var PolicyCache = class {
|
|
10567
|
+
// Map 的插入序即 LRU 序(get 时重新 set 到末尾)
|
|
10568
|
+
constructor(options) {
|
|
10569
|
+
this.options = options;
|
|
10570
|
+
}
|
|
10571
|
+
options;
|
|
10572
|
+
memory = /* @__PURE__ */ new Map();
|
|
10573
|
+
cacheKey(sessionId, promptSession) {
|
|
10574
|
+
return `${sessionId}::${promptSession}`;
|
|
10575
|
+
}
|
|
10576
|
+
cacheFilePath(sessionId, promptSession) {
|
|
10577
|
+
const promptHash = createHash2("md5").update(promptSession).digest("hex").slice(0, 12);
|
|
10578
|
+
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10579
|
+
return join10(cacheDir(), safeSessionId, `${promptHash}.toml`);
|
|
10580
|
+
}
|
|
10581
|
+
/** 取缓存:内存 → 文件;未命中返回 null。 */
|
|
10582
|
+
get(sessionId, promptSession) {
|
|
10583
|
+
if (!this.options.enabled) return null;
|
|
10584
|
+
const key = this.cacheKey(sessionId, promptSession);
|
|
10585
|
+
const hit = this.memory.get(key);
|
|
10586
|
+
if (hit !== void 0) {
|
|
10587
|
+
this.memory.delete(key);
|
|
10588
|
+
this.memory.set(key, hit);
|
|
10589
|
+
return hit;
|
|
10590
|
+
}
|
|
10591
|
+
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10592
|
+
if (existsSync15(file)) {
|
|
10593
|
+
try {
|
|
10594
|
+
const policy = readFileSync15(file, "utf-8");
|
|
10595
|
+
this.remember(key, policy);
|
|
10596
|
+
return policy;
|
|
10597
|
+
} catch {
|
|
10598
|
+
}
|
|
10599
|
+
}
|
|
10600
|
+
return null;
|
|
10601
|
+
}
|
|
10602
|
+
/** 写缓存(内存 + 文件)。落盘失败不影响判定(缓存是旁路产物)。 */
|
|
10603
|
+
put(sessionId, promptSession, policy) {
|
|
10604
|
+
if (!this.options.enabled) return;
|
|
10605
|
+
this.remember(this.cacheKey(sessionId, promptSession), policy);
|
|
10606
|
+
const file = this.cacheFilePath(sessionId, promptSession);
|
|
10607
|
+
try {
|
|
10608
|
+
mkdirSync6(join10(cacheDir(), sessionId.replace(/\//g, "_").replace(/\\/g, "_")), { recursive: true });
|
|
10609
|
+
writeFileSync6(file, policy, "utf-8");
|
|
10610
|
+
} catch {
|
|
10611
|
+
}
|
|
10612
|
+
}
|
|
10613
|
+
remember(key, policy) {
|
|
10614
|
+
if (this.memory.has(key)) this.memory.delete(key);
|
|
10615
|
+
this.memory.set(key, policy);
|
|
10616
|
+
while (this.memory.size > this.options.maxSize) {
|
|
10617
|
+
const oldest = this.memory.keys().next().value;
|
|
10618
|
+
if (oldest === void 0) break;
|
|
10619
|
+
this.memory.delete(oldest);
|
|
10620
|
+
}
|
|
10621
|
+
}
|
|
10622
|
+
invalidate(sessionId, promptSession) {
|
|
10623
|
+
this.memory.delete(this.cacheKey(sessionId, promptSession));
|
|
10624
|
+
try {
|
|
10625
|
+
unlinkSync3(this.cacheFilePath(sessionId, promptSession));
|
|
10626
|
+
} catch {
|
|
10627
|
+
}
|
|
10628
|
+
}
|
|
10629
|
+
invalidateSession(sessionId) {
|
|
10630
|
+
for (const k of [...this.memory.keys()]) {
|
|
10631
|
+
if (k.startsWith(`${sessionId}::`)) this.memory.delete(k);
|
|
10632
|
+
}
|
|
10633
|
+
const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10634
|
+
const dir = join10(cacheDir(), safeSessionId);
|
|
10635
|
+
try {
|
|
10636
|
+
if (existsSync15(dir)) {
|
|
10637
|
+
for (const f of readdirSync5(dir)) {
|
|
10638
|
+
try {
|
|
10639
|
+
unlinkSync3(join10(dir, f));
|
|
10640
|
+
} catch {
|
|
10641
|
+
}
|
|
10642
|
+
}
|
|
10643
|
+
rmdirSync(dir);
|
|
10644
|
+
}
|
|
10645
|
+
} catch {
|
|
10646
|
+
}
|
|
10647
|
+
}
|
|
10648
|
+
clear() {
|
|
10649
|
+
this.memory.clear();
|
|
10650
|
+
try {
|
|
10651
|
+
if (existsSync15(cacheDir())) {
|
|
10652
|
+
for (const sessionDir of readdirSync5(cacheDir())) {
|
|
10653
|
+
const dir = join10(cacheDir(), sessionDir);
|
|
10654
|
+
try {
|
|
10655
|
+
for (const f of readdirSync5(dir)) unlinkSync3(join10(dir, f));
|
|
10656
|
+
rmdirSync(dir);
|
|
10657
|
+
} catch {
|
|
10658
|
+
}
|
|
10659
|
+
}
|
|
10660
|
+
}
|
|
10661
|
+
} catch {
|
|
10662
|
+
}
|
|
10663
|
+
}
|
|
10664
|
+
/** 当前内存缓存条目数。 */
|
|
10665
|
+
size() {
|
|
10666
|
+
return this.memory.size;
|
|
10667
|
+
}
|
|
10668
|
+
};
|
|
10669
|
+
var defaultCache = null;
|
|
10670
|
+
function getDefaultCache() {
|
|
10671
|
+
if (!defaultCache) defaultCache = new PolicyCache(getCacheOptions());
|
|
10672
|
+
return defaultCache;
|
|
10673
|
+
}
|
|
10674
|
+
|
|
10675
|
+
// src/engine/analyzer.ts
|
|
10676
|
+
init_config();
|
|
10677
|
+
init_template_loader();
|
|
10678
|
+
init_asset_dir();
|
|
10679
|
+
init_token_stats();
|
|
10680
|
+
|
|
10681
|
+
// src/infra/logging.ts
|
|
10682
|
+
init_esm_shims();
|
|
10683
|
+
function safe(value, limit = 200) {
|
|
10684
|
+
const s = String(value ?? "");
|
|
10685
|
+
return s.length > limit ? `${s.slice(0, limit)}\u2026` : s;
|
|
10686
|
+
}
|
|
10687
|
+
var MossLogger = class {
|
|
10688
|
+
constructor(sessionId) {
|
|
10689
|
+
this.sessionId = sessionId;
|
|
10690
|
+
}
|
|
10691
|
+
sessionId;
|
|
10692
|
+
stepTimes = {};
|
|
10693
|
+
stepStart = null;
|
|
10694
|
+
startTime = Date.now();
|
|
10695
|
+
prefix() {
|
|
10696
|
+
return `[${safe(this.sessionId, 64)}]`;
|
|
10697
|
+
}
|
|
10698
|
+
logInput(promptSession, actionHistory, aNext, reason) {
|
|
10699
|
+
console.error(
|
|
10700
|
+
`${this.prefix()} agent_moss \u8F93\u5165 | prompt=${safe(promptSession, 100)}\u2026 | \u5386\u53F2\u52A8\u4F5C\u6570=${actionHistory.length} | \u4E0B\u4E00\u6B65\u52A8\u4F5C=${safe(aNext?.action_type ?? "?", 32)}:${safe(aNext?.action_detail ?? "?", 80)} | \u7406\u7531=${safe(reason, 80)}`
|
|
10701
|
+
);
|
|
10702
|
+
}
|
|
10703
|
+
startStep(stepName) {
|
|
10704
|
+
this.stepStart = Date.now();
|
|
10705
|
+
}
|
|
10706
|
+
endStep(stepName, detail = "") {
|
|
10707
|
+
if (this.stepStart === null) return;
|
|
10708
|
+
const elapsed = (Date.now() - this.stepStart) / 1e3;
|
|
10709
|
+
this.stepTimes[stepName] = elapsed;
|
|
10710
|
+
const detailStr = detail ? ` | ${safe(detail)}` : "";
|
|
10711
|
+
console.error(
|
|
10712
|
+
`${this.prefix()} \u6B65\u9AA4\u5B8C\u6210: ${safe(stepName, 48)} | \u8017\u65F6=${elapsed.toFixed(3)}s${detailStr}`
|
|
10713
|
+
);
|
|
10714
|
+
this.stepStart = null;
|
|
10715
|
+
}
|
|
10716
|
+
logLlmInteraction(step, promptSummary, responseSummary) {
|
|
10717
|
+
console.error(
|
|
10718
|
+
`${this.prefix()} LLM\u4EA4\u4E92 | \u6B65\u9AA4=${safe(step, 48)} | PROMPT\u6458\u8981=${safe(promptSummary, 200)} | \u54CD\u5E94\u6458\u8981=${safe(responseSummary, 200)}`
|
|
10719
|
+
);
|
|
10720
|
+
}
|
|
10721
|
+
logResult(decision, reason, policy = "") {
|
|
10722
|
+
const total = (Date.now() - this.startTime) / 1e3;
|
|
10723
|
+
const steps = Object.entries(this.stepTimes).map(([k, v]) => `${safe(k, 48)}=${v.toFixed(3)}s`).join(", ");
|
|
10724
|
+
console.error(
|
|
10725
|
+
`${this.prefix()} agent_moss \u7ED3\u679C | \u51B3\u7B56=${safe(decision, 16)} | \u7406\u7531=${safe(reason, 150)} | \u6B65\u9AA4\u8017\u65F6=[${steps}] | \u603B\u8017\u65F6=${total.toFixed(3)}s`
|
|
10726
|
+
);
|
|
10727
|
+
if (policy) {
|
|
10728
|
+
console.error(`${this.prefix()} \u751F\u6210\u7684Policy:
|
|
10729
|
+
${safe(policy, 2e3)}`);
|
|
10730
|
+
}
|
|
10731
|
+
}
|
|
10732
|
+
logError(step, error) {
|
|
10733
|
+
const name = error?.name ?? typeof error;
|
|
10734
|
+
console.error(
|
|
10735
|
+
`${this.prefix()} \u6B65\u9AA4\u5F02\u5E38: ${safe(step, 48)} | \u9519\u8BEF=${name}: ${safe(error?.message ?? error)}`
|
|
10736
|
+
);
|
|
10737
|
+
}
|
|
10738
|
+
};
|
|
10739
|
+
|
|
10740
|
+
// src/engine/analyzer.ts
|
|
10741
|
+
import { parse as parseToml } from "smol-toml";
|
|
10742
|
+
var POLICY_OUTPUT_DIR = join11(tmpdir2(), "agent_moss");
|
|
10743
|
+
var policyOutputDirOverride = null;
|
|
10744
|
+
function policyOutputDir() {
|
|
10745
|
+
return policyOutputDirOverride ?? POLICY_OUTPUT_DIR;
|
|
10746
|
+
}
|
|
10747
|
+
var DEFAULT_READONLY_POLICY = `landlock_optional = false
|
|
10748
|
+
mount_isolation_fallback = false
|
|
10749
|
+
|
|
10750
|
+
[path_groups]
|
|
10751
|
+
system_binaries = true
|
|
10752
|
+
system_libraries = true
|
|
10753
|
+
temp_directories = true
|
|
10754
|
+
device_files = false
|
|
10755
|
+
proc_filesystem = false
|
|
10756
|
+
network_config = false
|
|
10757
|
+
wsl_paths = false
|
|
10758
|
+
|
|
10759
|
+
[namespaces]
|
|
10760
|
+
mount = true
|
|
10761
|
+
pid = false
|
|
10762
|
+
network = true
|
|
10763
|
+
user = false
|
|
10764
|
+
|
|
10765
|
+
[resources]
|
|
10766
|
+
timeout_secs = 30
|
|
10767
|
+
max_memory_bytes = 268435456
|
|
10768
|
+
|
|
10769
|
+
[environment]
|
|
10770
|
+
whitelist = ["PATH", "LANG", "HOME", "USER", "HTTP_PROXY", "HTTPS_PROXY", "http_proxy", "https_proxy", "ALL_PROXY", "all_proxy", "NO_PROXY", "no_proxy"]
|
|
10771
|
+
`;
|
|
10772
|
+
function savePolicyToFile(sessionId, policyToml) {
|
|
10773
|
+
if (!isPolicySaveEnabled()) return null;
|
|
10774
|
+
try {
|
|
10775
|
+
const outDir = policyOutputDir();
|
|
10776
|
+
mkdirSync7(outDir, { recursive: true });
|
|
10777
|
+
const safeName = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
|
|
10778
|
+
const filepath = join11(outDir, `${safeName}.toml`);
|
|
10779
|
+
writeFileSync7(filepath, policyToml, "utf-8");
|
|
10780
|
+
return filepath;
|
|
10781
|
+
} catch (err) {
|
|
10782
|
+
console.warn(`[AgentMoss] \u7B56\u7565\u6587\u4EF6\u843D\u76D8\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u5224\u5B9A\uFF09: ${policyOutputDir()}: ${String(err)}`);
|
|
10783
|
+
return null;
|
|
10784
|
+
}
|
|
10785
|
+
}
|
|
10786
|
+
function parsePolicyFromLlm(response) {
|
|
10787
|
+
const tomlBlock = response.match(/```toml\s*\n([\s\S]*?)```/);
|
|
10788
|
+
if (tomlBlock) {
|
|
10789
|
+
const s = tomlBlock[1].trim();
|
|
10790
|
+
validateToml(s);
|
|
10791
|
+
return s;
|
|
10792
|
+
}
|
|
10793
|
+
const anyBlock = response.match(/```\s*\n([\s\S]*?)```/);
|
|
10794
|
+
if (anyBlock) {
|
|
10795
|
+
const s = anyBlock[1].trim();
|
|
10796
|
+
try {
|
|
10797
|
+
validateToml(s);
|
|
10798
|
+
return s;
|
|
10799
|
+
} catch {
|
|
10800
|
+
}
|
|
10801
|
+
}
|
|
10802
|
+
const whole = response.trim();
|
|
10803
|
+
validateToml(whole);
|
|
10804
|
+
return whole;
|
|
10805
|
+
}
|
|
10806
|
+
function validateToml(tomlStr) {
|
|
10807
|
+
try {
|
|
10808
|
+
parseToml(tomlStr);
|
|
10809
|
+
} catch (err) {
|
|
10810
|
+
throw new Error(`TOML \u89E3\u6790\u5931\u8D25: ${err.message}`);
|
|
10811
|
+
}
|
|
10812
|
+
}
|
|
10813
|
+
function loadPolicyMappingDoc() {
|
|
10814
|
+
const dir = resolveAssetDir(__dirname, "templates");
|
|
10815
|
+
if (dir) {
|
|
10816
|
+
const p = resolve10(dir, "policy_mapping.md");
|
|
10817
|
+
if (existsSync16(p)) return readFileSync16(p, "utf-8");
|
|
10818
|
+
}
|
|
10819
|
+
for (const cand of assetDirCandidates(__dirname, "templates")) {
|
|
10820
|
+
const p = resolve10(cand, "policy_mapping.md");
|
|
10821
|
+
if (existsSync16(p)) return readFileSync16(p, "utf-8");
|
|
10822
|
+
}
|
|
10823
|
+
return "";
|
|
10824
|
+
}
|
|
10825
|
+
function buildPolicyPrompt(promptSession) {
|
|
10826
|
+
const template = loadTemplates()["prompt1"] || "";
|
|
10827
|
+
return template.replaceAll("{prompt_policy_mapping_doc}", loadPolicyMappingDoc()).replaceAll("{prompt_session}", promptSession);
|
|
10828
|
+
}
|
|
10829
|
+
async function generatePolicyWithLLM(sessionId, promptSession) {
|
|
10830
|
+
const { default: OpenAI3 } = await import("openai");
|
|
10831
|
+
const cfg = getLLMConfig();
|
|
10832
|
+
const timeoutCfg = getTimeoutConfig();
|
|
10833
|
+
const retryCfg = getRetryConfig();
|
|
10834
|
+
const client = new OpenAI3({
|
|
10835
|
+
baseURL: cfg.baseUrl,
|
|
10836
|
+
apiKey: cfg.apiKey,
|
|
10837
|
+
timeout: (cfg.timeout || timeoutCfg.prompt1Timeout) * 1e3
|
|
10838
|
+
});
|
|
10839
|
+
const prompt1 = buildPolicyPrompt(promptSession);
|
|
10840
|
+
let lastErr = null;
|
|
10841
|
+
for (let attempt = 1; attempt <= Math.max(1, retryCfg.maxRetries); attempt++) {
|
|
10842
|
+
try {
|
|
10843
|
+
const resp = await client.chat.completions.create({
|
|
10844
|
+
model: cfg.model,
|
|
10845
|
+
temperature: 0.1,
|
|
10846
|
+
messages: [{ role: "user", content: prompt1 }]
|
|
10847
|
+
});
|
|
10848
|
+
try {
|
|
10849
|
+
const usage = resp.usage || {};
|
|
10850
|
+
recordTokenUsage({
|
|
10851
|
+
sessionId,
|
|
10852
|
+
step: "step2_policy_gen",
|
|
10853
|
+
model: cfg.model,
|
|
10854
|
+
promptTokens: usage.prompt_tokens || 0,
|
|
10855
|
+
completionTokens: usage.completion_tokens || 0,
|
|
10856
|
+
totalTokens: usage.total_tokens || 0
|
|
10857
|
+
});
|
|
10858
|
+
} catch {
|
|
10859
|
+
}
|
|
10860
|
+
return parsePolicyFromLlm(resp.choices?.[0]?.message?.content || "");
|
|
10861
|
+
} catch (err) {
|
|
10862
|
+
lastErr = err;
|
|
10863
|
+
if (attempt < Math.max(1, retryCfg.maxRetries)) {
|
|
10864
|
+
await new Promise((r) => setTimeout(r, retryCfg.retryInterval * 1e3));
|
|
10865
|
+
}
|
|
10866
|
+
}
|
|
10867
|
+
}
|
|
10868
|
+
throw lastErr ?? new Error("\u7B56\u7565\u751F\u6210\u5931\u8D25");
|
|
10869
|
+
}
|
|
10870
|
+
function allowResult(policy, judgment) {
|
|
10871
|
+
return {
|
|
10872
|
+
decision: "Allow",
|
|
10873
|
+
reason: judgment.reason,
|
|
10874
|
+
risk_level: judgment.riskLevel,
|
|
10875
|
+
risk_type: judgment.riskType,
|
|
10876
|
+
violated_layers: judgment.violatedLayers || [],
|
|
10877
|
+
policy,
|
|
10878
|
+
violated_policy: "",
|
|
10879
|
+
confidence: judgment.confidence
|
|
10880
|
+
};
|
|
10881
|
+
}
|
|
10882
|
+
async function analyze(input) {
|
|
10883
|
+
const mossLog = new MossLogger(input.sessionId);
|
|
10884
|
+
try {
|
|
10885
|
+
return await analyzeInner(input, mossLog);
|
|
10886
|
+
} catch (err) {
|
|
10887
|
+
mossLog.logError("unknown", err);
|
|
10888
|
+
const result = {
|
|
10889
|
+
decision: "Deny",
|
|
10890
|
+
reason: `\u5206\u6790\u8FC7\u7A0B\u53D1\u751F\u672A\u9884\u671F\u5F02\u5E38\uFF1A${String(err)}`,
|
|
10891
|
+
risk_level: "high",
|
|
10892
|
+
risk_type: "",
|
|
10893
|
+
violated_layers: [],
|
|
10894
|
+
policy: "",
|
|
10895
|
+
violated_policy: "\u672A\u77E5\u5F02\u5E38",
|
|
10896
|
+
confidence: 100
|
|
10897
|
+
};
|
|
10898
|
+
mossLog.logResult("Deny", result.reason);
|
|
10899
|
+
return result;
|
|
10900
|
+
}
|
|
10901
|
+
}
|
|
10902
|
+
async function analyzeInner(input, mossLog) {
|
|
10903
|
+
mossLog.logInput(input.promptSession, input.actionHistory || [], input.aNext, input.reason || "");
|
|
10904
|
+
mossLog.startStep("step1_security_judge");
|
|
10905
|
+
const judgment = await runAnalysis(input);
|
|
10906
|
+
mossLog.endStep(
|
|
10907
|
+
"step1_security_judge",
|
|
10908
|
+
`allowed=${judgment.allowed}, risk_level=${judgment.riskLevel}, source=${judgment.source}`
|
|
10909
|
+
);
|
|
10910
|
+
if (!judgment.allowed) {
|
|
10911
|
+
const denied = {
|
|
10912
|
+
decision: "Deny",
|
|
10913
|
+
reason: judgment.reason,
|
|
10914
|
+
risk_level: judgment.riskLevel,
|
|
10915
|
+
risk_type: judgment.riskType,
|
|
10916
|
+
violated_layers: judgment.violatedLayers || [],
|
|
10917
|
+
policy: "",
|
|
10918
|
+
violated_policy: `[${judgment.riskType}] ${judgment.reason}`,
|
|
10919
|
+
confidence: judgment.confidence
|
|
10920
|
+
};
|
|
10921
|
+
mossLog.logResult("Deny", denied.reason);
|
|
10922
|
+
return denied;
|
|
10923
|
+
}
|
|
10924
|
+
const cache3 = getDefaultCache();
|
|
10925
|
+
mossLog.startStep("step2_cache_lookup");
|
|
10926
|
+
const cached2 = cache3.get(input.sessionId, input.promptSession);
|
|
10927
|
+
if (cached2 !== null) {
|
|
10928
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u547D\u4E2D");
|
|
10929
|
+
savePolicyToFile(input.sessionId, cached2);
|
|
10930
|
+
const res = allowResult(cached2, judgment);
|
|
10931
|
+
mossLog.logResult("Allow", res.reason, cached2);
|
|
10932
|
+
return res;
|
|
10933
|
+
}
|
|
10934
|
+
if (!isPolicyGenEnabled()) {
|
|
10935
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u672A\u547D\u4E2D\uFF0CPolicy \u751F\u6210\u5DF2\u7981\u7528");
|
|
10936
|
+
mossLog.startStep("step2_default_policy");
|
|
10937
|
+
cache3.put(input.sessionId, input.promptSession, DEFAULT_READONLY_POLICY);
|
|
10938
|
+
savePolicyToFile(input.sessionId, DEFAULT_READONLY_POLICY);
|
|
10939
|
+
const res = allowResult(DEFAULT_READONLY_POLICY, judgment);
|
|
10940
|
+
mossLog.endStep("step2_default_policy", "\u4F7F\u7528\u9ED8\u8BA4\u6700\u5C0F\u6743\u9650 Policy");
|
|
10941
|
+
mossLog.logResult("Allow", res.reason, DEFAULT_READONLY_POLICY);
|
|
10942
|
+
return res;
|
|
10943
|
+
}
|
|
10944
|
+
if (judgment.source === "whitelist_bypass") {
|
|
10945
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u672A\u547D\u4E2D\uFF0C\u767D\u540D\u5355\u5DE5\u5177");
|
|
10946
|
+
cache3.put(input.sessionId, input.promptSession, DEFAULT_READONLY_POLICY);
|
|
10947
|
+
savePolicyToFile(input.sessionId, DEFAULT_READONLY_POLICY);
|
|
10948
|
+
const res = allowResult(DEFAULT_READONLY_POLICY, judgment);
|
|
10949
|
+
mossLog.logResult("Allow", res.reason, DEFAULT_READONLY_POLICY);
|
|
10950
|
+
return res;
|
|
10951
|
+
}
|
|
10952
|
+
mossLog.endStep("step2_cache_lookup", "\u7F13\u5B58\u672A\u547D\u4E2D\uFF0C\u542F\u7528 LLM \u751F\u6210");
|
|
10953
|
+
mossLog.startStep("step2_generate_policy");
|
|
10954
|
+
try {
|
|
10955
|
+
const policy = await generatePolicyWithLLM(input.sessionId, input.promptSession);
|
|
10956
|
+
cache3.put(input.sessionId, input.promptSession, policy);
|
|
10957
|
+
savePolicyToFile(input.sessionId, policy);
|
|
10958
|
+
mossLog.endStep("step2_generate_policy", "Policy \u751F\u6210\u5E76\u7F13\u5B58\u6210\u529F");
|
|
10959
|
+
const res = allowResult(policy, judgment);
|
|
10960
|
+
mossLog.logResult("Allow", res.reason, policy);
|
|
10961
|
+
return res;
|
|
10962
|
+
} catch (err) {
|
|
10963
|
+
mossLog.endStep("step2_generate_policy", `\u5931\u8D25: ${String(err)}`);
|
|
10964
|
+
mossLog.logError("step2_generate_policy", err);
|
|
10965
|
+
const failed = {
|
|
10966
|
+
decision: "Deny",
|
|
10967
|
+
reason: `\u5B89\u5168\u68C0\u6D4B\u901A\u8FC7\uFF0C\u4F46\u7B56\u7565\u751F\u6210\u5931\u8D25\uFF1A${String(err)}`,
|
|
10968
|
+
risk_level: judgment.riskLevel,
|
|
10969
|
+
risk_type: judgment.riskType,
|
|
10970
|
+
violated_layers: judgment.violatedLayers || [],
|
|
10971
|
+
policy: "",
|
|
10972
|
+
violated_policy: "\u65E0\u6CD5\u751F\u6210\u7B56\u7565\uFF1ALLM \u670D\u52A1\u5F02\u5E38\u6216\u8FD4\u56DE\u683C\u5F0F\u65E0\u6548",
|
|
10973
|
+
confidence: judgment.confidence
|
|
10974
|
+
};
|
|
10975
|
+
mossLog.logResult("Deny", failed.reason);
|
|
10976
|
+
return failed;
|
|
10977
|
+
}
|
|
10978
|
+
}
|
|
9827
10979
|
|
|
9828
10980
|
// src/actrail.ts
|
|
9829
10981
|
init_esm_shims();
|
|
@@ -10112,7 +11264,80 @@ function loadAgentGateSessions(dbPath = "") {
|
|
|
10112
11264
|
return readJsonDir(jsonDefault);
|
|
10113
11265
|
}
|
|
10114
11266
|
|
|
11267
|
+
// src/adapters/observable.ts
|
|
11268
|
+
init_esm_shims();
|
|
11269
|
+
function isRawFormat(data) {
|
|
11270
|
+
if (!data || typeof data !== "object") return false;
|
|
11271
|
+
const d = data;
|
|
11272
|
+
if ("current_action" in d) return true;
|
|
11273
|
+
if (!("a_next" in d)) return true;
|
|
11274
|
+
if (!("session_id" in d) && "agent_id" in d) return true;
|
|
11275
|
+
return false;
|
|
11276
|
+
}
|
|
11277
|
+
function adaptObservable(rawData) {
|
|
11278
|
+
const sessionId = rawData.session_id || rawData.agent_id || "unknown";
|
|
11279
|
+
const actionRaw = rawData.a_next || rawData.current_action || {};
|
|
11280
|
+
const aNext = actionRaw && typeof actionRaw === "object" ? {
|
|
11281
|
+
action_type: actionRaw.action_type || actionRaw.tool || "unknown",
|
|
11282
|
+
action_detail: actionRaw.action_detail || actionRaw.command || ""
|
|
11283
|
+
} : { action_type: "unknown", action_detail: "" };
|
|
11284
|
+
const historyRaw = Array.isArray(rawData.action_history) ? rawData.action_history : [];
|
|
11285
|
+
const actionHistory = historyRaw.filter((h) => h && typeof h === "object").map((h) => {
|
|
11286
|
+
const item = {
|
|
11287
|
+
action_type: String(h.action_type || h.name || h.tool || ""),
|
|
11288
|
+
action_detail: String(h.action_detail || h.command || "")
|
|
11289
|
+
};
|
|
11290
|
+
if ("output" in h) item.output = h.output ?? "";
|
|
11291
|
+
return item;
|
|
11292
|
+
});
|
|
11293
|
+
const result = {
|
|
11294
|
+
session_id: sessionId,
|
|
11295
|
+
prompt_session: rawData.prompt_session || rawData.prompt || "",
|
|
11296
|
+
action_history: actionHistory,
|
|
11297
|
+
a_next: aNext,
|
|
11298
|
+
reason: rawData.reason || (actionRaw && typeof actionRaw === "object" ? actionRaw.description : "") || "",
|
|
11299
|
+
os_type: rawData.os_type || "",
|
|
11300
|
+
cwd: rawData.cwd || "",
|
|
11301
|
+
agent_id: rawData.agent_id || ""
|
|
11302
|
+
};
|
|
11303
|
+
if (!result.reason && actionRaw && typeof actionRaw === "object") {
|
|
11304
|
+
result.reason = actionRaw.description || "";
|
|
11305
|
+
}
|
|
11306
|
+
let metadata = rawData.metadata;
|
|
11307
|
+
if (!metadata || typeof metadata !== "object" || Object.keys(metadata).length === 0) {
|
|
11308
|
+
metadata = {};
|
|
11309
|
+
for (const k of ["agent_id", "sandbox_id", "trace_id"]) {
|
|
11310
|
+
if (k in rawData && !(k in result)) metadata[k] = rawData[k];
|
|
11311
|
+
}
|
|
11312
|
+
}
|
|
11313
|
+
if (metadata && Object.keys(metadata).length > 0) result.metadata = metadata;
|
|
11314
|
+
return result;
|
|
11315
|
+
}
|
|
11316
|
+
function normalizeObservablePayload(body) {
|
|
11317
|
+
if (!isRawFormat(body)) return body;
|
|
11318
|
+
const adapted = adaptObservable(body);
|
|
11319
|
+
const actionType = String(adapted.a_next?.action_type || "").trim().toLowerCase();
|
|
11320
|
+
const actionDetail = String(adapted.a_next?.action_detail || "").trim();
|
|
11321
|
+
if (!actionDetail && (actionType === "" || actionType === "unknown")) {
|
|
11322
|
+
const err = new Error(
|
|
11323
|
+
"\u65E0\u6CD5\u8BC6\u522B\u672C\u6B21\u52A8\u4F5C\uFF1A\u65E2\u6CA1\u6709 a_next\uFF08\u6807\u51C6\u683C\u5F0F\uFF09\uFF0C\u4E5F\u6CA1\u6709 current_action\uFF08\u53EF\u89C2\u6D4B\u670D\u52A1\u539F\u59CB\u683C\u5F0F\uFF09"
|
|
11324
|
+
);
|
|
11325
|
+
err.issues = [{
|
|
11326
|
+
path: ["a_next"],
|
|
11327
|
+
message: err.message
|
|
11328
|
+
}];
|
|
11329
|
+
throw err;
|
|
11330
|
+
}
|
|
11331
|
+
return adapted;
|
|
11332
|
+
}
|
|
11333
|
+
|
|
10115
11334
|
// src/routes.ts
|
|
11335
|
+
init_rule_store();
|
|
11336
|
+
init_skill_manager();
|
|
11337
|
+
init_asset_dir();
|
|
11338
|
+
import { join as join13, dirname as dirname11 } from "path";
|
|
11339
|
+
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
11340
|
+
var __dirname9 = dirname11(fileURLToPath10(import.meta.url));
|
|
10116
11341
|
async function healthRoute(c) {
|
|
10117
11342
|
return c.json(HealthResponseSchema.parse({
|
|
10118
11343
|
instance: process.env.AGENT_MOSS_INSTANCE || ""
|
|
@@ -10122,13 +11347,13 @@ async function analyzeRoute(c) {
|
|
|
10122
11347
|
const t0 = performance.now();
|
|
10123
11348
|
try {
|
|
10124
11349
|
const body = await c.req.json();
|
|
10125
|
-
const req = AnalyzeRequestSchema.parse(body);
|
|
11350
|
+
const req = AnalyzeRequestSchema.parse(normalizeObservablePayload(body));
|
|
10126
11351
|
let osType = req.os_type;
|
|
10127
11352
|
if (!osType) {
|
|
10128
11353
|
osType = process.platform === "win32" ? "windows" : "linux";
|
|
10129
11354
|
}
|
|
10130
11355
|
const agentId = req.agent_id || req.metadata?.agent_type || "";
|
|
10131
|
-
const result = await
|
|
11356
|
+
const result = await analyze({
|
|
10132
11357
|
sessionId: req.session_id,
|
|
10133
11358
|
promptSession: req.prompt_session,
|
|
10134
11359
|
// output 必须透传:L2 的「授权(consent)」判定看的是**用户对提问的回答**,
|
|
@@ -10148,6 +11373,20 @@ async function analyzeRoute(c) {
|
|
|
10148
11373
|
output: h.output
|
|
10149
11374
|
}))
|
|
10150
11375
|
})),
|
|
11376
|
+
// prompt_history 必须透传:L2 意图一致性(logic-rules.ts 的
|
|
11377
|
+
// checkIntentConsistency)与 L3 语义分析(llm-analyzer.ts 的
|
|
11378
|
+
// formatPromptHistory)都靠它看**完整的交错历史**;只传 prompt_session
|
|
11379
|
+
// (最后一条用户消息)会让这两处在压缩/续跑场景下失明。
|
|
11380
|
+
// 历史 bug:请求模型里有这个字段,但路由的 runAnalysis 调用没映射它 →
|
|
11381
|
+
// 经 TS HTTP 入口的判定与 Python HTTP(routes.py 有透传)、与库内直调
|
|
11382
|
+
// runAnalysis 的结果都不一致(2026-10 修正,映射口径对齐 Python)。
|
|
11383
|
+
promptHistory: (req.prompt_history || []).map((t) => ({
|
|
11384
|
+
text: t.text,
|
|
11385
|
+
actions: t.actions.map((a) => ({
|
|
11386
|
+
actionType: a.action_type,
|
|
11387
|
+
actionDetail: a.action_detail
|
|
11388
|
+
}))
|
|
11389
|
+
})),
|
|
10151
11390
|
aNext: {
|
|
10152
11391
|
actionType: req.a_next.action_type,
|
|
10153
11392
|
actionDetail: req.a_next.action_detail
|
|
@@ -10165,15 +11404,15 @@ async function analyzeRoute(c) {
|
|
|
10165
11404
|
try {
|
|
10166
11405
|
reportEnforcementEvent({
|
|
10167
11406
|
traceId: req.session_id,
|
|
10168
|
-
decision: result.
|
|
11407
|
+
decision: result.decision === "Allow" ? "allow" : "deny",
|
|
10169
11408
|
toolName: req.a_next.action_type,
|
|
10170
11409
|
toolCommand: req.a_next.action_detail,
|
|
10171
11410
|
sessionId: req.session_id,
|
|
10172
11411
|
prompt: req.prompt_session,
|
|
10173
|
-
riskLevel: result.
|
|
10174
|
-
riskType: result.
|
|
11412
|
+
riskLevel: result.risk_level,
|
|
11413
|
+
riskType: result.risk_type,
|
|
10175
11414
|
reason: result.reason,
|
|
10176
|
-
violatedLayers: result.
|
|
11415
|
+
violatedLayers: result.violated_layers,
|
|
10177
11416
|
confidence: result.confidence,
|
|
10178
11417
|
durationMs: Math.round(durationMs * 10) / 10
|
|
10179
11418
|
});
|
|
@@ -10190,7 +11429,7 @@ async function analyzeRoute(c) {
|
|
|
10190
11429
|
}, req.session_id, Boolean(meta.is_error));
|
|
10191
11430
|
const judgment = incMatchedToDeny(inc);
|
|
10192
11431
|
if (judgment) {
|
|
10193
|
-
const layers = [...result.
|
|
11432
|
+
const layers = [...result.violated_layers];
|
|
10194
11433
|
if (!layers.includes("L2.2-stateful")) layers.push("L2.2-stateful");
|
|
10195
11434
|
console.error(
|
|
10196
11435
|
`[AgentMoss] stateful \u589E\u91CF\u62E6\u622A: agent=${agentId || "-"} property=${judgment.property_id} risk=${judgment.risk_level}`
|
|
@@ -10216,13 +11455,15 @@ async function analyzeRoute(c) {
|
|
|
10216
11455
|
}
|
|
10217
11456
|
return c.json(
|
|
10218
11457
|
AnalyzeResponseSchema.parse({
|
|
10219
|
-
decision: result.
|
|
11458
|
+
decision: result.decision,
|
|
10220
11459
|
reason: result.reason,
|
|
10221
|
-
risk_level: result.
|
|
10222
|
-
risk_type: result.
|
|
10223
|
-
violated_layers: result.
|
|
10224
|
-
policy
|
|
10225
|
-
violated_policy
|
|
11460
|
+
risk_level: result.risk_level,
|
|
11461
|
+
risk_type: result.risk_type,
|
|
11462
|
+
violated_layers: result.violated_layers,
|
|
11463
|
+
// policy / violated_policy 现在真的有值(镜像 Python analyze()):
|
|
11464
|
+
// Allow → 最小权限 TOML;Deny → 空 policy + violated_policy 描述
|
|
11465
|
+
policy: result.policy,
|
|
11466
|
+
violated_policy: result.violated_policy,
|
|
10226
11467
|
confidence: result.confidence,
|
|
10227
11468
|
analysis_duration_ms: Math.round(durationMs * 10) / 10
|
|
10228
11469
|
})
|
|
@@ -10230,12 +11471,12 @@ async function analyzeRoute(c) {
|
|
|
10230
11471
|
} catch (err) {
|
|
10231
11472
|
console.error("[AgentMoss] Analysis error:", err);
|
|
10232
11473
|
if (err instanceof SyntaxError) {
|
|
10233
|
-
return c.json({ error: "Invalid JSON"
|
|
11474
|
+
return c.json({ detail: err.message, error: "Invalid JSON" }, 422);
|
|
10234
11475
|
}
|
|
10235
11476
|
if (err?.issues) {
|
|
10236
|
-
return c.json({ error: "Validation error"
|
|
11477
|
+
return c.json({ detail: err.issues, error: "Validation error" }, 422);
|
|
10237
11478
|
}
|
|
10238
|
-
return c.json({ error: "Internal error"
|
|
11479
|
+
return c.json({ detail: String(err), error: "Internal error" }, 500);
|
|
10239
11480
|
}
|
|
10240
11481
|
}
|
|
10241
11482
|
var reviewStore = null;
|
|
@@ -10293,13 +11534,25 @@ async function reviewRoute(c) {
|
|
|
10293
11534
|
const violations = [];
|
|
10294
11535
|
if (sessions.length > 0 || tracker.nodes.length > 0) {
|
|
10295
11536
|
const eng = new FormalEngine();
|
|
11537
|
+
const verifySessions = sessions.length > 0 ? sessions.map((s) => ({ sessionId: s.sessionId, actionHistory: s.actionHistory })) : tracker.nodes.length > 0 ? [{
|
|
11538
|
+
sessionId: tracker.agentId,
|
|
11539
|
+
actionHistory: tracker.nodes.map((n) => ({
|
|
11540
|
+
actionType: n.actionType,
|
|
11541
|
+
actionDetail: n.actionDetail
|
|
11542
|
+
}))
|
|
11543
|
+
}] : [];
|
|
10296
11544
|
const full = eng.verify(
|
|
10297
|
-
|
|
11545
|
+
verifySessions,
|
|
10298
11546
|
req.a_next ? { actionType: req.a_next.action_type, actionDetail: req.a_next.action_detail } : void 0
|
|
10299
11547
|
);
|
|
10300
11548
|
verified = full.verified;
|
|
10301
11549
|
for (const v of full.violations) {
|
|
10302
|
-
violations.push({
|
|
11550
|
+
violations.push({
|
|
11551
|
+
property_id: v.propertyId,
|
|
11552
|
+
reason: v.reason,
|
|
11553
|
+
risk_level: v.riskLevel,
|
|
11554
|
+
path: v.path
|
|
11555
|
+
});
|
|
10303
11556
|
}
|
|
10304
11557
|
}
|
|
10305
11558
|
const pending = tracker.pendingPrefixes.map((p) => ({
|
|
@@ -10329,6 +11582,101 @@ async function reviewRoute(c) {
|
|
|
10329
11582
|
return c.json({ error: "Internal error", detail: String(err) }, 500);
|
|
10330
11583
|
}
|
|
10331
11584
|
}
|
|
11585
|
+
function getBrainRuleStore() {
|
|
11586
|
+
return new BrainRuleStore();
|
|
11587
|
+
}
|
|
11588
|
+
function getBrainSkillManager() {
|
|
11589
|
+
const dir = resolveAssetDir(__dirname9, "skills") ?? join13(__dirname9, "..", "..", "agent_moss", "skills");
|
|
11590
|
+
return new BrainSkillManager(dir);
|
|
11591
|
+
}
|
|
11592
|
+
async function brainRulesRoute(c) {
|
|
11593
|
+
const store = getBrainRuleStore();
|
|
11594
|
+
const layerRaw = c.req.query("layer");
|
|
11595
|
+
const layer = layerRaw ? parseInt(layerRaw, 10) : void 0;
|
|
11596
|
+
const rules = store.getActiveRules(layer);
|
|
11597
|
+
return c.json({
|
|
11598
|
+
rules: rules.map((r) => ({
|
|
11599
|
+
id: r.id,
|
|
11600
|
+
layer: r.layer,
|
|
11601
|
+
rule_type: r.ruleType,
|
|
11602
|
+
pattern: r.pattern,
|
|
11603
|
+
scope: r.scope,
|
|
11604
|
+
confidence: r.confidence,
|
|
11605
|
+
explanation: r.explanation
|
|
11606
|
+
}))
|
|
11607
|
+
});
|
|
11608
|
+
}
|
|
11609
|
+
async function brainDraftsRoute(c) {
|
|
11610
|
+
const store = getBrainRuleStore();
|
|
11611
|
+
return c.json({
|
|
11612
|
+
drafts: store.getDraftRules().map((r) => ({
|
|
11613
|
+
id: r.id,
|
|
11614
|
+
layer: r.layer,
|
|
11615
|
+
rule_type: r.ruleType,
|
|
11616
|
+
pattern: r.pattern,
|
|
11617
|
+
scope: r.scope,
|
|
11618
|
+
confidence: r.confidence,
|
|
11619
|
+
explanation: r.explanation,
|
|
11620
|
+
source_event_ids: r.sourceEventIds
|
|
11621
|
+
}))
|
|
11622
|
+
});
|
|
11623
|
+
}
|
|
11624
|
+
async function brainApproveRoute(c) {
|
|
11625
|
+
const ruleId = c.req.param("rule_id") ?? "";
|
|
11626
|
+
try {
|
|
11627
|
+
getBrainRuleStore().approveRule(ruleId);
|
|
11628
|
+
return c.json({ status: "approved" });
|
|
11629
|
+
} catch (err) {
|
|
11630
|
+
return c.json({ detail: String(err.message) }, 400);
|
|
11631
|
+
}
|
|
11632
|
+
}
|
|
11633
|
+
async function brainRejectRoute(c) {
|
|
11634
|
+
const ruleId = c.req.param("rule_id") ?? "";
|
|
11635
|
+
const reason = c.req.query("reason") || "";
|
|
11636
|
+
try {
|
|
11637
|
+
getBrainRuleStore().rejectRule(ruleId);
|
|
11638
|
+
return c.json({ status: "rejected" });
|
|
11639
|
+
} catch (err) {
|
|
11640
|
+
return c.json({ detail: String(err.message) }, 400);
|
|
11641
|
+
}
|
|
11642
|
+
}
|
|
11643
|
+
async function brainStatsRoute(c) {
|
|
11644
|
+
return c.json(getBrainRuleStore().getStats());
|
|
11645
|
+
}
|
|
11646
|
+
async function brainSkillsRoute(c) {
|
|
11647
|
+
const mgr = getBrainSkillManager();
|
|
11648
|
+
const skills = mgr.getActiveSkills();
|
|
11649
|
+
mgr.close();
|
|
11650
|
+
return c.json({ skills });
|
|
11651
|
+
}
|
|
11652
|
+
async function brainExportRoute(c) {
|
|
11653
|
+
const store = getBrainRuleStore();
|
|
11654
|
+
return c.json({
|
|
11655
|
+
rules: store.getRulesForExport().map((r) => ({
|
|
11656
|
+
id: r.id,
|
|
11657
|
+
layer: r.layer,
|
|
11658
|
+
rule_type: r.ruleType,
|
|
11659
|
+
pattern: r.pattern,
|
|
11660
|
+
scope: r.scope,
|
|
11661
|
+
confidence: r.confidence,
|
|
11662
|
+
explanation: r.explanation,
|
|
11663
|
+
origin_node: r.originNode
|
|
11664
|
+
}))
|
|
11665
|
+
});
|
|
11666
|
+
}
|
|
11667
|
+
async function brainImportRoute(c) {
|
|
11668
|
+
const body = await c.req.json();
|
|
11669
|
+
const rules = Array.isArray(body) ? body : [];
|
|
11670
|
+
getBrainRuleStore().importRules(rules.map((r) => ({
|
|
11671
|
+
layer: r.layer ?? 1,
|
|
11672
|
+
ruleType: r.rule_type ?? "",
|
|
11673
|
+
pattern: r.pattern ?? "",
|
|
11674
|
+
scope: r.scope ?? "all",
|
|
11675
|
+
confidence: r.confidence ?? 0.9,
|
|
11676
|
+
explanation: r.explanation ?? ""
|
|
11677
|
+
})));
|
|
11678
|
+
return c.json({ imported: rules.length });
|
|
11679
|
+
}
|
|
10332
11680
|
|
|
10333
11681
|
// src/console/index.ts
|
|
10334
11682
|
init_esm_shims();
|
|
@@ -10338,12 +11686,14 @@ init_esm_shims();
|
|
|
10338
11686
|
init_runtime_config();
|
|
10339
11687
|
init_token_stats();
|
|
10340
11688
|
init_rules_loader();
|
|
11689
|
+
init_asset_dir();
|
|
11690
|
+
init_rules_mode();
|
|
10341
11691
|
import { Hono } from "hono";
|
|
10342
|
-
import { readFileSync as
|
|
10343
|
-
import { join as
|
|
10344
|
-
import { fileURLToPath as
|
|
10345
|
-
var
|
|
10346
|
-
var STATIC_DIR =
|
|
11692
|
+
import { readFileSync as readFileSync18, existsSync as existsSync18, unlinkSync as unlinkSync4 } from "fs";
|
|
11693
|
+
import { join as join14, dirname as dirname12 } from "path";
|
|
11694
|
+
import { fileURLToPath as fileURLToPath11 } from "url";
|
|
11695
|
+
var __dirname10 = dirname12(fileURLToPath11(import.meta.url));
|
|
11696
|
+
var STATIC_DIR = join14(__dirname10, "static");
|
|
10347
11697
|
var CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
|
|
10348
11698
|
function getConsoleToken() {
|
|
10349
11699
|
return process.env[CONSOLE_TOKEN_ENV] || "";
|
|
@@ -10358,7 +11708,20 @@ function verifyAuth(req) {
|
|
|
10358
11708
|
function authMiddleware() {
|
|
10359
11709
|
return async (c, next) => {
|
|
10360
11710
|
if (!verifyAuth(c.req.raw)) {
|
|
10361
|
-
return c.json({
|
|
11711
|
+
return c.json({ detail: "Unauthorized: invalid or missing Bearer token" }, 401);
|
|
11712
|
+
}
|
|
11713
|
+
await next();
|
|
11714
|
+
};
|
|
11715
|
+
}
|
|
11716
|
+
function displayRuntimeConfig() {
|
|
11717
|
+
return isRuntimeConfigDisabled() ? generateSourceDefaults() : loadRuntimeConfig();
|
|
11718
|
+
}
|
|
11719
|
+
function builtinOnlyReadOnlyMiddleware() {
|
|
11720
|
+
return async (c, next) => {
|
|
11721
|
+
if (isRuntimeConfigDisabled() && !["GET", "HEAD", "OPTIONS"].includes(c.req.method)) {
|
|
11722
|
+
return c.json({
|
|
11723
|
+
detail: `\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u4E3A ${getRulesMode()}\uFF08runtime \u914D\u7F6E\u5DF2\u7981\u7528\uFF09\uFF1AConsole \u53EA\u8BFB\uFF0C\u4FEE\u6539\u4E0D\u4F1A\u4FDD\u5B58\u3002\u8BF7\u5207\u6362\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u5E76\u91CD\u542F\u670D\u52A1\u540E\u518D\u6539\u89C4\u5219\u3002`
|
|
11724
|
+
}, 403);
|
|
10362
11725
|
}
|
|
10363
11726
|
await next();
|
|
10364
11727
|
};
|
|
@@ -10366,23 +11729,34 @@ function authMiddleware() {
|
|
|
10366
11729
|
function createConsoleRouter() {
|
|
10367
11730
|
const console2 = new Hono();
|
|
10368
11731
|
console2.use("/api/*", authMiddleware());
|
|
11732
|
+
console2.use("/api/*", builtinOnlyReadOnlyMiddleware());
|
|
10369
11733
|
const serveIndex = (c) => {
|
|
10370
|
-
const indexFile =
|
|
10371
|
-
if (
|
|
10372
|
-
return c.html(
|
|
11734
|
+
const indexFile = join14(STATIC_DIR, "index.html");
|
|
11735
|
+
if (existsSync18(indexFile)) {
|
|
11736
|
+
return c.html(readFileSync18(indexFile, "utf-8"));
|
|
10373
11737
|
}
|
|
10374
11738
|
return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
|
|
10375
11739
|
};
|
|
10376
11740
|
console2.get("/", serveIndex);
|
|
10377
11741
|
console2.get("", serveIndex);
|
|
10378
11742
|
console2.get("/api/layers", (c) => {
|
|
10379
|
-
const rc =
|
|
10380
|
-
return c.json({ layers: rc.layers, env_overrides: getEnvOverrides() });
|
|
11743
|
+
const rc = displayRuntimeConfig();
|
|
11744
|
+
return c.json({ layers: rc.layers, env_overrides: getEnvOverrides(), builtin_only: isBuiltinOnly(), rules_mode: getRulesMode() });
|
|
10381
11745
|
});
|
|
10382
11746
|
console2.put("/api/layers", async (c) => {
|
|
10383
11747
|
const body = await c.req.json();
|
|
11748
|
+
const validKeys = /* @__PURE__ */ new Set(["L1_heuristic", "L2_logic_rules", "L3_llm_analysis"]);
|
|
11749
|
+
if (!validKeys.has(body.layer_key)) {
|
|
11750
|
+
return c.json({ detail: `Invalid layer_key: ${body.layer_key}` }, 400);
|
|
11751
|
+
}
|
|
11752
|
+
const envOverrides = getEnvOverrides();
|
|
11753
|
+
if (envOverrides[body.layer_key]?.overridden) {
|
|
11754
|
+
return c.json({
|
|
11755
|
+
detail: `\u5C42\u7EA7 ${body.layer_key} \u88AB\u73AF\u5883\u53D8\u91CF ${envOverrides[body.layer_key].env_var}=1 \u5F3A\u5236\u8986\u76D6\uFF0CWeb \u8BBE\u7F6E\u65E0\u6548`
|
|
11756
|
+
}, 409);
|
|
11757
|
+
}
|
|
10384
11758
|
const rc = updateLayerEnabled(body.layer_key, body.enabled);
|
|
10385
|
-
return c.json({ layers: rc.layers, env_overrides:
|
|
11759
|
+
return c.json({ layers: rc.layers, env_overrides: envOverrides });
|
|
10386
11760
|
});
|
|
10387
11761
|
function getL22View() {
|
|
10388
11762
|
const ltlHuman = {
|
|
@@ -10402,7 +11776,7 @@ function createConsoleRouter() {
|
|
|
10402
11776
|
const rules = mc.rules || [];
|
|
10403
11777
|
let catEnabled = true;
|
|
10404
11778
|
try {
|
|
10405
|
-
const rt =
|
|
11779
|
+
const rt = displayRuntimeConfig();
|
|
10406
11780
|
catEnabled = rt.L2_2?.ltl_rules?.category_enabled !== false;
|
|
10407
11781
|
} catch {
|
|
10408
11782
|
}
|
|
@@ -10458,8 +11832,22 @@ function createConsoleRouter() {
|
|
|
10458
11832
|
return rc;
|
|
10459
11833
|
}
|
|
10460
11834
|
console2.get("/api/rules", (c) => {
|
|
10461
|
-
const rc =
|
|
10462
|
-
|
|
11835
|
+
const rc = displayRuntimeConfig();
|
|
11836
|
+
const layer = c.req.query("layer");
|
|
11837
|
+
const category = c.req.query("category");
|
|
11838
|
+
const out = {};
|
|
11839
|
+
for (const layerKey of ["L1_rules", "L2_rules"]) {
|
|
11840
|
+
if (layer && layer !== layerKey) continue;
|
|
11841
|
+
const layerData = rc[layerKey] || {};
|
|
11842
|
+
const picked = {};
|
|
11843
|
+
for (const [catName, catData] of Object.entries(layerData)) {
|
|
11844
|
+
if (category && category !== catName) continue;
|
|
11845
|
+
picked[catName] = catData;
|
|
11846
|
+
}
|
|
11847
|
+
out[layerKey] = picked;
|
|
11848
|
+
}
|
|
11849
|
+
if (!category) out.L2_2 = getL22View();
|
|
11850
|
+
return c.json(out);
|
|
10463
11851
|
});
|
|
10464
11852
|
console2.put("/api/rules/enabled", async (c) => {
|
|
10465
11853
|
const b = await c.req.json();
|
|
@@ -10468,6 +11856,9 @@ function createConsoleRouter() {
|
|
|
10468
11856
|
});
|
|
10469
11857
|
console2.put("/api/rules/deny_mode", async (c) => {
|
|
10470
11858
|
const b = await c.req.json();
|
|
11859
|
+
if (!["deny_write", "deny_read", "deny_both"].includes(b.deny_mode)) {
|
|
11860
|
+
return c.json({ detail: `\u65E0\u6548\u7684 deny_mode: ${b.deny_mode}` }, 400);
|
|
11861
|
+
}
|
|
10471
11862
|
const rc = updateRuleDenyMode(b.layer, b.category, b.rule_id, b.deny_mode);
|
|
10472
11863
|
return c.json(withL22View(rc));
|
|
10473
11864
|
});
|
|
@@ -10499,13 +11890,15 @@ function createConsoleRouter() {
|
|
|
10499
11890
|
});
|
|
10500
11891
|
console2.delete("/api/rules", async (c) => {
|
|
10501
11892
|
const b = await c.req.json();
|
|
10502
|
-
deleteCustomRule(b.layer, b.category, b.rule_id);
|
|
10503
|
-
|
|
11893
|
+
const rc = deleteCustomRule(b.layer, b.category, b.rule_id);
|
|
11894
|
+
if (rc === null) {
|
|
11895
|
+
return c.json({ detail: "\u5185\u7F6E\u89C4\u5219\u4E0D\u53EF\u5220\u9664\uFF0C\u53EA\u80FD\u7981\u7528" }, 403);
|
|
11896
|
+
}
|
|
10504
11897
|
return c.json(withL22View(rc));
|
|
10505
11898
|
});
|
|
10506
11899
|
console2.get("/api/skills", (c) => {
|
|
10507
|
-
const rc =
|
|
10508
|
-
return c.json(rc.L3_skills);
|
|
11900
|
+
const rc = displayRuntimeConfig();
|
|
11901
|
+
return c.json(rc.L3_skills || {});
|
|
10509
11902
|
});
|
|
10510
11903
|
console2.put("/api/skills/enabled", async (c) => {
|
|
10511
11904
|
const b = await c.req.json();
|
|
@@ -10524,25 +11917,32 @@ function createConsoleRouter() {
|
|
|
10524
11917
|
});
|
|
10525
11918
|
console2.delete("/api/skills", async (c) => {
|
|
10526
11919
|
const b = await c.req.json();
|
|
10527
|
-
deleteCustomSkill(b.skill_id, b.category);
|
|
10528
|
-
|
|
11920
|
+
const rc = deleteCustomSkill(b.skill_id, b.category);
|
|
11921
|
+
if (rc === null) {
|
|
11922
|
+
return c.json({ detail: "\u5185\u7F6E skill \u4E0D\u53EF\u5220\u9664\uFF0C\u53EA\u80FD\u7981\u7528" }, 403);
|
|
11923
|
+
}
|
|
10529
11924
|
return c.json(withL22View(rc));
|
|
10530
11925
|
});
|
|
10531
11926
|
console2.get("/api/skills/:skill_id/content", async (c) => {
|
|
10532
11927
|
const skillId = c.req.param("skill_id");
|
|
10533
11928
|
if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
|
|
10534
|
-
return c.json({
|
|
11929
|
+
return c.json({ detail: "\u975E\u6CD5 skill id" }, 400);
|
|
10535
11930
|
}
|
|
10536
|
-
const
|
|
10537
|
-
|
|
10538
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
11931
|
+
const userPath = join14(dirname12(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
|
|
11932
|
+
const builtinDir = resolveAssetDir(__dirname10, "skills");
|
|
11933
|
+
const candidates = [
|
|
11934
|
+
...isRuntimeConfigDisabled() ? [] : [userPath],
|
|
11935
|
+
builtinDir ? join14(builtinDir, `${skillId}.md`) : ""
|
|
11936
|
+
].filter(Boolean);
|
|
11937
|
+
for (const p of candidates) {
|
|
11938
|
+
if (existsSync18(p)) {
|
|
11939
|
+
return c.json({ skill_id: skillId, path: p, content: readFileSync18(p, "utf-8") });
|
|
11940
|
+
}
|
|
10541
11941
|
}
|
|
10542
|
-
return c.
|
|
11942
|
+
return c.json({ detail: `skill ${skillId} \u4E0D\u5B58\u5728` }, 404);
|
|
10543
11943
|
});
|
|
10544
11944
|
console2.get("/api/agents", (c) => {
|
|
10545
|
-
const rc =
|
|
11945
|
+
const rc = displayRuntimeConfig();
|
|
10546
11946
|
return c.json({ agents: getAgentRules(rc) });
|
|
10547
11947
|
});
|
|
10548
11948
|
console2.put("/api/agents/rules/enabled", async (c) => {
|
|
@@ -10581,7 +11981,7 @@ function createConsoleRouter() {
|
|
|
10581
11981
|
});
|
|
10582
11982
|
const enabledScenarioIds = () => (process.env.AGENT_MOSS_SCENARIO || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
10583
11983
|
console2.get("/api/scenarios", (c) => {
|
|
10584
|
-
const all = getScenarioRules(
|
|
11984
|
+
const all = getScenarioRules(displayRuntimeConfig());
|
|
10585
11985
|
const enabled = enabledScenarioIds();
|
|
10586
11986
|
if (!enabled.length) return c.json({ scenarios: {}, enabled: [] });
|
|
10587
11987
|
const shown = {};
|
|
@@ -10623,20 +12023,27 @@ function createConsoleRouter() {
|
|
|
10623
12023
|
return c.json({ scenarios: getScenarioRules(rc), enabled: enabledScenarioIds() });
|
|
10624
12024
|
});
|
|
10625
12025
|
console2.get("/api/config", (c) => {
|
|
10626
|
-
const rc =
|
|
10627
|
-
return c.json({ ...rc, L2_2: getL22View() });
|
|
12026
|
+
const rc = displayRuntimeConfig();
|
|
12027
|
+
return c.json({ ...rc, L2_2: getL22View(), builtin_only: isBuiltinOnly(), rules_mode: getRulesMode() });
|
|
10628
12028
|
});
|
|
10629
12029
|
console2.get("/api/env-overrides", (c) => {
|
|
10630
12030
|
return c.json({ overrides: getEnvOverrides() });
|
|
10631
12031
|
});
|
|
10632
12032
|
console2.post("/api/reset", (c) => {
|
|
10633
12033
|
const defaults = generateSourceDefaults();
|
|
10634
|
-
|
|
12034
|
+
try {
|
|
12035
|
+
const p = getRuntimeConfigPath();
|
|
12036
|
+
if (existsSync18(p)) unlinkSync4(p);
|
|
12037
|
+
} catch {
|
|
12038
|
+
}
|
|
10635
12039
|
return c.json(withL22View(defaults));
|
|
10636
12040
|
});
|
|
12041
|
+
const parseDays = (v) => {
|
|
12042
|
+
const n = parseInt(v || "0", 10);
|
|
12043
|
+
return Number.isFinite(n) ? n : 0;
|
|
12044
|
+
};
|
|
10637
12045
|
console2.get("/api/token-stats", (c) => {
|
|
10638
|
-
|
|
10639
|
-
return c.json(getTokenStats(days));
|
|
12046
|
+
return c.json(getTokenStats(parseDays(c.req.query("days"))));
|
|
10640
12047
|
});
|
|
10641
12048
|
console2.get("/api/token-stats/recent", (c) => {
|
|
10642
12049
|
const limit = parseInt(c.req.query("limit") || "20", 10);
|
|
@@ -10644,47 +12051,69 @@ function createConsoleRouter() {
|
|
|
10644
12051
|
return c.json({ records: getRecentRecords(limit, offset), total: getRecentRecordsCount() });
|
|
10645
12052
|
});
|
|
10646
12053
|
console2.get("/api/token-stats/trend", (c) => {
|
|
10647
|
-
|
|
10648
|
-
return c.json(getTokenTrend(days));
|
|
12054
|
+
return c.json(getTokenTrend(parseDays(c.req.query("days"))));
|
|
10649
12055
|
});
|
|
10650
12056
|
console2.post("/api/token-stats/reset", (c) => {
|
|
10651
12057
|
resetTokenStats();
|
|
10652
|
-
return c.json({
|
|
12058
|
+
return c.json({ detail: "Token stats reset successfully" });
|
|
10653
12059
|
});
|
|
10654
12060
|
return console2;
|
|
10655
12061
|
}
|
|
10656
12062
|
|
|
10657
12063
|
// src/server.ts
|
|
10658
12064
|
init_runtime_config();
|
|
12065
|
+
init_rules_mode();
|
|
10659
12066
|
init_rules_loader();
|
|
10660
12067
|
function logDeploymentSelfCheck() {
|
|
10661
12068
|
try {
|
|
12069
|
+
const mode = getRulesMode();
|
|
12070
|
+
const builtinOnly = isBuiltinOnly();
|
|
12071
|
+
const runtimeDisabled = isRuntimeConfigDisabled();
|
|
10662
12072
|
console.log("================ AgentMoss \u90E8\u7F72\u81EA\u68C0 ================");
|
|
10663
|
-
|
|
10664
|
-
|
|
10665
|
-
|
|
10666
|
-
|
|
10667
|
-
|
|
10668
|
-
|
|
10669
|
-
|
|
10670
|
-
for (const p of overrides) console.error(` ${p}`);
|
|
12073
|
+
if (runtimeDisabled) {
|
|
12074
|
+
console.log(`runtime \u914D\u7F6E: ${getRuntimeConfigPath()}\uFF08**\u5DF2\u5FFD\u7565**\uFF1A\u89C4\u5219\u6765\u6E90\u6A21\u5F0F=${mode}\uFF09`);
|
|
12075
|
+
if (builtinOnly) {
|
|
12076
|
+
console.error(
|
|
12077
|
+
"\u89C4\u5219\u6765\u6E90: **\u4EC5\u5185\u7F6E** \u2014\u2014 \u4E0D\u8BFB runtime \u914D\u7F6E / Console \u81EA\u5B9A\u4E49 / \u89C4\u5219\u76EE\u5F55 / AGENT_MOSS_CUSTOM_RULES\uFF1B\u4E0D seed\u3001\u4E0D merge\u3001\u4E0D\u5199\u56DE\u3002"
|
|
12078
|
+
);
|
|
12079
|
+
} else {
|
|
10671
12080
|
console.error(
|
|
10672
|
-
"
|
|
12081
|
+
"\u89C4\u5219\u6765\u6E90: **\u51B7\u542F\u52A8\uFF08cold\uFF09** \u2014\u2014 \u542F\u52A8\u65F6\u8BFB\u53D6\u5185\u7F6E\u89C4\u5219 + \u89C4\u5219\u76EE\u5F55 / AGENT_MOSS_CUSTOM_RULES / \u573A\u666F\uFF0C\u4E4B\u540E\u51BB\u7ED3\uFF1B\u4E0D\u8BFB runtime \u914D\u7F6E / Console\uFF0C\u4E0D seed\u3001\u4E0D merge\u3001\u4E0D\u5199\u56DE\u3002"
|
|
10673
12082
|
);
|
|
12083
|
+
console.error("\u89C4\u5219\u76EE\u5F55\u5185\u5BB9\u6539\u52A8\u9700\u91CD\u542F\u670D\u52A1\u624D\u751F\u6548\u3002");
|
|
10674
12084
|
}
|
|
10675
|
-
|
|
12085
|
+
console.error("Console: \u53EA\u8BFB\uFF08\u89C4\u5219\u5199\u63A5\u53E3\u8FD4\u56DE 403\uFF09\uFF1B\u4FEE\u6539\u89C4\u5219\u9700\u91CD\u542F\u670D\u52A1\u5E76\u5207\u6362\u89C4\u5219\u6765\u6E90\u6A21\u5F0F\u3002");
|
|
12086
|
+
} else {
|
|
12087
|
+
console.log(`runtime \u914D\u7F6E: ${getRuntimeConfigPath()}`);
|
|
10676
12088
|
}
|
|
10677
|
-
|
|
10678
|
-
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
const
|
|
10682
|
-
|
|
10683
|
-
|
|
10684
|
-
|
|
10685
|
-
);
|
|
12089
|
+
const layerKeys = ["L1_heuristic", "L2_logic_rules", "L3_llm_analysis", "agent_rules_enabled", "scenario_rules_enabled"];
|
|
12090
|
+
console.log("\u5C42\u7EA7\u5F00\u5173: " + layerKeys.map((k) => `${k}=${isLayerEnabled(k) ? "on" : "OFF"}`).join(" "));
|
|
12091
|
+
if (!builtinOnly) {
|
|
12092
|
+
try {
|
|
12093
|
+
const dirs = rulesDirs().filter((d) => existsSync19(d));
|
|
12094
|
+
if (dirs.length > 0) {
|
|
12095
|
+
console.log(`\u89C4\u5219\u76EE\u5F55: ${dirs.join(", ")}\uFF08\u53EA\u8BFB\u5176\u4E2D ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09`);
|
|
12096
|
+
}
|
|
12097
|
+
const loaded = ruleFilesLoaded();
|
|
12098
|
+
if (loaded.length > 0) {
|
|
12099
|
+
const ov = loadOverrideRules();
|
|
12100
|
+
const ex = loadExtraRules();
|
|
12101
|
+
console.error(`\u26A0\uFE0F \u5DF2\u4ECE\u89C4\u5219\u76EE\u5F55\u52A0\u8F7D ${loaded.length} \u4E2A\u6587\u4EF6\uFF08\u6574\u4EFD\u66FF\u6362 / \u8FFD\u52A0\uFF0C\u5185\u7F6E\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4EEC\u4E0A\u9762\uFF09:`);
|
|
12102
|
+
for (const f of loaded) console.error(` ${f}`);
|
|
12103
|
+
const ovs = Object.entries(ov).sort().map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
12104
|
+
if (ovs) console.error(` \u8986\u76D6\u7C7B\u522B: ${ovs}\uFF08**\u6574\u4EFD\u66FF\u6362**\uFF1A\u672A\u5217\u51FA\u7684\u5185\u7F6E\u89C4\u5219\u4E0D\u518D\u751F\u6548\uFF09`);
|
|
12105
|
+
const exs = Object.entries(ex).sort().map(([k, v]) => `${k}\xD7${v.length}`).join(", ");
|
|
12106
|
+
if (exs) console.log(` \u8FFD\u52A0\u7C7B\u522B: ${exs}\uFF08\u5185\u7F6E\u4FDD\u7559\uFF0C\u8FFD\u52A0\u5728\u5176\u540E\u4E00\u8D77\u5224\u5B9A\uFF09`);
|
|
12107
|
+
}
|
|
12108
|
+
const ignored = ignoredRuleFiles();
|
|
12109
|
+
if (ignored.length > 0) {
|
|
12110
|
+
console.error(
|
|
12111
|
+
`\u26A0\uFE0F \u89C4\u5219\u76EE\u5F55\u91CC\u6709 ${ignored.length} \u4E2A json **\u4E0D\u4F1A\u88AB\u8BFB\u53D6**\uFF08\u53EA\u8BA4 ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09:`
|
|
12112
|
+
);
|
|
12113
|
+
for (const f of ignored) console.error(` ${f}`);
|
|
12114
|
+
}
|
|
12115
|
+
} catch {
|
|
10686
12116
|
}
|
|
10687
|
-
} catch {
|
|
10688
12117
|
}
|
|
10689
12118
|
const scenarioRaw = (process.env.AGENT_MOSS_SCENARIO || "").trim();
|
|
10690
12119
|
if (scenarioRaw) {
|
|
@@ -10706,18 +12135,26 @@ function createApp() {
|
|
|
10706
12135
|
app.get("/api/v1/health", healthRoute);
|
|
10707
12136
|
app.post("/api/v1/analyze", analyzeRoute);
|
|
10708
12137
|
app.post("/api/v1/review", reviewRoute);
|
|
12138
|
+
app.get("/api/v1/brain/rules", brainRulesRoute);
|
|
12139
|
+
app.get("/api/v1/brain/rules/drafts", brainDraftsRoute);
|
|
12140
|
+
app.post("/api/v1/brain/rules/:rule_id/approve", brainApproveRoute);
|
|
12141
|
+
app.post("/api/v1/brain/rules/:rule_id/reject", brainRejectRoute);
|
|
12142
|
+
app.get("/api/v1/brain/stats", brainStatsRoute);
|
|
12143
|
+
app.get("/api/v1/brain/skills", brainSkillsRoute);
|
|
12144
|
+
app.get("/api/v1/brain/rules/export", brainExportRoute);
|
|
12145
|
+
app.post("/api/v1/brain/rules/import", brainImportRoute);
|
|
10709
12146
|
app.route("/console", createConsoleRouter());
|
|
10710
12147
|
app.get("/console/", (c) => c.redirect("/console"));
|
|
10711
12148
|
return app;
|
|
10712
12149
|
}
|
|
10713
12150
|
async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
|
|
10714
12151
|
for (let port = startPort; port < startPort + scan; port++) {
|
|
10715
|
-
const free = await new Promise((
|
|
12152
|
+
const free = await new Promise((resolve11) => {
|
|
10716
12153
|
const server = net.createServer();
|
|
10717
12154
|
server.listen(port, host, () => {
|
|
10718
|
-
server.close(() =>
|
|
12155
|
+
server.close(() => resolve11(true));
|
|
10719
12156
|
});
|
|
10720
|
-
server.on("error", () =>
|
|
12157
|
+
server.on("error", () => resolve11(false));
|
|
10721
12158
|
});
|
|
10722
12159
|
if (free) {
|
|
10723
12160
|
if (port !== startPort) {
|
|
@@ -10773,11 +12210,71 @@ async function startServer(host = "127.0.0.1", port = 9090) {
|
|
|
10773
12210
|
}
|
|
10774
12211
|
};
|
|
10775
12212
|
}
|
|
12213
|
+
async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_moss.sock") {
|
|
12214
|
+
const app = createApp();
|
|
12215
|
+
let server = null;
|
|
12216
|
+
return {
|
|
12217
|
+
socketPath,
|
|
12218
|
+
close: async () => {
|
|
12219
|
+
if (!server) return;
|
|
12220
|
+
await new Promise((resolve11) => {
|
|
12221
|
+
server.close(() => resolve11());
|
|
12222
|
+
});
|
|
12223
|
+
server = null;
|
|
12224
|
+
},
|
|
12225
|
+
serve: async () => {
|
|
12226
|
+
const { mkdirSync: mkdirSync8, existsSync: existsSync20, unlinkSync: unlinkSync5 } = await import("fs");
|
|
12227
|
+
const { dirname: dirname13 } = await import("path");
|
|
12228
|
+
const dir = dirname13(socketPath);
|
|
12229
|
+
if (dir) {
|
|
12230
|
+
try {
|
|
12231
|
+
mkdirSync8(dir, { recursive: true });
|
|
12232
|
+
} catch {
|
|
12233
|
+
}
|
|
12234
|
+
}
|
|
12235
|
+
if (existsSync20(socketPath)) {
|
|
12236
|
+
const { connect } = await import("net");
|
|
12237
|
+
const inUse = await new Promise((resolve11) => {
|
|
12238
|
+
const probe = connect(socketPath);
|
|
12239
|
+
const done = (v) => {
|
|
12240
|
+
probe.destroy();
|
|
12241
|
+
resolve11(v);
|
|
12242
|
+
};
|
|
12243
|
+
probe.once("connect", () => done(true));
|
|
12244
|
+
probe.once("error", () => done(false));
|
|
12245
|
+
probe.setTimeout(500, () => done(false));
|
|
12246
|
+
});
|
|
12247
|
+
if (inUse) {
|
|
12248
|
+
console.warn(
|
|
12249
|
+
`[AgentMoss] socket ${socketPath} \u5DF2\u6709\u670D\u52A1\u5728\u76D1\u542C\uFF0C\u4E0D\u6E05\u7406\uFF08bind \u4F1A\u56E0\u5360\u7528\u800C\u5931\u8D25\uFF09`
|
|
12250
|
+
);
|
|
12251
|
+
} else {
|
|
12252
|
+
try {
|
|
12253
|
+
unlinkSync5(socketPath);
|
|
12254
|
+
console.log(`[AgentMoss] \u6E05\u7406\u6B8B\u7559 socket \u6587\u4EF6: ${socketPath}`);
|
|
12255
|
+
} catch {
|
|
12256
|
+
}
|
|
12257
|
+
}
|
|
12258
|
+
}
|
|
12259
|
+
logDeploymentSelfCheck();
|
|
12260
|
+
await warmUpEngine();
|
|
12261
|
+
const { getRequestListener } = await import("@hono/node-server");
|
|
12262
|
+
const { createServer } = await import("http");
|
|
12263
|
+
server = createServer(getRequestListener(app.fetch));
|
|
12264
|
+
await new Promise((resolve11, reject) => {
|
|
12265
|
+
server.once("error", reject);
|
|
12266
|
+
server.listen(socketPath, () => resolve11());
|
|
12267
|
+
});
|
|
12268
|
+
console.log(`AgentMoss HTTP server on unix socket: ${socketPath}`);
|
|
12269
|
+
}
|
|
12270
|
+
};
|
|
12271
|
+
}
|
|
10776
12272
|
export {
|
|
10777
12273
|
createApp,
|
|
10778
12274
|
findFreePort,
|
|
10779
12275
|
logDeploymentSelfCheck,
|
|
10780
|
-
startServer
|
|
12276
|
+
startServer,
|
|
12277
|
+
startUnixSocketServer
|
|
10781
12278
|
};
|
|
10782
12279
|
//! 顺序即 fail-closed 短路优先级:verify 逐个执行,首个返回违规的性质成为
|
|
10783
12280
|
//! first_violation,后续不再评估。调整顺序影响判定归属,须与 Python
|