@kenhkl/agent-moss 0.8.5 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3448 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // node_modules/tsup/assets/esm_shims.js
12
+ import path from "path";
13
+ import { fileURLToPath } from "url";
14
+ var getFilename, getDirname, __dirname;
15
+ var init_esm_shims = __esm({
16
+ "node_modules/tsup/assets/esm_shims.js"() {
17
+ "use strict";
18
+ getFilename = () => fileURLToPath(import.meta.url);
19
+ getDirname = () => path.dirname(getFilename());
20
+ __dirname = /* @__PURE__ */ getDirname();
21
+ }
22
+ });
23
+
24
+ // src/config.ts
25
+ var config_exports = {};
26
+ __export(config_exports, {
27
+ detectProvider: () => detectProvider,
28
+ getCustomRules: () => getCustomRules,
29
+ getLLMConfig: () => getLLMConfig,
30
+ getLLMFailMode: () => getLLMFailMode,
31
+ getProviderHeaders: () => getProviderHeaders,
32
+ getRetryConfig: () => getRetryConfig,
33
+ getTimeoutConfig: () => getTimeoutConfig,
34
+ isHeuristicEnabled: () => isHeuristicEnabled,
35
+ isLLMAvailable: () => isLLMAvailable,
36
+ isLogicRulesEnabled: () => isLogicRulesEnabled,
37
+ updateLLMConfig: () => updateLLMConfig
38
+ });
39
+ import fs from "fs";
40
+ import path2 from "path";
41
+ import os from "os";
42
+ function loadConfigFile(filePath) {
43
+ try {
44
+ const raw = fs.readFileSync(filePath, "utf-8");
45
+ return JSON.parse(raw);
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+ function resolveConfigPath() {
51
+ const envPath = process.env.AGENT_MOSS_CONFIG_PATH;
52
+ if (envPath) return envPath;
53
+ const userConfig = path2.join(os.homedir(), ".config", "agentmoss", "config.json");
54
+ if (fs.existsSync(userConfig)) return userConfig;
55
+ const defaultConfig = path2.join(__dirname, "..", "..", "agent_moss", "infra", "config.json.example");
56
+ if (fs.existsSync(defaultConfig)) {
57
+ const configDir = path2.dirname(userConfig);
58
+ fs.mkdirSync(configDir, { recursive: true });
59
+ const pkgConfig = loadConfigFile(defaultConfig);
60
+ const safeConfig = pkgConfig ? { ...pkgConfig, llm: { ...pkgConfig.llm, api_key: "" } } : {};
61
+ fs.writeFileSync(userConfig, JSON.stringify(safeConfig, null, 4), "utf-8");
62
+ console.log(`[AgentMoss] \u5DF2\u521B\u5EFA\u7528\u6237\u914D\u7F6E\u6587\u4EF6: ${userConfig}`);
63
+ console.log(`[AgentMoss] \u8BF7\u7F16\u8F91\u8BE5\u6587\u4EF6\u914D\u7F6E\u4F60\u7684 LLM API Key`);
64
+ }
65
+ return userConfig;
66
+ }
67
+ function loadLLMConfigFromFile(configPath) {
68
+ const data = loadConfigFile(configPath);
69
+ if (!data?.llm) return {};
70
+ const llm = data.llm;
71
+ return {
72
+ provider: llm.provider || "",
73
+ baseUrl: llm.base_url || "",
74
+ apiKey: llm.api_key || "",
75
+ model: llm.model || "",
76
+ temperature: llm.temperature ?? 0.1,
77
+ maxTokens: llm.max_tokens ?? 4096,
78
+ timeout: llm.timeout ?? 40
79
+ };
80
+ }
81
+ function loadTimeoutRetryFromFile(configPath) {
82
+ const data = loadConfigFile(configPath);
83
+ const t = data?.timeout ?? {};
84
+ const r = data?.retry ?? {};
85
+ return {
86
+ timeout: {
87
+ totalTimeout: Number(t.total_timeout ?? 90),
88
+ prompt1Timeout: Number(t.prompt1_timeout ?? 40),
89
+ prompt2Timeout: Number(t.prompt2_timeout ?? 20),
90
+ stepInterval: Number(t.step_interval ?? 0)
91
+ },
92
+ retry: {
93
+ maxRetries: Number(r.max_retries ?? 2),
94
+ retryInterval: Number(r.retry_interval ?? 2)
95
+ }
96
+ };
97
+ }
98
+ function getTimeoutConfig() {
99
+ return timeoutConfig;
100
+ }
101
+ function getRetryConfig() {
102
+ return retryConfig;
103
+ }
104
+ function loadCustomRules() {
105
+ try {
106
+ const raw = process.env.AGENT_MOSS_CUSTOM_RULES;
107
+ if (!raw) return [];
108
+ const parsed = JSON.parse(raw);
109
+ if (!Array.isArray(parsed)) return [];
110
+ return parsed.filter((r) => r.pattern && r.action);
111
+ } catch {
112
+ return [];
113
+ }
114
+ }
115
+ function getLLMConfig() {
116
+ return llmConfig;
117
+ }
118
+ function updateLLMConfig(partial) {
119
+ llmConfig = { ...llmConfig, ...partial };
120
+ }
121
+ function getCustomRules() {
122
+ return customRules;
123
+ }
124
+ function isLLMAvailable() {
125
+ if (process.env.AGENT_MOSS_DISABLE_LLM === "1") return false;
126
+ return llmConfig.apiKey.length > 0;
127
+ }
128
+ function getLLMFailMode() {
129
+ const val = (process.env.AGENT_MOSS_LLM_FAIL_MODE || "fail_open").toLowerCase();
130
+ return val === "fail_closed" ? "fail_closed" : "fail_open";
131
+ }
132
+ function detectProvider(baseUrl, providerHint = "") {
133
+ if (providerHint) {
134
+ const hint = providerHint.toLowerCase();
135
+ if (["claude", "anthropic"].includes(hint)) return "anthropic";
136
+ if (["google", "gemini"].includes(hint)) return "gemini";
137
+ if (["zai-coding-plan", "zhipu-coding-plan", "zhipuai-coding-plan"].includes(hint)) return "zai-coding-plan";
138
+ if (["zai", "z-ai", "z.ai", "zai-cn", "zai-china", "zhipu", "glm-cn", "bigmodel"].includes(hint)) return "zhipu";
139
+ if (["xai", "xai-grok"].includes(hint)) return "xai";
140
+ if (["minimax", "minimax-openai"].includes(hint)) return "minimax";
141
+ if (["minimax-anthropic"].includes(hint)) return "minimax-anthropic";
142
+ if (["other", "custom", "local"].includes(hint)) return "openai-compatible";
143
+ return hint;
144
+ }
145
+ const url = baseUrl.toLowerCase();
146
+ if (url.includes("openrouter.ai")) return "openrouter";
147
+ if (url.includes("api.openai.com")) return "openai";
148
+ if (url.includes("anthropic.com") || url.includes("api.anthropic.com")) return "anthropic";
149
+ if (url.includes("generativelanguage.googleapis.com") || url.includes("gemini")) return "gemini";
150
+ if (url.includes("api.deepseek.com")) return "deepseek";
151
+ if (url.includes("api.groq.com")) return "groq";
152
+ if (url.includes("api.mistral.ai")) return "mistral";
153
+ if (url.includes("api.together.xyz")) return "together";
154
+ if (url.includes("api.x.ai")) return "xai";
155
+ if (url.includes("api.minimaxi.com")) return "minimax";
156
+ if (url.includes("api-ai.gitcode.com")) return "gitcode";
157
+ if (url.includes("api.z.ai")) return "zai-coding-plan";
158
+ if (url.includes("open.bigmodel.cn") || url.includes("bigmodel")) return "zhipu";
159
+ if (url.includes(":11434")) return "ollama";
160
+ return "openai-compatible";
161
+ }
162
+ function getProviderHeaders(provider) {
163
+ return PROVIDER_HEADERS[provider] || {};
164
+ }
165
+ function isHeuristicEnabled() {
166
+ return process.env.AGENT_MOSS_DISABLE_HEURISTIC !== "1";
167
+ }
168
+ function isLogicRulesEnabled() {
169
+ return process.env.AGENT_MOSS_DISABLE_LOGIC_RULES !== "1";
170
+ }
171
+ var timeoutConfig, retryConfig, llmConfig, customRules, PROVIDER_HEADERS;
172
+ var init_config = __esm({
173
+ "src/config.ts"() {
174
+ "use strict";
175
+ init_esm_shims();
176
+ llmConfig = (() => {
177
+ const configPath = resolveConfigPath();
178
+ const fileConfig = fs.existsSync(configPath) ? loadLLMConfigFromFile(configPath) : {};
179
+ const tr = loadTimeoutRetryFromFile(configPath);
180
+ timeoutConfig = tr.timeout;
181
+ retryConfig = tr.retry;
182
+ return {
183
+ provider: process.env.AGENT_MOSS_LLM_PROVIDER || fileConfig.provider || "openai",
184
+ baseUrl: process.env.AGENT_MOSS_LLM_BASE_URL || fileConfig.baseUrl || "https://api.openai.com/v1",
185
+ apiKey: process.env.AGENT_MOSS_LLM_API_KEY || process.env.OPENAI_API_KEY || fileConfig.apiKey || "",
186
+ model: process.env.AGENT_MOSS_LLM_MODEL || fileConfig.model || "gpt-4o",
187
+ temperature: parseFloat(process.env.AGENT_MOSS_LLM_TEMPERATURE || String(fileConfig.temperature ?? "0.1")),
188
+ maxTokens: parseInt(process.env.AGENT_MOSS_LLM_MAX_TOKENS || String(fileConfig.maxTokens ?? "4096"), 10),
189
+ // 优先用 env,否则用 timeout 块的 prompt1Timeout(对齐 py)
190
+ timeout: parseInt(process.env.AGENT_MOSS_LLM_TIMEOUT || String(fileConfig.timeout ?? String(tr.timeout.prompt1Timeout)), 10)
191
+ };
192
+ })();
193
+ customRules = loadCustomRules();
194
+ PROVIDER_HEADERS = {
195
+ "openrouter": {
196
+ "HTTP-Referer": "shturl.cc/LlVD5pyWViHjM",
197
+ "X-OpenRouter-Title": "AgentMoss"
198
+ },
199
+ "xai": {
200
+ "HTTP-Referer": "shturl.cc/LlVD5pyWViHjM"
201
+ }
202
+ };
203
+ }
204
+ });
205
+
206
+ // src/agents/index.ts
207
+ import { readFileSync, existsSync } from "fs";
208
+ import { resolve, dirname } from "path";
209
+ import { fileURLToPath as fileURLToPath2 } from "url";
210
+ function resolveAgentsDir() {
211
+ if (existsSync(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
212
+ if (existsSync(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
213
+ return AGENTS_DIR_PROD;
214
+ }
215
+ function getAgentExtensions(agentId) {
216
+ if (!agentId) return null;
217
+ if (cache.has(agentId)) return cache.get(agentId) ?? null;
218
+ const agentsDir = resolveAgentsDir();
219
+ const rulesFile = resolve(agentsDir, agentId, "rules.json");
220
+ if (!existsSync(rulesFile)) {
221
+ cache.set(agentId, null);
222
+ return null;
223
+ }
224
+ try {
225
+ const raw = readFileSync(rulesFile, "utf-8");
226
+ const ext = JSON.parse(raw);
227
+ if (!ext.agent_id) {
228
+ console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
229
+ cache.set(agentId, null);
230
+ return null;
231
+ }
232
+ cache.set(agentId, ext);
233
+ console.log(
234
+ `[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`
235
+ );
236
+ return ext;
237
+ } catch (e) {
238
+ console.error(`[AgentMoss agents] \u52A0\u8F7D agent ${agentId} rules.json \u5931\u8D25:`, e.message);
239
+ cache.set(agentId, null);
240
+ return null;
241
+ }
242
+ }
243
+ var __filename2, __dirname2, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
244
+ var init_agents = __esm({
245
+ "src/agents/index.ts"() {
246
+ "use strict";
247
+ init_esm_shims();
248
+ __filename2 = fileURLToPath2(import.meta.url);
249
+ __dirname2 = dirname(__filename2);
250
+ AGENTS_DIR_PROD = resolve(__dirname2, "agents");
251
+ AGENTS_DIR_DEV_FALLBACK = resolve(__dirname2, "../../../agent_moss/agents");
252
+ cache = /* @__PURE__ */ new Map();
253
+ }
254
+ });
255
+
256
+ // src/engine/inline-analyzer.ts
257
+ function isLiteralTextCommand(command) {
258
+ ECHO_CMD_RE.lastIndex = 0;
259
+ return ECHO_CMD_RE.test(command);
260
+ }
261
+ function extractInlineCode(command) {
262
+ const parts = [];
263
+ let m;
264
+ INLINE_CODE_RE.lastIndex = 0;
265
+ while ((m = INLINE_CODE_RE.exec(command)) !== null) {
266
+ const code = m[1] ?? m[2];
267
+ if (code) parts.push(code);
268
+ }
269
+ ECHO_STRING_RE.lastIndex = 0;
270
+ while ((m = ECHO_STRING_RE.exec(command)) !== null) {
271
+ const code = m[1] ?? m[2];
272
+ if (code) parts.push(code);
273
+ }
274
+ return parts.join("\n");
275
+ }
276
+ function hasWriteExecChain(command) {
277
+ return WRITE_EXEC_CHAIN_PATTERNS.some((p) => {
278
+ p.lastIndex = 0;
279
+ return p.test(command);
280
+ });
281
+ }
282
+ function hasDirTraversal(code) {
283
+ const lower = code.toLowerCase();
284
+ return DIR_TRAVERSAL_KEYWORDS.some((kw) => lower.includes(kw));
285
+ }
286
+ function hasReadWriteKeyword(code) {
287
+ const lower = code.toLowerCase();
288
+ return READ_WRITE_KEYWORDS.some((kw) => lower.includes(kw));
289
+ }
290
+ function analyzeInlineScript(command) {
291
+ const code = extractInlineCode(command);
292
+ if (!code) {
293
+ return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
294
+ }
295
+ if (hasWriteExecChain(command)) {
296
+ return {
297
+ actionable: true,
298
+ allowed: false,
299
+ reason: "\u5185\u8054\u811A\u672C\u542B\u5199\u5165\u6587\u4EF6\u540E\u6267\u884C\u7684\u6A21\u5F0F\uFF0C\u7591\u4F3C\u7ED5\u8FC7\u654F\u611F\u8DEF\u5F84\u68C0\u6D4B",
300
+ riskLevel: "critical",
301
+ riskType: "script_execution"
302
+ };
303
+ }
304
+ const redirMatch = command.match(REDIRECT_TO_SENSITIVE_RE);
305
+ if (redirMatch) {
306
+ return {
307
+ actionable: true,
308
+ allowed: false,
309
+ reason: `\u91CD\u5B9A\u5411\u5199\u5165\u654F\u611F\u8DEF\u5F84: ${redirMatch[1]}`,
310
+ riskLevel: "critical",
311
+ riskType: "file_access"
312
+ };
313
+ }
314
+ const isEchoOrPrintf1 = isLiteralTextCommand(command);
315
+ if (!isEchoOrPrintf1 && hasDirTraversal(code) && hasReadWriteKeyword(code)) {
316
+ return { actionable: false, allowed: true, reason: "", riskLevel: "low", riskType: "" };
317
+ }
318
+ const isEchoOrPrintf = isLiteralTextCommand(command);
319
+ if (!isEchoOrPrintf && hasReadWriteKeyword(code)) {
320
+ return {
321
+ actionable: true,
322
+ allowed: false,
323
+ reason: "\u5185\u8054\u811A\u672C\u542B\u654F\u611F\u8DEF\u5F84\u4E14\u542B\u6587\u4EF6\u8BFB\u5199/\u547D\u4EE4\u6267\u884C\u8C03\u7528\uFF0C\u5224\u4E3A\u771F\u5B9E\u8BBF\u95EE",
324
+ riskLevel: "critical",
325
+ riskType: "file_access"
326
+ };
327
+ }
328
+ return {
329
+ actionable: true,
330
+ allowed: true,
331
+ reason: "\u654F\u611F\u8DEF\u5F84\u4EC5\u4F5C\u4E3A\u6587\u672C\u5B57\u9762\u91CF\u51FA\u73B0\uFF0C\u672A\u4F34\u968F\u8BFB\u5199\u64CD\u4F5C",
332
+ riskLevel: "low",
333
+ riskType: "file_access"
334
+ };
335
+ }
336
+ var INLINE_CODE_RE, ECHO_STRING_RE, ECHO_CMD_RE, READ_WRITE_KEYWORDS, WRITE_EXEC_CHAIN_PATTERNS, SENSITIVE_PATH_FRAGMENTS, REDIRECT_TO_SENSITIVE_RE, DIR_TRAVERSAL_KEYWORDS;
337
+ var init_inline_analyzer = __esm({
338
+ "src/engine/inline-analyzer.ts"() {
339
+ "use strict";
340
+ init_esm_shims();
341
+ INLINE_CODE_RE = /(?:python|python3|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cera][a-zA-Z]*\s+)"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
342
+ ECHO_STRING_RE = /\b(?:echo|printf)\s+"([^"\\]*(?:\\.[^"\\]*)*)"|\b(?:echo|printf)\s+'([^'\\]*(?:\\.[^'\\]*)*)'/gi;
343
+ ECHO_CMD_RE = /(?:^|\s|;|&&|\|\|)(?:echo|printf)\s/gi;
344
+ READ_WRITE_KEYWORDS = [
345
+ // 函数调用形态(关键词后跟 '(')
346
+ "open(",
347
+ "read(",
348
+ "write(",
349
+ "fopen(",
350
+ "system(",
351
+ "exec(",
352
+ "eval(",
353
+ "popen(",
354
+ "load(",
355
+ "dump(",
356
+ // 文件读写(组合形态)
357
+ "readline",
358
+ "readlines",
359
+ "writelines",
360
+ "getpass",
361
+ "pickle.load",
362
+ "yaml.load",
363
+ "json.load",
364
+ "json.read",
365
+ "configparser",
366
+ "fread",
367
+ "fwrite",
368
+ "fgets",
369
+ // 子进程执行(组合形态)
370
+ "os.system",
371
+ "os.popen",
372
+ "subprocess",
373
+ "os.exec",
374
+ "os.spawn",
375
+ "import os",
376
+ "import subprocess",
377
+ "shell=true",
378
+ "check_output",
379
+ "check_call",
380
+ "run(",
381
+ // 绕过变体检测
382
+ "getattr(",
383
+ "__builtins__",
384
+ "globals(",
385
+ "locals(",
386
+ // PHP
387
+ "file_get_contents",
388
+ "file_put_contents",
389
+ "passthru",
390
+ "shell_exec",
391
+ "proc_open",
392
+ // Perl-Ruby
393
+ "<stdin>",
394
+ "open my"
395
+ ];
396
+ WRITE_EXEC_CHAIN_PATTERNS = [
397
+ // echo/cat/printf "..." > file && bash/sh/python file
398
+ /(?:echo|cat|printf)\b.*?>\s*\S+.*(?:&&|;|\|)\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\s/gis,
399
+ // ... | bash / | sh / | python(管道喂解释器执行)
400
+ /\|\s*(?:bash|sh|zsh|python\d?|perl|ruby|node|php)\b/gi,
401
+ // eval "$(cat ...)" / eval "$(echo ...)"
402
+ /eval\s+"\$\(/gi,
403
+ // xargs bash / xargs sh
404
+ /\|\s*xargs\s+(?:bash|sh|zsh)/gi
405
+ ];
406
+ SENSITIVE_PATH_FRAGMENTS = [
407
+ "/etc/shadow",
408
+ "/etc/gshadow",
409
+ "/etc/passwd",
410
+ "/etc/sudoers",
411
+ "/etc/hosts",
412
+ "/etc/crontab",
413
+ "/etc/systemd/",
414
+ "/etc/ssh/sshd_config",
415
+ "/boot/",
416
+ "/proc/sys/",
417
+ "/sys/",
418
+ "/dev/mem",
419
+ "/dev/kmsg",
420
+ ".ssh/id_rsa",
421
+ ".ssh/id_ed25519",
422
+ ".ssh/authorized_keys"
423
+ ];
424
+ REDIRECT_TO_SENSITIVE_RE = new RegExp(`>{1,2}\\s*(${SENSITIVE_PATH_FRAGMENTS.map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})`, "i");
425
+ DIR_TRAVERSAL_KEYWORDS = [
426
+ // Python 目录遍历
427
+ "os.listdir",
428
+ "os.walk",
429
+ "os.scandir",
430
+ "glob.glob",
431
+ "glob.iglob",
432
+ "pathlib.",
433
+ "iterdir",
434
+ "rglob",
435
+ "scandir(",
436
+ "listdir("
437
+ ];
438
+ }
439
+ });
440
+
441
+ // src/engine/logic-rules.ts
442
+ import * as fs2 from "fs";
443
+ function runLogicCheck(input) {
444
+ const { actionHistory, aNext, cwd } = input;
445
+ const actionDetail = aNext.actionDetail.toLowerCase();
446
+ const actionType = aNext.actionType.toLowerCase();
447
+ const resolvedCwd = cwd || "/";
448
+ const EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
449
+ const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
450
+ const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd);
451
+ if (rbwResult) return rbwResult;
452
+ if (input.promptSession && input.promptSession.trim()) {
453
+ if (!isExcludedType) {
454
+ const promptLower = input.promptSession.toLowerCase();
455
+ for (const pattern of INTENT_DEVIATION_PATTERNS) {
456
+ const intentMatched = pattern.intentKeywords.some((kw) => promptLower.includes(kw));
457
+ const dangerousMatched = pattern.dangerousActions.some((kw) => actionDetail.includes(kw));
458
+ if (intentMatched && dangerousMatched) {
459
+ return {
460
+ allowed: false,
461
+ reason: pattern.reason,
462
+ riskLevel: "high",
463
+ riskType: "intent_deviation",
464
+ confidence: 85,
465
+ source: "logic_rule",
466
+ violatedLayers: ["2"]
467
+ };
468
+ }
469
+ }
470
+ }
471
+ }
472
+ const indirectResult = checkIndirectFileAccess(actionType, actionDetail, resolvedCwd);
473
+ if (indirectResult) return indirectResult;
474
+ if (actionDetail.includes("sudo") || actionDetail.includes("su -") || actionDetail.includes("chmod u+s") || actionDetail.includes("setuid")) {
475
+ const detailAfterSudo = actionDetail.replace(/(^|\|\||&&|\s)sudo\s+/g, " ");
476
+ const isAdminCmd = ["apt", "systemctl", "yum", "dnf", "pacman", "zypper", "apk"].some((kw) => detailAfterSudo.includes(kw));
477
+ if (!isAdminCmd) {
478
+ const adminContext = actionHistory.some(
479
+ (h) => h.actionDetail.includes("sudo") || h.actionDetail.includes("apt") || h.actionDetail.includes("systemctl") || h.actionDetail.includes("yum") || h.actionDetail.includes("dnf") || h.actionDetail.includes("pacman") || h.actionDetail.includes("zypper") || h.actionDetail.includes("apk")
480
+ );
481
+ if (!adminContext) {
482
+ return {
483
+ allowed: false,
484
+ reason: "\u68C0\u6D4B\u5230\u63D0\u6743\u64CD\u4F5C\uFF0C\u672A\u53D1\u73B0\u5408\u6CD5\u7BA1\u7406\u4E0A\u4E0B\u6587",
485
+ riskLevel: "high",
486
+ riskType: "privilege_escalation",
487
+ confidence: 85,
488
+ source: "logic_rule",
489
+ violatedLayers: ["2"]
490
+ };
491
+ }
492
+ }
493
+ }
494
+ const pwdResult = checkPasswordConsent(actionType, actionDetail, actionHistory);
495
+ if (pwdResult) return pwdResult;
496
+ const userDelResult = checkUserDeletionConsent(actionType, actionDetail, actionHistory);
497
+ if (userDelResult) return userDelResult;
498
+ return null;
499
+ }
500
+ function extractFilePaths(text) {
501
+ const paths = [];
502
+ const unixPaths = text.match(/(?:^|\s)(\/[^\s;|&><'"]+)/g);
503
+ if (unixPaths) paths.push(...unixPaths.map((p) => p.trim()));
504
+ const quotedPaths = text.match(/["']\/([^"']+)["']/g);
505
+ if (quotedPaths) paths.push(...quotedPaths.map((p) => p.replace(/["']/g, "")));
506
+ return [...new Set(paths)];
507
+ }
508
+ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, cwd) {
509
+ if (NON_WRITE_TOOLS.has(actionType)) return null;
510
+ let isWrite = WRITE_KEYWORDS.some((kw) => actionType.includes(kw) || actionDetail.includes(kw));
511
+ if (!isWrite && actionDetail.includes(">")) {
512
+ const firstWord = actionDetail.split(/\s/)[0]?.toLowerCase() || "";
513
+ if (!READ_ONLY_REDIR_CMDS.has(firstWord)) {
514
+ isWrite = true;
515
+ }
516
+ }
517
+ if (!isWrite) return null;
518
+ const targetPaths = extractFilePaths(actionDetail);
519
+ if (targetPaths.length === 0) return null;
520
+ const historyReadPaths = /* @__PURE__ */ new Set();
521
+ for (const action of actionHistory) {
522
+ const histDetail = action.actionDetail.toLowerCase();
523
+ const histType = action.actionType.toLowerCase();
524
+ const isRead = READ_KEYWORDS.some((kw) => histType.includes(kw) || histDetail.includes(kw));
525
+ if (isRead) {
526
+ for (const p of extractFilePaths(histDetail)) {
527
+ historyReadPaths.add(p);
528
+ }
529
+ }
530
+ }
531
+ const unreadPaths = [];
532
+ for (const path3 of targetPaths) {
533
+ const hasRead = [...historyReadPaths].some(
534
+ (rp) => path3 === rp || path3.startsWith(rp) || rp.startsWith(path3)
535
+ );
536
+ if (!hasRead) {
537
+ let isCreate = !fs2.existsSync(path3);
538
+ if (!isCreate) {
539
+ isCreate = CREATE_KEYWORDS.some((kw) => reason.toLowerCase().includes(kw) || actionDetail.includes(kw));
540
+ }
541
+ if (!isCreate) {
542
+ unreadPaths.push(path3);
543
+ }
544
+ }
545
+ }
546
+ if (unreadPaths.length > 0) {
547
+ return {
548
+ allowed: false,
549
+ reason: `\u8FDD\u53CD read_before_write \u5B89\u5168\u539F\u5219\uFF1A\u5BF9\u6587\u4EF6 ${unreadPaths[0]} \u6267\u884C\u5199\u5165\u64CD\u4F5C\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5BF9\u8BE5\u6587\u4EF6\u7684\u8BFB\u53D6\u64CD\u4F5C`,
550
+ riskLevel: "high",
551
+ riskType: "read_before_write",
552
+ confidence: 85,
553
+ source: "logic_rule",
554
+ violatedLayers: ["2"]
555
+ };
556
+ }
557
+ return null;
558
+ }
559
+ function checkPasswordConsent(actionType, actionDetail, actionHistory) {
560
+ if (actionType !== "bash") return null;
561
+ const isPasswordModify = PASSWORD_MODIFY_PATTERNS.some((p) => p.test(actionDetail));
562
+ if (!isPasswordModify) return null;
563
+ const hasConsent = actionHistory.some(
564
+ (h) => h.actionType.toLowerCase() === "ask_user_question" && h.actionDetail.trim().length > 0
565
+ );
566
+ if (hasConsent) return null;
567
+ return {
568
+ allowed: false,
569
+ reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u5BC6\u7801\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
570
+ riskLevel: "high",
571
+ riskType: "consent_missing",
572
+ confidence: 85,
573
+ source: "logic_rule",
574
+ violatedLayers: ["2"]
575
+ };
576
+ }
577
+ function checkUserDeletionConsent(actionType, actionDetail, actionHistory) {
578
+ if (actionType !== "bash") return null;
579
+ const isUserDeletion = USER_DELETION_PATTERNS.some((p) => p.test(actionDetail));
580
+ if (!isUserDeletion) return null;
581
+ const hasConsent = actionHistory.some(
582
+ (h) => h.actionType.toLowerCase() === "ask_user_question" && h.actionDetail.trim().length > 0
583
+ );
584
+ if (hasConsent) return null;
585
+ return {
586
+ allowed: false,
587
+ reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
588
+ riskLevel: "high",
589
+ riskType: "consent_missing",
590
+ confidence: 85,
591
+ source: "logic_rule",
592
+ violatedLayers: ["2"]
593
+ };
594
+ }
595
+ function checkIndirectFileAccess(actionType, actionDetail, cwd) {
596
+ if (actionType !== "bash" && actionType !== "executebash" && actionType !== "terminal-executebash") {
597
+ return null;
598
+ }
599
+ INLINE_SCRIPT_RE.lastIndex = 0;
600
+ if (!INLINE_SCRIPT_RE.test(actionDetail)) {
601
+ return null;
602
+ }
603
+ const lowerDetail = actionDetail.toLowerCase();
604
+ const hasTraversal = DIR_TRAVERSAL_KW.some((kw) => lowerDetail.includes(kw));
605
+ if (!hasTraversal) {
606
+ return null;
607
+ }
608
+ const READ_KEYWORDS2 = ["read(", "readline", "readlines", "fread", "fgets", "load(", "getpass", "file_get_contents"];
609
+ const WRITE_KEYWORDS2 = ["write(", "writelines", "fwrite", "file_put_contents"];
610
+ const EXEC_KEYWORDS = ["system(", "exec(", "eval(", "popen(", "os.system", "os.popen", "subprocess", "os.exec", "os.spawn", "shell=true", "check_output", "check_call", "run(", "passthru", "shell_exec", "proc_open"];
611
+ const OPEN_KEYWORDS = ["open(", "fopen("];
612
+ const code = extractInlineCode(actionDetail);
613
+ if (!code) return null;
614
+ const codeLower = code.toLowerCase();
615
+ const hasAnyOp = READ_KEYWORDS2.some((kw) => codeLower.includes(kw)) || WRITE_KEYWORDS2.some((kw) => codeLower.includes(kw)) || EXEC_KEYWORDS.some((kw) => codeLower.includes(kw)) || OPEN_KEYWORDS.some((kw) => codeLower.includes(kw));
616
+ if (!hasAnyOp) {
617
+ return null;
618
+ }
619
+ const hasWrite = WRITE_KEYWORDS2.some((kw) => codeLower.includes(kw));
620
+ const hasExec = EXEC_KEYWORDS.some((kw) => codeLower.includes(kw));
621
+ const blockAllSensitive = hasWrite || hasExec;
622
+ let actualFiles = [];
623
+ if (cwd && cwd !== "/") {
624
+ try {
625
+ actualFiles = fs2.readdirSync(cwd);
626
+ } catch {
627
+ }
628
+ }
629
+ if (actualFiles.length > 0) {
630
+ for (const cp of CREDENTIAL_PATHS) {
631
+ const fname = cp.path.split("/").pop() || cp.path;
632
+ if (actualFiles.some((f) => f === fname || f.toLowerCase() === fname.toLowerCase())) {
633
+ return {
634
+ allowed: false,
635
+ reason: `\u5185\u8054\u811A\u672C\u4F7F\u7528\u76EE\u5F55\u904D\u5386\u52A8\u6001\u53D1\u73B0\u6587\u4EF6\u5E76\u6267\u884C\u8BFB\u5199\u64CD\u4F5C\uFF0Ccwd (${cwd}) \u4E2D\u5305\u542B\u51ED\u636E\u6587\u4EF6: ${fname}`,
636
+ riskLevel: "high",
637
+ riskType: "file_access",
638
+ confidence: 95,
639
+ source: "logic_rule",
640
+ violatedLayers: ["2"],
641
+ isCredential: true
642
+ };
643
+ }
644
+ }
645
+ if (blockAllSensitive) {
646
+ for (const sp of [...ROOT_SENSITIVE_PATHS, ...HOME_SENSITIVE_PATHS]) {
647
+ const fname = sp.path.split("/").pop() || sp.path;
648
+ if (actualFiles.some((f) => f === fname || f.toLowerCase() === fname.toLowerCase())) {
649
+ return {
650
+ allowed: false,
651
+ reason: `\u5185\u8054\u811A\u672C\u4F7F\u7528\u76EE\u5F55\u904D\u5386\u52A8\u6001\u53D1\u73B0\u6587\u4EF6\u5E76\u6267\u884C\u5199\u64CD\u4F5C\uFF0Ccwd (${cwd}) \u4E2D\u5305\u542B\u654F\u611F\u6587\u4EF6: ${fname}`,
652
+ riskLevel: "high",
653
+ riskType: "file_access",
654
+ confidence: 95,
655
+ source: "logic_rule",
656
+ violatedLayers: ["2"]
657
+ };
658
+ }
659
+ }
660
+ }
661
+ return null;
662
+ }
663
+ return {
664
+ allowed: false,
665
+ reason: "\u5185\u8054\u811A\u672C\u4F7F\u7528\u76EE\u5F55\u904D\u5386\u51FD\u6570\u52A8\u6001\u53D1\u73B0\u6587\u4EF6\u5E76\u6267\u884C\u8BFB\u5199\u64CD\u4F5C\uFF0C\u65E0\u6CD5\u83B7\u53D6 cwd \u5B9E\u9645\u6587\u4EF6\u5217\u8868\uFF0C\u8F6C\u5C423 LLM \u5206\u6790",
666
+ riskLevel: "medium",
667
+ riskType: "file_access",
668
+ confidence: 70,
669
+ source: "logic_rule",
670
+ violatedLayers: ["2"]
671
+ };
672
+ }
673
+ var ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, NON_WRITE_TOOLS, READ_ONLY_REDIR_CMDS, WRITE_KEYWORDS, READ_KEYWORDS, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW;
674
+ var init_logic_rules = __esm({
675
+ "src/engine/logic-rules.ts"() {
676
+ "use strict";
677
+ init_esm_shims();
678
+ init_inline_analyzer();
679
+ ROOT_SENSITIVE_PATHS = [
680
+ { path: "/etc/shadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6", credential: true },
681
+ { path: "/etc/gshadow", riskLevel: "critical", desc: "\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6", credential: true },
682
+ // 注意:/etc/passwd 仅从 L1 启发式规则移除(公开可读),L2 敏感路径仍保留
683
+ { path: "/etc/passwd", riskLevel: "high", desc: "\u7CFB\u7EDF\u7528\u6237\u6587\u4EF6" },
684
+ { path: "/etc/sudoers", riskLevel: "critical", desc: "sudo \u914D\u7F6E", credential: true },
685
+ // 系统配置(仅拦截写入)
686
+ { path: "/etc/hosts", riskLevel: "medium", desc: "DNS \u89E3\u6790\u914D\u7F6E" },
687
+ { path: "/etc/crontab", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1" },
688
+ { path: "/etc/cron.d/", riskLevel: "high", desc: "\u7CFB\u7EDF\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55\uFF08\u62C6\u5206\u5F0F crontab\uFF09" },
689
+ { path: "/var/spool/cron/", riskLevel: "high", desc: "\u7528\u6237\u7EA7\u5B9A\u65F6\u4EFB\u52A1\u76EE\u5F55" },
690
+ { path: "/etc/systemd/", riskLevel: "high", desc: "systemd \u670D\u52A1\u914D\u7F6E" },
691
+ { path: "/etc/ssh/sshd_config", riskLevel: "high", desc: "SSH \u670D\u52A1\u914D\u7F6E" },
692
+ { path: "/etc/pam.d/", riskLevel: "high", desc: "PAM \u53EF\u63D2\u62D4\u8BA4\u8BC1\u6A21\u5757\u914D\u7F6E" },
693
+ // 危险目录(仅拦截写入)
694
+ { path: "/boot/", riskLevel: "critical", desc: "\u542F\u52A8\u5F15\u5BFC\u76EE\u5F55" },
695
+ { path: "/proc/sys/", riskLevel: "high", desc: "\u5185\u6838\u53C2\u6570" },
696
+ { path: "/sys/", riskLevel: "high", desc: "sysfs \u5185\u6838\u63A5\u53E3" },
697
+ // 设备文件(/dev/null、/dev/zero、/dev/urandom 已移除,误报率极高)
698
+ { path: "/dev/random", riskLevel: "high", desc: "\u963B\u585E\u5F0F\u968F\u673A\u6570\u8BBE\u5907\uFF08\u8BFB\u53D6\u8017\u5C3D\u71B5\u6C60\uFF09" },
699
+ { path: "/dev/mem", riskLevel: "critical", desc: "\u7269\u7406\u5185\u5B58\u8BBF\u95EE\u8BBE\u5907" },
700
+ { path: "/dev/kmsg", riskLevel: "high", desc: "\u5185\u6838\u6D88\u606F\u7F13\u51B2\u533A" },
701
+ // SSH 主机私钥
702
+ { path: "/etc/ssh/ssh_host_rsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A RSA \u79C1\u94A5", credential: true },
703
+ { path: "/etc/ssh/ssh_host_ed25519_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A Ed25519 \u79C1\u94A5", credential: true },
704
+ { path: "/etc/ssh/ssh_host_ecdsa_key", riskLevel: "critical", desc: "SSH \u4E3B\u673A ECDSA \u79C1\u94A5", credential: true }
705
+ ];
706
+ HOME_SENSITIVE_PATHS = [
707
+ { path: ".ssh/id_rsa", riskLevel: "critical", desc: "SSH \u79C1\u94A5", credential: true },
708
+ { path: ".ssh/id_ed25519", riskLevel: "critical", desc: "SSH \u79C1\u94A5 (ed25519)", credential: true },
709
+ { path: ".ssh/authorized_keys", riskLevel: "high", desc: "SSH \u6388\u6743\u5BC6\u94A5" },
710
+ // 持久化与命令劫持(仅拦截写入)
711
+ { path: ".bashrc", riskLevel: "high", desc: "\u7528\u6237 Shell \u521D\u59CB\u5316\u811A\u672C" },
712
+ { path: ".bash_profile", riskLevel: "high", desc: "\u7528\u6237 Shell \u767B\u5F55\u811A\u672C" },
713
+ { path: ".profile", riskLevel: "high", desc: "\u7528\u6237\u901A\u7528\u767B\u5F55\u811A\u672C" },
714
+ { path: ".aws/credentials", riskLevel: "critical", desc: "AWS \u51ED\u8BC1", credential: true },
715
+ { path: ".gitconfig", riskLevel: "medium", desc: "Git \u914D\u7F6E" },
716
+ { path: ".gnupg", riskLevel: "high", desc: "GPG \u5BC6\u94A5\u76EE\u5F55" }
717
+ ];
718
+ CREDENTIAL_PATHS = [
719
+ { path: "credentials.yml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
720
+ { path: "credentials.yaml", riskLevel: "high", desc: "\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" },
721
+ { path: "secrets.yml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
722
+ { path: "secrets.yaml", riskLevel: "high", desc: "\u5BC6\u94A5\u914D\u7F6E\u6587\u4EF6" },
723
+ { path: ".env", riskLevel: "high", desc: "\u73AF\u5883\u53D8\u91CF/\u51ED\u636E\u914D\u7F6E\u6587\u4EF6" }
724
+ ];
725
+ NON_WRITE_TOOLS = /* @__PURE__ */ new Set(["ask_user_question", "glob", "grep", "list_dir", "count_text_length", "search"]);
726
+ READ_ONLY_REDIR_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump"]);
727
+ WRITE_KEYWORDS = ["write", "\u5199\u5165", "save", "\u4FDD\u5B58", "modify", "\u4FEE\u6539", "update", "\u66F4\u65B0", "delete", "\u5220\u9664", "remove", "\u79FB\u9664", "create", "\u521B\u5EFA", "append", "\u8FFD\u52A0", "truncate", "\u622A\u65AD", "overwrite", "\u8986\u76D6", "replace", "\u66FF\u6362"];
728
+ READ_KEYWORDS = ["read", "\u8BFB\u53D6", "cat", "head", "tail", "less", "more", "view", "\u67E5\u770B", "open", "\u6253\u5F00", "load", "\u52A0\u8F7D", "grep", "search", "\u641C\u7D22", "find", "\u67E5\u627E"];
729
+ CREATE_KEYWORDS = ["\u521B\u5EFA", "\u65B0\u5EFA", "create", "new file", "touch"];
730
+ PASSWORD_MODIFY_PATTERNS = [
731
+ /\|\s*passwd\b/i,
732
+ /\bpasswd\s+--stdin\b/i,
733
+ /\bchpasswd\b/i,
734
+ /\bchgpasswd\b/i,
735
+ /\b(newusers|lnewusers)\b/i,
736
+ /\bpasswd\s+-[a-zA-Z]*d\b/i,
737
+ /\bpasswd\s+-[a-zA-Z]*l\b/i
738
+ ];
739
+ USER_DELETION_PATTERNS = [
740
+ /\buserdel\s/i,
741
+ /\bdeluser\s/i,
742
+ /\bluserdel\s/i,
743
+ /\bgroupdel\s/i,
744
+ /\blgroupdel\s/i
745
+ ];
746
+ INTENT_DEVIATION_PATTERNS = [
747
+ {
748
+ intentKeywords: ["\u8BFB\u53D6", "\u67E5\u770B", "\u5206\u6790", "read", "analyze", "view", "list", "\u5217\u51FA"],
749
+ dangerousActions: ["\u5220\u9664", "remove", "delete", "rm ", "\u683C\u5F0F\u5316", "format"],
750
+ reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u8BFB\u53D6/\u5206\u6790\uFF0C\u4F46\u6267\u884C\u4E86\u5220\u9664\u64CD\u4F5C"
751
+ },
752
+ {
753
+ intentKeywords: ["\u67E5\u8BE2", "\u641C\u7D22", "search", "query", "find"],
754
+ dangerousActions: ["\u4FEE\u6539", "update", "write", "\u5199\u5165", "alter"],
755
+ reason: "\u539F\u59CB\u610F\u56FE\u4E3A\u67E5\u8BE2/\u641C\u7D22\uFF0C\u4F46\u6267\u884C\u4E86\u4FEE\u6539\u64CD\u4F5C"
756
+ }
757
+ ];
758
+ INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
759
+ DIR_TRAVERSAL_KW = [
760
+ "os.listdir",
761
+ "os.walk",
762
+ "os.scandir",
763
+ "glob.glob",
764
+ "glob.iglob",
765
+ "pathlib.",
766
+ "iterdir",
767
+ "rglob",
768
+ "scandir(",
769
+ "listdir("
770
+ ];
771
+ }
772
+ });
773
+
774
+ // src/engine/heuristic.ts
775
+ function runHeuristicCheck(input) {
776
+ const command = `${input.aNext.actionType}: ${input.aNext.actionDetail}`;
777
+ const matched = [];
778
+ let highestSeverity = "low";
779
+ let highestRiskType = "";
780
+ const userRules = getCustomRules();
781
+ for (const rule of userRules) {
782
+ try {
783
+ const re = new RegExp(rule.pattern);
784
+ if (re.test(command)) {
785
+ if (rule.action === "Deny") {
786
+ matched.push(`\u81EA\u5B9A\u4E49\u89C4\u5219: ${rule.pattern}`);
787
+ if (severityRank(rule.severity) > severityRank(highestSeverity)) {
788
+ highestSeverity = rule.severity;
789
+ highestRiskType = "custom_rule";
790
+ }
791
+ }
792
+ }
793
+ } catch {
794
+ }
795
+ }
796
+ let topRiskType = "";
797
+ for (const rule of DANGEROUS_PATTERNS) {
798
+ if (rule.pattern.test(command)) {
799
+ matched.push(rule.description);
800
+ if (severityRank(rule.severity) > severityRank(highestSeverity)) {
801
+ highestSeverity = rule.severity;
802
+ topRiskType = rule.riskType;
803
+ }
804
+ }
805
+ }
806
+ highestRiskType = topRiskType || highestRiskType;
807
+ const agentExt = getAgentExtensions(input.agentId);
808
+ if (agentExt?.extra_command_patterns) {
809
+ for (const rule of agentExt.extra_command_patterns) {
810
+ try {
811
+ const re = new RegExp(rule.pattern, "i");
812
+ if (re.test(command)) {
813
+ matched.push(`agent \u4E13\u5C5E\u89C4\u5219: ${rule.reason}`);
814
+ if (severityRank(rule.risk_level) > severityRank(highestSeverity)) {
815
+ highestSeverity = rule.risk_level;
816
+ highestRiskType = rule.risk_type;
817
+ }
818
+ }
819
+ } catch {
820
+ }
821
+ }
822
+ }
823
+ const injectionText = `${input.aNext.actionType} ${input.aNext.actionDetail} ${input.reason || ""} ${input.promptSession || ""}`.toLowerCase();
824
+ const injectionHits = checkInjectionKeywords(injectionText, agentExt?.extra_injection_keywords);
825
+ if (injectionHits.length > 0) {
826
+ const topHit = injectionHits.reduce((a, b) => severityRank(a.riskLevel) >= severityRank(b.riskLevel) ? a : b);
827
+ matched.push(...injectionHits.map((h) => `\u6CE8\u5165\u5173\u952E\u8BCD [${h.lang}]: ${h.keyword}`));
828
+ if (severityRank(topHit.riskLevel) > severityRank(highestSeverity)) {
829
+ highestSeverity = topHit.riskLevel;
830
+ highestRiskType = "prompt_injection";
831
+ }
832
+ }
833
+ const actionTypeL = input.aNext.actionType.toLowerCase();
834
+ const detailL = input.aNext.actionDetail.toLowerCase();
835
+ const isExcludedType = SENSITIVE_EXCLUDED_PREFIXES.some((p) => actionTypeL.startsWith(p));
836
+ if (!isExcludedType) {
837
+ const pathResult = checkSensitivePath(detailL, input.cwd || "/", agentExt);
838
+ if (pathResult) return pathResult;
839
+ }
840
+ const dangerousResult = checkDangerousPatterns(actionTypeL, detailL);
841
+ if (dangerousResult) return dangerousResult;
842
+ const lateralResult = checkLateralMovement(detailL);
843
+ if (lateralResult) return lateralResult;
844
+ if (matched.length > 0) {
845
+ return {
846
+ allowed: false,
847
+ reason: `\u68C0\u6D4B\u5230\u9AD8\u5371\u64CD\u4F5C: ${matched.join(", ")}`,
848
+ riskLevel: highestSeverity,
849
+ riskType: highestRiskType,
850
+ confidence: 95,
851
+ source: "heuristic",
852
+ violatedLayers: ["1.1"]
853
+ // 对齐 py coordinator.py:165(L1.1 = 命中高危命令模式/注入关键词)
854
+ };
855
+ }
856
+ return null;
857
+ }
858
+ function checkInjectionKeywords(text, extraKeywords) {
859
+ const hits = [];
860
+ for (const entry of INJECTION_KEYWORDS) {
861
+ if (text.includes(entry.keyword.toLowerCase())) {
862
+ hits.push(entry);
863
+ }
864
+ }
865
+ if (extraKeywords) {
866
+ for (const entry of extraKeywords) {
867
+ if (text.includes(entry.keyword.toLowerCase())) {
868
+ hits.push({ keyword: entry.keyword, riskLevel: entry.risk_level, lang: entry.lang });
869
+ }
870
+ }
871
+ }
872
+ return hits;
873
+ }
874
+ function isInlineScriptCommand(actionDetail) {
875
+ 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;
876
+ return re.test(actionDetail);
877
+ }
878
+ function severityRank(s) {
879
+ const ranks = { low: 1, medium: 2, high: 3, critical: 4 };
880
+ return ranks[s] || 0;
881
+ }
882
+ function checkSensitivePath(command, cwd, agentExt) {
883
+ const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
884
+ const _WRITE_CMD_RES = [/\brm\b/, /\bunlink\b/, /\brmdir\b/, /\bshred\b/, /\bcp\b/, /\bmv\b/, /\binstall\b/, /\btee\b/, /\bdd\b/, /\bmkfs\b/, /\b(fdisk|parted|cfdisk|sfdisk)\b/, /\bchmod\b/, /\bchown\b/, /\btruncate\b/];
885
+ const _DEVNULL_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/;
886
+ const _READONLY_CMDS = /* @__PURE__ */ new Set(["cat", "head", "tail", "less", "more", "grep", "find", "awk", "sed", "sort", "uniq", "wc", "cut", "strings", "od", "xxd", "hexdump", "tr", "file", "stat", "du", "df", "ls", "dir", "tree", "nl", "tac", "rev", "lsblk", "blockdev", "smartctl", "udevadm", "dmidecode", "lscpu", "lspci", "lsusb", "lsmem", "lsns", "lsof", "hwinfo", "inxi", "fdisk", "parted", "dmesg", "journalctl", "systemctl", "hostnamectl", "localectl", "timedatectl", "ps", "top", "free", "vmstat", "iostat", "mpstat", "pidof", "pgrep", "ip", "ifconfig", "route", "ss", "netstat", "arp", "ethtool", "nmcli", "uname", "arch", "nproc", "getconf", "getent", "id", "whoami", "who", "w", "last", "uptime", "lsmod", "modinfo", "sysctl"]);
887
+ const isWriteOpFlag = _WRITE_CMD_RES.some((rx) => rx.test(command)) || /\bsed\s+-[a-zA-Z]*i/.test(command) || command.includes(">") && !command.replace(_DEVNULL_RE, "").includes(">") || command.includes(">") && command.replace(_DEVNULL_RE, "").includes(">") && !_READONLY_CMDS.has(command.trim().split(/\s+/)[0]?.toLowerCase() || "") || WRITE_KEYWORDS.some((kw) => "".includes(kw) || command.includes(kw));
888
+ for (const sp of ROOT_SENSITIVE_PATHS) {
889
+ if (command.includes(sp.path.toLowerCase())) {
890
+ if (sp.path === "/dev/random") {
891
+ if (isWriteOpFlag) {
892
+ continue;
893
+ }
894
+ return {
895
+ allowed: false,
896
+ reason: `\u8BFB\u53D6\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
897
+ riskLevel: sp.riskLevel,
898
+ riskType: "file_access",
899
+ confidence: 90,
900
+ source: "heuristic",
901
+ violatedLayers: ["1.1"]
902
+ };
903
+ }
904
+ if (!isWriteOpFlag && !sp.credential) {
905
+ continue;
906
+ }
907
+ return {
908
+ allowed: false,
909
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE\u7CFB\u7EDF\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
910
+ riskLevel: sp.riskLevel,
911
+ riskType: "file_access",
912
+ confidence: 90,
913
+ source: "heuristic",
914
+ violatedLayers: ["1.1"],
915
+ isCredential: !!sp.credential
916
+ };
917
+ }
918
+ }
919
+ for (const sp of HOME_SENSITIVE_PATHS) {
920
+ const expanded = `${expandedCwd}/${sp.path}`;
921
+ const pathRe = sp.path.endsWith(".pub") ? new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(sp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
922
+ const expandedRe = sp.path.endsWith(".pub") ? new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "i") : new RegExp(expanded.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "(?!\\.pub)", "i");
923
+ if (pathRe.test(command) || expandedRe.test(command)) {
924
+ if (!isWriteOpFlag && !sp.credential) {
925
+ continue;
926
+ }
927
+ return {
928
+ allowed: false,
929
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
930
+ riskLevel: sp.riskLevel,
931
+ riskType: "file_access",
932
+ confidence: 90,
933
+ source: "heuristic",
934
+ violatedLayers: ["1.1"],
935
+ isCredential: !!sp.credential
936
+ };
937
+ }
938
+ }
939
+ if (agentExt?.extra_sensitive_paths && agentExt.extra_sensitive_paths.length > 0) {
940
+ for (const sp of agentExt.extra_sensitive_paths) {
941
+ const spPath = sp.path.toLowerCase();
942
+ const expanded = `${expandedCwd}/${spPath}`;
943
+ if (command.includes(spPath) || command.includes(expanded.toLowerCase())) {
944
+ const denyMode = sp.deny_mode || (sp.credential ? "deny_both" : sp.read_only ? "deny_read" : "deny_write");
945
+ if (denyMode === "deny_read") {
946
+ if (isWriteOpFlag) continue;
947
+ } else if (denyMode === "deny_write") {
948
+ if (!isWriteOpFlag) continue;
949
+ }
950
+ return {
951
+ allowed: false,
952
+ reason: `\u5C1D\u8BD5\u8BBF\u95EE agent \u4E13\u5C5E\u654F\u611F\u8DEF\u5F84: ${sp.desc} (${sp.path})`,
953
+ riskLevel: sp.risk_level,
954
+ riskType: "file_access",
955
+ confidence: 90,
956
+ source: "heuristic",
957
+ violatedLayers: ["1.1"],
958
+ isCredential: !!sp.credential
959
+ };
960
+ }
961
+ }
962
+ }
963
+ for (const cp of CREDENTIAL_PATHS) {
964
+ const escaped = cp.path.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
965
+ const re = cp.path.startsWith(".") ? new RegExp(`(?:^|[\\s/\\\\'"])${escaped}\\b`) : new RegExp(`\\b${escaped}\\b`);
966
+ if (re.test(command)) {
967
+ return {
968
+ allowed: false,
969
+ reason: `\u8BBF\u95EE\u654F\u611F\u8DEF\u5F84: ${cp.desc} (${cp.path})`,
970
+ riskLevel: cp.riskLevel,
971
+ riskType: "file_access",
972
+ confidence: 90,
973
+ source: "heuristic",
974
+ violatedLayers: ["1.1"],
975
+ isCredential: true
976
+ };
977
+ }
978
+ }
979
+ if ((command.includes("rm ") || command.includes("unlink")) && (command.includes("/etc/") || command.includes("/root/") || command.includes("/var/log/"))) {
980
+ return {
981
+ allowed: false,
982
+ reason: "\u68C0\u6D4B\u5230\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55\u5185\u5BB9",
983
+ riskLevel: "critical",
984
+ riskType: "file_access",
985
+ confidence: 90,
986
+ source: "heuristic",
987
+ violatedLayers: ["1.1"]
988
+ };
989
+ }
990
+ return null;
991
+ }
992
+ function checkDangerousPatterns(actionType, actionDetail) {
993
+ if (actionType === "file_write" || actionType === "file_edit") return null;
994
+ const TEMP_DIRS = ["/tmp/", "/var/tmp/", "/run/", "/dev/shm/"];
995
+ const isTempPath = TEMP_DIRS.some((td) => actionDetail.includes(td));
996
+ if (!isTempPath && (actionDetail.includes("*") || actionDetail.includes("?")) && ["rm ", "rm ", "del ", "del ", "delete", "remove ", "remove ", "\u5220\u9664", "\u79FB\u9664"].some((kw) => actionDetail.includes(kw))) {
997
+ return {
998
+ allowed: false,
999
+ reason: "\u68C0\u6D4B\u5230\u901A\u914D\u7B26\u7ED3\u5408\u5220\u9664\u64CD\u4F5C\uFF0C\u53EF\u80FD\u9020\u6210\u6279\u91CF\u8BEF\u5220",
1000
+ riskLevel: "high",
1001
+ riskType: "script_execution",
1002
+ confidence: 90,
1003
+ source: "heuristic",
1004
+ violatedLayers: ["1.1"]
1005
+ };
1006
+ }
1007
+ if (/>\s*\/etc\//.test(actionDetail) || />\s*\/boot\//.test(actionDetail) || />\s*\/proc\//.test(actionDetail)) {
1008
+ return {
1009
+ allowed: false,
1010
+ reason: "\u68C0\u6D4B\u5230\u91CD\u5B9A\u5411\u8986\u76D6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55",
1011
+ riskLevel: "critical",
1012
+ riskType: "file_access",
1013
+ confidence: 95,
1014
+ source: "heuristic",
1015
+ violatedLayers: ["1.1"]
1016
+ };
1017
+ }
1018
+ return null;
1019
+ }
1020
+ function extractRemoteHost(command) {
1021
+ const m = command.match(/(?:ssh|scp|rsync)\s+(?:-[\w]+\s+)*(\S+@\S+|\b(?!-)[\w.-]+\b)/);
1022
+ return m ? m[1] : null;
1023
+ }
1024
+ function isLocalHost(host) {
1025
+ const localNames = ["localhost", "127.0.0.1", "::1", process.env.HOSTNAME || "", "0.0.0.0"];
1026
+ return localNames.includes(host.replace(/.*@/, ""));
1027
+ }
1028
+ function checkLateralMovement(actionDetail) {
1029
+ if (/(?:ssh|scp|rsync)\s+/.test(actionDetail)) {
1030
+ const targetHost = extractRemoteHost(actionDetail);
1031
+ if (targetHost && !isLocalHost(targetHost)) {
1032
+ return {
1033
+ allowed: false,
1034
+ reason: `\u5C1D\u8BD5\u6A2A\u5411\u79FB\u52A8\u5230\u8FDC\u7A0B\u4E3B\u673A: ${targetHost}`,
1035
+ riskLevel: "medium",
1036
+ riskType: "lateral_movement",
1037
+ confidence: 80,
1038
+ source: "heuristic",
1039
+ violatedLayers: ["1.1"]
1040
+ };
1041
+ }
1042
+ }
1043
+ return null;
1044
+ }
1045
+ var INJECTION_KEYWORDS, DANGEROUS_PATTERNS, SENSITIVE_EXCLUDED_PREFIXES;
1046
+ var init_heuristic = __esm({
1047
+ "src/engine/heuristic.ts"() {
1048
+ "use strict";
1049
+ init_esm_shims();
1050
+ init_config();
1051
+ init_agents();
1052
+ init_logic_rules();
1053
+ INJECTION_KEYWORDS = [
1054
+ // ---- Instruction Override ----
1055
+ { keyword: "ignore previous instructions", riskLevel: "critical", lang: "en" },
1056
+ { keyword: "ignore all previous", riskLevel: "critical", lang: "en" },
1057
+ { keyword: "disregard all prior", riskLevel: "critical", lang: "en" },
1058
+ { keyword: "forget everything", riskLevel: "high", lang: "en" },
1059
+ { keyword: "\u5FFD\u7565\u4E4B\u524D\u7684\u6307\u4EE4", riskLevel: "critical", lang: "zh" },
1060
+ { keyword: "\u5FFD\u7565\u6240\u6709\u5148\u524D\u7684", riskLevel: "critical", lang: "zh" },
1061
+ { keyword: "\u5FFD\u7565\u4E0A\u9762\u6240\u6709", riskLevel: "high", lang: "zh" },
1062
+ { keyword: "\u8BF7\u5FD8\u8BB0\u4E4B\u524D", riskLevel: "high", lang: "zh" },
1063
+ // ---- Role Hijacking ----
1064
+ { keyword: "you are now", riskLevel: "high", lang: "en" },
1065
+ { keyword: "you are no longer", riskLevel: "high", lang: "en" },
1066
+ { keyword: "new instructions", riskLevel: "high", lang: "en" },
1067
+ { keyword: "from now on, you", riskLevel: "high", lang: "en" },
1068
+ { keyword: "\u4F60\u73B0\u5728\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
1069
+ { keyword: "\u4F60\u4E0D\u518D\u662F\u4E00\u4E2A", riskLevel: "high", lang: "zh" },
1070
+ { keyword: "\u4ECE\u73B0\u5728\u5F00\u59CB\u4F60", riskLevel: "high", lang: "zh" },
1071
+ { keyword: "\u4F60\u7684\u65B0\u89D2\u8272\u662F", riskLevel: "high", lang: "zh" },
1072
+ // ---- Command Injection ----
1073
+ { keyword: "system prompt", riskLevel: "high", lang: "en" },
1074
+ { keyword: "reveal your instructions", riskLevel: "high", lang: "en" },
1075
+ { keyword: "output your instructions", riskLevel: "high", lang: "en" },
1076
+ { keyword: "repeat your instructions", riskLevel: "medium", lang: "en" },
1077
+ { keyword: "\u7CFB\u7EDF\u63D0\u793A", riskLevel: "high", lang: "zh" },
1078
+ { keyword: "\u8F93\u51FA\u4F60\u7684\u6307\u4EE4", riskLevel: "high", lang: "zh" },
1079
+ { keyword: "\u91CD\u590D\u4F60\u7684\u6307\u4EE4", riskLevel: "medium", lang: "zh" },
1080
+ // ---- Social Engineering ----
1081
+ { keyword: "this is an authorized test", riskLevel: "high", lang: "en" },
1082
+ { keyword: "security audit approved", riskLevel: "high", lang: "en" },
1083
+ { keyword: "admin override", riskLevel: "critical", lang: "en" },
1084
+ { keyword: "debug mode", riskLevel: "high", lang: "en" },
1085
+ { keyword: "maintenance mode", riskLevel: "high", lang: "en" },
1086
+ { keyword: "\u8FD9\u662F\u6388\u6743\u7684\u6D4B\u8BD5", riskLevel: "high", lang: "zh" },
1087
+ { keyword: "\u5B89\u5168\u5BA1\u8BA1\u5DF2\u6279\u51C6", riskLevel: "high", lang: "zh" },
1088
+ { keyword: "\u7BA1\u7406\u5458\u8986\u76D6", riskLevel: "critical", lang: "zh" },
1089
+ { keyword: "\u8C03\u8BD5\u6A21\u5F0F", riskLevel: "high", lang: "zh" },
1090
+ // ---- XSS ----
1091
+ { keyword: "<script>", riskLevel: "high", lang: "en" },
1092
+ { keyword: "javascript:", riskLevel: "high", lang: "en" },
1093
+ { keyword: "onerror=", riskLevel: "high", lang: "en" },
1094
+ { keyword: "onload=", riskLevel: "high", lang: "en" },
1095
+ // ---- Fake Boundaries ----
1096
+ { keyword: "=== END OF", riskLevel: "high", lang: "en" },
1097
+ { keyword: "--- END SYSTEM", riskLevel: "high", lang: "en" },
1098
+ { keyword: "[END OF", riskLevel: "high", lang: "en" },
1099
+ { keyword: "=== \u7ED3\u675F", riskLevel: "high", lang: "zh" },
1100
+ { keyword: "--- \u7ED3\u675F\u7CFB\u7EDF", riskLevel: "high", lang: "zh" },
1101
+ // ---- Developer Mode ----
1102
+ { keyword: "developer mode", riskLevel: "critical", lang: "en" },
1103
+ { keyword: "DAN mode", riskLevel: "critical", lang: "en" },
1104
+ { keyword: "jailbreak", riskLevel: "critical", lang: "en" },
1105
+ { keyword: "\u5F00\u53D1\u8005\u6A21\u5F0F", riskLevel: "critical", lang: "zh" },
1106
+ { keyword: "\u8D8A\u72F1", riskLevel: "critical", lang: "zh" }
1107
+ // 注:xiaoO 专属绕过词(绕过审计/禁用安全检查/关闭防护/bypass audit 等)
1108
+ // 已迁至 agent_moss/agents/xiaoo/rules.json 的 extra_injection_keywords,
1109
+ // 由 agent_ext 机制注入,不再污染通用层。
1110
+ ];
1111
+ DANGEROUS_PATTERNS = [
1112
+ // --- Critical: rm command graded detection (strict to loose, first match wins) ---
1113
+ // 1. rm -rf root dir (含通配符如 /* /.* 等) — 使用负向先行断言 (?!\w):/ 后面不是单词字符时匹配
1114
+ { pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\/(?!\w)/i, severity: "critical", description: "\u9012\u5F52\u5F3A\u5236\u5220\u9664\u6839\u76EE\u5F55 (rm -rf /)", riskType: "script_execution" },
1115
+ // 注意:已移除"rm -rf /任意绝对路径"的宽泛规则,避免误杀 /tmp 等临时目录清理操作
1116
+ // 2. rm with -r or -f targeting system dirs — var 分支排除 /var/tmp/(合法清理)
1117
+ { pattern: /\brm\s+-[a-zA-Z]*[rf][a-zA-Z]*\s+\/(etc|var(?!\/tmp)|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u5220\u9664\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55", riskType: "script_execution" },
1118
+ // 3. rm -r absolute path (no -f) — 排除 /tmp/ /var/tmp/ /run/ /dev/shm/(CI/开发高频合法清理)
1119
+ { pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*\s+\/(?!tmp\/|var\/tmp\/|run\/|dev\/shm\/)/i, severity: "high", description: "\u9012\u5F52\u5220\u9664\u64CD\u4F5C (rm -r /...)", riskType: "script_execution" },
1120
+ // 4. rm -f absolute path (no -r) — 同样排除临时目录
1121
+ { pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*\s+\/(?!tmp\/|var\/tmp\/|run\/|dev\/shm\/)/i, severity: "medium", description: "\u5F3A\u5236\u5220\u9664\u6587\u4EF6 (rm -f /...)", riskType: "script_execution" },
1122
+ // dd 磁盘清零 — 只拦截写入系统关键目录,放行 /tmp 等临时目录
1123
+ { pattern: /dd\s+if=\/dev\/zero.*\bof\s*=\s*\/(etc|var|home|usr|boot|root|opt|srv|sys|proc)\b/i, severity: "critical", description: "\u78C1\u76D8\u6E05\u96F6\u5199\u5165\u5173\u952E\u7CFB\u7EDF\u76EE\u5F55 (dd if=/dev/zero of=/\u7CFB\u7EDF\u76EE\u5F55)", riskType: "script_execution" },
1124
+ // mkfs 格式化 — 只拦截对块设备(/dev/sdX 等)的格式化,放行对普通文件的格式化
1125
+ { pattern: /mkfs\.\w+\s+\/dev\/(sd|hd|nvme|vd|loop|mapper|md|dm)\S+/i, severity: "critical", description: "\u683C\u5F0F\u5316\u5757\u8BBE\u5907 (mkfs /dev/\u5757\u8BBE\u5907)", riskType: "script_execution" },
1126
+ { pattern: /:\(\)\s*\{.*:\|:&.*\};:/i, severity: "critical", description: "Fork Bomb", riskType: "resource_exhaustion" },
1127
+ // --- Critical: remote code execution ---
1128
+ { pattern: /curl\s+.*\|\s*(bash|sh|zsh|dash)/i, severity: "critical", description: "curl pipe shell \u6267\u884C", riskType: "script_execution" },
1129
+ { pattern: /wget\s+.*\|\s*(bash|sh)/i, severity: "critical", description: "wget pipe shell \u6267\u884C", riskType: "script_execution" },
1130
+ { pattern: /eval\s+\$\(curl|eval\s+\$\(wget/i, severity: "critical", description: "eval \u8FDC\u7A0B\u4EE3\u7801\u6267\u884C", riskType: "script_execution" },
1131
+ { pattern: /base64\s+-d\s+.*\|\s*bash/i, severity: "critical", description: "base64 \u89E3\u7801\u6267\u884C", riskType: "script_execution" },
1132
+ // --- Critical: reverse shell ---
1133
+ { pattern: /\/dev\/tcp\/|bash\s+-i\s+>&/i, severity: "critical", description: "\u53CD\u5F39 Shell", riskType: "script_execution" },
1134
+ { pattern: /nc\s+-[el]\s+.*\d{1,5}/i, severity: "high", description: "Netcat \u53CD\u5F39\u8FDE\u63A5", riskType: "lateral_movement" },
1135
+ // --- Critical: sensitive file access ---
1136
+ { pattern: /\/etc\/shadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6 (/etc/shadow)", riskType: "file_access" },
1137
+ { pattern: /\/etc\/gshadow/i, severity: "critical", description: "\u8BBF\u95EE\u7CFB\u7EDF\u7EC4\u5BC6\u7801\u6587\u4EF6 (/etc/gshadow)", riskType: "file_access" },
1138
+ // 注意:/etc/passwd 已从拦截列表移除,因为它是公开可读的标准系统文件,真正敏感的是 /etc/shadow
1139
+ { pattern: /\.ssh\/id_rsa(?!\.pub)/i, severity: "critical", description: "\u8BBF\u95EE SSH \u79C1\u94A5\u6587\u4EF6", riskType: "file_access" },
1140
+ // --- Critical: privilege escalation / container escape ---
1141
+ { pattern: /\/var\/run\/docker\.sock/i, severity: "critical", description: "\u8BBF\u95EE Docker socket\uFF0C\u53EF\u80FD\u5BFC\u81F4\u5BB9\u5668\u9003\u9038", riskType: "privilege_escalation" },
1142
+ // --- High: dangerous permissions / ownership ---
1143
+ { pattern: /chmod\s+[0-7]*777[0-7]*/i, severity: "high", description: "\u5168\u6743\u9650\u8BBE\u7F6E (chmod 777)", riskType: "script_execution" },
1144
+ { pattern: /chown\s+root|chown\s+-R\s+\w+:\w+\s+\//i, severity: "high", description: "\u4FEE\u6539\u6240\u6709\u8005\u4E3A root / \u9012\u5F52\u4FEE\u6539\u6839\u76EE\u5F55\u6240\u6709\u8005", riskType: "script_execution" },
1145
+ // sudo 提权 — 只拦截 sudo 后跟的危险命令,不一刀切拦截所有 sudo
1146
+ { pattern: /sudo\s+(rm|chmod|chown|dd|mkfs|format|iptables|crontab|passwd|userdel|useradd|groupdel)\b/i, severity: "high", description: "\u63D0\u6743\u6267\u884C\u9AD8\u5371\u547D\u4EE4 (sudo + \u5371\u9669\u64CD\u4F5C)", riskType: "privilege_escalation" },
1147
+ // --- High: system config overwrite ---
1148
+ { pattern: />\s*\/etc\//i, severity: "critical", description: "\u8986\u76D6\u5199\u5165\u7CFB\u7EDF\u914D\u7F6E\u76EE\u5F55", riskType: "file_access" },
1149
+ // --- High: download script + execute risk ---
1150
+ { pattern: /(?:curl|wget)\s+.*(?:-o|--output|>)\s*\S+\.(?:sh|bash)\b/i, severity: "high", description: "\u8FDC\u7A0B\u4E0B\u8F7D\u811A\u672C\u5230\u672C\u5730", riskType: "script_execution" },
1151
+ { pattern: /(?:curl|wget)\s+.*\s+&&?\s*(?:chmod\s+\+x|bash|sh)/i, severity: "high", description: "\u4E0B\u8F7D\u540E\u6267\u884C", riskType: "script_execution" },
1152
+ // --- High: package manager pipe-to-shell ---
1153
+ { pattern: /git\s+clone\s+.*\|.*bash/i, severity: "high", description: "git clone pipe shell", riskType: "script_execution" },
1154
+ { pattern: /pip\s+install\s+.*\|\s*bash/i, severity: "high", description: "pip install pipe shell", riskType: "script_execution" },
1155
+ { pattern: /npm\s+install\s+-g\s+.*\|\s*bash/i, severity: "high", description: "npm install pipe shell", riskType: "script_execution" },
1156
+ // --- Medium: destructive git operations (降级为 medium,允许但记录) ---
1157
+ { pattern: /git\s+push\s+.*--force/i, severity: "medium", description: "\u5F3A\u5236\u63A8\u9001\u64CD\u4F5C (git push --force)\uFF0C\u53EF\u80FD\u8986\u76D6\u8FDC\u7A0B\u63D0\u4EA4\u5386\u53F2", riskType: "script_execution" },
1158
+ // --- High: network port scanning (横向移动侦察) ---
1159
+ { pattern: /\bnmap\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF (nmap)\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
1160
+ { pattern: /\b(masscan|zmap|unicornscan)\s+/i, severity: "high", description: "\u7F51\u7EDC\u7AEF\u53E3\u626B\u63CF\u5DE5\u5177\uFF0C\u53EF\u80FD\u7528\u4E8E\u6A2A\u5411\u79FB\u52A8\u4FA6\u5BDF", riskType: "lateral_movement" },
1161
+ // --- Medium: credential export ---
1162
+ { pattern: /export\s+.*(?:TOKEN|KEY|SECRET|PASSWORD)/i, severity: "medium", description: "\u5BFC\u51FA\u654F\u611F\u51ED\u8BC1", riskType: "data_exfiltration" },
1163
+ // --- High: shell rc file append (persistence) ---
1164
+ { pattern: />>\s*(~|\/home\/[^/]+)\/(\.bashrc|\.zshrc|\.profile|\.bash_profile|\.zprofile)/, severity: "high", description: "\u68C0\u6D4B\u5230\u4FEE\u6539 Shell \u914D\u7F6E\u6587\u4EF6\uFF08~/.bashrc \u7B49\uFF09\uFF0C\u53EF\u80FD\u7528\u4E8E\u6301\u4E45\u5316\u540E\u95E8", riskType: "persistence" },
1165
+ // --- High: device file write (只拦截重定向写入设备文件,不拦截读取) ---
1166
+ { pattern: />\s*\/dev\/(sd|hd|nvme|vd|loop|mem|kmsg|port|zero|random|urandom|full|core)/i, severity: "high", description: "\u91CD\u5B9A\u5411\u5199\u5165\u8BBE\u5907\u6587\u4EF6", riskType: "file_access" },
1167
+ // --- High: non-interactive password modification ---
1168
+ { pattern: /\|\s*passwd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7BA1\u9053\u65B9\u5F0F\u8C03\u7528 passwd\uFF08\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\uFF09", riskType: "script_execution" },
1169
+ { pattern: /\bpasswd\s+--stdin\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 passwd --stdin \u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u8F93\u5165", riskType: "script_execution" },
1170
+ { pattern: /\bchpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 chpasswd \u6279\u91CF\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4", riskType: "script_execution" },
1171
+ { pattern: /\b(newusers|lnewusers)\b/i, severity: "high", description: "\u68C0\u6D4B\u5230 newusers/lnewusers \u6279\u91CF\u7528\u6237\u6DFB\u52A0\u547D\u4EE4", riskType: "script_execution" },
1172
+ { pattern: /\bpasswd\s+-[a-zA-Z]*d\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u5BC6\u7801\u64CD\u4F5C (passwd -d)", riskType: "script_execution" },
1173
+ { pattern: /\bpasswd\s+-[a-zA-Z]*l\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u9501\u5B9A\u7528\u6237\u8D26\u6237\u64CD\u4F5C (passwd -l)", riskType: "script_execution" },
1174
+ // --- High: non-interactive password modification (extended) ---
1175
+ { pattern: /\blpasswd\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u64CD\u4F5C (lpasswd)", riskType: "script_execution" },
1176
+ { pattern: /\bgpasswd\s+(?!-[adA])/i, severity: "high", description: "\u68C0\u6D4B\u5230\u7EC4\u5BC6\u7801\u7BA1\u7406\u64CD\u4F5C (gpasswd)", riskType: "script_execution" },
1177
+ { pattern: /\bchgpasswd\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u6279\u91CF\u4FEE\u6539\u7EC4\u5BC6\u7801\u64CD\u4F5C (chgpasswd)", riskType: "script_execution" },
1178
+ // --- High: user/group deletion (irreversible, requires user consent) ---
1179
+ { pattern: /\buserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (userdel)", riskType: "script_execution" },
1180
+ { pattern: /\bdeluser\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (deluser)", riskType: "script_execution" },
1181
+ { pattern: /\bluserdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u64CD\u4F5C (luserdel)", riskType: "script_execution" },
1182
+ { pattern: /\bgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (groupdel)", riskType: "script_execution" },
1183
+ { pattern: /\blgroupdel\s/i, severity: "high", description: "\u68C0\u6D4B\u5230\u5220\u9664\u7528\u6237\u7EC4\u64CD\u4F5C (lgroupdel)", riskType: "script_execution" },
1184
+ // --- High: generic command -p parameter password setting ---
1185
+ { pattern: /\b(useradd|luseradd|groupadd|usermod|lusermod|groupmod|lgroupmod)\b[^&|;]*?\s-p\b/i, severity: "high", description: "\u68C0\u6D4B\u5230\u901A\u8FC7 -p \u53C2\u6570\u8BBE\u7F6E\u6216\u4FEE\u6539\u5BC6\u7801 (useradd/usermod/groupmod)", riskType: "script_execution" }
1186
+ ];
1187
+ SENSITIVE_EXCLUDED_PREFIXES = ["memory-", "skill-", "skill-manager-", "mailbox-", "app-"];
1188
+ }
1189
+ });
1190
+
1191
+ // src/engine/skill-loader.ts
1192
+ import { readFileSync as readFileSync2, readdirSync as readdirSync2, existsSync as existsSync3 } from "fs";
1193
+ import { resolve as resolve2, dirname as dirname2 } from "path";
1194
+ import { fileURLToPath as fileURLToPath3 } from "url";
1195
+ function loadAllSkills() {
1196
+ if (cachedSkills) return cachedSkills;
1197
+ cachedSkills = {};
1198
+ const dir = existsSync3(SKILLS_DIR) ? SKILLS_DIR : SKILLS_DIR_FALLBACK;
1199
+ if (!existsSync3(dir)) {
1200
+ console.warn("[AgentMoss] Skills directory not found:", SKILLS_DIR, "and fallback:", SKILLS_DIR_FALLBACK);
1201
+ return cachedSkills;
1202
+ }
1203
+ const files = readdirSync2(dir).filter((f) => f.endsWith(".md"));
1204
+ for (const f of files) {
1205
+ const name = f.replace(".md", "");
1206
+ cachedSkills[name] = readFileSync2(resolve2(dir, f), "utf-8");
1207
+ }
1208
+ console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
1209
+ return cachedSkills;
1210
+ }
1211
+ function matchSkills(actionType, actionDetail, reason, maxSkills = 3) {
1212
+ const skills = loadAllSkills();
1213
+ const combined = `${actionType} ${actionDetail} ${reason}`.toLowerCase();
1214
+ const actionDetailLower = actionDetail.toLowerCase();
1215
+ const actionTypeLower = actionType.toLowerCase();
1216
+ const reasonLower = reason.toLowerCase();
1217
+ const scores = {};
1218
+ for (const [skillName, keywords] of Object.entries(SKILL_KEYWORD_MAP)) {
1219
+ let score = 0;
1220
+ for (const kw of keywords) {
1221
+ if (combined.includes(kw)) {
1222
+ if (kw === actionTypeLower) score += 2;
1223
+ else if (actionDetailLower.includes(kw)) score += 1.5;
1224
+ else if (reasonLower.includes(kw)) score += 1;
1225
+ else score += 0.5;
1226
+ }
1227
+ }
1228
+ if (score > 0) scores[skillName] = score;
1229
+ }
1230
+ const sorted = Object.entries(scores).sort((a, b) => b[1] - a[1]);
1231
+ const top = sorted.slice(0, maxSkills);
1232
+ const maxScore = top[0]?.[1] || 1;
1233
+ const results = [];
1234
+ for (const [name, score] of top) {
1235
+ const content = skills[name];
1236
+ if (content) {
1237
+ results.push({
1238
+ skillName: name,
1239
+ relevance: Math.min(score / maxScore, 1),
1240
+ skillContent: content
1241
+ });
1242
+ }
1243
+ }
1244
+ if (results.length === 0) {
1245
+ const fallback = skills["general_tool_risk_guard"];
1246
+ if (fallback) {
1247
+ results.push({ skillName: "general_tool_risk_guard", relevance: 0.3, skillContent: fallback });
1248
+ }
1249
+ }
1250
+ return results;
1251
+ }
1252
+ function formatSkillsForPrompt(skills) {
1253
+ if (skills.length === 0) return "(\u672A\u5339\u914D\u5230\u7279\u5B9A\u5B89\u5168\u68C0\u6D4B\u89C4\u5219)";
1254
+ return skills.map(
1255
+ (s) => `### \u5B89\u5168\u68C0\u6D4B\u89C4\u5219: ${s.skillName} (\u76F8\u5173\u5EA6: ${Math.round(s.relevance * 100)}%)
1256
+
1257
+ ${s.skillContent}`
1258
+ ).join("\n\n---\n\n");
1259
+ }
1260
+ var __filename3, __dirname3, SKILLS_DIR, SKILLS_DIR_FALLBACK, SKILL_KEYWORD_MAP, cachedSkills;
1261
+ var init_skill_loader = __esm({
1262
+ "src/engine/skill-loader.ts"() {
1263
+ "use strict";
1264
+ init_esm_shims();
1265
+ __filename3 = fileURLToPath3(import.meta.url);
1266
+ __dirname3 = dirname2(__filename3);
1267
+ SKILLS_DIR = resolve2(__dirname3, "../skills");
1268
+ SKILLS_DIR_FALLBACK = resolve2(__dirname3, "../../../agent_moss/skills");
1269
+ SKILL_KEYWORD_MAP = {
1270
+ "file_access_guard": ["file", "\u6587\u4EF6", "path", "\u8DEF\u5F84", "read", "write", "cat", "open", "access", "\u8BBF\u95EE", "\u8BFB\u5199", "copy", "cp", "mv", "move"],
1271
+ "script_execution_guard": ["command", "shell", "bash", "exec", "execute", "\u6267\u884C", "\u547D\u4EE4", "script", "\u811A\u672C", "run", "\u8FD0\u884C", "subprocess", "system", "eval", "compile", "\u5B89\u88C5", "install", "pip", "npm"],
1272
+ "data_exfiltration_guard": ["upload", "\u4E0A\u4F20", "send", "\u53D1\u9001", "transfer", "\u4F20\u8F93", "export", "\u5BFC\u51FA", "download", "\u4E0B\u8F7D", "post", "http", "request", "fetch", "curl", "wget", "\u5916\u4F20", "\u6CC4\u9732", "exfil", "network", "\u7F51\u7EDC"],
1273
+ "browser_web_access_guard": ["browser", "\u6D4F\u89C8\u5668", "web", "\u7F51\u9875", "url", "navigate", "\u6D4F\u89C8", "scrape", "crawl", "\u722C\u53D6", "click", "screenshot"],
1274
+ "email_operation_guard": ["email", "\u90AE\u4EF6", "mail", "smtp", "send_email", "\u53D1\u9001\u90AE\u4EF6", "inbox", "\u6536\u4EF6\u7BB1", "\u9644\u4EF6", "attachment"],
1275
+ "general_tool_risk_guard": ["tool", "\u5DE5\u5177", "api", "invoke", "\u8C03\u7528", "function", "\u51FD\u6570"],
1276
+ "lateral_movement_guard": ["ssh", "scp", "ftp", "sftp", "rdp", "vnc", "telnet", "\u5185\u7F51", "\u6A2A\u5411", "tunnel", "\u4EE3\u7406", "proxy", "port", "\u7AEF\u53E3", "scan", "\u626B\u63CF", "ping", "traceroute", "nslookup"],
1277
+ "persistence_backdoor_guard": ["cron", "crontab", "\u5B9A\u65F6", "scheduled", "startup", "\u542F\u52A8\u9879", "service", "\u670D\u52A1", "daemon", "\u5B88\u62A4", "registry", "\u6CE8\u518C\u8868", "backdoor", "\u540E\u95E8", "persist", "\u6301\u4E45\u5316", "authorized_keys", "bashrc", "zshrc", "profile", ".rc", "shell config", "shell \u914D\u7F6E"],
1278
+ // 注意:避免裸 'memory'/'内存'/'dd'——会误命中 memory-addMemory(写记忆工具,
1279
+ // 'dd' 是 'addMemory' 的子串)等无害操作。用组合词/命令前缀精准匹配攻击场景。
1280
+ "resource_exhaustion_guard": ["fork", "bomb", "\u70B8\u5F39", "memory bomb", "oom", "out of memory", "\u5185\u5B58\u6EA2\u51FA", "\u5185\u5B58\u8017\u5C3D", "\u5185\u5B58\u6CC4\u6F0F", "disk", "\u78C1\u76D8", "cpu", "process", "\u8FDB\u7A0B", "dd if=", "/dev/zero", "/dev/null", "yes >", "while true", "\u65E0\u9650\u5FAA\u73AF", "\u8017\u5C3D", "exhaust", "\u8D44\u6E90"],
1281
+ "skill_installation_guard": ["skill", "plugin", "\u63D2\u4EF6", "extension", "\u6269\u5C55", "install skill", "\u5B89\u88C5\u63D2\u4EF6", "\u5B89\u88C5\u6280\u80FD", "marketplace", "\u5546\u5E97"],
1282
+ "supply_chain_guard": ["dependency", "\u4F9D\u8D56", "package", "\u5305", "npm install", "pip install", "requirements", "import", "library", "\u5E93", "registry", "\u4ED3\u5E93", "supply", "\u4F9B\u5E94\u94FE", "typosquatting"],
1283
+ "intent_deviation_guard": ["rm", "del", "delete", "remove", "\u5220\u9664", "format", "\u683C\u5F0F\u5316", "drop", "truncate", "\u6E05\u7A7A", "wipe", "\u64E6\u9664", "\u91CD\u7F6E", "reset", "sudo", "root", "admin", "\u7BA1\u7406\u5458", "privilege", "\u63D0\u6743"],
1284
+ "indirect_access_guard": ["listdir", "os.walk", "os.scandir", "glob.glob", "glob.iglob", "pathlib", "iterdir", "rglob", "scandir", "os.listdir", "\u76EE\u5F55\u904D\u5386", "traversal"]
1285
+ };
1286
+ cachedSkills = null;
1287
+ }
1288
+ });
1289
+
1290
+ // src/engine/token-stats.ts
1291
+ var token_stats_exports = {};
1292
+ __export(token_stats_exports, {
1293
+ getRecentRecords: () => getRecentRecords,
1294
+ getTokenStats: () => getTokenStats,
1295
+ getTokenTrend: () => getTokenTrend,
1296
+ recordTokenUsage: () => recordTokenUsage,
1297
+ resetTokenStats: () => resetTokenStats
1298
+ });
1299
+ import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync6, mkdirSync, unlinkSync } from "fs";
1300
+ import { join as join3 } from "path";
1301
+ import { homedir as homedir2 } from "os";
1302
+ function getStatsPath() {
1303
+ const envPath = process.env[ENV_STATS_PATH];
1304
+ return envPath || STATS_FILE;
1305
+ }
1306
+ function loadAllRecords(statsPath) {
1307
+ if (!existsSync6(statsPath)) return [];
1308
+ try {
1309
+ const data = JSON.parse(readFileSync6(statsPath, "utf-8"));
1310
+ if (Array.isArray(data)) return data;
1311
+ if (data && Array.isArray(data.records)) return data.records;
1312
+ return [];
1313
+ } catch (e) {
1314
+ return [];
1315
+ }
1316
+ }
1317
+ function saveRecords(statsPath, records) {
1318
+ try {
1319
+ const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
1320
+ if (dir && !existsSync6(dir)) mkdirSync(dir, { recursive: true });
1321
+ writeFileSync(statsPath, JSON.stringify(records, null, 2), "utf-8");
1322
+ } catch (e) {
1323
+ }
1324
+ }
1325
+ function recordTokenUsage(record) {
1326
+ const statsPath = getStatsPath();
1327
+ const records = loadAllRecords(statsPath);
1328
+ records.push({
1329
+ timestamp: record.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
1330
+ session_id: record.sessionId,
1331
+ step: record.step,
1332
+ model: record.model,
1333
+ prompt_tokens: record.promptTokens,
1334
+ completion_tokens: record.completionTokens,
1335
+ total_tokens: record.totalTokens
1336
+ });
1337
+ if (records.length > MAX_RECORDS) {
1338
+ records.splice(0, records.length - MAX_RECORDS);
1339
+ }
1340
+ saveRecords(statsPath, records);
1341
+ }
1342
+ function filterByDate(records, days = 0, startDate, endDate) {
1343
+ if (startDate || endDate) {
1344
+ let startDt = startDate ? new Date(startDate) : /* @__PURE__ */ new Date(-864e13);
1345
+ let endDt = endDate ? new Date(endDate) : void 0;
1346
+ if (endDate) {
1347
+ const e = new Date(endDate);
1348
+ e.setHours(23, 59, 59, 999);
1349
+ endDt = e;
1350
+ }
1351
+ const endMs = endDt ? endDt.getTime() : Number.MAX_SAFE_INTEGER;
1352
+ return records.filter((r) => {
1353
+ try {
1354
+ const ts = new Date(r.timestamp).getTime();
1355
+ return ts >= startDt.getTime() && ts <= endMs;
1356
+ } catch {
1357
+ return true;
1358
+ }
1359
+ });
1360
+ } else if (days > 0) {
1361
+ const cutoff = Date.now() - days * 864e5;
1362
+ return records.filter((r) => {
1363
+ try {
1364
+ return new Date(r.timestamp).getTime() >= cutoff;
1365
+ } catch {
1366
+ return true;
1367
+ }
1368
+ });
1369
+ }
1370
+ return records;
1371
+ }
1372
+ function aggregate(records) {
1373
+ let totalPrompt = 0, totalCompletion = 0, totalTotal = 0;
1374
+ const byStep = {};
1375
+ const byModel = {};
1376
+ const byDate = {};
1377
+ const bucket = (b, key, r) => {
1378
+ const e = b[key] || (b[key] = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, calls: 0 });
1379
+ e.prompt_tokens += r.prompt_tokens || 0;
1380
+ e.completion_tokens += r.completion_tokens || 0;
1381
+ e.total_tokens += r.total_tokens || 0;
1382
+ e.calls += 1;
1383
+ };
1384
+ for (const r of records) {
1385
+ totalPrompt += r.prompt_tokens || 0;
1386
+ totalCompletion += r.completion_tokens || 0;
1387
+ totalTotal += r.total_tokens || 0;
1388
+ bucket(byStep, r.step || "unknown", r);
1389
+ bucket(byModel, r.model || "unknown", r);
1390
+ let dateKey = "unknown";
1391
+ try {
1392
+ dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
1393
+ } catch {
1394
+ }
1395
+ bucket(byDate, dateKey, r);
1396
+ }
1397
+ const sortedDate = Object.keys(byDate).sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
1398
+ const sortedByDate = {};
1399
+ for (const k of sortedDate) sortedByDate[k] = byDate[k];
1400
+ return { totalPrompt, totalCompletion, totalTotal, byStep, byModel, byDate: sortedByDate };
1401
+ }
1402
+ function getTokenStats(days = 0, startDate, endDate) {
1403
+ const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
1404
+ const a = aggregate(records);
1405
+ return {
1406
+ total_prompt_tokens: a.totalPrompt,
1407
+ total_completion_tokens: a.totalCompletion,
1408
+ total_tokens: a.totalTotal,
1409
+ total_calls: records.length,
1410
+ by_step: a.byStep,
1411
+ by_model: a.byModel,
1412
+ by_date: a.byDate
1413
+ };
1414
+ }
1415
+ function getRecentRecords(limit = 20) {
1416
+ const records = loadAllRecords(getStatsPath());
1417
+ return records.slice(-limit);
1418
+ }
1419
+ function getTokenTrend(days = 0, startDate, endDate) {
1420
+ const records = filterByDate(loadAllRecords(getStatsPath()), days, startDate, endDate);
1421
+ const series = {};
1422
+ const datesSet = /* @__PURE__ */ new Set();
1423
+ for (const r of records) {
1424
+ let dateKey;
1425
+ try {
1426
+ dateKey = new Date(r.timestamp).toISOString().slice(0, 10);
1427
+ } catch {
1428
+ continue;
1429
+ }
1430
+ const model = r.model || "unknown";
1431
+ const tokens = r.total_tokens || 0;
1432
+ datesSet.add(dateKey);
1433
+ series[model] = series[model] || {};
1434
+ series[model][dateKey] = (series[model][dateKey] || 0) + tokens;
1435
+ }
1436
+ const dates = Array.from(datesSet).sort();
1437
+ const models = Object.keys(series).sort();
1438
+ return { dates, models, series };
1439
+ }
1440
+ function resetTokenStats() {
1441
+ const statsPath = getStatsPath();
1442
+ if (existsSync6(statsPath)) {
1443
+ try {
1444
+ unlinkSync(statsPath);
1445
+ } catch {
1446
+ }
1447
+ }
1448
+ }
1449
+ var STATS_DIR, STATS_FILE, ENV_STATS_PATH, MAX_RECORDS;
1450
+ var init_token_stats = __esm({
1451
+ "src/engine/token-stats.ts"() {
1452
+ "use strict";
1453
+ init_esm_shims();
1454
+ STATS_DIR = join3(homedir2(), ".config", "agentmoss");
1455
+ STATS_FILE = join3(STATS_DIR, "agent_moss_token_stats.json");
1456
+ ENV_STATS_PATH = "AGENT_MOSS_TOKEN_STATS_PATH";
1457
+ MAX_RECORDS = 1e4;
1458
+ }
1459
+ });
1460
+
1461
+ // src/infra/runtime-config.ts
1462
+ var runtime_config_exports = {};
1463
+ __export(runtime_config_exports, {
1464
+ DENY_MODE_LABELS: () => DENY_MODE_LABELS,
1465
+ addCustomRule: () => addCustomRule,
1466
+ addCustomSkill: () => addCustomSkill,
1467
+ deleteCustomRule: () => deleteCustomRule,
1468
+ deleteCustomSkill: () => deleteCustomSkill,
1469
+ filterEnabledRules: () => filterEnabledRules,
1470
+ generateSourceDefaults: () => generateSourceDefaults,
1471
+ getDisabledL2RulesWithSkipL3: () => getDisabledL2RulesWithSkipL3,
1472
+ getDisabledSkillIds: () => getDisabledSkillIds,
1473
+ getEnabledL1CommandPatterns: () => getEnabledL1CommandPatterns,
1474
+ getEnabledL1CommandPatternsOrDefault: () => getEnabledL1CommandPatternsOrDefault,
1475
+ getEnabledL1InjectionKeywords: () => getEnabledL1InjectionKeywords,
1476
+ getEnabledL1InjectionKeywordsOrDefault: () => getEnabledL1InjectionKeywordsOrDefault,
1477
+ getEnabledL2IntentPatterns: () => getEnabledL2IntentPatterns,
1478
+ getEnabledL2IntentPatternsOrDefault: () => getEnabledL2IntentPatternsOrDefault,
1479
+ getEnabledL2PasswordPatterns: () => getEnabledL2PasswordPatterns,
1480
+ getEnabledL2PasswordPatternsOrDefault: () => getEnabledL2PasswordPatternsOrDefault,
1481
+ getEnabledL2SensitivePaths: () => getEnabledL2SensitivePaths,
1482
+ getEnabledL2SensitivePathsOrDefault: () => getEnabledL2SensitivePathsOrDefault,
1483
+ getEnabledL2UserDeletionPatterns: () => getEnabledL2UserDeletionPatterns,
1484
+ getEnabledL2UserDeletionPatternsOrDefault: () => getEnabledL2UserDeletionPatternsOrDefault,
1485
+ getEnvOverrides: () => getEnvOverrides,
1486
+ getProfileOrDetect: () => getProfileOrDetect,
1487
+ getRuntimeConfigPath: () => getRuntimeConfigPath,
1488
+ hasRuntimeConfig: () => hasRuntimeConfig,
1489
+ isL2CategoryEnabled: () => isL2CategoryEnabled,
1490
+ isLayerEnabled: () => isLayerEnabled,
1491
+ loadRuntimeConfig: () => loadRuntimeConfig,
1492
+ mergeConfig: () => mergeConfig,
1493
+ saveRuntimeConfig: () => saveRuntimeConfig,
1494
+ updateCategoryEnabled: () => updateCategoryEnabled,
1495
+ updateLayerEnabled: () => updateLayerEnabled,
1496
+ updateRuleDenyMode: () => updateRuleDenyMode,
1497
+ updateRuleEnabled: () => updateRuleEnabled,
1498
+ updateRuleSkipL3: () => updateRuleSkipL3,
1499
+ updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
1500
+ updateSkillEnabled: () => updateSkillEnabled
1501
+ });
1502
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
1503
+ import { join as join4 } from "path";
1504
+ import { homedir as homedir3 } from "os";
1505
+ function getRuntimeConfigPath() {
1506
+ const envPath = process.env[ENV_RUNTIME_CONFIG_PATH];
1507
+ if (envPath) return envPath;
1508
+ return RUNTIME_CONFIG_PATH;
1509
+ }
1510
+ function saveRuntimeConfig(config) {
1511
+ const p = getRuntimeConfigPath();
1512
+ const dir = p.substring(0, p.lastIndexOf("/"));
1513
+ if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
1514
+ writeFileSync2(p, JSON.stringify(config, null, 2), "utf-8");
1515
+ }
1516
+ function emptyRuntimeConfig() {
1517
+ return {
1518
+ version: 1,
1519
+ layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
1520
+ L1_rules: {},
1521
+ L2_rules: {},
1522
+ L3_skills: {}
1523
+ };
1524
+ }
1525
+ function loadRuntimeConfig() {
1526
+ const p = getRuntimeConfigPath();
1527
+ if (!existsSync7(p)) {
1528
+ const seeded = mergeConfig({}, generateSourceDefaults());
1529
+ try {
1530
+ saveRuntimeConfig(seeded);
1531
+ } catch {
1532
+ }
1533
+ return seeded;
1534
+ }
1535
+ try {
1536
+ const data = JSON.parse(readFileSync7(p, "utf-8"));
1537
+ const merged = mergeConfig(data, generateSourceDefaults());
1538
+ return {
1539
+ version: data.version || 1,
1540
+ layers: {
1541
+ L1_heuristic: data.layers?.L1_heuristic ?? true,
1542
+ L2_logic_rules: data.layers?.L2_logic_rules ?? true,
1543
+ L3_llm_analysis: data.layers?.L3_llm_analysis ?? true
1544
+ },
1545
+ L1_rules: merged.L1_rules || {},
1546
+ L2_rules: merged.L2_rules || {},
1547
+ L3_skills: merged.L3_skills || {}
1548
+ };
1549
+ } catch {
1550
+ return emptyRuntimeConfig();
1551
+ }
1552
+ }
1553
+ function isLayerEnabled(layerKey) {
1554
+ const disableKey = LAYER_DISABLE_ENV[layerKey];
1555
+ if (disableKey && process.env[disableKey] === "1") return false;
1556
+ const runtime = loadRuntimeConfig();
1557
+ const layers = runtime.layers || {};
1558
+ if (layerKey in layers) return !!layers[layerKey];
1559
+ return true;
1560
+ }
1561
+ function getEnvOverrides() {
1562
+ const overrides = {};
1563
+ for (const [layerKey, envKey] of Object.entries(LAYER_DISABLE_ENV)) {
1564
+ const val = process.env[envKey];
1565
+ if (val !== void 0) {
1566
+ overrides[layerKey] = {
1567
+ env_var: envKey,
1568
+ env_value: val,
1569
+ overridden: val === "1",
1570
+ effect: val === "1" ? "disabled" : "no_override"
1571
+ };
1572
+ }
1573
+ }
1574
+ return overrides;
1575
+ }
1576
+ function filterEnabledRules(rules, categoryEnabled = true) {
1577
+ if (!categoryEnabled) return [];
1578
+ return rules.filter((r) => r.enabled !== false);
1579
+ }
1580
+ function getEnabledL1CommandPatterns(runtime) {
1581
+ const cat = runtime.L1_rules?.dangerous_commands || { category_enabled: true, rules: [] };
1582
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
1583
+ pattern: r.pattern || "",
1584
+ risk_level: r.risk_level || "",
1585
+ risk_type: r.risk_type || "",
1586
+ reason: r.reason || ""
1587
+ }));
1588
+ }
1589
+ function getEnabledL2SensitivePaths(runtime) {
1590
+ let cat = runtime.L1_rules?.sensitive_path_access;
1591
+ if (!cat || !cat.rules || cat.rules.length === 0) {
1592
+ cat = runtime.L2_rules?.sensitive_path_access || { category_enabled: true, rules: [] };
1593
+ }
1594
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => {
1595
+ const out = { path: r.path || "", risk_level: r.risk_level || "", desc: r.desc || "", deny_mode: r.deny_mode || "deny_write" };
1596
+ if (r.credential) out.credential = true;
1597
+ if (r.read_only) out.read_only = true;
1598
+ return out;
1599
+ });
1600
+ }
1601
+ function hasRuntimeConfig() {
1602
+ const p = getRuntimeConfigPath();
1603
+ if (!existsSync7(p)) return false;
1604
+ try {
1605
+ JSON.parse(readFileSync7(p, "utf-8"));
1606
+ return true;
1607
+ } catch {
1608
+ return false;
1609
+ }
1610
+ }
1611
+ function getDisabledL2RulesWithSkipL3() {
1612
+ let runtime;
1613
+ try {
1614
+ runtime = loadRuntimeConfig();
1615
+ } catch {
1616
+ return {};
1617
+ }
1618
+ const result = {};
1619
+ for (const layerKey of ["L1_rules", "L2_rules"]) {
1620
+ const layer = runtime[layerKey] || {};
1621
+ for (const [catName, cat] of Object.entries(layer)) {
1622
+ if (!cat.category_enabled) continue;
1623
+ const items = [];
1624
+ for (const r of cat.rules || []) {
1625
+ if (r.enabled === false && r.skip_l3_on_disabled) {
1626
+ if (catName === "sensitive_path_access") {
1627
+ if (r.path) items.push(r.path);
1628
+ } else if (catName === "intent_consistency") {
1629
+ items.push(...r.intent_keywords || []);
1630
+ } else if (r.pattern) {
1631
+ items.push(r.pattern);
1632
+ }
1633
+ }
1634
+ }
1635
+ if (items.length > 0) result[catName] = items;
1636
+ }
1637
+ }
1638
+ return result;
1639
+ }
1640
+ function _slugify(text) {
1641
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "x";
1642
+ }
1643
+ function _generatePatternId(pattern) {
1644
+ return `pat_${_slugify(pattern)}`;
1645
+ }
1646
+ function _deriveDenyMode(rule) {
1647
+ if (rule.deny_mode) return rule.deny_mode;
1648
+ if (rule.credential) return "deny_both";
1649
+ if (rule.read_only) return "deny_read";
1650
+ return "deny_write";
1651
+ }
1652
+ function _syncDenyModeFields(rule) {
1653
+ const dm = rule.deny_mode || "deny_write";
1654
+ if (dm === "deny_both") {
1655
+ rule.credential = true;
1656
+ delete rule.read_only;
1657
+ } else if (dm === "deny_read") {
1658
+ delete rule.credential;
1659
+ rule.read_only = true;
1660
+ } else {
1661
+ delete rule.credential;
1662
+ delete rule.read_only;
1663
+ }
1664
+ }
1665
+ function _l1CommandPatternsToRules() {
1666
+ return DANGEROUS_PATTERNS.map((p) => ({
1667
+ id: _generatePatternId(p.pattern instanceof RegExp ? p.pattern.source : p.pattern),
1668
+ pattern: p.pattern instanceof RegExp ? p.pattern.source : p.pattern,
1669
+ risk_level: p.severity,
1670
+ risk_type: p.riskType,
1671
+ reason: p.description,
1672
+ enabled: true,
1673
+ builtin: true
1674
+ }));
1675
+ }
1676
+ function _l1InjectionKeywordsToRules() {
1677
+ return INJECTION_KEYWORDS.map((kw) => ({
1678
+ id: `inj_${_slugify(kw.keyword)}`,
1679
+ keyword: kw.keyword,
1680
+ risk_level: kw.riskLevel,
1681
+ lang: kw.lang || "en",
1682
+ enabled: true,
1683
+ builtin: true
1684
+ }));
1685
+ }
1686
+ function _l2SensitivePathsToRules() {
1687
+ const all = [...ROOT_SENSITIVE_PATHS, ...HOME_SENSITIVE_PATHS, ...CREDENTIAL_PATHS];
1688
+ return all.map((sp) => {
1689
+ const denyMode = _deriveDenyMode({
1690
+ deny_mode: sp.deny_mode,
1691
+ credential: sp.credential,
1692
+ read_only: sp.read_only
1693
+ });
1694
+ const rule = {
1695
+ id: `path_${_slugify(sp.path)}`,
1696
+ path: sp.path,
1697
+ risk_level: sp.riskLevel,
1698
+ desc: sp.desc,
1699
+ deny_mode: denyMode,
1700
+ source_deny_mode: denyMode,
1701
+ skip_l3_on_disabled: true,
1702
+ enabled: true,
1703
+ builtin: true
1704
+ };
1705
+ _syncDenyModeFields(rule);
1706
+ return rule;
1707
+ });
1708
+ }
1709
+ function _l2IntentPatternsToRules() {
1710
+ return INTENT_DEVIATION_PATTERNS.map((pat, i) => ({
1711
+ id: `intent_${i + 1}_${_slugify(pat.reason.slice(0, 30))}`,
1712
+ intent_keywords: pat.intentKeywords,
1713
+ dangerous_actions: pat.dangerousActions,
1714
+ reason: pat.reason,
1715
+ skip_l3_on_disabled: true,
1716
+ enabled: true,
1717
+ builtin: true
1718
+ }));
1719
+ }
1720
+ function _l2PasswordPatternsToRules() {
1721
+ return PASSWORD_MODIFY_PATTERNS.map((p) => ({
1722
+ id: `pw_${_slugify(p.source)}`,
1723
+ pattern: p.source,
1724
+ skip_l3_on_disabled: true,
1725
+ enabled: true,
1726
+ builtin: true
1727
+ }));
1728
+ }
1729
+ function _l2UserDeletionPatternsToRules() {
1730
+ return USER_DELETION_PATTERNS.map((p) => ({
1731
+ id: `ud_${_slugify(p.source)}`,
1732
+ pattern: p.source,
1733
+ skip_l3_on_disabled: true,
1734
+ enabled: true,
1735
+ builtin: true
1736
+ }));
1737
+ }
1738
+ function _l3SkillsToRules() {
1739
+ const skillCategories = {
1740
+ file_operations: ["file_access_guard", "script_execution_guard", "indirect_access_guard"],
1741
+ network_security: [
1742
+ "data_exfiltration_guard",
1743
+ "browser_web_access_guard",
1744
+ "lateral_movement_guard",
1745
+ "email_operation_guard"
1746
+ ],
1747
+ persistence_supply_chain: [
1748
+ "persistence_backdoor_guard",
1749
+ "supply_chain_guard",
1750
+ "skill_installation_guard"
1751
+ ],
1752
+ intent_and_general: ["intent_deviation_guard", "general_tool_risk_guard"],
1753
+ resource: ["resource_exhaustion_guard"]
1754
+ };
1755
+ const out = {};
1756
+ for (const [cat, ids] of Object.entries(skillCategories)) {
1757
+ out[cat] = {
1758
+ category_enabled: true,
1759
+ category_desc: "",
1760
+ skills: ids.map((s) => ({
1761
+ id: s,
1762
+ keywords: SKILL_KEYWORD_MAP[s] || [],
1763
+ enabled: true,
1764
+ builtin: true
1765
+ }))
1766
+ };
1767
+ }
1768
+ const categorized = new Set(Object.values(skillCategories).flat());
1769
+ for (const s of Object.keys(SKILL_KEYWORD_MAP)) {
1770
+ if (!categorized.has(s)) {
1771
+ (out.intent_and_general ??= { category_enabled: true, category_desc: "", skills: [] }).skills.push({
1772
+ id: s,
1773
+ keywords: SKILL_KEYWORD_MAP[s],
1774
+ enabled: true,
1775
+ builtin: true
1776
+ });
1777
+ }
1778
+ }
1779
+ out.user_custom = { category_enabled: true, category_desc: "", skills: [] };
1780
+ return out;
1781
+ }
1782
+ function generateSourceDefaults() {
1783
+ return {
1784
+ version: 1,
1785
+ layers: { L1_heuristic: true, L2_logic_rules: true, L3_llm_analysis: true },
1786
+ L1_rules: {
1787
+ dangerous_commands: {
1788
+ category_enabled: true,
1789
+ category_desc: "\u5371\u9669\u547D\u4EE4\u68C0\u6D4B\uFF08\u9012\u5F52\u5220\u9664\u3001\u5168\u6743\u9650\u8BBE\u7F6E\u3001\u63D0\u6743\u6267\u884C\u7B49\uFF09",
1790
+ rules: _l1CommandPatternsToRules()
1791
+ },
1792
+ injection_detection: {
1793
+ category_enabled: true,
1794
+ category_desc: "Prompt \u6CE8\u5165\u653B\u51FB\u68C0\u6D4B",
1795
+ rules: _l1InjectionKeywordsToRules()
1796
+ },
1797
+ sensitive_path_access: {
1798
+ category_enabled: true,
1799
+ category_desc: "\u654F\u611F\u8DEF\u5F84\u8BBF\u95EE\u68C0\u6D4B\uFF08deny_both/deny_read/deny_write\uFF09",
1800
+ rules: _l2SensitivePathsToRules()
1801
+ },
1802
+ user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49\u547D\u4EE4\u89C4\u5219", rules: [] }
1803
+ },
1804
+ L2_rules: {
1805
+ intent_consistency: {
1806
+ category_enabled: true,
1807
+ category_desc: "\u610F\u56FE\u4E00\u81F4\u6027\u68C0\u6D4B",
1808
+ rules: _l2IntentPatternsToRules()
1809
+ },
1810
+ password_modify_consent: {
1811
+ category_enabled: true,
1812
+ category_desc: "\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u6388\u6743\u68C0\u6D4B",
1813
+ rules: _l2PasswordPatternsToRules()
1814
+ },
1815
+ user_deletion_consent: {
1816
+ category_enabled: true,
1817
+ category_desc: "\u7528\u6237/\u7EC4\u5220\u9664\u64CD\u4F5C\u6388\u6743\u68C0\u6D4B",
1818
+ rules: _l2UserDeletionPatternsToRules()
1819
+ },
1820
+ read_before_write: { category_enabled: true, category_desc: "read_before_write \u5B89\u5168\u539F\u5219\u68C0\u6D4B", rules: [] },
1821
+ user_custom: { category_enabled: true, category_desc: "\u7528\u6237\u81EA\u5B9A\u4E49 L2 \u903B\u8F91\u89C4\u5219", rules: [] }
1822
+ },
1823
+ L3_skills: _l3SkillsToRules()
1824
+ };
1825
+ }
1826
+ function _ruleFingerprint(rule) {
1827
+ if (rule.intent_keywords && rule.intent_keywords.length || rule.dangerous_actions && rule.dangerous_actions.length) {
1828
+ return JSON.stringify([
1829
+ [...rule.intent_keywords || []].sort(),
1830
+ [...rule.dangerous_actions || []].sort()
1831
+ ]);
1832
+ }
1833
+ if (rule.path) return JSON.stringify([rule.path]);
1834
+ if (rule.pattern) return JSON.stringify([rule.pattern]);
1835
+ if (rule.keyword) return JSON.stringify([rule.keyword, rule.lang || ""]);
1836
+ return JSON.stringify([rule.id || ""]);
1837
+ }
1838
+ function _mergeRuleCategories(userCfg, srcDefaults, layerKey) {
1839
+ const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
1840
+ const srcLayer = srcDefaults[layerKey] || {};
1841
+ for (const [catName, srcCat] of Object.entries(srcLayer)) {
1842
+ if (!(catName in userLayer)) {
1843
+ userLayer[catName] = srcCat;
1844
+ continue;
1845
+ }
1846
+ const userCat = userLayer[catName];
1847
+ if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
1848
+ if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
1849
+ const srcRules = srcCat.rules || [];
1850
+ const userRules = userCat.rules || (userCat.rules = []);
1851
+ const userByFp = /* @__PURE__ */ new Map();
1852
+ for (const r of userRules) userByFp.set(_ruleFingerprint(r), r);
1853
+ const srcFps = /* @__PURE__ */ new Set();
1854
+ for (const srcR of srcRules) {
1855
+ const fp = _ruleFingerprint(srcR);
1856
+ srcFps.add(fp);
1857
+ if (userByFp.has(fp)) {
1858
+ const existing = userByFp.get(fp);
1859
+ const userEnabled = existing.enabled;
1860
+ const userSkipL3 = existing.skip_l3_on_disabled;
1861
+ const userDenyMode = existing.deny_mode;
1862
+ const userSourceDeny = existing.source_deny_mode;
1863
+ Object.keys(existing).forEach((k) => delete existing[k]);
1864
+ Object.assign(existing, srcR);
1865
+ existing.enabled = userEnabled;
1866
+ if (userSkipL3 !== void 0) existing.skip_l3_on_disabled = userSkipL3;
1867
+ if (userDenyMode && userSourceDeny && userDenyMode !== userSourceDeny) {
1868
+ existing.deny_mode = userDenyMode;
1869
+ }
1870
+ if (existing.deny_mode) _syncDenyModeFields(existing);
1871
+ } else {
1872
+ const newR = { ...srcR, enabled: true };
1873
+ userRules.push(newR);
1874
+ }
1875
+ }
1876
+ for (const r of userRules) {
1877
+ if (r.builtin && !srcFps.has(_ruleFingerprint(r))) {
1878
+ r.source_removed = true;
1879
+ r.enabled = false;
1880
+ }
1881
+ }
1882
+ if (catName === "sensitive_path_access") {
1883
+ for (const r of userRules) {
1884
+ if (!r.deny_mode) r.deny_mode = _deriveDenyMode(r);
1885
+ _syncDenyModeFields(r);
1886
+ }
1887
+ }
1888
+ }
1889
+ for (const catName of Object.keys(userLayer)) {
1890
+ if (catName === "user_custom") continue;
1891
+ if (!(catName in srcLayer)) delete userLayer[catName];
1892
+ }
1893
+ }
1894
+ function _mergeSkillCategories(userCfg, srcDefaults, layerKey) {
1895
+ const userLayer = userCfg[layerKey] || (userCfg[layerKey] = {});
1896
+ const srcLayer = srcDefaults[layerKey] || {};
1897
+ for (const [catName, srcCat] of Object.entries(srcLayer)) {
1898
+ if (!(catName in userLayer)) {
1899
+ userLayer[catName] = srcCat;
1900
+ continue;
1901
+ }
1902
+ const userCat = userLayer[catName];
1903
+ if (userCat.category_desc === void 0) userCat.category_desc = srcCat.category_desc || "";
1904
+ if (userCat.category_enabled === void 0) userCat.category_enabled = srcCat.category_enabled ?? true;
1905
+ const userSkills = userCat.skills || (userCat.skills = []);
1906
+ const existingIds = new Set(userSkills.map((s) => s.id));
1907
+ for (const srcS of srcCat.skills || []) {
1908
+ if (!existingIds.has(srcS.id)) userSkills.push({ ...srcS });
1909
+ }
1910
+ }
1911
+ }
1912
+ function mergeConfig(userCfg, srcDefaults) {
1913
+ const cfg = userCfg;
1914
+ if (cfg.version === void 0) cfg.version = srcDefaults.version || 1;
1915
+ if (!cfg.layers) cfg.layers = {};
1916
+ for (const [layerKey, defaultVal] of Object.entries(srcDefaults.layers)) {
1917
+ if (!(layerKey in cfg.layers)) cfg.layers[layerKey] = defaultVal;
1918
+ }
1919
+ _migrateCategoriesAcrossLayers(
1920
+ cfg,
1921
+ "L2_rules",
1922
+ "L1_rules",
1923
+ ["sensitive_path_access"]
1924
+ );
1925
+ _mergeRuleCategories(cfg, srcDefaults, "L1_rules");
1926
+ _mergeRuleCategories(cfg, srcDefaults, "L2_rules");
1927
+ _mergeSkillCategories(cfg, srcDefaults, "L3_skills");
1928
+ return cfg;
1929
+ }
1930
+ function _migrateCategoriesAcrossLayers(cfg, srcLayer, dstLayer, catNames) {
1931
+ const src = cfg[srcLayer] || {};
1932
+ const dst = cfg[dstLayer] || (cfg[dstLayer] = {});
1933
+ for (const catName of catNames) {
1934
+ if (!(catName in src)) continue;
1935
+ const srcCat = src[catName];
1936
+ if (!dst[catName]) {
1937
+ dst[catName] = { category_enabled: true, category_desc: "", rules: [] };
1938
+ }
1939
+ const dstCat = dst[catName];
1940
+ if (!dstCat.category_desc && srcCat.category_desc) dstCat.category_desc = srcCat.category_desc;
1941
+ if (dstCat.category_enabled === void 0) dstCat.category_enabled = srcCat.category_enabled;
1942
+ if (srcCat.rules && srcCat.rules.length > 0) {
1943
+ const dstFps = new Set((dstCat.rules || []).map((r) => _ruleFingerprint(r)));
1944
+ for (const r of srcCat.rules) {
1945
+ const fp = _ruleFingerprint(r);
1946
+ if (!dstFps.has(fp)) {
1947
+ (dstCat.rules = dstCat.rules || []).push(r);
1948
+ dstFps.add(fp);
1949
+ }
1950
+ }
1951
+ }
1952
+ delete src[catName];
1953
+ }
1954
+ }
1955
+ function getEnabledL1InjectionKeywords(runtime) {
1956
+ const cat = runtime.L1_rules?.injection_detection || { category_enabled: true, rules: [] };
1957
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
1958
+ keyword: r.keyword || "",
1959
+ risk_level: r.risk_level || "",
1960
+ lang: r.lang || "en"
1961
+ }));
1962
+ }
1963
+ function getEnabledL2IntentPatterns(runtime) {
1964
+ const cat = runtime.L2_rules?.intent_consistency || { category_enabled: true, rules: [] };
1965
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => ({
1966
+ intent_keywords: r.intent_keywords || [],
1967
+ dangerous_actions: r.dangerous_actions || [],
1968
+ reason: r.reason || ""
1969
+ }));
1970
+ }
1971
+ function getEnabledL2PasswordPatterns(runtime) {
1972
+ const cat = runtime.L2_rules?.password_modify_consent || { category_enabled: true, rules: [] };
1973
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
1974
+ }
1975
+ function getEnabledL2UserDeletionPatterns(runtime) {
1976
+ const cat = runtime.L2_rules?.user_deletion_consent || { category_enabled: true, rules: [] };
1977
+ return filterEnabledRules(cat.rules || [], cat.category_enabled).map((r) => r.pattern || "");
1978
+ }
1979
+ function isL2CategoryEnabled(runtime, categoryKey) {
1980
+ const cat = runtime.L2_rules?.[categoryKey];
1981
+ return cat?.category_enabled ?? true;
1982
+ }
1983
+ function getDisabledSkillIds(runtime) {
1984
+ const disabled = /* @__PURE__ */ new Set();
1985
+ for (const catData of Object.values(runtime.L3_skills || {})) {
1986
+ if (!catData.category_enabled) {
1987
+ for (const s of catData.skills || []) disabled.add(s.id);
1988
+ } else {
1989
+ for (const s of catData.skills || []) {
1990
+ if (s.enabled === false) disabled.add(s.id);
1991
+ }
1992
+ }
1993
+ }
1994
+ return disabled;
1995
+ }
1996
+ function getEnabledL1CommandPatternsOrDefault(runtime) {
1997
+ const enabled = getEnabledL1CommandPatterns(runtime);
1998
+ if (enabled.length) return enabled;
1999
+ if (!hasRuntimeConfig()) return _l1CommandPatternsToRules();
2000
+ return [];
2001
+ }
2002
+ function getEnabledL1InjectionKeywordsOrDefault(runtime) {
2003
+ const enabled = getEnabledL1InjectionKeywords(runtime);
2004
+ if (enabled.length) return enabled;
2005
+ if (!hasRuntimeConfig()) return _l1InjectionKeywordsToRules();
2006
+ return [];
2007
+ }
2008
+ function getEnabledL2SensitivePathsOrDefault(runtime) {
2009
+ const enabled = getEnabledL2SensitivePaths(runtime);
2010
+ if (enabled.length) return enabled;
2011
+ if (!hasRuntimeConfig()) return _l2SensitivePathsToRules();
2012
+ return [];
2013
+ }
2014
+ function getEnabledL2IntentPatternsOrDefault(runtime) {
2015
+ const enabled = getEnabledL2IntentPatterns(runtime);
2016
+ if (enabled.length) return enabled;
2017
+ if (!hasRuntimeConfig()) return _l2IntentPatternsToRules();
2018
+ return [];
2019
+ }
2020
+ function getEnabledL2PasswordPatternsOrDefault(runtime) {
2021
+ const enabled = getEnabledL2PasswordPatterns(runtime);
2022
+ if (enabled.length) return enabled;
2023
+ if (!hasRuntimeConfig()) {
2024
+ return PASSWORD_MODIFY_PATTERNS.map((p) => p.source);
2025
+ }
2026
+ return [];
2027
+ }
2028
+ function getEnabledL2UserDeletionPatternsOrDefault(runtime) {
2029
+ const enabled = getEnabledL2UserDeletionPatterns(runtime);
2030
+ if (enabled.length) return enabled;
2031
+ if (!hasRuntimeConfig()) {
2032
+ return USER_DELETION_PATTERNS.map((p) => p.source);
2033
+ }
2034
+ return [];
2035
+ }
2036
+ function _validateSkillId(skillId) {
2037
+ if (!skillId || !SKILL_ID_RE.test(skillId)) {
2038
+ throw new Error(`skill_id '${skillId}' \u4E0D\u5408\u6CD5\uFF1A\u53EA\u5141\u8BB8\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u4E0B\u5212\u7EBF\u548C\u8FDE\u5B57\u7B26`);
2039
+ }
2040
+ }
2041
+ function updateLayerEnabled(layerKey, enabled) {
2042
+ const runtime = loadRuntimeConfig();
2043
+ runtime.layers[layerKey] = enabled;
2044
+ saveRuntimeConfig(runtime);
2045
+ return runtime;
2046
+ }
2047
+ function updateRuleEnabled(layer, category, ruleId, enabled) {
2048
+ const runtime = loadRuntimeConfig();
2049
+ for (const r of runtime[layer]?.[category]?.rules || []) {
2050
+ if (r.id === ruleId) {
2051
+ r.enabled = enabled;
2052
+ break;
2053
+ }
2054
+ }
2055
+ saveRuntimeConfig(runtime);
2056
+ return runtime;
2057
+ }
2058
+ function updateRuleDenyMode(layer, category, ruleId, denyMode) {
2059
+ const runtime = loadRuntimeConfig();
2060
+ for (const r of runtime[layer]?.[category]?.rules || []) {
2061
+ if (r.id === ruleId) {
2062
+ r.deny_mode = denyMode;
2063
+ _syncDenyModeFields(r);
2064
+ break;
2065
+ }
2066
+ }
2067
+ saveRuntimeConfig(runtime);
2068
+ return runtime;
2069
+ }
2070
+ function updateRuleSkipL3(layer, category, ruleId, skipL3) {
2071
+ const runtime = loadRuntimeConfig();
2072
+ for (const r of runtime[layer]?.[category]?.rules || []) {
2073
+ if (r.id === ruleId) {
2074
+ r.skip_l3_on_disabled = skipL3;
2075
+ break;
2076
+ }
2077
+ }
2078
+ saveRuntimeConfig(runtime);
2079
+ return runtime;
2080
+ }
2081
+ function updateCategoryEnabled(layer, category, enabled) {
2082
+ const runtime = loadRuntimeConfig();
2083
+ const cat = runtime[layer]?.[category];
2084
+ if (cat) cat.category_enabled = enabled;
2085
+ saveRuntimeConfig(runtime);
2086
+ return runtime;
2087
+ }
2088
+ function addCustomRule(layer, category, rule) {
2089
+ const runtime = loadRuntimeConfig();
2090
+ const layerObj = runtime[layer] || (runtime[layer] = {});
2091
+ const cat = layerObj[category] || (layerObj[category] = { category_enabled: true, category_desc: "", rules: [] });
2092
+ const rules = cat.rules || (cat.rules = []);
2093
+ const newRule = { ...rule, builtin: false, enabled: true };
2094
+ if (!newRule.id) {
2095
+ newRule.id = `custom_${Date.now()}_${_slugify(JSON.stringify(rule).slice(0, 30))}`;
2096
+ }
2097
+ if (newRule.deny_mode) _syncDenyModeFields(newRule);
2098
+ rules.push(newRule);
2099
+ saveRuntimeConfig(runtime);
2100
+ return runtime;
2101
+ }
2102
+ function deleteCustomRule(layer, category, ruleId) {
2103
+ const runtime = loadRuntimeConfig();
2104
+ const rules = runtime[layer]?.[category]?.rules || [];
2105
+ const idx = rules.findIndex((r) => r.id === ruleId);
2106
+ if (idx >= 0) {
2107
+ const r = rules[idx];
2108
+ if (r.builtin && !r.source_removed) return null;
2109
+ rules.splice(idx, 1);
2110
+ saveRuntimeConfig(runtime);
2111
+ }
2112
+ return runtime;
2113
+ }
2114
+ function addCustomSkill(skillId, category, keywords, content) {
2115
+ _validateSkillId(skillId);
2116
+ const runtime = loadRuntimeConfig();
2117
+ const skillsDir = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills");
2118
+ try {
2119
+ mkdirSync2(skillsDir, { recursive: true });
2120
+ } catch {
2121
+ }
2122
+ writeFileSync2(join4(skillsDir, `${skillId}.md`), content, "utf-8");
2123
+ const l3 = runtime.L3_skills || (runtime.L3_skills = {});
2124
+ const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
2125
+ (cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
2126
+ saveRuntimeConfig(runtime);
2127
+ return runtime;
2128
+ }
2129
+ function deleteCustomSkill(skillId, category) {
2130
+ _validateSkillId(skillId);
2131
+ const runtime = loadRuntimeConfig();
2132
+ const skills = runtime.L3_skills?.[category]?.skills || [];
2133
+ const idx = skills.findIndex((s) => s.id === skillId);
2134
+ if (idx >= 0) {
2135
+ if (skills[idx].builtin) return null;
2136
+ skills.splice(idx, 1);
2137
+ const skillPath = join4(RUNTIME_CONFIG_DIR, "agent_moss_skills", `${skillId}.md`);
2138
+ try {
2139
+ if (existsSync7(skillPath)) unlinkSync2(skillPath);
2140
+ } catch {
2141
+ }
2142
+ saveRuntimeConfig(runtime);
2143
+ }
2144
+ return runtime;
2145
+ }
2146
+ function updateSkillEnabled(skillId, enabled) {
2147
+ const runtime = loadRuntimeConfig();
2148
+ for (const catData of Object.values(runtime.L3_skills || {})) {
2149
+ for (const s of catData.skills || []) {
2150
+ if (s.id === skillId) {
2151
+ s.enabled = enabled;
2152
+ break;
2153
+ }
2154
+ }
2155
+ }
2156
+ saveRuntimeConfig(runtime);
2157
+ return runtime;
2158
+ }
2159
+ function updateSkillCategoryEnabled(category, enabled) {
2160
+ const runtime = loadRuntimeConfig();
2161
+ const cat = runtime.L3_skills?.[category];
2162
+ if (cat) cat.category_enabled = enabled;
2163
+ saveRuntimeConfig(runtime);
2164
+ return runtime;
2165
+ }
2166
+ function getProfileOrDetect() {
2167
+ return null;
2168
+ }
2169
+ var RUNTIME_CONFIG_DIR, RUNTIME_CONFIG_PATH, ENV_RUNTIME_CONFIG_PATH, LAYER_DISABLE_ENV, DENY_MODE_LABELS, SKILL_ID_RE;
2170
+ var init_runtime_config = __esm({
2171
+ "src/infra/runtime-config.ts"() {
2172
+ "use strict";
2173
+ init_esm_shims();
2174
+ init_heuristic();
2175
+ init_logic_rules();
2176
+ init_skill_loader();
2177
+ RUNTIME_CONFIG_DIR = join4(homedir3(), ".config", "agentmoss");
2178
+ RUNTIME_CONFIG_PATH = join4(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
2179
+ ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
2180
+ LAYER_DISABLE_ENV = {
2181
+ L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
2182
+ L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
2183
+ L3_llm_analysis: "AGENT_MOSS_DISABLE_LLM"
2184
+ };
2185
+ DENY_MODE_LABELS = {
2186
+ deny_write: "\u4EC5\u62E6\u622A\u5199\u5165",
2187
+ deny_read: "\u4EC5\u62E6\u622A\u8BFB\u53D6",
2188
+ deny_both: "\u8BFB\u5199\u5747\u62E6\u622A"
2189
+ };
2190
+ SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
2191
+ }
2192
+ });
2193
+
2194
+ // src/engine/coordinator.ts
2195
+ init_esm_shims();
2196
+ init_heuristic();
2197
+ init_logic_rules();
2198
+ init_inline_analyzer();
2199
+
2200
+ // src/engine/llm-analyzer.ts
2201
+ init_esm_shims();
2202
+ init_config();
2203
+ init_skill_loader();
2204
+
2205
+ // src/engine/template-loader.ts
2206
+ init_esm_shims();
2207
+ import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
2208
+ import { resolve as resolve3, dirname as dirname3 } from "path";
2209
+ import { fileURLToPath as fileURLToPath4 } from "url";
2210
+ var __filename4 = fileURLToPath4(import.meta.url);
2211
+ var __dirname4 = dirname3(__filename4);
2212
+ var TEMPLATES_DIR = resolve3(__dirname4, "../templates");
2213
+ var TEMPLATES_DIR_FALLBACK = resolve3(__dirname4, "../../../agent_moss/templates");
2214
+ var TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
2215
+ var cachedTemplates = null;
2216
+ function loadTemplates() {
2217
+ if (cachedTemplates) return cachedTemplates;
2218
+ cachedTemplates = {};
2219
+ const dir = existsSync4(TEMPLATES_DIR) ? TEMPLATES_DIR : TEMPLATES_DIR_FALLBACK;
2220
+ if (!existsSync4(dir)) {
2221
+ console.warn("[AgentMoss] Templates directory not found:", TEMPLATES_DIR, "and fallback:", TEMPLATES_DIR_FALLBACK);
2222
+ return cachedTemplates;
2223
+ }
2224
+ for (const file of TEMPLATE_FILES) {
2225
+ const filePath = resolve3(dir, file);
2226
+ if (existsSync4(filePath)) {
2227
+ const key = file.replace(".txt", "").replace("_template", "");
2228
+ cachedTemplates[key] = readFileSync3(filePath, "utf-8");
2229
+ }
2230
+ }
2231
+ console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
2232
+ return cachedTemplates;
2233
+ }
2234
+
2235
+ // src/engine/script-analyzer.ts
2236
+ init_esm_shims();
2237
+ import { readFileSync as readFileSync4 } from "fs";
2238
+ import { resolve as resolve4, join } from "path";
2239
+ var SUSPICIOUS_PATTERNS = [
2240
+ // Network exfiltration (curl/wget POST in scripts is almost never legitimate)
2241
+ { name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
2242
+ { name: "curl --data", pattern: /curl\s+(.*\s)?--data/i },
2243
+ { name: "curl -d", pattern: /curl\s+(.*\s)?-d\s/i },
2244
+ { name: "wget --post-data", pattern: /wget\s+(.*\s)?--post-data/i },
2245
+ { name: "wget --post-file", pattern: /wget\s+(.*\s)?--post-file/i },
2246
+ // Reverse shell
2247
+ { name: "nc -e", pattern: /\bnc\s+.*-e\b/ },
2248
+ { name: "ncat -e", pattern: /\bncat\s+.*-e\b/ },
2249
+ { name: "/dev/tcp", pattern: /\/dev\/tcp\// },
2250
+ { name: "/dev/udp", pattern: /\/dev\/udp\// },
2251
+ // Base64 obfuscation
2252
+ { name: "$(base64", pattern: /\$\(\s*base64/ },
2253
+ { name: "`base64", pattern: /`\s*base64/ },
2254
+ { name: "base64 decode pipe", pattern: /base64\s+(-d|--decode)\s*\|/ },
2255
+ // Sensitive file access
2256
+ { name: "/etc/shadow", pattern: /\/etc\/shadow/ },
2257
+ { name: "id_rsa", pattern: /\bid_rsa\b/ },
2258
+ { name: "id_ed25519", pattern: /\bid_ed25519\b/ },
2259
+ // Persistence backdoor
2260
+ { name: "crontab install", pattern: /crontab\s+-/ },
2261
+ { name: "authorized_keys", pattern: /authorized_keys/ },
2262
+ { name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
2263
+ ];
2264
+ var SCRIPT_EXEC_PATTERNS = [
2265
+ /bash\s+(\S+\.sh)/,
2266
+ /sh\s+(\S+\.sh)/,
2267
+ /\.\/(\S+\.sh)/,
2268
+ /source\s+(\S+\.sh)/,
2269
+ /\.\s+(\S+\.sh)/,
2270
+ /zsh\s+(\S+\.sh)/
2271
+ ];
2272
+ var KEYWORD_COMBINATION_RISKS = [
2273
+ {
2274
+ group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
2275
+ group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
2276
+ level: "critical",
2277
+ reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + Base64\u7F16\u7801\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u653B\u51FB\u8005\u53EF\u80FD\u5C06\u654F\u611F\u6570\u636E\u7F16\u7801\u540E\u5916\u4F20\u3002"
2278
+ },
2279
+ {
2280
+ group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
2281
+ group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
2282
+ level: "critical",
2283
+ reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u654F\u611F\u6587\u4EF6\u8BBF\u95EE\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u51ED\u8BC1\u5916\u4F20\u98CE\u9669\u3002"
2284
+ },
2285
+ {
2286
+ group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
2287
+ group2: /* @__PURE__ */ new Set(),
2288
+ level: "high",
2289
+ reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
2290
+ },
2291
+ {
2292
+ group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
2293
+ group2: /* @__PURE__ */ new Set(),
2294
+ level: "high",
2295
+ reason: "\u68C0\u6D4B\u5230\u6301\u4E45\u5316\u540E\u95E8\u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5728\u7CFB\u7EDF\u4E2D\u690D\u5165\u540E\u95E8\u3002"
2296
+ }
2297
+ ];
2298
+ var SINGLE_KEYWORD_RISKS = {
2299
+ "curl POST": { level: "high", reason: "\u68C0\u6D4B\u5230 curl POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
2300
+ "curl --data": { level: "high", reason: "\u68C0\u6D4B\u5230 curl --data \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
2301
+ "curl -d": { level: "high", reason: "\u68C0\u6D4B\u5230 curl -d \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
2302
+ "wget --post-data": { level: "high", reason: "\u68C0\u6D4B\u5230 wget POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
2303
+ "wget --post-file": { level: "high", reason: "\u68C0\u6D4B\u5230 wget \u6587\u4EF6\u4E0A\u4F20\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6587\u4EF6\u3002" },
2304
+ "nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
2305
+ "ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
2306
+ "/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
2307
+ "/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
2308
+ "$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
2309
+ "`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
2310
+ "base64 decode pipe": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u89E3\u7801\u540E\u7BA1\u9053\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
2311
+ "/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
2312
+ "id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
2313
+ "id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
2314
+ "crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
2315
+ "authorized_keys": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539 SSH \u6388\u6743\u5BC6\u94A5\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
2316
+ "ssh no host key check": { level: "high", reason: "\u68C0\u6D4B\u5230 SSH \u8DF3\u8FC7\u4E3B\u673A\u5BC6\u94A5\u9A8C\u8BC1\uFF0C\u53EF\u80FD\u662F\u6A2A\u5411\u79FB\u52A8\u3002" }
2317
+ };
2318
+ var LEVEL_ORDER = { low: 1, medium: 2, high: 3, critical: 4 };
2319
+ function emptyResult() {
2320
+ return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
2321
+ }
2322
+ function extractScriptPath(command) {
2323
+ for (const pattern of SCRIPT_EXEC_PATTERNS) {
2324
+ const m = command.match(pattern);
2325
+ if (m) return m[1];
2326
+ }
2327
+ return null;
2328
+ }
2329
+ function scanScriptForKeywords(content) {
2330
+ const matched = [];
2331
+ for (const { name, pattern } of SUSPICIOUS_PATTERNS) {
2332
+ if (pattern.test(content)) matched.push(name);
2333
+ }
2334
+ return matched;
2335
+ }
2336
+ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
2337
+ if (matchedKeywords.length === 0) return { level: "", reason: "" };
2338
+ const keywordSet = new Set(matchedKeywords);
2339
+ for (const rule of KEYWORD_COMBINATION_RISKS) {
2340
+ const hitGroup1 = [...rule.group1].some((k) => keywordSet.has(k));
2341
+ const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
2342
+ if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
2343
+ }
2344
+ const networkPostKeywords = /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]);
2345
+ const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
2346
+ if (hasNetworkPost) {
2347
+ const envPatterns = ["$env", "$(env)", "$USER", "$(hostname)", "$(pwd)", "$HOME"];
2348
+ const contentLower = scriptContent.toLowerCase();
2349
+ for (const p of envPatterns) {
2350
+ if (contentLower.includes(p.toLowerCase())) {
2351
+ return {
2352
+ level: "critical",
2353
+ reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF\u6536\u96C6\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u811A\u672C\u53EF\u80FD\u5C06\u7528\u6237\u73AF\u5883\u53D8\u91CF\uFF08\u53EF\u80FD\u5305\u542B API Key\u3001\u5BC6\u94A5\u7B49\u654F\u611F\u4FE1\u606F\uFF09\u53D1\u9001\u5230\u8FDC\u7A0B\u670D\u52A1\u5668\u3002"
2354
+ };
2355
+ }
2356
+ }
2357
+ }
2358
+ let bestLevel = "";
2359
+ let bestReason = "";
2360
+ for (const kw of matchedKeywords) {
2361
+ const risk = SINGLE_KEYWORD_RISKS[kw];
2362
+ if (risk && (LEVEL_ORDER[risk.level] || 0) > (LEVEL_ORDER[bestLevel] || 0)) {
2363
+ bestLevel = risk.level;
2364
+ bestReason = risk.reason;
2365
+ }
2366
+ }
2367
+ return { level: bestLevel, reason: bestReason };
2368
+ }
2369
+ function readScriptContent(scriptPath, maxLines = 500) {
2370
+ try {
2371
+ const expandedPath = scriptPath.startsWith("~") ? join(process.env.HOME || "/home/user", scriptPath.slice(2)) : resolve4(scriptPath);
2372
+ const raw = readFileSync4(expandedPath, "utf-8");
2373
+ const lines = raw.split("\n").slice(0, maxLines);
2374
+ return { content: lines.join("\n"), lineCount: lines.length, error: "" };
2375
+ } catch (e) {
2376
+ const msg = e instanceof Error ? e.message : String(e);
2377
+ return { content: "", lineCount: 0, error: `\u8BFB\u53D6\u811A\u672C\u5931\u8D25: ${msg}` };
2378
+ }
2379
+ }
2380
+ function analyzeScriptContent(actionType, actionDetail, maxLines = 500) {
2381
+ if (actionType.toLowerCase() !== "bash") return emptyResult();
2382
+ const scriptPath = extractScriptPath(actionDetail);
2383
+ if (!scriptPath) return emptyResult();
2384
+ const { content, lineCount, error } = readScriptContent(scriptPath, maxLines);
2385
+ if (error) return { ...emptyResult(), scriptPath, error };
2386
+ const matchedKeywords = scanScriptForKeywords(content);
2387
+ const isSuspicious = matchedKeywords.length > 0;
2388
+ let riskLevel = "";
2389
+ let riskReason = "";
2390
+ if (isSuspicious) {
2391
+ const risk = assessKeywordCombinationRisk(matchedKeywords, content);
2392
+ riskLevel = risk.level;
2393
+ riskReason = risk.reason;
2394
+ }
2395
+ return { scriptPath, scriptContent: content, matchedKeywords, lineCount, isSuspicious, error: "", riskLevel, riskReason };
2396
+ }
2397
+ function formatScriptAnalysisForPrompt(result) {
2398
+ if (!result.scriptPath) return "";
2399
+ if (result.error) return `\u811A\u672C\u5185\u5BB9\u5206\u6790\u5931\u8D25: ${result.error}`;
2400
+ if (!result.isSuspicious) return "";
2401
+ const keywordsStr = result.matchedKeywords.join(", ");
2402
+ let riskHint = "";
2403
+ if (result.riskLevel && result.riskReason) {
2404
+ riskHint = `
2405
+
2406
+ \u98CE\u9669\u8BC4\u4F30: ${result.riskLevel.toUpperCase()} \u7EA7\u522B
2407
+ \u98CE\u9669\u539F\u56E0: ${result.riskReason}
2408
+ \u5EFA\u8BAE: \u6B64\u811A\u672C\u5E94\u88AB\u62D2\u7EDD\uFF08Deny\uFF09\uFF0C\u9664\u975E\u6709\u660E\u786E\u7684\u4E1A\u52A1\u9700\u6C42\u4E14\u5DF2\u786E\u8BA4\u76EE\u6807\u670D\u52A1\u5668\u53EF\u4FE1\u3002`;
2409
+ }
2410
+ return `\u68C0\u6D4B\u5230\u811A\u672C\u6267\u884C\uFF0C\u4E14\u811A\u672C\u5185\u5BB9\u5305\u542B\u53EF\u7591\u5173\u952E\u8BCD:
2411
+ \u811A\u672C\u8DEF\u5F84: ${result.scriptPath}
2412
+ \u53EF\u7591\u5173\u952E\u8BCD: ${keywordsStr}
2413
+ \u811A\u672C\u884C\u6570: ${result.lineCount}${riskHint}
2414
+
2415
+ ### \u811A\u672C\u5185\u5BB9 ###
2416
+ \`\`\`
2417
+ ${result.scriptContent}
2418
+ \`\`\`
2419
+ `;
2420
+ }
2421
+
2422
+ // src/engine/script-content-analyzer.ts
2423
+ init_esm_shims();
2424
+ init_heuristic();
2425
+ init_logic_rules();
2426
+ import { readFileSync as readFileSync5, statSync, existsSync as existsSync5 } from "fs";
2427
+ import { resolve as resolve5, join as join2, isAbsolute, dirname as dirname4 } from "path";
2428
+ import { homedir } from "os";
2429
+ function emptyChain() {
2430
+ return {
2431
+ nodes: [],
2432
+ entryPath: "",
2433
+ maxDepthReached: false,
2434
+ maxSizeReached: false,
2435
+ totalSize: 0,
2436
+ totalLines: 0,
2437
+ hasCritical: false,
2438
+ hasHigh: false,
2439
+ firstHitNode: null,
2440
+ error: ""
2441
+ };
2442
+ }
2443
+ function newNode(path3, depth) {
2444
+ return {
2445
+ scriptPath: path3,
2446
+ rawContent: "",
2447
+ preprocessedContent: "",
2448
+ lineCount: 0,
2449
+ fileSize: 0,
2450
+ depth,
2451
+ childPaths: [],
2452
+ error: "",
2453
+ l1Hit: false,
2454
+ l1RiskLevel: "",
2455
+ l1RiskType: "",
2456
+ l1Reason: "",
2457
+ l1MatchedPatterns: [],
2458
+ l2Hit: false,
2459
+ l2ViolatedRule: "",
2460
+ l2RiskLevel: "",
2461
+ l2RiskType: "",
2462
+ l2Reason: "",
2463
+ matchedKeywords: [],
2464
+ isSuspicious: false,
2465
+ keywordRiskLevel: "",
2466
+ keywordRiskReason: ""
2467
+ };
2468
+ }
2469
+ function stripComments(content) {
2470
+ return content.split("\n").filter((line) => !line.trimStart().startsWith("#")).join("\n");
2471
+ }
2472
+ function stripOutputText(content) {
2473
+ let result = content.replace(/echo\s+".*?"/gs, 'echo ""');
2474
+ result = result.replace(/echo\s+'.*?'/gs, "echo ''");
2475
+ result = result.replace(/printf\s+".*?"[^\n]*/gs, 'printf ""');
2476
+ result = result.replace(/printf\s+'.*?'[^\n]*/gs, "printf ''");
2477
+ return result;
2478
+ }
2479
+ function expandSimpleVariables(content) {
2480
+ const varMap = {};
2481
+ const assignmentRe = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)=(.+)$/;
2482
+ const collectAssignments = (text) => {
2483
+ for (const line of text.split("\n")) {
2484
+ const m = line.match(assignmentRe);
2485
+ if (m) {
2486
+ const varName = m[1];
2487
+ let varVal = m[2].trim();
2488
+ if (varVal && !varVal.includes("$") && !varVal.includes("`")) {
2489
+ if (varVal.startsWith('"') && varVal.endsWith('"') || varVal.startsWith("'") && varVal.endsWith("'")) {
2490
+ varVal = varVal.slice(1, -1);
2491
+ }
2492
+ varMap[varName] = varVal;
2493
+ }
2494
+ }
2495
+ }
2496
+ };
2497
+ collectAssignments(content);
2498
+ let expanded = content;
2499
+ for (let round = 0; round < 3; round++) {
2500
+ const sortedVars = Object.keys(varMap).sort((a, b) => b.length - a.length);
2501
+ let newExpanded = expanded;
2502
+ for (const v of sortedVars) {
2503
+ const val = varMap[v];
2504
+ newExpanded = newExpanded.split("${" + v + "}").join(val);
2505
+ newExpanded = newExpanded.split("$" + v).join(val);
2506
+ }
2507
+ if (newExpanded === expanded) break;
2508
+ expanded = newExpanded;
2509
+ collectAssignments(expanded);
2510
+ }
2511
+ return expanded;
2512
+ }
2513
+ function preprocessScriptContent(content) {
2514
+ let result = stripComments(content);
2515
+ result = stripOutputText(result);
2516
+ result = expandSimpleVariables(result);
2517
+ return result;
2518
+ }
2519
+ var SUSPICIOUS_PATTERNS2 = [
2520
+ { name: "curl POST", pattern: /curl\s+(.*\s)?-X\s+POST/i },
2521
+ { name: "curl --data", pattern: /curl\s+(.*\s)?--data/i },
2522
+ { name: "curl -d", pattern: /curl\s+(.*\s)?-d\s/i },
2523
+ { name: "wget --post-data", pattern: /wget\s+(.*\s)?--post-data/i },
2524
+ { name: "wget --post-file", pattern: /wget\s+(.*\s)?--post-file/i },
2525
+ { name: "nc -e", pattern: /\bnc\s+.*-e\b/ },
2526
+ { name: "ncat -e", pattern: /\bncat\s+.*-e\b/ },
2527
+ { name: "/dev/tcp", pattern: /\/dev\/tcp\// },
2528
+ { name: "/dev/udp", pattern: /\/dev\/udp\// },
2529
+ { name: "$(base64", pattern: /\$\(\s*base64/ },
2530
+ { name: "`base64", pattern: /`\s*base64/ },
2531
+ { name: "base64 decode pipe", pattern: /base64\s+(-d|--decode)\s*\|/ },
2532
+ { name: "/etc/shadow", pattern: /\/etc\/shadow/ },
2533
+ { name: "id_rsa", pattern: /\bid_rsa\b/ },
2534
+ { name: "id_ed25519", pattern: /\bid_ed25519\b/ },
2535
+ { name: "crontab install", pattern: /crontab\s+-/ },
2536
+ { name: "authorized_keys", pattern: /authorized_keys/ },
2537
+ { name: "ssh no host key check", pattern: /ssh\s+.*StrictHostKeyChecking\s*=\s*no/ }
2538
+ ];
2539
+ var SCRIPT_EXEC_PATTERNS2 = [
2540
+ /(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
2541
+ /(?:^|\s|&&|\|\|)\s*\/bin\/bash\s+(\S+)/i,
2542
+ /(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
2543
+ /(?:^|\s|&&|\|\|)\s*zsh\s+(\S+)/i,
2544
+ /\.\/(\S+)/,
2545
+ /(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
2546
+ /(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
2547
+ /(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
2548
+ /(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
2549
+ /(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
2550
+ /(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
2551
+ /(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
2552
+ /(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
2553
+ ];
2554
+ var KEYWORD_COMBINATION_RISKS2 = [
2555
+ {
2556
+ group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
2557
+ group2: /* @__PURE__ */ new Set(["$(base64", "`base64", "base64 decode pipe"]),
2558
+ level: "critical",
2559
+ reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + Base64\u7F16\u7801\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u653B\u51FB\u8005\u53EF\u80FD\u5C06\u654F\u611F\u6570\u636E\u7F16\u7801\u540E\u5916\u4F20\u3002"
2560
+ },
2561
+ {
2562
+ group1: /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]),
2563
+ group2: /* @__PURE__ */ new Set(["/etc/shadow", "id_rsa", "id_ed25519"]),
2564
+ level: "critical",
2565
+ reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u654F\u611F\u6587\u4EF6\u8BBF\u95EE\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u51ED\u8BC1\u5916\u4F20\u98CE\u9669\u3002"
2566
+ },
2567
+ {
2568
+ group1: /* @__PURE__ */ new Set(["nc -e", "ncat -e", "/dev/tcp", "/dev/udp"]),
2569
+ group2: /* @__PURE__ */ new Set(),
2570
+ level: "high",
2571
+ reason: "\u68C0\u6D4B\u5230\u53CD\u5F39 Shell \u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5EFA\u7ACB\u8FDC\u7A0B\u63A7\u5236\u901A\u9053\u3002"
2572
+ },
2573
+ {
2574
+ group1: /* @__PURE__ */ new Set(["crontab install", "authorized_keys", "ssh no host key check"]),
2575
+ group2: /* @__PURE__ */ new Set(),
2576
+ level: "high",
2577
+ reason: "\u68C0\u6D4B\u5230\u6301\u4E45\u5316\u540E\u95E8\u7279\u5F81\uFF0C\u653B\u51FB\u8005\u53EF\u80FD\u5728\u7CFB\u7EDF\u4E2D\u690D\u5165\u540E\u95E8\u3002"
2578
+ }
2579
+ ];
2580
+ var SINGLE_KEYWORD_RISKS2 = {
2581
+ "curl POST": { level: "high", reason: "\u68C0\u6D4B\u5230 curl POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
2582
+ "curl --data": { level: "high", reason: "\u68C0\u6D4B\u5230 curl --data \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
2583
+ "curl -d": { level: "high", reason: "\u68C0\u6D4B\u5230 curl -d \u53C2\u6570\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
2584
+ "wget --post-data": { level: "high", reason: "\u68C0\u6D4B\u5230 wget POST \u8BF7\u6C42\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6570\u636E\u3002" },
2585
+ "wget --post-file": { level: "high", reason: "\u68C0\u6D4B\u5230 wget \u6587\u4EF6\u4E0A\u4F20\uFF0C\u53EF\u80FD\u5411\u5916\u90E8\u670D\u52A1\u5668\u53D1\u9001\u6587\u4EF6\u3002" },
2586
+ "nc -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 nc -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
2587
+ "ncat -e": { level: "critical", reason: "\u68C0\u6D4B\u5230 ncat -e \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
2588
+ "/dev/tcp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/tcp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
2589
+ "/dev/udp": { level: "critical", reason: "\u68C0\u6D4B\u5230 /dev/udp \u53CD\u5F39 Shell \u7279\u5F81\u3002" },
2590
+ "$(base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
2591
+ "`base64": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u7F16\u7801\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
2592
+ "base64 decode pipe": { level: "high", reason: "\u68C0\u6D4B\u5230 Base64 \u89E3\u7801\u540E\u7BA1\u9053\u6267\u884C\uFF0C\u53EF\u80FD\u662F\u6DF7\u6DC6\u9003\u9038\u3002" },
2593
+ "/etc/shadow": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE\u7CFB\u7EDF\u5BC6\u7801\u6587\u4EF6\u3002" },
2594
+ "id_rsa": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
2595
+ "id_ed25519": { level: "critical", reason: "\u68C0\u6D4B\u5230\u8BBF\u95EE SSH \u79C1\u94A5\u3002" },
2596
+ "crontab install": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539\u5B9A\u65F6\u4EFB\u52A1\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
2597
+ "authorized_keys": { level: "high", reason: "\u68C0\u6D4B\u5230\u4FEE\u6539 SSH \u6388\u6743\u5BC6\u94A5\uFF0C\u53EF\u80FD\u662F\u6301\u4E45\u5316\u540E\u95E8\u3002" },
2598
+ "ssh no host key check": { level: "high", reason: "\u68C0\u6D4B\u5230 SSH \u8DF3\u8FC7\u4E3B\u673A\u5BC6\u94A5\u9A8C\u8BC1\uFF0C\u53EF\u80FD\u662F\u6A2A\u5411\u79FB\u52A8\u3002" }
2599
+ };
2600
+ var LEVEL_ORDER2 = { low: 1, medium: 2, high: 3, critical: 4 };
2601
+ function scanScriptForKeywords2(content) {
2602
+ const matched = [];
2603
+ for (const { name, pattern } of SUSPICIOUS_PATTERNS2) {
2604
+ if (pattern.test(content)) matched.push(name);
2605
+ }
2606
+ return matched;
2607
+ }
2608
+ function assessKeywordCombinationRisk2(matchedKeywords, scriptContent) {
2609
+ if (matchedKeywords.length === 0) return { level: "", reason: "" };
2610
+ const keywordSet = new Set(matchedKeywords);
2611
+ for (const rule of KEYWORD_COMBINATION_RISKS2) {
2612
+ const hitGroup1 = [...rule.group1].some((k) => keywordSet.has(k));
2613
+ const hitGroup2 = rule.group2.size === 0 ? true : [...rule.group2].some((k) => keywordSet.has(k));
2614
+ if (hitGroup1 && hitGroup2) return { level: rule.level, reason: rule.reason };
2615
+ }
2616
+ const networkPostKeywords = /* @__PURE__ */ new Set(["curl POST", "curl --data", "curl -d", "wget --post-data", "wget --post-file"]);
2617
+ const hasNetworkPost = [...networkPostKeywords].some((k) => keywordSet.has(k));
2618
+ if (hasNetworkPost) {
2619
+ const envPatterns = ["$env", "$(env)", "$USER", "$(hostname)", "$(pwd)", "$HOME"];
2620
+ const contentLower = scriptContent.toLowerCase();
2621
+ for (const p of envPatterns) {
2622
+ if (contentLower.includes(p.toLowerCase())) {
2623
+ return {
2624
+ level: "critical",
2625
+ reason: "\u68C0\u6D4B\u5230\u7F51\u7EDC\u6570\u636E\u53D1\u9001 + \u7CFB\u7EDF\u73AF\u5883\u53D8\u91CF\u6536\u96C6\u7EC4\u5408\uFF0C\u6784\u6210 Critical \u7EA7\u522B\u6570\u636E\u5916\u4F20\u98CE\u9669\u3002\u811A\u672C\u53EF\u80FD\u5C06\u7528\u6237\u73AF\u5883\u53D8\u91CF\uFF08\u53EF\u80FD\u5305\u542B API Key\u3001\u5BC6\u94A5\u7B49\u654F\u611F\u4FE1\u606F\uFF09\u53D1\u9001\u5230\u8FDC\u7A0B\u670D\u52A1\u5668\u3002"
2626
+ };
2627
+ }
2628
+ }
2629
+ }
2630
+ let bestLevel = "";
2631
+ let bestReason = "";
2632
+ for (const kw of matchedKeywords) {
2633
+ const risk = SINGLE_KEYWORD_RISKS2[kw];
2634
+ if (risk && (LEVEL_ORDER2[risk.level] || 0) > (LEVEL_ORDER2[bestLevel] || 0)) {
2635
+ bestLevel = risk.level;
2636
+ bestReason = risk.reason;
2637
+ }
2638
+ }
2639
+ return { level: bestLevel, reason: bestReason };
2640
+ }
2641
+ function extractScriptPath2(command) {
2642
+ for (const pattern of SCRIPT_EXEC_PATTERNS2) {
2643
+ const m = command.match(pattern);
2644
+ if (m) return m[1];
2645
+ }
2646
+ return null;
2647
+ }
2648
+ function extractInlineScriptPath(actionDetail) {
2649
+ const re = /open\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
2650
+ let m;
2651
+ while ((m = re.exec(actionDetail)) !== null) {
2652
+ const p = m[1];
2653
+ let resolved;
2654
+ try {
2655
+ resolved = resolvePath(p);
2656
+ if (existsSync5(resolved)) {
2657
+ const stat = statSync(resolved);
2658
+ if (stat.isFile()) return resolved;
2659
+ }
2660
+ } catch {
2661
+ }
2662
+ }
2663
+ return null;
2664
+ }
2665
+ function resolvePath(p) {
2666
+ let expanded = p;
2667
+ if (expanded.startsWith("~/") || expanded === "~") {
2668
+ expanded = join2(homedir(), expanded.slice(1));
2669
+ }
2670
+ return resolve5(expanded);
2671
+ }
2672
+ function readScriptContent2(scriptPath, maxFileSize = 2 * 1024 * 1024) {
2673
+ try {
2674
+ const path3 = resolvePath(scriptPath);
2675
+ if (!existsSync5(path3)) return { content: "", lineCount: 0, fileSize: 0, error: `\u811A\u672C\u4E0D\u5B58\u5728: ${scriptPath}` };
2676
+ const stat = statSync(path3);
2677
+ if (!stat.isFile()) return { content: "", lineCount: 0, fileSize: 0, error: `\u4E0D\u662F\u6587\u4EF6: ${scriptPath}` };
2678
+ if (stat.size > maxFileSize) {
2679
+ return { content: "", lineCount: 0, fileSize: stat.size, error: `\u811A\u672C\u6587\u4EF6\u8FC7\u5927 (${stat.size} bytes)\uFF0C\u8DF3\u8FC7\u8BFB\u53D6` };
2680
+ }
2681
+ const content = readFileSync5(path3, "utf-8");
2682
+ const lineCount = content ? content.split("\n").length : 0;
2683
+ return { content, lineCount, fileSize: stat.size, error: "" };
2684
+ } catch (e) {
2685
+ const msg = e instanceof Error ? e.message : String(e);
2686
+ return { content: "", lineCount: 0, fileSize: 0, error: `\u8BFB\u53D6\u811A\u672C\u5931\u8D25: ${msg}` };
2687
+ }
2688
+ }
2689
+ function extractChildScriptPaths(content, baseDir) {
2690
+ const paths = [];
2691
+ for (const pattern of CHILD_SCRIPT_PATTERNS) {
2692
+ pattern.lastIndex = 0;
2693
+ let m;
2694
+ while ((m = pattern.exec(content)) !== null) {
2695
+ const pathStr = m[1];
2696
+ if (pathStr.startsWith("-") || pathStr.startsWith("$")) continue;
2697
+ if (pathStr.includes("/dev/") || pathStr.startsWith("|") || pathStr.startsWith(";")) continue;
2698
+ const full = isAbsolute(pathStr) ? pathStr : join2(baseDir, pathStr);
2699
+ const resolved = resolvePath(full);
2700
+ if (existsSync5(resolved)) {
2701
+ try {
2702
+ if (statSync(resolved).isFile() && !paths.includes(resolved)) paths.push(resolved);
2703
+ } catch {
2704
+ }
2705
+ }
2706
+ }
2707
+ }
2708
+ return paths;
2709
+ }
2710
+ function resolveScriptChain(entryPath, input, maxDepth = 3, maxFileSize = 2 * 1024 * 1024, maxTotalSize = 6 * 1024 * 1024) {
2711
+ const visited = /* @__PURE__ */ new Set();
2712
+ const result = emptyChain();
2713
+ result.entryPath = entryPath;
2714
+ const entry = resolvePath(entryPath);
2715
+ const baseDir = existsSync5(entry) ? dirname4(entry) : input.cwd || process.cwd();
2716
+ resolveRecursive({
2717
+ path: entry,
2718
+ depth: 0,
2719
+ maxDepth,
2720
+ maxFileSize,
2721
+ maxTotalSize,
2722
+ visited,
2723
+ result,
2724
+ baseDir,
2725
+ input
2726
+ });
2727
+ result.hasCritical = result.nodes.some((n) => n.l1RiskLevel === "critical" || n.l2RiskLevel === "critical" || n.keywordRiskLevel === "critical");
2728
+ result.hasHigh = result.nodes.some((n) => ["high", "critical"].includes(n.l1RiskLevel) || ["high", "critical"].includes(n.l2RiskLevel) || ["high", "critical"].includes(n.keywordRiskLevel));
2729
+ result.firstHitNode = result.nodes.find((n) => n.l1Hit || n.l2Hit || n.isSuspicious) || null;
2730
+ return result;
2731
+ }
2732
+ function resolveRecursive(ctx) {
2733
+ const { path: path3, depth, maxDepth, maxFileSize, maxTotalSize, visited, result, baseDir, input } = ctx;
2734
+ if (visited.has(path3)) return;
2735
+ if (depth > maxDepth) {
2736
+ result.maxDepthReached = true;
2737
+ return;
2738
+ }
2739
+ if (result.totalSize >= maxTotalSize) {
2740
+ result.maxSizeReached = true;
2741
+ return;
2742
+ }
2743
+ visited.add(path3);
2744
+ const { content, lineCount, fileSize, error } = readScriptContent2(path3, maxFileSize);
2745
+ if (error) {
2746
+ const node2 = newNode(path3, depth);
2747
+ node2.error = error;
2748
+ node2.fileSize = fileSize;
2749
+ node2.lineCount = lineCount;
2750
+ result.nodes.push(node2);
2751
+ return;
2752
+ }
2753
+ result.totalSize += fileSize;
2754
+ result.totalLines += lineCount;
2755
+ const preprocessed = preprocessScriptContent(content);
2756
+ const node = newNode(path3, depth);
2757
+ node.rawContent = content;
2758
+ node.preprocessedContent = preprocessed;
2759
+ node.lineCount = lineCount;
2760
+ node.fileSize = fileSize;
2761
+ result.nodes.push(node);
2762
+ const virtualInput = {
2763
+ ...input,
2764
+ aNext: { actionType: "bash", actionDetail: preprocessed }
2765
+ };
2766
+ const l1 = runHeuristicCheck(virtualInput);
2767
+ if (l1 && !l1.allowed) {
2768
+ node.l1Hit = true;
2769
+ node.l1RiskLevel = l1.riskLevel;
2770
+ node.l1RiskType = l1.riskType;
2771
+ node.l1Reason = l1.reason;
2772
+ node.l1MatchedPatterns = [];
2773
+ if (l1.riskLevel === "critical") {
2774
+ result.hasCritical = true;
2775
+ result.firstHitNode = node;
2776
+ return;
2777
+ }
2778
+ }
2779
+ const childPaths = extractChildScriptPaths(preprocessed, baseDir);
2780
+ node.childPaths = childPaths;
2781
+ for (const childPath of childPaths) {
2782
+ resolveRecursive({
2783
+ path: resolvePath(childPath),
2784
+ depth: depth + 1,
2785
+ maxDepth,
2786
+ maxFileSize,
2787
+ maxTotalSize,
2788
+ visited,
2789
+ result,
2790
+ baseDir: dirname4(resolvePath(childPath)),
2791
+ input
2792
+ });
2793
+ if (result.hasCritical) return;
2794
+ }
2795
+ }
2796
+ function scanScriptChainWithL1(chain, input) {
2797
+ for (const node of chain.nodes) {
2798
+ if (node.error || !node.preprocessedContent || node.l1Hit) continue;
2799
+ const virtualInput = {
2800
+ ...input,
2801
+ aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
2802
+ };
2803
+ const l1 = runHeuristicCheck(virtualInput);
2804
+ if (l1 && !l1.allowed) {
2805
+ node.l1Hit = true;
2806
+ node.l1RiskLevel = l1.riskLevel;
2807
+ node.l1RiskType = l1.riskType;
2808
+ node.l1Reason = l1.reason;
2809
+ if (l1.riskLevel === "critical") {
2810
+ chain.hasCritical = true;
2811
+ chain.firstHitNode = node;
2812
+ break;
2813
+ }
2814
+ }
2815
+ }
2816
+ return chain;
2817
+ }
2818
+ function scanScriptChainWithL2(chain, input) {
2819
+ for (const node of chain.nodes) {
2820
+ if (node.error || !node.preprocessedContent) continue;
2821
+ if (node.l1RiskLevel === "critical") break;
2822
+ const virtualInput = {
2823
+ ...input,
2824
+ aNext: { actionType: "bash", actionDetail: node.preprocessedContent }
2825
+ };
2826
+ const l2 = runLogicCheck(virtualInput);
2827
+ if (l2 && !l2.allowed) {
2828
+ node.l2Hit = true;
2829
+ node.l2ViolatedRule = `script_logic:${node.scriptPath}`;
2830
+ node.l2RiskLevel = l2.riskLevel;
2831
+ node.l2RiskType = l2.riskType;
2832
+ node.l2Reason = `\u811A\u672C ${node.scriptPath} (\u6DF1\u5EA6 ${node.depth}) \u4E2D: ${l2.reason}`;
2833
+ if (l2.riskLevel === "critical") {
2834
+ chain.hasCritical = true;
2835
+ chain.firstHitNode = node;
2836
+ break;
2837
+ }
2838
+ }
2839
+ }
2840
+ if (!chain.hasHigh) {
2841
+ chain.hasHigh = chain.nodes.some((n) => ["high", "critical"].includes(n.l2RiskLevel));
2842
+ }
2843
+ return chain;
2844
+ }
2845
+ function scanScriptChainKeywords(chain) {
2846
+ for (const node of chain.nodes) {
2847
+ if (node.error || !node.preprocessedContent) continue;
2848
+ if (node.l1Hit || node.l2Hit) continue;
2849
+ const matched = scanScriptForKeywords2(node.preprocessedContent);
2850
+ node.matchedKeywords = matched;
2851
+ node.isSuspicious = matched.length > 0;
2852
+ if (matched.length > 0) {
2853
+ const risk = assessKeywordCombinationRisk2(matched, node.preprocessedContent);
2854
+ node.keywordRiskLevel = risk.level;
2855
+ node.keywordRiskReason = risk.reason;
2856
+ }
2857
+ }
2858
+ return chain;
2859
+ }
2860
+ function analyzeScriptContent2(input) {
2861
+ const actionType = input.aNext.actionType.toLowerCase();
2862
+ if (actionType !== "bash") return emptyChain();
2863
+ const scriptPath = extractScriptPath2(input.aNext.actionDetail);
2864
+ let resolvedPath = scriptPath;
2865
+ if (!resolvedPath) {
2866
+ const inline = extractInlineScriptPath(input.aNext.actionDetail);
2867
+ if (!inline) return emptyChain();
2868
+ resolvedPath = inline;
2869
+ }
2870
+ const chain = resolveScriptChain(resolvedPath, input);
2871
+ if (chain.nodes.length === 0) return chain;
2872
+ scanScriptChainWithL1(chain, input);
2873
+ scanScriptChainWithL2(chain, input);
2874
+ scanScriptChainKeywords(chain);
2875
+ return chain;
2876
+ }
2877
+ function formatScriptChainForPrompt(chain) {
2878
+ if (chain.nodes.length === 0) return "";
2879
+ const errorNodes = chain.nodes.filter((n) => n.error);
2880
+ if (errorNodes.length > 0 && chain.nodes.length === 1) {
2881
+ return `\u26A0\uFE0F \u811A\u672C\u5185\u5BB9\u5206\u6790\u5931\u8D25: ${errorNodes[0].error}`;
2882
+ }
2883
+ const suspiciousNodes = chain.nodes.filter((n) => n.isSuspicious && !n.l1Hit && !n.l2Hit);
2884
+ if (suspiciousNodes.length === 0) {
2885
+ const safeNodes = chain.nodes.filter((n) => !n.error);
2886
+ if (safeNodes.length === 0) return "";
2887
+ const parts2 = ["\u2713 \u811A\u672C\u5185\u5BB9\u5DF2\u5206\u6790\uFF0C\u672A\u53D1\u73B0\u5371\u9669/\u53EF\u7591\u64CD\u4F5C\uFF08L1/L2/\u5173\u952E\u8BCD\u626B\u63CF\u5747\u672A\u547D\u4E2D\uFF09\uFF1A"];
2888
+ for (const node of safeNodes) {
2889
+ const pre = node.preprocessedContent || "";
2890
+ let preview = pre.slice(0, 600);
2891
+ if (pre.length > 600) preview += `
2892
+ ... (\u622A\u65AD\uFF0C\u5B8C\u6574\u5185\u5BB9 ${pre.length} \u5B57\u7B26)`;
2893
+ parts2.push(
2894
+ ` \u811A\u672C\u8DEF\u5F84: ${node.scriptPath}\uFF08\u9012\u5F52\u6DF1\u5EA6 ${node.depth}\uFF0C${node.lineCount} \u884C\uFF09
2895
+ \u5185\u5BB9\uFF08\u9884\u5904\u7406\u540E\uFF09:
2896
+ \`\`\`
2897
+ ${preview}
2898
+ \`\`\``
2899
+ );
2900
+ }
2901
+ return parts2.join("\n");
2902
+ }
2903
+ const parts = [];
2904
+ for (const node of suspiciousNodes) {
2905
+ const keywordsStr = node.matchedKeywords.join(", ");
2906
+ let riskHint = "";
2907
+ if (node.keywordRiskLevel && node.keywordRiskReason) {
2908
+ riskHint = `
2909
+
2910
+ \u{1F534} **\u98CE\u9669\u8BC4\u4F30**: ${node.keywordRiskLevel.toUpperCase()} \u7EA7\u522B
2911
+ **\u98CE\u9669\u539F\u56E0**: ${node.keywordRiskReason}
2912
+ **\u5EFA\u8BAE**: \u6B64\u811A\u672C\u5E94\u88AB\u62D2\u7EDD\uFF08Deny\uFF09\uFF0C\u9664\u975E\u6709\u660E\u786E\u7684\u4E1A\u52A1\u9700\u6C42\u4E14\u5DF2\u786E\u8BA4\u76EE\u6807\u670D\u52A1\u5668\u53EF\u4FE1\u3002`;
2913
+ }
2914
+ const pre = node.preprocessedContent;
2915
+ let contentPreview = pre.slice(0, 2e3);
2916
+ if (pre.length > 2e3) contentPreview += `
2917
+ ... (\u622A\u65AD\uFF0C\u5B8C\u6574\u5185\u5BB9 ${pre.length} \u5B57\u7B26)`;
2918
+ parts.push(
2919
+ `\u26A0\uFE0F \u811A\u672C\u5185\u5BB9\u5305\u542B\u53EF\u7591\u5173\u952E\u8BCD:
2920
+ \u811A\u672C\u8DEF\u5F84: ${node.scriptPath}
2921
+ \u9012\u5F52\u6DF1\u5EA6: ${node.depth}
2922
+ \u53EF\u7591\u5173\u952E\u8BCD: ${keywordsStr}
2923
+ \u811A\u672C\u884C\u6570: ${node.lineCount}${riskHint}
2924
+
2925
+ ### \u811A\u672C\u5185\u5BB9\uFF08\u9884\u5904\u7406\u540E\uFF09###
2926
+ \`\`\`
2927
+ ${contentPreview}
2928
+ \`\`\`
2929
+ `
2930
+ );
2931
+ }
2932
+ let header = `\u26A0\uFE0F \u68C0\u6D4B\u5230\u811A\u672C\u6267\u884C\u94FE\uFF08\u5171 ${chain.nodes.length} \u4E2A\u811A\u672C\uFF09\uFF0C\u5176\u4E2D ${suspiciousNodes.length} \u4E2A\u5305\u542B\u53EF\u7591\u5173\u952E\u8BCD:
2933
+ `;
2934
+ if (chain.maxDepthReached) header += "\u26A0\uFE0F \u6CE8\u610F\uFF1A\u9012\u5F52\u8FFD\u8E2A\u89E6\u8FBE\u6DF1\u5EA6\u4E0A\u9650\uFF0C\u53EF\u80FD\u5B58\u5728\u66F4\u6DF1\u5C42\u7684\u811A\u672C\u8C03\u7528\u672A\u88AB\u626B\u63CF\u3002\n";
2935
+ if (chain.maxSizeReached) header += "\u26A0\uFE0F \u6CE8\u610F\uFF1A\u7D2F\u8BA1\u6587\u4EF6\u5927\u5C0F\u89E6\u8FBE\u4E0A\u9650\uFF0C\u90E8\u5206\u811A\u672C\u53EF\u80FD\u672A\u88AB\u5B8C\u6574\u8BFB\u53D6\u3002\n";
2936
+ return header + parts.join("\n---\n\n");
2937
+ }
2938
+ var CHILD_SCRIPT_PATTERNS = [
2939
+ /(?:^|\s|&&|\|\|)\s*bash\s+(\S+)/i,
2940
+ /(?:^|\s|&&|\|\|)\s*sh\s+(\S+)/i,
2941
+ /(?:^|\s|&&|\|\|)\s*source\s+(\S+)/i,
2942
+ /(?:^|\s|&&|\|\|)\s*\.\s+(\S+)/,
2943
+ /(?:^|\s|&&|\|\|)\s*python3?\s+(\S+\.py)/i,
2944
+ /(?:^|\s|&&|\|\|)\s*node\s+(\S+\.js)/i,
2945
+ /(?:^|\s|&&|\|\|)\s*ruby\s+(\S+\.rb)/i,
2946
+ /(?:^|\s|&&|\|\|)\s*perl\s+(\S+\.pl)/i,
2947
+ /(?:^|\s|&&|\|\|)\s*php\s+(\S+\.php)/i,
2948
+ /(?:^|\s|&&|\|\|)\s*lua\s+(\S+\.lua)/i
2949
+ ];
2950
+
2951
+ // src/engine/llm-analyzer.ts
2952
+ import { appendFileSync } from "fs";
2953
+ import OpenAI from "openai";
2954
+ var llmCache = /* @__PURE__ */ new Map();
2955
+ var LLM_CACHE_MAX = 200;
2956
+ async function runLLMAnalysis(input, previous) {
2957
+ if (!isLLMAvailable()) {
2958
+ console.log("[AgentMoss] LLM not configured, skipping deep analysis");
2959
+ return {
2960
+ allowed: true,
2961
+ reason: "LLM \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7\u6DF1\u5C42\u5206\u6790",
2962
+ riskLevel: "low",
2963
+ riskType: "",
2964
+ confidence: 50,
2965
+ source: "llm",
2966
+ violatedLayers: []
2967
+ };
2968
+ }
2969
+ const globalConfig = getLLMConfig();
2970
+ const config = input.llmConfig?.apiKey ? { ...globalConfig, ...input.llmConfig } : globalConfig;
2971
+ const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
2972
+ const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
2973
+ const cached = llmCache.get(cacheKey);
2974
+ if (cached) {
2975
+ console.log("[AgentMoss] LLM cache hit");
2976
+ return cached;
2977
+ }
2978
+ const matchedSkills = matchSkills(input.aNext.actionType, input.aNext.actionDetail, input.reason);
2979
+ const templates = loadTemplates();
2980
+ let scriptAnalysis;
2981
+ let scriptChainText = "";
2982
+ if (previous.scriptChain) {
2983
+ scriptChainText = formatScriptChainForPrompt(previous.scriptChain);
2984
+ } else {
2985
+ scriptAnalysis = analyzeScriptContent(input.aNext.actionType, input.aNext.actionDetail);
2986
+ }
2987
+ const prompt = buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, previous.skipL3Hints ?? "", scriptChainText);
2988
+ logPrompt(prompt, input.aNext, previous.llmLogPath);
2989
+ const result = await analyzeWithLLM(prompt, config);
2990
+ if (llmCache.size >= LLM_CACHE_MAX) {
2991
+ const firstKey = llmCache.keys().next().value;
2992
+ if (firstKey) llmCache.delete(firstKey);
2993
+ }
2994
+ llmCache.set(cacheKey, result);
2995
+ return result;
2996
+ }
2997
+ function logPrompt(prompt, aNext, logPath) {
2998
+ if (!logPath) return;
2999
+ try {
3000
+ const actionType = aNext.actionType || "unknown";
3001
+ const actionDetail = (aNext.actionDetail || "").slice(0, 100);
3002
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [LLM_PROMPT] action_type=${actionType}, action_detail=${actionDetail}... prompt_length=${prompt.length}
3003
+ ${prompt}
3004
+ --- END PROMPT ---
3005
+ `;
3006
+ appendFileSync(logPath, line, "utf-8");
3007
+ } catch (e) {
3008
+ console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
3009
+ }
3010
+ }
3011
+ function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
3012
+ const skillsText = formatSkillsForPrompt(matchedSkills);
3013
+ const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
3014
+ const scriptAnalysisText = scriptChainText ? scriptChainText : scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
3015
+ const hintsText = formatHints(previous);
3016
+ const securityJudgeTemplate = templates["security_judge"];
3017
+ if (securityJudgeTemplate) {
3018
+ return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").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("}}", "}");
3019
+ }
3020
+ console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
3021
+ return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
3022
+ - \u4F1A\u8BDD ID: ${input.sessionId}
3023
+ - \u7528\u6237\u4EFB\u52A1: ${input.promptSession || "\u672A\u6307\u5B9A"}
3024
+ - \u64CD\u4F5C\u7CFB\u7EDF: ${input.osType}
3025
+ - \u5DE5\u4F5C\u76EE\u5F55: ${input.cwd || "/"}
3026
+ - \u64CD\u4F5C\u539F\u56E0: ${input.reason || "\u672A\u8BF4\u660E"}
3027
+ - \u5386\u53F2\u64CD\u4F5C\u5E8F\u5217:
3028
+ ${historyStr}
3029
+
3030
+ - \u5373\u5C06\u6267\u884C\u7684\u64CD\u4F5C:
3031
+ \u7C7B\u578B: ${input.aNext.actionType}
3032
+ \u8BE6\u60C5: ${input.aNext.actionDetail}
3033
+
3034
+ ## \u53EF\u7528\u5B89\u5168 Skill
3035
+ ${skillsText}
3036
+
3037
+ ## \u524D\u7F6E\u68C0\u6D4B\u7ED3\u679C
3038
+ ${hintsText}
3039
+ ${scriptAnalysisText ? `
3040
+ ## \u811A\u672C\u5185\u5BB9\u5206\u6790
3041
+ ${scriptAnalysisText}` : ""}
3042
+
3043
+ \u8BF7\u6839\u636E\u4EE5\u4E0A\u4FE1\u606F\uFF0C\u5BF9\u5373\u5C06\u6267\u884C\u7684\u64CD\u4F5C\u8FDB\u884C\u5B89\u5168\u5224\u65AD\u3002\u524D\u7F6E\u68C0\u6D4B\u4E3A low/medium \u4E14\u6709\u5408\u7406\u6267\u884C\u7406\u7531\u65F6\u5E94\u5F53\u5141\u8BB8\u3002\u4F60\u5FC5\u987B\u8F93\u51FA\u4E25\u683C\u7684 JSON \u683C\u5F0F\uFF0C\u4E0D\u8981\u5305\u542B\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3001\u6CE8\u91CA\u6216 markdown \u6807\u8BB0\u3002
3044
+
3045
+ \u8F93\u51FA\u683C\u5F0F:
3046
+ {"allowed": true/false, "reason": "\u5224\u65AD\u7406\u7531", "riskLevel": "low|medium|high|critical", "riskType": "\u98CE\u9669\u7C7B\u578B", "confidence": 0-100}`;
3047
+ }
3048
+ function formatHints(previous) {
3049
+ const hints = [];
3050
+ if (previous.heuristic) {
3051
+ hints.push(
3052
+ `\u542F\u53D1\u5F0F\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.heuristic.riskLevel}]:
3053
+ \u7C7B\u578B: ${previous.heuristic.riskType}
3054
+ \u539F\u56E0: ${previous.heuristic.reason}`
3055
+ );
3056
+ }
3057
+ if (previous.logic) {
3058
+ hints.push(
3059
+ `\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u53D1\u73B0\u98CE\u9669 [\u98CE\u9669\u7B49\u7EA7: ${previous.logic.riskLevel}]:
3060
+ \u7C7B\u578B: ${previous.logic.riskType}
3061
+ \u539F\u56E0: ${previous.logic.reason}`
3062
+ );
3063
+ }
3064
+ return hints.length > 0 ? hints.join("\n\n") : "\u542F\u53D1\u5F0F\u68C0\u6D4B\u548C\u903B\u8F91\u89C4\u5219\u68C0\u6D4B\u5747\u672A\u53D1\u73B0\u98CE\u9669\u3002";
3065
+ }
3066
+ async function analyzeWithLLM(prompt, config) {
3067
+ console.log(`[AgentMoss] LLM analyzing with model: ${config.model}, url: ${config.baseUrl}`);
3068
+ const provider = detectProvider(config.baseUrl, config.provider);
3069
+ const extraHeaders = getProviderHeaders(provider);
3070
+ if (Object.keys(extraHeaders).length > 0) {
3071
+ console.log(`[AgentMoss] Provider detected: ${provider}, injecting headers: ${Object.keys(extraHeaders).join(", ")}`);
3072
+ }
3073
+ const timeoutCfg = getTimeoutConfig();
3074
+ const retryCfg = getRetryConfig();
3075
+ const client = new OpenAI({
3076
+ baseURL: config.baseUrl,
3077
+ apiKey: config.apiKey,
3078
+ // 单次 LLM 调用超时用 prompt1Timeout(对齐 py);env 覆盖的 config.timeout 仍向后兼容
3079
+ timeout: (config.timeout || timeoutCfg.prompt1Timeout) * 1e3,
3080
+ defaultHeaders: Object.keys(extraHeaders).length > 0 ? extraHeaders : void 0
3081
+ });
3082
+ const maxRetries = parseInt(process.env.AGENT_MOSS_LLM_RETRIES || String(retryCfg.maxRetries), 10);
3083
+ const totalTimeoutMs = timeoutCfg.totalTimeout * 1e3;
3084
+ const abortController = new AbortController();
3085
+ const totalTimeoutTimer = setTimeout(() => {
3086
+ console.error(`[AgentMoss] totalTimeout (${timeoutCfg.totalTimeout}s) exceeded, aborting all LLM calls`);
3087
+ abortController.abort();
3088
+ }, totalTimeoutMs);
3089
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
3090
+ if (abortController.signal.aborted) {
3091
+ break;
3092
+ }
3093
+ try {
3094
+ const response = await client.chat.completions.create({
3095
+ model: config.model,
3096
+ temperature: config.temperature || 0.1,
3097
+ max_tokens: config.maxTokens || 1024,
3098
+ messages: [
3099
+ { role: "system", content: "\u53EA\u8F93\u51FA\u7EAF JSON\uFF0C\u4E0D\u8981\u5305\u542B markdown \u6807\u8BB0\u3001\u4EE3\u7801\u5757\u6216\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002" },
3100
+ { role: "user", content: prompt }
3101
+ ],
3102
+ signal: abortController.signal
3103
+ });
3104
+ const content = response.choices[0]?.message?.content || "{}";
3105
+ console.log(`[AgentMoss] LLM raw response (attempt ${attempt + 1}/${maxRetries + 1}): ${content.substring(0, 300)}`);
3106
+ let jsonStr = content.trim();
3107
+ if (jsonStr.startsWith("```")) {
3108
+ jsonStr = jsonStr.replace(/^```[\w]*\n?/, "").replace(/\n?```$/, "");
3109
+ }
3110
+ const parsed = JSON.parse(jsonStr);
3111
+ try {
3112
+ const usage = response.usage;
3113
+ const { recordTokenUsage: recordTokenUsage2 } = await Promise.resolve().then(() => (init_token_stats(), token_stats_exports));
3114
+ recordTokenUsage2({
3115
+ sessionId: "audit",
3116
+ step: "L3_security_judge",
3117
+ model: response.model || config.model,
3118
+ promptTokens: usage?.prompt_tokens || 0,
3119
+ completionTokens: usage?.completion_tokens || 0,
3120
+ totalTokens: usage?.total_tokens || 0
3121
+ });
3122
+ } catch {
3123
+ }
3124
+ clearTimeout(totalTimeoutTimer);
3125
+ return {
3126
+ allowed: parsed.allowed !== false,
3127
+ reason: parsed.reason || "LLM \u5206\u6790\u5B8C\u6210",
3128
+ riskLevel: ["low", "medium", "high", "critical"].includes(parsed.riskLevel) ? parsed.riskLevel : "low",
3129
+ riskType: parsed.riskType || "",
3130
+ confidence: typeof parsed.confidence === "number" ? Math.min(100, Math.max(0, parsed.confidence)) : 70,
3131
+ source: "llm",
3132
+ violatedLayers: ["3"]
3133
+ };
3134
+ } catch (err) {
3135
+ if (abortController.signal.aborted) {
3136
+ console.error(`[AgentMoss] LLM analysis aborted by totalTimeout (${timeoutCfg.totalTimeout}s)`);
3137
+ break;
3138
+ }
3139
+ if (attempt < maxRetries) {
3140
+ console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
3141
+ await new Promise((resolve6) => setTimeout(resolve6, retryCfg.retryInterval * 1e3));
3142
+ } else {
3143
+ clearTimeout(totalTimeoutTimer);
3144
+ console.error("[AgentMoss] LLM call failed after all retries:", err.message);
3145
+ throw new Error(`LLM \u5206\u6790\u5931\u8D25 (${maxRetries + 1} \u6B21\u5C1D\u8BD5): ${err.message}`);
3146
+ }
3147
+ }
3148
+ }
3149
+ clearTimeout(totalTimeoutTimer);
3150
+ throw new Error(`LLM \u5206\u6790\u8D85\u65F6\uFF08totalTimeout ${timeoutCfg.totalTimeout}s\uFF09`);
3151
+ }
3152
+
3153
+ // src/engine/coordinator.ts
3154
+ init_config();
3155
+ init_agents();
3156
+ var FULLY_SAFE_TOOLS = /* @__PURE__ */ new Set([
3157
+ "ask_user_question",
3158
+ "glob",
3159
+ "list_dir",
3160
+ "ls",
3161
+ "count_text_length",
3162
+ "filemgr-globfiles"
3163
+ ]);
3164
+ var TRUSTED_SKILLS_BASE = /* @__PURE__ */ new Set([]);
3165
+ var READONLY_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
3166
+ "read",
3167
+ "file_read",
3168
+ "read_file",
3169
+ "head",
3170
+ "tail",
3171
+ "grep",
3172
+ "filemgr-readfile",
3173
+ "filemgr-grepfiles"
3174
+ ]);
3175
+ var FULLY_SAFE_BASH_PATTERNS = [
3176
+ /^ls(?:\s|$)/i,
3177
+ /^dir(?:\s|$)/i,
3178
+ /^pwd(?:\s|$)/i,
3179
+ /^which\s+/i,
3180
+ /^whereis\s+/i,
3181
+ /^realpath\s+/i,
3182
+ /^basename\s+/i,
3183
+ /^dirname\s+/i,
3184
+ /^file\s+/i,
3185
+ /^stat\s+/i,
3186
+ /^du(?:\s|$)/i,
3187
+ /^echo\s+/i,
3188
+ /^printf\s+/i,
3189
+ /^type\s+/i,
3190
+ /^command\s+/i,
3191
+ // 系统信息查询(只读,不涉及文件/网络/权限)
3192
+ /^whoami(?:\s|$)/i,
3193
+ /^id(?:\s|$)/i,
3194
+ /^hostname(?:\s|$)/i,
3195
+ /^uname(?:\s|$)/i,
3196
+ /^date(?:\s|$)/i,
3197
+ /^env(?:\s|$)/i,
3198
+ /^printenv(?:\s|$)/i,
3199
+ /^tty(?:\s|$)/i,
3200
+ /^arch(?:\s|$)/i,
3201
+ /^uptime(?:\s|$)/i,
3202
+ /^groups(?:\s|$)/i,
3203
+ /^logname(?:\s|$)/i
3204
+ ];
3205
+ var READONLY_SENSITIVE_BASH_PATTERNS = [
3206
+ /^cat\s+/i,
3207
+ /^head(?:\s|$)/i,
3208
+ /^tail(?:\s|$)/i,
3209
+ /^less(?:\s|$)/i,
3210
+ /^more(?:\s|$)/i,
3211
+ /^wc(?:\s|$)/i,
3212
+ /^grep(?:\s|$)/i,
3213
+ /^find\s+/i,
3214
+ /^ag(?:\s|$)/i,
3215
+ /^rg(?:\s|$)/i,
3216
+ /^awk\s+/i,
3217
+ /^sed\s+/i,
3218
+ /^cut\s+/i,
3219
+ /^sort(?:\s|$)/i,
3220
+ /^uniq(?:\s|$)/i
3221
+ ];
3222
+ function extractFirstCommand(command) {
3223
+ let main = command.split("|")[0].trim();
3224
+ for (const sep of ["&&", "||"]) {
3225
+ const idx = main.indexOf(sep);
3226
+ if (idx !== -1) {
3227
+ main = main.substring(0, idx).trim();
3228
+ break;
3229
+ }
3230
+ }
3231
+ return main;
3232
+ }
3233
+ function extractRawCommand(detail) {
3234
+ const m = detail.match(/command=([^,]+)/);
3235
+ return m ? m[1].trim() : detail;
3236
+ }
3237
+ function matchesAny(cmd, patterns) {
3238
+ return patterns.some((p) => p.test(cmd));
3239
+ }
3240
+ async function runAnalysis(input) {
3241
+ const heuristicResult = isHeuristicEnabled() ? runHeuristicCheck(input) : null;
3242
+ const isInlineScriptCmd = isInlineScriptCommand(input.aNext.actionDetail.toLowerCase());
3243
+ const isLiteralTextCmd = isLiteralTextCommand(input.aNext.actionDetail.toLowerCase());
3244
+ if (heuristicResult && !heuristicResult.allowed) {
3245
+ const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && heuristicResult.riskType === "file_access";
3246
+ if (!skipInlineFileAccess) {
3247
+ return heuristicResult;
3248
+ }
3249
+ }
3250
+ let scriptChain = null;
3251
+ if (input.aNext.actionType.toLowerCase() === "bash") {
3252
+ scriptChain = analyzeScriptContent2(input);
3253
+ if (scriptChain.nodes.length > 0) {
3254
+ for (const node of scriptChain.nodes) {
3255
+ if (node.l1Hit && (node.l1RiskLevel === "high" || node.l1RiskLevel === "critical")) {
3256
+ return {
3257
+ allowed: false,
3258
+ reason: `\u811A\u672C\u5185\u5BB9\u5305\u542B\u5371\u9669\u64CD\u4F5C: ${node.l1Reason} (\u6587\u4EF6: ${node.scriptPath}, \u6DF1\u5EA6: ${node.depth})`,
3259
+ riskLevel: node.l1RiskLevel,
3260
+ riskType: node.l1RiskType,
3261
+ confidence: 90,
3262
+ source: "heuristic_script_scan",
3263
+ violatedLayers: ["1.1-script"]
3264
+ };
3265
+ }
3266
+ }
3267
+ }
3268
+ }
3269
+ const actionType = input.aNext.actionType.toLowerCase();
3270
+ const rawDetail = extractRawCommand(input.aNext.actionDetail);
3271
+ const lowerDetail = rawDetail.toLowerCase();
3272
+ if (FULLY_SAFE_TOOLS.has(actionType)) {
3273
+ return {
3274
+ allowed: true,
3275
+ reason: `\u5B8C\u5168\u5B89\u5168\u5DE5\u5177\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${actionType}`,
3276
+ riskLevel: heuristicResult?.riskLevel || "low",
3277
+ riskType: "",
3278
+ confidence: 95,
3279
+ source: "whitelist_bypass",
3280
+ violatedLayers: []
3281
+ };
3282
+ }
3283
+ const agentExt = getAgentExtensions(input.agentId);
3284
+ const trustedSkills = new Set(TRUSTED_SKILLS_BASE);
3285
+ if (agentExt?.builtin_safe_skills) {
3286
+ for (const s of agentExt.builtin_safe_skills) trustedSkills.add(s);
3287
+ }
3288
+ if (actionType === "skill" && trustedSkills.has(lowerDetail)) {
3289
+ return {
3290
+ allowed: true,
3291
+ reason: `\u5185\u7F6E\u5B89\u5168 Skill\uFF0C\u8DF3\u8FC7\u6DF1\u5EA6\u5206\u6790: ${lowerDetail}`,
3292
+ riskLevel: heuristicResult?.riskLevel || "low",
3293
+ riskType: "",
3294
+ confidence: 95,
3295
+ source: "whitelist_bypass",
3296
+ violatedLayers: []
3297
+ };
3298
+ }
3299
+ let skipLLM = READONLY_SENSITIVE_TOOLS.has(actionType);
3300
+ if (actionType === "bash" || actionType === "executebash" || actionType === "terminal-executebash") {
3301
+ const firstCmd = extractFirstCommand(lowerDetail);
3302
+ if (matchesAny(firstCmd, FULLY_SAFE_BASH_PATTERNS)) {
3303
+ const fullCmdHit = DANGEROUS_PATTERNS.some(
3304
+ (p) => p.severity === "critical" || p.severity === "high"
3305
+ ) && DANGEROUS_PATTERNS.some(
3306
+ (p) => (p.severity === "critical" || p.severity === "high") && p.pattern.test(lowerDetail)
3307
+ );
3308
+ if (!fullCmdHit) {
3309
+ return {
3310
+ allowed: true,
3311
+ reason: `\u5B89\u5168\u7684\u53EA\u8BFB bash \u547D\u4EE4: ${firstCmd}`,
3312
+ riskLevel: "low",
3313
+ riskType: "",
3314
+ confidence: 95,
3315
+ source: "whitelist_bypass",
3316
+ violatedLayers: []
3317
+ };
3318
+ }
3319
+ }
3320
+ if (matchesAny(firstCmd, READONLY_SENSITIVE_BASH_PATTERNS)) {
3321
+ skipLLM = true;
3322
+ }
3323
+ }
3324
+ const logicResult = isLogicRulesEnabled() ? runLogicCheck(input) : null;
3325
+ const l1FileAccessHit = !!(heuristicResult && !heuristicResult.allowed && heuristicResult.riskType === "file_access");
3326
+ const effectiveResult = logicResult && !logicResult.allowed ? logicResult : l1FileAccessHit ? heuristicResult : null;
3327
+ if (effectiveResult) {
3328
+ const isCredentialPath = !!effectiveResult.isCredential;
3329
+ const skipInlineFileAccess = (isInlineScriptCmd || isLiteralTextCmd) && effectiveResult.riskType === "file_access";
3330
+ if (skipInlineFileAccess) {
3331
+ const inlineJudgment = analyzeInlineScript(input.aNext.actionDetail);
3332
+ if (inlineJudgment.actionable) {
3333
+ if (!inlineJudgment.allowed) {
3334
+ return {
3335
+ allowed: false,
3336
+ reason: inlineJudgment.reason,
3337
+ riskLevel: inlineJudgment.riskLevel,
3338
+ riskType: inlineJudgment.riskType,
3339
+ confidence: 90,
3340
+ source: "logic_rule",
3341
+ violatedLayers: ["1.2"]
3342
+ };
3343
+ }
3344
+ return {
3345
+ allowed: true,
3346
+ reason: inlineJudgment.reason,
3347
+ riskLevel: inlineJudgment.riskLevel,
3348
+ riskType: inlineJudgment.riskType,
3349
+ confidence: 90,
3350
+ source: "logic_rule",
3351
+ violatedLayers: []
3352
+ };
3353
+ }
3354
+ }
3355
+ if ((effectiveResult.riskLevel === "high" || effectiveResult.riskLevel === "critical") && (!skipInlineFileAccess || isCredentialPath)) {
3356
+ return effectiveResult;
3357
+ }
3358
+ }
3359
+ if (scriptChain && scriptChain.nodes.length > 0 && isLogicRulesEnabled()) {
3360
+ for (const node of scriptChain.nodes) {
3361
+ if (node.error || !node.preprocessedContent) continue;
3362
+ if (node.l1RiskLevel === "critical") break;
3363
+ if (node.l2Hit && (node.l2RiskLevel === "high" || node.l2RiskLevel === "critical")) {
3364
+ return {
3365
+ allowed: false,
3366
+ reason: node.l2Reason,
3367
+ riskLevel: node.l2RiskLevel,
3368
+ riskType: node.l2RiskType,
3369
+ confidence: 85,
3370
+ source: "logic_rule_script_scan",
3371
+ violatedLayers: ["1.2-script"]
3372
+ };
3373
+ }
3374
+ }
3375
+ }
3376
+ if (skipLLM || !isLLMAvailable()) {
3377
+ return {
3378
+ allowed: true,
3379
+ reason: "\u53EA\u8BFB\u64CD\u4F5C\u4E14\u65E0\u903B\u8F91\u89C4\u5219\u8FDD\u89C4\uFF0C\u8DF3\u8FC7 LLM \u5206\u6790",
3380
+ riskLevel: logicResult?.riskLevel || "low",
3381
+ riskType: "",
3382
+ confidence: 85,
3383
+ source: "heuristic_and_logic_only",
3384
+ violatedLayers: []
3385
+ };
3386
+ }
3387
+ let skipL3Hints = "\u65E0\uFF08\u5F53\u524D\u65E0\u7981\u7528\u89C4\u5219\u547D\u4E2D\uFF09";
3388
+ try {
3389
+ const { getDisabledL2RulesWithSkipL3: getDisabledL2RulesWithSkipL32 } = await Promise.resolve().then(() => (init_runtime_config(), runtime_config_exports));
3390
+ const disabledL2 = getDisabledL2RulesWithSkipL32();
3391
+ if (disabledL2 && Object.keys(disabledL2).length > 0) {
3392
+ const actionLower = input.aNext.actionDetail.toLowerCase();
3393
+ const matched = [];
3394
+ for (const items of Object.values(disabledL2)) {
3395
+ for (const item of items) {
3396
+ if (item.toLowerCase().includes(actionLower) || actionLower.includes(item.toLowerCase())) {
3397
+ matched.push(item);
3398
+ }
3399
+ }
3400
+ }
3401
+ if (matched.length > 0) {
3402
+ skipL3Hints = `\u26A0\uFE0F \u91CD\u8981\u7EA6\u675F\uFF1A\u7528\u6237\u5DF2\u4E3B\u52A8\u7981\u7528\u9488\u5BF9\u4EE5\u4E0B\u5185\u5BB9\u7684\u5B89\u5168\u62E6\u622A\u89C4\u5219\uFF0C\u8FD9\u662F\u7528\u6237\u7684\u660E\u786E\u51B3\u7B56\uFF1A
3403
+ ${matched.join(", ")}
3404
+ \u5BF9\u8FD9\u4E9B\u8DEF\u5F84/\u5173\u952E\u8BCD\u7684\u76F8\u5173\u64CD\u4F5C\uFF0C\u5FC5\u987B\u89C6\u4E3A\u5141\u8BB8\u7684\uFF0C\u4E0D\u5141\u8BB8\u4EC5\u56E0\u6D89\u53CA\u8FD9\u4E9B\u5185\u5BB9\u5C31\u62D2\u7EDD\u3002\u4F46\u4F60\u4ECD\u9700\u68C0\u67E5\u547D\u4EE4\u7684\u5176\u4ED6\u90E8\u5206\u662F\u5426\u6709\u72EC\u7ACB\u4E8E\u8FD9\u4E9B\u5185\u5BB9\u7684\u5B89\u5168\u98CE\u9669\uFF08\u5982\u7F51\u7EDC\u5916\u4F20\u3001\u63D0\u6743\u7B49\uFF09\u3002`;
3405
+ }
3406
+ }
3407
+ } catch {
3408
+ }
3409
+ let llmResult;
3410
+ try {
3411
+ llmResult = await runLLMAnalysis(input, {
3412
+ heuristic: heuristicResult,
3413
+ logic: logicResult,
3414
+ skipL3Hints,
3415
+ scriptChain: scriptChain ?? void 0,
3416
+ llmLogPath: input.llmLogPath
3417
+ });
3418
+ } catch (e) {
3419
+ const { getLLMFailMode: getLLMFailMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3420
+ if (getLLMFailMode2() === "fail_open") {
3421
+ console.warn(`[AgentMoss] LLM \u5206\u6790\u5931\u8D25\uFF0Cfail_open \u6A21\u5F0F\uFF1A\u5141\u8BB8\u6267\u884C\uFF08\u524D\u4E24\u5C42\u672A\u62E6\u622A\uFF09: ${e.message}`);
3422
+ return {
3423
+ allowed: true,
3424
+ reason: `LLM \u5206\u6790\u5931\u8D25\uFF08warn-allow\uFF0Cfail_open\uFF09: ${e.message}`,
3425
+ riskLevel: "low",
3426
+ riskType: "",
3427
+ confidence: 50,
3428
+ source: "llm_failure_fallback",
3429
+ violatedLayers: []
3430
+ };
3431
+ }
3432
+ return {
3433
+ allowed: false,
3434
+ reason: `LLM \u5206\u6790\u5931\u8D25\uFF0C\u56E0\u65E0\u6CD5\u5B8C\u6210\u6DF1\u5EA6\u8BED\u4E49\u5206\u6790\uFF0C\u6309 fail-closed \u539F\u5219\u62D2\u7EDD: ${e.message}`,
3435
+ riskLevel: "medium",
3436
+ riskType: "",
3437
+ confidence: 60,
3438
+ source: "llm_failure_fallback",
3439
+ violatedLayers: ["3"]
3440
+ };
3441
+ }
3442
+ return llmResult;
3443
+ }
3444
+ export {
3445
+ TRUSTED_SKILLS_BASE,
3446
+ runAnalysis
3447
+ };
3448
+ //# sourceMappingURL=coordinator.js.map