@kenhkl/agent-moss 0.15.1 → 0.15.4

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.
@@ -0,0 +1,31 @@
1
+ {
2
+ "llm": {
3
+ "api_key": "your-api-key-here",
4
+ "model": "gpt-4o",
5
+ "temperature": 0.1,
6
+ "base_url": "https://api.openai.com/v1"
7
+ },
8
+ "timeout": {
9
+ "total_timeout": 90.0,
10
+ "prompt1_timeout": 40.0,
11
+ "prompt2_timeout": 20.0,
12
+ "step_interval": 0.0
13
+ },
14
+ "cache": {
15
+ "enabled": true,
16
+ "max_size": 1000
17
+ },
18
+ "retry": {
19
+ "max_retries": 2,
20
+ "retry_interval": 2.0
21
+ },
22
+ "security": {
23
+ "enabled": true,
24
+ "heuristic_enabled": true,
25
+ "logic_rules_enabled": true,
26
+ "llm_analysis_enabled": true,
27
+ "rules_path": "",
28
+ "skills_dir": ""
29
+ },
30
+ "log_level": "INFO"
31
+ }
@@ -156,8 +156,12 @@ function resolveConfigPath() {
156
156
  if (envPath) return envPath;
157
157
  const userConfig = path2.join(getConfigDir(), "config.json");
158
158
  if (fs.existsSync(userConfig)) return userConfig;
159
- const defaultConfig = path2.join(__dirname, "..", "..", "agent_moss", "infra", "config.json.example");
160
- if (fs.existsSync(defaultConfig)) {
159
+ const defaultCandidates = [
160
+ path2.join(__dirname, "config.json.example"),
161
+ path2.join(__dirname, "..", "..", "agent_moss", "infra", "config.json.example")
162
+ ];
163
+ const defaultConfig = defaultCandidates.find((p) => fs.existsSync(p));
164
+ if (defaultConfig) {
161
165
  const configDir = path2.dirname(userConfig);
162
166
  fs.mkdirSync(configDir, { recursive: true });
163
167
  const pkgConfig = loadConfigFile(defaultConfig);
@@ -1430,6 +1434,7 @@ __export(runtime_config_exports, {
1430
1434
  getRuntimeConfigPath: () => getRuntimeConfigPath,
1431
1435
  getScenarioRules: () => getScenarioRules,
1432
1436
  hasRuntimeConfig: () => hasRuntimeConfig,
1437
+ invalidateRuntimeConfigCache: () => invalidateRuntimeConfigCache,
1433
1438
  isL2CategoryEnabled: () => isL2CategoryEnabled,
1434
1439
  isLayerEnabled: () => isLayerEnabled,
1435
1440
  loadRuntimeConfig: () => loadRuntimeConfig,
@@ -1448,7 +1453,7 @@ __export(runtime_config_exports, {
1448
1453
  updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
1449
1454
  updateSkillEnabled: () => updateSkillEnabled
1450
1455
  });
1451
- import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3 } from "fs";
1456
+ import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync4, mkdirSync, unlinkSync, readdirSync as readdirSync3, statSync } from "fs";
1452
1457
  import { join as join3 } from "path";
1453
1458
  import { fileURLToPath as fileURLToPath5 } from "url";
1454
1459
  import { dirname as dirname3 } from "path";
@@ -1465,6 +1470,7 @@ function saveRuntimeConfig(config) {
1465
1470
  const dir = p.substring(0, p.lastIndexOf("/"));
1466
1471
  if (dir && !existsSync4(dir)) mkdirSync(dir, { recursive: true });
1467
1472
  writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
1473
+ invalidateRuntimeConfigCache();
1468
1474
  }
1469
1475
  function emptyRuntimeConfig() {
1470
1476
  return {
@@ -1478,6 +1484,9 @@ function emptyRuntimeConfig() {
1478
1484
  scenario_rules: {}
1479
1485
  };
1480
1486
  }
1487
+ function invalidateRuntimeConfigCache() {
1488
+ runtimeConfigCache = null;
1489
+ }
1481
1490
  function loadRuntimeConfig() {
1482
1491
  const p = getRuntimeConfigPath();
1483
1492
  if (!existsSync4(p)) {
@@ -1489,9 +1498,14 @@ function loadRuntimeConfig() {
1489
1498
  return seeded;
1490
1499
  }
1491
1500
  try {
1501
+ const st = statSync(p);
1502
+ const cacheKey = `${p}|${st.mtimeMs}|${st.size}`;
1503
+ if (runtimeConfigCache && runtimeConfigCache.key === cacheKey) {
1504
+ return runtimeConfigCache.value;
1505
+ }
1492
1506
  const data = JSON.parse(readFileSync4(p, "utf-8"));
1493
1507
  const merged = mergeConfig(data, generateSourceDefaults());
1494
- return {
1508
+ const result = {
1495
1509
  version: data.version || 1,
1496
1510
  layers: {
1497
1511
  L1_heuristic: data.layers?.L1_heuristic ?? true,
@@ -1505,6 +1519,8 @@ function loadRuntimeConfig() {
1505
1519
  agent_rules: merged.agent_rules || {},
1506
1520
  scenario_rules: merged.scenario_rules || {}
1507
1521
  };
1522
+ runtimeConfigCache = { key: cacheKey, value: result };
1523
+ return result;
1508
1524
  } catch {
1509
1525
  return emptyRuntimeConfig();
1510
1526
  }
@@ -2494,7 +2510,7 @@ function updateScenarioCategoryEnabled(scenarioId, category, enabled) {
2494
2510
  saveRuntimeConfig(runtime);
2495
2511
  return runtime;
2496
2512
  }
2497
- var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
2513
+ var __dirname4, ENV_RUNTIME_CONFIG_PATH, RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DEFAULT_LAYERS, DENY_MODE_LABELS, runtimeConfigCache, _AGENTS_DIR_PROD, _AGENTS_DIR_DEV, _SCENARIOS_DIR_PROD, _SCENARIOS_DIR_DEV, SKILL_ID_RE;
2498
2514
  var init_runtime_config = __esm({
2499
2515
  "src/infra/runtime-config.ts"() {
2500
2516
  "use strict";
@@ -2524,6 +2540,7 @@ var init_runtime_config = __esm({
2524
2540
  deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
2525
2541
  deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
2526
2542
  };
2543
+ runtimeConfigCache = null;
2527
2544
  _AGENTS_DIR_PROD = join3(__dirname4, "agents");
2528
2545
  _AGENTS_DIR_DEV = join3(__dirname4, "..", "..", "..", "agent_moss", "agents");
2529
2546
  _SCENARIOS_DIR_PROD = join3(__dirname4, "scenarios");
@@ -2818,6 +2835,56 @@ function isInlineScriptCommand(actionDetail) {
2818
2835
  const re = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
2819
2836
  return re.test(actionDetail);
2820
2837
  }
2838
+ function resolvePathTokens(segment) {
2839
+ const out = [];
2840
+ const low = segment.toLowerCase();
2841
+ for (const m of low.matchAll(HOME_PREFIX_RE)) {
2842
+ const prefix = m[0];
2843
+ const endIdx = (m.index ?? 0) + m[0].length;
2844
+ const tail = low.slice(endIdx);
2845
+ if (!tail || !(tail[0] === "/" || tail[0] === " ")) {
2846
+ out.push([prefix, prefix]);
2847
+ continue;
2848
+ }
2849
+ const token = tail.trim().split(/\s+/)[0] || "";
2850
+ if (!token.startsWith("/")) continue;
2851
+ const cleaned = token.replace(/\/+$/, "").replace(/^['"]|['"]$/g, "");
2852
+ const segments = cleaned.split("/").filter((s) => s && s !== ".");
2853
+ if (!segments.some((s) => s === "..")) continue;
2854
+ let depth = 0;
2855
+ const kept = [];
2856
+ let escaped = false;
2857
+ for (const s of segments) {
2858
+ if (s === "..") {
2859
+ depth -= 1;
2860
+ if (kept.length) kept.pop();
2861
+ if (depth < 0) {
2862
+ escaped = true;
2863
+ depth = 0;
2864
+ }
2865
+ } else {
2866
+ depth += 1;
2867
+ kept.push(s);
2868
+ }
2869
+ }
2870
+ let resolved = kept.length ? "~/" + kept.join("/") : "~";
2871
+ if (escaped) resolved += "\uFF08\u26A0\uFE0F .. \u5F39\u6808\u8D85\u51FA\u4E3B\u76EE\u5F55\u6839\uFF0C\u5B9E\u9645\u76EE\u6807\u5728\u4E3B\u76EE\u5F55\u4E4B\u5916\uFF09";
2872
+ out.push([prefix + token, resolved]);
2873
+ }
2874
+ return out;
2875
+ }
2876
+ function buildPathContext(actionDetail) {
2877
+ if (!PATH_CONTEXT_TRIGGER_RE.test(actionDetail)) return "";
2878
+ const stripped = stripLiteralTextSegments(actionDetail);
2879
+ const lines = [];
2880
+ for (const seg of splitCommandSegments(stripped)) {
2881
+ for (const [raw, lex] of resolvePathTokens(seg)) {
2882
+ lines.push(`- \u547D\u4EE4\u6BB5 \`${seg.trim().slice(0, 60)}\`\uFF1A\`${raw}\` \u8BCD\u6CD5\u89E3\u6790\u4E3A \`${lex}\``);
2883
+ }
2884
+ }
2885
+ if (!lines.length) return "";
2886
+ return "## \u8DEF\u5F84\u89E3\u6790\u4E8B\u5B9E\uFF08\u8BCD\u6CD5\u5206\u6790\uFF0C\u786E\u5B9A\u6027\u7ED3\u8BBA\uFF09\n" + lines.join("\n") + "\n\n## \u8FD0\u884C\u65F6\u672A\u9A8C\u8BC1\uFF08\u4F9B\u53C2\u8003\uFF0C\u975E\u4E8B\u5B9E\uFF09\n- \u5206\u6790\u5668\u4E0D\u8BFB\u6587\u4EF6\u7CFB\u7EDF\uFF1A\u65E0\u6CD5\u786E\u8BA4\u8DEF\u5F84\u4E2D\u7684\u4E2D\u95F4\u76EE\u5F55\u662F\u5426\u4E3A\u7B26\u53F7\u94FE\u63A5\uFF1B\u82E5\u4E2D\u95F4\u6BB5\u5728\u6267\u884C\u65F6\u662F\uFF08\u6216\u88AB\u66FF\u6362\u4E3A\uFF09\u6307\u5411\u4E3B\u76EE\u5F55\u5916\u7684\u8F6F\u94FE\uFF0C\u5B9E\u9645\u64CD\u4F5C\u76EE\u6807\u53EF\u80FD\u968F\u4E4B\u6539\u53D8\u3002\u8BCD\u6CD5\u89E3\u6790\u7ED3\u679C\u57FA\u4E8E\u6807\u51C6\u8DEF\u5F84\u8BED\u4E49\uFF0C\u4E0D\u5E94\u56E0\u731C\u6D4B\u300C\u89E3\u6790\u4F9D\u8D56\u67D0\u6BB5\u662F\u5426\u5B58\u5728\u300D\u800C\u5426\u5B9A\u5B83\u3002";
2887
+ }
2821
2888
  function stripLiteralTextSegments(actionDetail) {
2822
2889
  const writeScriptExec = />\s*[/~]\S*\.(sh|py|pl|rb)\b/.test(actionDetail) && /(?:&&|\|\||;|\n|\|)\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)/.test(actionDetail);
2823
2890
  const pipeToInterpreter = /\|\s*(?:bash|sh|zsh|dash|python|perl|ruby|node)\b/.test(actionDetail);
@@ -3182,7 +3249,7 @@ function isArchiveReadOfDir(command, dirPath) {
3182
3249
  }
3183
3250
  if (fw === "cp" || fw === "rsync") {
3184
3251
  const segPaths = s.match(/[/~][^\s;|&><'"]*/g) || [];
3185
- if (segPaths.length && segPaths[0].includes(dpath)) return true;
3252
+ if (segPaths[0]?.includes(dpath)) return true;
3186
3253
  }
3187
3254
  }
3188
3255
  return false;
@@ -3344,7 +3411,7 @@ function checkLateralMovement(actionDetail) {
3344
3411
  }
3345
3412
  return null;
3346
3413
  }
3347
- var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
3414
+ var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, PATH_CONTEXT_TRIGGER_RE, HOME_PREFIX_RE, SENSITIVE_EXCLUDED_PREFIXES, SEARCH_ACTION_PREFIXES, HOME_TARGET_RE, HOME_ASSIGN_RE;
3348
3415
  var init_heuristic = __esm({
3349
3416
  "src/engine/heuristic.ts"() {
3350
3417
  "use strict";
@@ -3372,6 +3439,8 @@ var init_heuristic = __esm({
3372
3439
  description: c.reason
3373
3440
  }));
3374
3441
  })();
3442
+ PATH_CONTEXT_TRIGGER_RE = /(?:^|[;&|]\s*)(?:rm|rmdir|unlink|ln|mv|cp|dd|truncate|shred|chmod|chown)\b/i;
3443
+ HOME_PREFIX_RE = /(?:~|~(?=\/)|\$HOME\b|\$\{HOME\}|\/home\/[a-z0-9_.-]+)/gi;
3375
3444
  SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
3376
3445
  SEARCH_ACTION_PREFIXES = ["search-", "web-", "browser-", "query-", "websearch", "web_search"];
3377
3446
  HOME_TARGET_RE = /(?:^|[\s="'($])(?:~|(?:\$home|\$\{home\}))(?:\/(?:\.\*|\*|\.)?)?(?=[\s"';}|&)]|$)/i;
@@ -3787,7 +3856,7 @@ init_esm_shims();
3787
3856
  init_heuristic();
3788
3857
  init_logic_rules();
3789
3858
  init_inline_analyzer();
3790
- import { statSync as statSync2, existsSync as existsSync9 } from "fs";
3859
+ import { statSync as statSync3, existsSync as existsSync9 } from "fs";
3791
3860
  import { resolve as resolve8, join as join6, isAbsolute as isAbsolute2 } from "path";
3792
3861
  import { homedir as homedir4 } from "os";
3793
3862
 
@@ -5448,7 +5517,7 @@ function extractInlineScriptPath(actionDetail) {
5448
5517
  try {
5449
5518
  const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
5450
5519
  if (existsSync9(resolved)) {
5451
- const stat = statSync2(resolved);
5520
+ const stat = statSync3(resolved);
5452
5521
  if (stat.isFile()) return resolved;
5453
5522
  }
5454
5523
  } catch {
@@ -5462,7 +5531,7 @@ function extractInlineScriptPath(actionDetail) {
5462
5531
  try {
5463
5532
  const resolved = resolvePath2(p);
5464
5533
  if (existsSync9(resolved)) {
5465
- const stat = statSync2(resolved);
5534
+ const stat = statSync3(resolved);
5466
5535
  if (stat.isFile()) return resolved;
5467
5536
  }
5468
5537
  } catch {
@@ -5478,7 +5547,7 @@ function extractInlineScriptPath(actionDetail) {
5478
5547
  try {
5479
5548
  resolved = resolvePath2(p);
5480
5549
  if (existsSync9(resolved)) {
5481
- const stat = statSync2(resolved);
5550
+ const stat = statSync3(resolved);
5482
5551
  if (stat.isFile()) return resolved;
5483
5552
  }
5484
5553
  } catch {
@@ -5689,7 +5758,7 @@ async function runLLMAnalysis(input, previous) {
5689
5758
  } else {
5690
5759
  scriptAnalysis = analyzeScriptContent(input.aNext.actionType, input.aNext.actionDetail);
5691
5760
  }
5692
- const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText);
5761
+ const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText, previous.pathContext ?? "");
5693
5762
  logPrompt(prompt, input.aNext, previous.llmLogPath);
5694
5763
  const result = await analyzeWithLLM(prompt, config);
5695
5764
  if (llmCache.size >= LLM_CACHE_MAX) {
@@ -5713,7 +5782,7 @@ ${prompt}
5713
5782
  console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
5714
5783
  }
5715
5784
  }
5716
- function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
5785
+ function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "", pathContext = "") {
5717
5786
  const skillsText = formatSkillsForPrompt(matchedSkills);
5718
5787
  const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
5719
5788
  const promptHistoryText = formatPromptHistory(input.promptHistory);
@@ -5721,7 +5790,7 @@ function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis,
5721
5790
  const hintsText = formatHints(previous);
5722
5791
  const securityJudgeTemplate = templates["security_judge"];
5723
5792
  if (securityJudgeTemplate) {
5724
- return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{prompt_history_text}", promptHistoryText).replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{{", "{").replaceAll("}}", "}");
5793
+ return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{prompt_history_text}", promptHistoryText).replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{path_context}", pathContext || "\uFF08\u672C\u547D\u4EE4\u65E0\u4E3B\u76EE\u5F55\u76F8\u5173\u8DEF\u5F84\u6BB5\uFF0C\u4E0D\u9002\u7528\uFF09").replaceAll("{{", "{").replaceAll("}}", "}");
5725
5794
  }
5726
5795
  console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
5727
5796
  return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
@@ -7605,6 +7674,21 @@ async function runAnalysis(input) {
7605
7674
  if ((effectiveResult.riskLevel === "high" || effectiveResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
7606
7675
  return effectiveResult;
7607
7676
  }
7677
+ } else {
7678
+ if (isInlineScriptCmd || isLiteralTextCmd) {
7679
+ const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
7680
+ if (inlineJudgment.actionable && !inlineJudgment.allowed) {
7681
+ return {
7682
+ allowed: false,
7683
+ reason: inlineJudgment.reason,
7684
+ riskLevel: inlineJudgment.riskLevel,
7685
+ riskType: inlineJudgment.riskType,
7686
+ confidence: 90,
7687
+ source: "logic_rule",
7688
+ violatedLayers: ["L2.1"]
7689
+ };
7690
+ }
7691
+ }
7608
7692
  }
7609
7693
  if (isLogicRulesEnabled()) {
7610
7694
  const formalSessions = [];
@@ -7706,12 +7790,14 @@ async function runAnalysis(input) {
7706
7790
  }
7707
7791
  let llmResult;
7708
7792
  try {
7793
+ const pathContext = buildPathContext(input.aNext.actionDetail);
7709
7794
  llmResult = await runLLMAnalysis(input, {
7710
7795
  heuristic: heuristicResult,
7711
7796
  logic: logicResult,
7712
7797
  skipL3Hints,
7713
7798
  scriptChain: scriptChain ?? void 0,
7714
- llmLogPath: input.llmLogPath
7799
+ llmLogPath: input.llmLogPath,
7800
+ pathContext
7715
7801
  });
7716
7802
  } catch (e) {
7717
7803
  const { getLLMFailMode: getLLMFailMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));