@kenhkl/agent-moss 0.16.14 → 0.17.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.
package/dist/cli.js CHANGED
@@ -1,6 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
+ }) : x)(function(x) {
7
+ if (typeof require !== "undefined") return require.apply(this, arguments);
8
+ throw Error('Dynamic require of "' + x + '" is not supported');
9
+ });
4
10
  var __esm = (fn, res) => function __init() {
5
11
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
12
  };
@@ -26,6 +32,10 @@ var init_esm_shims = __esm({
26
32
  import { join } from "path";
27
33
  import { homedir } from "os";
28
34
  import { readFileSync } from "fs";
35
+ function isSafeId(value) {
36
+ if (!value || value.length > ID_MAX_LEN) return false;
37
+ return ID_PATTERN.test(value);
38
+ }
29
39
  function getRulesRoot() {
30
40
  const raw = (process.env[ENV_RULES_ROOT] ?? "").trim();
31
41
  return raw ? raw : null;
@@ -99,7 +109,7 @@ function getTokenStatsPath() {
99
109
  }
100
110
  return join(getDataRoot(), "agent_moss_token_stats.json");
101
111
  }
102
- var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT, ENV_RULES_ROOT;
112
+ var ENV_DATA_ROOT, ENV_INSTANCE, DEFAULT_DATA_ROOT, ENV_RULES_ROOT, ID_MAX_LEN, ID_PATTERN;
103
113
  var init_paths = __esm({
104
114
  "src/infra/paths.ts"() {
105
115
  "use strict";
@@ -108,6 +118,8 @@ var init_paths = __esm({
108
118
  ENV_INSTANCE = "AGENT_MOSS_INSTANCE";
109
119
  DEFAULT_DATA_ROOT = join(homedir(), ".config", "agentmoss");
110
120
  ENV_RULES_ROOT = "AGENT_MOSS_RULES_ROOT";
121
+ ID_MAX_LEN = 64;
122
+ ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
111
123
  }
112
124
  });
113
125
 
@@ -185,7 +197,7 @@ function parseDirEnv(envName) {
185
197
  function rulesDirs() {
186
198
  if (isBuiltinOnly()) return [];
187
199
  const dirs = [];
188
- for (const env of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env));
200
+ for (const env2 of RULES_DIR_ENVS) dirs.push(...parseDirEnv(env2));
189
201
  dirs.push(resolve(dataRootDir(), "rules"));
190
202
  return [...new Set(dirs)];
191
203
  }
@@ -233,6 +245,7 @@ function loadJson(name) {
233
245
  }
234
246
  }
235
247
  function resolveScenarioRulesFile(scenarioId) {
248
+ if (!isSafeId(scenarioId)) return null;
236
249
  const rel = join2("scenarios", scenarioId, "rules.json");
237
250
  for (const dir of rulesDirs()) {
238
251
  const p = resolve(dir, rel);
@@ -870,6 +883,491 @@ var init_rules_contract = __esm({
870
883
  }
871
884
  });
872
885
 
886
+ // src/infra/security-policy.ts
887
+ import { createHash as createHash2, randomBytes, timingSafeEqual } from "crypto";
888
+ import { chmodSync, closeSync, existsSync as existsSync3, lstatSync, mkdirSync, openSync, readFileSync as readFileSync4, readSync, realpathSync, writeSync } from "fs";
889
+ import { homedir as homedir2, tmpdir } from "os";
890
+ import { basename, isAbsolute, join as join4, relative, resolve as resolve2, dirname as dirname2 } from "path";
891
+ function env(name, dflt = "") {
892
+ return (process.env[name] ?? dflt).trim();
893
+ }
894
+ function envInt(name, dflt) {
895
+ const raw = env(name);
896
+ if (!raw) return dflt;
897
+ const n = Number.parseInt(raw, 10);
898
+ return Number.isFinite(n) ? n : dflt;
899
+ }
900
+ function splitList(raw) {
901
+ return raw.replace(/;/g, ",").split(",").map((x) => x.trim()).filter(Boolean);
902
+ }
903
+ function originCheckEnabled() {
904
+ return TRUTHY2.includes(env("AGENT_MOSS_ORIGIN_CHECK", "1").toLowerCase());
905
+ }
906
+ function allowedOrigins() {
907
+ return splitList(env("AGENT_MOSS_CORS_ORIGINS"));
908
+ }
909
+ function normOrigin(o) {
910
+ return (o || "").trim().replace(/\/+$/, "");
911
+ }
912
+ function originAllowed(origin, hostHeader, extra) {
913
+ const o = normOrigin(origin);
914
+ if (!o) return true;
915
+ const entries = (extra ?? allowedOrigins()).map(normOrigin);
916
+ if (entries.includes(o)) return true;
917
+ const host = (hostHeader || "").trim();
918
+ if (!host) return false;
919
+ try {
920
+ const u = new URL(o);
921
+ return u.host === host;
922
+ } catch {
923
+ return false;
924
+ }
925
+ }
926
+ function authMode() {
927
+ return (env("AGENT_MOSS_AUTH_MODE", AUTH_MODE_AUTO) || AUTH_MODE_AUTO).toLowerCase();
928
+ }
929
+ function autoTokenPath() {
930
+ let root;
931
+ try {
932
+ root = getDataRoot();
933
+ } catch {
934
+ root = join4(homedir2(), ".config", "agentmoss");
935
+ }
936
+ return join4(root, "auth_token");
937
+ }
938
+ function readFdToken() {
939
+ const raw = env("AGENT_MOSS_AUTH_TOKEN_FD");
940
+ if (!raw) return "";
941
+ const fd = Number.parseInt(raw, 10);
942
+ if (!Number.isFinite(fd)) return "";
943
+ if (fdTokenCache.has(fd)) return fdTokenCache.get(fd);
944
+ const buf = Buffer.alloc(8192);
945
+ let n = 0;
946
+ try {
947
+ n = readSync(fd, buf, 0, buf.length, null);
948
+ } catch (e) {
949
+ console.error(`[AgentMoss] \u4ECE fd ${fd} \u8BFB\u53D6\u8BBF\u95EE token \u5931\u8D25:`, e);
950
+ }
951
+ try {
952
+ closeSync(fd);
953
+ } catch {
954
+ }
955
+ const tok = n > 0 ? buf.subarray(0, n).toString("utf-8").trim() : "";
956
+ fdTokenCache.set(fd, tok);
957
+ return tok;
958
+ }
959
+ function readExplicitToken() {
960
+ const tok = env("AGENT_MOSS_AUTH_TOKEN");
961
+ if (tok) return tok;
962
+ const fdTok = readFdToken();
963
+ if (fdTok) return fdTok;
964
+ const path6 = env("AGENT_MOSS_AUTH_TOKEN_FILE");
965
+ if (path6) {
966
+ try {
967
+ return readFileSync4(path6, "utf-8").trim();
968
+ } catch {
969
+ return "";
970
+ }
971
+ }
972
+ return "";
973
+ }
974
+ function ensureAutoToken() {
975
+ const path6 = autoTokenPath();
976
+ try {
977
+ if (existsSync3(path6) && !lstatSync(path6).isSymbolicLink()) {
978
+ const tok2 = readFileSync4(path6, "utf-8").trim();
979
+ if (tok2) {
980
+ try {
981
+ chmodSync(path6, 384);
982
+ } catch {
983
+ }
984
+ return tok2;
985
+ }
986
+ }
987
+ const tok = randomBytes(32).toString("base64url");
988
+ ensurePrivateDir(dirname2(path6));
989
+ if (!writePrivateText(path6, tok + "\n")) return "";
990
+ console.warn(
991
+ `[AgentMoss] \u5DF2\u751F\u6210\u8BBF\u95EE token \u5E76\u5199\u5165 ${path6}\uFF080600\uFF09\u3002\u5BA2\u6237\u7AEF\u9700\u5E26 \`Authorization: Bearer <token>\`\uFF1B\u67E5\u770B\uFF1Acat ${path6}\uFF1B\u5982\u9700\u5173\u95ED\u9274\u6743\uFF1AAGENT_MOSS_AUTH_MODE=off`
992
+ );
993
+ return tok;
994
+ } catch (e) {
995
+ console.error("[AgentMoss] \u751F\u6210\u8BBF\u95EE token \u5931\u8D25\uFF08\u672C\u6B21\u8FD0\u884C\u5C06\u4E0D\u9274\u6743\uFF09:", e);
996
+ return "";
997
+ }
998
+ }
999
+ function parseAgentTokens(raw) {
1000
+ const out = {};
1001
+ for (const pair of splitList(raw)) {
1002
+ const i = pair.indexOf("=");
1003
+ if (i > 0) {
1004
+ const a = pair.slice(0, i).trim();
1005
+ const t = pair.slice(i + 1).trim();
1006
+ if (a && t) out[a] = t;
1007
+ }
1008
+ }
1009
+ return out;
1010
+ }
1011
+ function resolveAuth() {
1012
+ const rawAgents = env("AGENT_MOSS_AGENT_TOKENS");
1013
+ const key = [
1014
+ authMode(),
1015
+ env("AGENT_MOSS_AUTH_TOKEN"),
1016
+ env("AGENT_MOSS_AUTH_TOKEN_FILE"),
1017
+ env("AGENT_MOSS_AUTH_TOKEN_FD"),
1018
+ rawAgents,
1019
+ env("AGENT_MOSS_DATA_DIR")
1020
+ ].join("\0");
1021
+ if (tokenCache && tokenCache.key === key) {
1022
+ return [tokenCache.token, tokenCache.agents, tokenCache.error];
1023
+ }
1024
+ let error = "";
1025
+ let token = readExplicitToken();
1026
+ if (!token && env("AGENT_MOSS_AUTH_TOKEN_FD") && authMode() !== AUTH_MODE_OFF) {
1027
+ error = "AGENT_MOSS_AUTH_TOKEN_FD \u5DF2\u8BBE\u7F6E\uFF0C\u4F46\u6CA1\u80FD\u4ECE\u8BE5\u6587\u4EF6\u63CF\u8FF0\u7B26\u8BFB\u5230 token\u3002\u62D2\u7EDD\u964D\u7EA7\u5230\u78C1\u76D8 token\uFF08\u90A3\u4F1A\u5728\u672C\u673A\u7559\u4E0B\u4E00\u4EFD\u540C\u7528\u6237\u53EF\u8BFB\u7684\u51ED\u8BC1\uFF09\u3002";
1028
+ console.error(`[agent-moss] ${error}`);
1029
+ }
1030
+ if (!token && !error && authMode() !== AUTH_MODE_OFF) token = ensureAutoToken();
1031
+ const agents = parseAgentTokens(rawAgents);
1032
+ tokenCache = { key, token, agents, error };
1033
+ return [token, agents, error];
1034
+ }
1035
+ function agentTokens() {
1036
+ return resolveAuth()[1];
1037
+ }
1038
+ function authConfigError() {
1039
+ return resolveAuth()[2];
1040
+ }
1041
+ function failFastOnAuthError() {
1042
+ const err = authConfigError();
1043
+ if (err) {
1044
+ console.error(`[agent-moss] \u9274\u6743\u914D\u7F6E\u9519\u8BEF\uFF0C\u62D2\u7EDD\u542F\u52A8: ${err}`);
1045
+ process.exit(1);
1046
+ }
1047
+ }
1048
+ function authEnabled() {
1049
+ if (authMode() === AUTH_MODE_OFF) return false;
1050
+ const [tok, agents] = resolveAuth();
1051
+ return Boolean(tok) || Object.keys(agents).length > 0;
1052
+ }
1053
+ function bearer(headers) {
1054
+ const get = (k) => headers instanceof Headers ? headers.get(k) ?? "" : headers[k] ?? "";
1055
+ const v = get("authorization") || get("Authorization");
1056
+ return v.toLowerCase().startsWith("bearer ") ? v.slice(7).trim() : "";
1057
+ }
1058
+ function constEq(a, b) {
1059
+ const ba = Buffer.from(a, "utf-8");
1060
+ const bb = Buffer.from(b, "utf-8");
1061
+ if (ba.length !== bb.length) return false;
1062
+ return timingSafeEqual(ba, bb);
1063
+ }
1064
+ function checkRequestAuth(headers, path6) {
1065
+ if (authMode() === AUTH_MODE_OFF) return [true, ""];
1066
+ const isHealth = HEALTH_PATHS.includes(path6.replace(/\/+$/, ""));
1067
+ const [gt0, agents, err] = resolveAuth();
1068
+ if (err) return isHealth ? [true, ""] : [false, ""];
1069
+ if (!gt0 && Object.keys(agents).length === 0) return [true, ""];
1070
+ if (isHealth) return [true, ""];
1071
+ const tok = bearer(headers);
1072
+ if (!tok) return [false, ""];
1073
+ if (path6.startsWith("/console/api")) {
1074
+ const consoleTok = env("AGENT_MOSS_CONSOLE_TOKEN");
1075
+ if (consoleTok && constEq(tok, consoleTok)) return [true, "console"];
1076
+ }
1077
+ const gt = gt0;
1078
+ if (gt && constEq(tok, gt)) return [true, "global"];
1079
+ for (const [agent, expected] of Object.entries(agents)) {
1080
+ if (constEq(tok, expected)) return [true, `agent:${agent}`];
1081
+ }
1082
+ return [false, ""];
1083
+ }
1084
+ function bindAgent(principal, agentId) {
1085
+ const tokens = agentTokens();
1086
+ if (Object.keys(tokens).length === 0) return [true, agentId];
1087
+ if (!principal.startsWith("agent:")) return [true, agentId];
1088
+ const bound = principal.slice(6);
1089
+ if (!agentId) return [true, bound];
1090
+ return [bound === agentId, bound];
1091
+ }
1092
+ function principalOf(headers, path6 = "") {
1093
+ const [ok, principal] = checkRequestAuth(headers, path6);
1094
+ return ok ? principal : "";
1095
+ }
1096
+ function principalHash(principal) {
1097
+ if (!principal) return "";
1098
+ return createHash2("sha256").update(principal, "utf-8").digest("hex").slice(0, 12);
1099
+ }
1100
+ function homeDir() {
1101
+ try {
1102
+ return homedir2();
1103
+ } catch {
1104
+ return tmpdir();
1105
+ }
1106
+ }
1107
+ function logRoots() {
1108
+ const raw = env("AGENT_MOSS_LOG_ROOTS");
1109
+ if (raw) return splitList(raw);
1110
+ return [.../* @__PURE__ */ new Set([homeDir(), "/tmp", tmpdir()])];
1111
+ }
1112
+ function reviewRoots() {
1113
+ const roots = [join4(homeDir(), ".local", "share", "agent_gate")];
1114
+ for (const name of ["AGENT_GATE_DB", "AGENT_GATE_JSON_PATH"]) {
1115
+ const v = env(name);
1116
+ if (v) roots.push(existsSync3(v) && lstatSync(v).isDirectory() ? v : dirname2(v));
1117
+ }
1118
+ roots.push(...splitList(env("AGENT_MOSS_REVIEW_PATHS")));
1119
+ return [...new Set(roots)];
1120
+ }
1121
+ function realpathLoose(p) {
1122
+ const abs = resolve2(p.replace(/^~(?=$|\/)/, homeDir()));
1123
+ try {
1124
+ return realpathSync(abs);
1125
+ } catch {
1126
+ }
1127
+ const parts = [];
1128
+ let cur = abs;
1129
+ for (; ; ) {
1130
+ const parent = dirname2(cur);
1131
+ if (parent === cur) return null;
1132
+ parts.unshift(basename(cur));
1133
+ cur = parent;
1134
+ try {
1135
+ return join4(realpathSync(cur), ...parts);
1136
+ } catch {
1137
+ }
1138
+ }
1139
+ }
1140
+ function pathWithin(p, roots) {
1141
+ if (!p) return true;
1142
+ const target = realpathLoose(p);
1143
+ if (target === null) return false;
1144
+ for (const root of roots) {
1145
+ const rootReal = realpathLoose(root);
1146
+ if (rootReal === null) continue;
1147
+ const rel = relative(rootReal, target);
1148
+ if (rel === "" || !rel.startsWith("..") && !isAbsolute(rel)) return true;
1149
+ }
1150
+ return false;
1151
+ }
1152
+ function ensurePrivateDir(dir) {
1153
+ try {
1154
+ mkdirSync(dir, { recursive: true });
1155
+ if (lstatSync(dir).isSymbolicLink()) return false;
1156
+ if (typeof process.getuid === "function" && lstatSync(dir).uid !== process.getuid()) return false;
1157
+ chmodSync(dir, 448);
1158
+ return true;
1159
+ } catch {
1160
+ return false;
1161
+ }
1162
+ }
1163
+ function writePrivateText(file, text) {
1164
+ try {
1165
+ const flags = __require("fs").constants;
1166
+ let openFlags = flags.O_WRONLY | flags.O_CREAT | flags.O_TRUNC;
1167
+ if (flags.O_NOFOLLOW) openFlags |= flags.O_NOFOLLOW;
1168
+ const fd = openSync(file, openFlags, 384);
1169
+ try {
1170
+ writeSync(fd, text);
1171
+ } finally {
1172
+ closeSync(fd);
1173
+ }
1174
+ return true;
1175
+ } catch {
1176
+ return false;
1177
+ }
1178
+ }
1179
+ function perRequestLlmEnabled() {
1180
+ return TRUTHY2.includes(env("AGENT_MOSS_ALLOW_PER_REQUEST_LLM", "1").toLowerCase());
1181
+ }
1182
+ function validateLlmBaseUrl(url, allowPrivate) {
1183
+ if (!url) return [true, ""];
1184
+ let u;
1185
+ try {
1186
+ u = new URL(url);
1187
+ } catch {
1188
+ return [false, "baseUrl \u4E0D\u662F\u5408\u6CD5 URL"];
1189
+ }
1190
+ if (u.protocol !== "http:" && u.protocol !== "https:") {
1191
+ return [false, `baseUrl \u534F\u8BAE\u4E0D\u53D7\u652F\u6301: ${u.protocol}`];
1192
+ }
1193
+ const host = (u.hostname || "").toLowerCase();
1194
+ if (!host) return [false, "baseUrl \u7F3A\u5C11\u4E3B\u673A\u540D"];
1195
+ if (BLOCKED_HOSTS.includes(host)) return [false, `baseUrl \u6307\u5411\u4E91\u5143\u6570\u636E\u5730\u5740\uFF0C\u5DF2\u62D2\u7EDD: ${host}`];
1196
+ if (/^169\.254\.\d{1,3}\.\d{1,3}$/.test(host)) {
1197
+ return [false, `baseUrl \u6307\u5411\u94FE\u8DEF\u672C\u5730\u5730\u5740\uFF0C\u5DF2\u62D2\u7EDD: ${host}`];
1198
+ }
1199
+ const allowlist = splitList(env("AGENT_MOSS_LLM_HOST_ALLOWLIST")).map((h) => h.toLowerCase());
1200
+ if (allowlist.length && !allowlist.includes(host)) {
1201
+ return [false, `baseUrl \u4E3B\u673A\u4E0D\u5728\u5141\u8BB8\u5217\u8868: ${host}`];
1202
+ }
1203
+ if (allowPrivate === void 0) {
1204
+ allowPrivate = TRUTHY2.includes(env("AGENT_MOSS_TRUST_PRIVATE_LLM", "1").toLowerCase());
1205
+ }
1206
+ if (!allowPrivate) {
1207
+ if (host === "localhost" || host === "::1") return [false, "baseUrl \u6307\u5411\u672C\u673A\uFF0C\u5DF2\u62D2\u7EDD"];
1208
+ const m = host.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/);
1209
+ if (m) {
1210
+ const [a, b] = [Number(m[1]), Number(m[2])];
1211
+ if (a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 127 || a === 169 && b === 254) {
1212
+ return [false, `baseUrl \u6307\u5411\u5185\u7F51\u5730\u5740\uFF0C\u5DF2\u62D2\u7EDD: ${host}`];
1213
+ }
1214
+ }
1215
+ }
1216
+ return [true, ""];
1217
+ }
1218
+ function hardenExistingPaths() {
1219
+ try {
1220
+ const roots = [join4(homeDir(), ".config", "agentmoss"), join4(homeDir(), ".agentmoss")];
1221
+ const names = [
1222
+ "agent_moss_runtime.json",
1223
+ "config.json",
1224
+ "agent_moss_settings.json",
1225
+ "agent_moss_token_stats.json",
1226
+ "stateful_chains.db",
1227
+ "stateful_chains.db-wal",
1228
+ "stateful_chains.db-shm"
1229
+ ];
1230
+ for (const root of roots) {
1231
+ try {
1232
+ if (existsSync3(root) && !lstatSync(root).isSymbolicLink()) chmodSync(root, 448);
1233
+ } catch {
1234
+ }
1235
+ for (const n of names) {
1236
+ const p = join4(root, n);
1237
+ try {
1238
+ if (existsSync3(p) && !lstatSync(p).isSymbolicLink()) chmodSync(p, 384);
1239
+ } catch {
1240
+ }
1241
+ }
1242
+ }
1243
+ } catch {
1244
+ }
1245
+ }
1246
+ function chainDetailMode() {
1247
+ return (env("AGENT_MOSS_CHAIN_DETAIL", "redacted") || "redacted").toLowerCase();
1248
+ }
1249
+ function redactChain(chain, currentSessionId) {
1250
+ if (!chain || chain.length === 0) return [];
1251
+ if (chainDetailMode() === "full") return [...chain];
1252
+ return chain.map((step) => {
1253
+ if (Array.isArray(step) && step.length >= 3) {
1254
+ const [sid, seq, , ts] = step;
1255
+ if (String(sid) === String(currentSessionId)) return [...step];
1256
+ return ["<other-session>", seq, "<\u5DF2\u9690\u85CF>", ts ?? ""];
1257
+ }
1258
+ return step;
1259
+ });
1260
+ }
1261
+ function sessionSweepThreshold() {
1262
+ return envInt("AGENT_MOSS_SESSION_SWEEP_THRESHOLD", 50);
1263
+ }
1264
+ function sessionSweepWindow() {
1265
+ return envInt("AGENT_MOSS_SESSION_SWEEP_WINDOW_SEC", 300);
1266
+ }
1267
+ function sessionSweepAction() {
1268
+ return (env("AGENT_MOSS_SESSION_SWEEP_ACTION", "log") || "log").toLowerCase();
1269
+ }
1270
+ function noteSessionAccess(key, sessionId) {
1271
+ const limit = sessionSweepThreshold();
1272
+ if (limit <= 0 || !sessionId) return [0, false];
1273
+ const now = Date.now() / 1e3;
1274
+ const cutoff = now - sessionSweepWindow();
1275
+ let entries = (sessionWindow.get(key) ?? []).filter(([t]) => t >= cutoff);
1276
+ entries.push([now, sessionId]);
1277
+ if (entries.length > limit * 4) entries = entries.slice(-limit * 4);
1278
+ sessionWindow.set(key, entries);
1279
+ if (sessionWindow.size > 1e4) sessionWindow.clear();
1280
+ const distinct = new Set(entries.map(([, s]) => s)).size;
1281
+ return [distinct, distinct > limit];
1282
+ }
1283
+ function queueAbuseThreshold() {
1284
+ return envInt("AGENT_MOSS_QUEUE_ABUSE_THRESHOLD", 10);
1285
+ }
1286
+ function queueAbuseWindow() {
1287
+ return envInt("AGENT_MOSS_QUEUE_ABUSE_WINDOW_SEC", 60);
1288
+ }
1289
+ function queueAbuseAction() {
1290
+ return (env("AGENT_MOSS_QUEUE_ABUSE_ACTION", "log") || "log").toLowerCase();
1291
+ }
1292
+ function noteQueueReject(key) {
1293
+ const limit = queueAbuseThreshold();
1294
+ if (limit <= 0 || !key) return [0, false];
1295
+ const now = Date.now() / 1e3;
1296
+ const cutoff = now - queueAbuseWindow();
1297
+ let entries = (queueRejects.get(key) ?? []).filter((t) => t >= cutoff);
1298
+ entries.push(now);
1299
+ if (entries.length > limit * 4) entries = entries.slice(-limit * 4);
1300
+ queueRejects.set(key, entries);
1301
+ if (queueRejects.size > 1e4) queueRejects.clear();
1302
+ return [entries.length, entries.length > limit];
1303
+ }
1304
+ function socketMode() {
1305
+ const raw = env("AGENT_MOSS_SOCKET_MODE", "600");
1306
+ const mode = parseInt(raw, 8);
1307
+ if (!Number.isFinite(mode) || mode < 0 || mode > 511) {
1308
+ console.warn(`[agent-moss] AGENT_MOSS_SOCKET_MODE=${raw} \u975E\u6CD5\uFF0C\u56DE\u9000 600`);
1309
+ return 384;
1310
+ }
1311
+ return mode;
1312
+ }
1313
+ function errorDetailMode() {
1314
+ return (env("AGENT_MOSS_ERROR_DETAIL", "generic") || "generic").toLowerCase();
1315
+ }
1316
+ function maxBodyBytes() {
1317
+ return envInt("AGENT_MOSS_MAX_BODY_BYTES", 16 * 1024 * 1024);
1318
+ }
1319
+ function poolOf(path6) {
1320
+ if (path6 === "/api/v1/analyze" || path6.startsWith("/api/v1/analyze/")) return "biz";
1321
+ if (path6 === "/api/v1/review" || path6.startsWith("/api/v1/review/")) return "review";
1322
+ if (path6 === "/console" || path6.startsWith("/console/") || path6.startsWith("/api/v1/brain")) return "ops";
1323
+ if (path6 === "/api/v1/health") return "health";
1324
+ return "other";
1325
+ }
1326
+ function poolLimit(name) {
1327
+ let dflt = POOL_DEFAULTS[name] ?? 4;
1328
+ if (name === "biz") dflt = envInt("AGENT_MOSS_MAX_INFLIGHT", dflt);
1329
+ return envInt(`AGENT_MOSS_POOL_${name.toUpperCase()}`, dflt);
1330
+ }
1331
+ function unauthInflight() {
1332
+ return envInt("AGENT_MOSS_MAX_UNAUTH_INFLIGHT", 4);
1333
+ }
1334
+ function unauthPerIp() {
1335
+ return envInt("AGENT_MOSS_MAX_UNAUTH_PER_IP", 2);
1336
+ }
1337
+ function agentInflight() {
1338
+ return envInt("AGENT_MOSS_MAX_AGENT_INFLIGHT", 8);
1339
+ }
1340
+ function boundAgentOf(principal) {
1341
+ return principal.startsWith("agent:") ? principal.slice("agent:".length) : "";
1342
+ }
1343
+ function rateLimitQps() {
1344
+ return envInt("AGENT_MOSS_RATE_LIMIT_QPS", 100);
1345
+ }
1346
+ function maxConnections() {
1347
+ return envInt("AGENT_MOSS_MAX_CONNECTIONS", 128);
1348
+ }
1349
+ function keepAliveSec() {
1350
+ return envInt("AGENT_MOSS_KEEP_ALIVE_SEC", 5);
1351
+ }
1352
+ var TRUTHY2, HEALTH_PATHS, AUTH_MODE_AUTO, AUTH_MODE_OFF, tokenCache, fdTokenCache, BLOCKED_HOSTS, sessionWindow, queueRejects, POOL_DEFAULTS;
1353
+ var init_security_policy = __esm({
1354
+ "src/infra/security-policy.ts"() {
1355
+ "use strict";
1356
+ init_esm_shims();
1357
+ init_paths();
1358
+ TRUTHY2 = ["1", "true", "yes", "on"];
1359
+ HEALTH_PATHS = ["/api/v1/health"];
1360
+ AUTH_MODE_AUTO = "auto";
1361
+ AUTH_MODE_OFF = "off";
1362
+ tokenCache = null;
1363
+ fdTokenCache = /* @__PURE__ */ new Map();
1364
+ BLOCKED_HOSTS = ["169.254.169.254", "metadata.google.internal", "metadata.tencentyun.com"];
1365
+ sessionWindow = /* @__PURE__ */ new Map();
1366
+ queueRejects = /* @__PURE__ */ new Map();
1367
+ POOL_DEFAULTS = { biz: 16, review: 1, ops: 4, health: 4, other: 4 };
1368
+ }
1369
+ });
1370
+
873
1371
  // src/package-json.ts
874
1372
  import { createRequire } from "module";
875
1373
  var require2, pkg, version;
@@ -897,7 +1395,10 @@ var init_models = __esm({
897
1395
  // 工具返回给 agent 的内容。仅 L2 的授权(consent)判定用:判断「用户是否已授权」
898
1396
  // 看的是 ask_user_question 的**回答**,不是提问正文。历史 bug:模型里没有这个字段、
899
1397
  // 路由也没透传,TS 的授权判定退化成「提问正文非空」,等于问过任何问题即已授权。
900
- output: z.string().optional().describe("Tool result / user answer (needed by L2 consent checks)")
1398
+ output: z.string().optional().describe("Tool result / user answer (needed by L2 consent checks)"),
1399
+ // 动作真实发生时间(可选)。供 P4「提权→外传」时间窗口判定;缺省时该性质
1400
+ // 退回不设窗口的旧行为。与 Python ActionItem.called_at 对齐。
1401
+ called_at: z.string().optional().describe("Action timestamp (optional)")
901
1402
  });
902
1403
  PromptTurnSchema = z.object({
903
1404
  text: z.string().default("").describe("User prompt text at this turn"),
@@ -1538,7 +2039,7 @@ function checkReadBeforeWrite(actionType, actionDetail, actionHistory, reason, c
1538
2039
  targetPaths.push(...segPathsFiltered);
1539
2040
  }
1540
2041
  }
1541
- const uniqueTargetPaths = [...new Set(targetPaths)];
2042
+ const uniqueTargetPaths = [...new Set(targetPaths)].filter((p) => !PSEUDO_FS_PREFIXES.some((pfx) => p.startsWith(pfx)));
1542
2043
  if (uniqueTargetPaths.length === 0) return null;
1543
2044
  const historyReadPaths = /* @__PURE__ */ new Set();
1544
2045
  for (const action of actionHistory) {
@@ -1717,7 +2218,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
1717
2218
  violatedLayers: ["L2.1"]
1718
2219
  };
1719
2220
  }
1720
- var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
2221
+ var _LOGIC, _HEURISTIC, EXCLUDED_PREFIXES, TEMP_DIRS, ROOT_SENSITIVE_PATHS, HOME_SENSITIVE_PATHS, CREDENTIAL_PATHS, ROOT_SENSITIVE_PATHS_WINDOWS, HOME_SENSITIVE_PATHS_WINDOWS, CREDENTIAL_PATHS_WINDOWS, ROOT_SENSITIVE_PATHS_MACOS, HOME_SENSITIVE_PATHS_MACOS, CREDENTIAL_PATHS_MACOS, ROOT_SENSITIVE_PATHS_HARMONY, HOME_SENSITIVE_PATHS_HARMONY, CREDENTIAL_PATHS_HARMONY, _scopeIndex, NON_WRITE_TOOLS, NON_WRITE_TOOL_PREFIXES, PSEUDO_FS_PREFIXES, WRITE_KEYWORDS, SED_INPLACE_RE, DEVNULL_REDIRECT_RE, CREATE_KEYWORDS, PASSWORD_MODIFY_PATTERNS, USER_DELETION_PATTERNS, INTENT_DEVIATION_PATTERNS, COMBO_STAGES, COMBO_CHAINS, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
1721
2222
  var init_logic_rules = __esm({
1722
2223
  "src/engine/logic-rules.ts"() {
1723
2224
  "use strict";
@@ -1764,6 +2265,7 @@ var init_logic_rules = __esm({
1764
2265
  "cat"
1765
2266
  ]);
1766
2267
  NON_WRITE_TOOL_PREFIXES = _LOGIC.non_write_tool_prefixes ?? [];
2268
+ PSEUDO_FS_PREFIXES = ["/sys/", "/proc/", "/dev/", "/run/"];
1767
2269
  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", "edit"];
1768
2270
  SED_INPLACE_RE = /\bsed\s+(?:-\S+\s+)*(?:-[a-zA-Z]*i[a-zA-Z]*|--in-place)\b/i;
1769
2271
  DEVNULL_REDIRECT_RE = /(?:2|&|1)?\s*>\s*\/dev\/null\b/g;
@@ -1814,22 +2316,22 @@ var init_logic_rules = __esm({
1814
2316
  });
1815
2317
 
1816
2318
  // src/engine/asset-dir.ts
1817
- import { existsSync as existsSync4 } from "fs";
1818
- import { join as join4, resolve as resolve2 } from "path";
2319
+ import { existsSync as existsSync5 } from "fs";
2320
+ import { join as join5, resolve as resolve3 } from "path";
1819
2321
  function assetDirCandidates(moduleDir, name) {
1820
2322
  const root = getRulesRoot();
1821
2323
  if (root) {
1822
- const p = join4(root, name);
2324
+ const p = join5(root, name);
1823
2325
  return [p, p];
1824
2326
  }
1825
2327
  return [
1826
- resolve2(moduleDir, name),
1827
- resolve2(moduleDir, "../../..", "agent_moss", name)
2328
+ resolve3(moduleDir, name),
2329
+ resolve3(moduleDir, "../../..", "agent_moss", name)
1828
2330
  ];
1829
2331
  }
1830
2332
  function resolveAssetDir(moduleDir, name) {
1831
2333
  for (const dir of assetDirCandidates(moduleDir, name)) {
1832
- if (existsSync4(dir)) return dir;
2334
+ if (existsSync5(dir)) return dir;
1833
2335
  }
1834
2336
  return null;
1835
2337
  }
@@ -1842,8 +2344,8 @@ var init_asset_dir = __esm({
1842
2344
  });
1843
2345
 
1844
2346
  // src/engine/skill-loader.ts
1845
- import { readFileSync as readFileSync4, readdirSync as readdirSync3, existsSync as existsSync5 } from "fs";
1846
- import { resolve as resolve3, dirname as dirname2 } from "path";
2347
+ import { readFileSync as readFileSync5, readdirSync as readdirSync3, existsSync as existsSync6 } from "fs";
2348
+ import { resolve as resolve4, dirname as dirname3 } from "path";
1847
2349
  import { fileURLToPath as fileURLToPath3 } from "url";
1848
2350
  function loadAllSkills() {
1849
2351
  if (cachedSkills) return cachedSkills;
@@ -1857,13 +2359,13 @@ function loadAllSkills() {
1857
2359
  const files = readdirSync3(dir).filter((f) => f.endsWith(".md"));
1858
2360
  for (const f of files) {
1859
2361
  const name = f.replace(".md", "");
1860
- cachedSkills[name] = readFileSync4(resolve3(dir, f), "utf-8");
2362
+ cachedSkills[name] = readFileSync5(resolve4(dir, f), "utf-8");
1861
2363
  }
1862
2364
  console.log(`[AgentMoss] Loaded ${Object.keys(cachedSkills).length} skills from ${dir}`);
1863
2365
  return cachedSkills;
1864
2366
  }
1865
2367
  function userSkillsDir() {
1866
- return resolve3(getDataRoot(), "agent_moss_skills");
2368
+ return resolve4(getDataRoot(), "agent_moss_skills");
1867
2369
  }
1868
2370
  function runtimeCustomSkillKeywords() {
1869
2371
  if (isRuntimeConfigDisabled()) return {};
@@ -1887,8 +2389,8 @@ function loadUserSkill(name) {
1887
2389
  if (isRuntimeConfigDisabled()) return "";
1888
2390
  if (userSkillCache[name] !== void 0) return userSkillCache[name];
1889
2391
  try {
1890
- const p = resolve3(userSkillsDir(), `${name}.md`);
1891
- userSkillCache[name] = existsSync5(p) ? readFileSync4(p, "utf-8") : "";
2392
+ const p = resolve4(userSkillsDir(), `${name}.md`);
2393
+ userSkillCache[name] = existsSync6(p) ? readFileSync5(p, "utf-8") : "";
1892
2394
  } catch {
1893
2395
  userSkillCache[name] = "";
1894
2396
  }
@@ -1957,7 +2459,7 @@ var init_skill_loader = __esm({
1957
2459
  init_asset_dir();
1958
2460
  init_runtime_config_bridge();
1959
2461
  __filename3 = fileURLToPath3(import.meta.url);
1960
- __dirname3 = dirname2(__filename3);
2462
+ __dirname3 = dirname3(__filename3);
1961
2463
  SKILL_KEYWORD_MAP = (() => {
1962
2464
  const map = {};
1963
2465
  const cats = loadL3();
@@ -2037,10 +2539,10 @@ __export(runtime_config_exports, {
2037
2539
  updateSkillCategoryEnabled: () => updateSkillCategoryEnabled,
2038
2540
  updateSkillEnabled: () => updateSkillEnabled
2039
2541
  });
2040
- import { readFileSync as readFileSync5, writeFileSync, existsSync as existsSync6, mkdirSync, unlinkSync, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
2041
- import { join as join5 } from "path";
2542
+ import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync7, mkdirSync as mkdirSync2, unlinkSync, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
2543
+ import { join as join6 } from "path";
2042
2544
  import { fileURLToPath as fileURLToPath4 } from "url";
2043
- import { dirname as dirname3 } from "path";
2545
+ import { dirname as dirname4 } from "path";
2044
2546
  function runtimeDir() {
2045
2547
  return getRuntimeConfigDir();
2046
2548
  }
@@ -2067,7 +2569,7 @@ function saveRuntimeConfig(config) {
2067
2569
  }
2068
2570
  const p = getRuntimeConfigPath();
2069
2571
  const dir = p.substring(0, p.lastIndexOf("/"));
2070
- if (dir && !existsSync6(dir)) mkdirSync(dir, { recursive: true });
2572
+ if (dir && !existsSync7(dir)) mkdirSync2(dir, { recursive: true });
2071
2573
  writeFileSync(p, JSON.stringify(config, null, 2), "utf-8");
2072
2574
  invalidateRuntimeConfigCache();
2073
2575
  }
@@ -2101,7 +2603,7 @@ function loadRuntimeConfig() {
2101
2603
  };
2102
2604
  }
2103
2605
  const p = getRuntimeConfigPath();
2104
- if (!existsSync6(p)) {
2606
+ if (!existsSync7(p)) {
2105
2607
  const seeded = mergeConfig({}, generateSourceDefaults());
2106
2608
  try {
2107
2609
  saveRuntimeConfig(seeded);
@@ -2115,7 +2617,7 @@ function loadRuntimeConfig() {
2115
2617
  if (runtimeConfigCache && runtimeConfigCache.key === cacheKey) {
2116
2618
  return runtimeConfigCache.value;
2117
2619
  }
2118
- const data = JSON.parse(readFileSync5(p, "utf-8"));
2620
+ const data = JSON.parse(readFileSync6(p, "utf-8"));
2119
2621
  const merged = mergeConfig(data, generateSourceDefaults());
2120
2622
  const result = {
2121
2623
  version: data.version || 1,
@@ -2263,7 +2765,7 @@ function hasRuntimeConfig() {
2263
2765
  if (cached2 !== void 0) return cached2;
2264
2766
  let ok = false;
2265
2767
  try {
2266
- JSON.parse(readFileSync5(p, "utf-8"));
2768
+ JSON.parse(readFileSync6(p, "utf-8"));
2267
2769
  ok = true;
2268
2770
  } catch {
2269
2771
  ok = false;
@@ -2378,7 +2880,7 @@ function _l2SensitivePathsToRules() {
2378
2880
  const all = [...root, ...home, ...credential];
2379
2881
  for (const sp of all) {
2380
2882
  const os4 = String(sp.os || "linux").toLowerCase();
2381
- const denyMode = _deriveDenyMode({
2883
+ const denyMode = sp.denyMode || _deriveDenyMode({
2382
2884
  deny_mode: sp.deny_mode,
2383
2885
  credential: sp.credential,
2384
2886
  read_only: sp.read_only
@@ -2484,8 +2986,8 @@ function _l3SkillsToRules() {
2484
2986
  function _scenarioRulesToDefaults() {
2485
2987
  const out = {};
2486
2988
  const _root = getRulesRoot();
2487
- const scenDir = _root ? join5(_root, "scenarios") : existsSync6(_SCENARIOS_DIR_PROD) ? _SCENARIOS_DIR_PROD : existsSync6(_SCENARIOS_DIR_DEV) ? _SCENARIOS_DIR_DEV : _SCENARIOS_DIR_PROD;
2488
- if (!existsSync6(scenDir)) return out;
2989
+ const scenDir = _root ? join6(_root, "scenarios") : existsSync7(_SCENARIOS_DIR_PROD) ? _SCENARIOS_DIR_PROD : existsSync7(_SCENARIOS_DIR_DEV) ? _SCENARIOS_DIR_DEV : _SCENARIOS_DIR_PROD;
2990
+ if (!existsSync7(scenDir)) return out;
2489
2991
  let entries;
2490
2992
  try {
2491
2993
  entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
@@ -2493,11 +2995,11 @@ function _scenarioRulesToDefaults() {
2493
2995
  return out;
2494
2996
  }
2495
2997
  for (const scenarioId of entries) {
2496
- const rulesFile = resolveScenarioRulesFile(scenarioId) ?? join5(scenDir, scenarioId, "rules.json");
2497
- if (!existsSync6(rulesFile)) continue;
2998
+ const rulesFile = resolveScenarioRulesFile(scenarioId) ?? join6(scenDir, scenarioId, "rules.json");
2999
+ if (!existsSync7(rulesFile)) continue;
2498
3000
  let data;
2499
3001
  try {
2500
- data = JSON.parse(readFileSync5(rulesFile, "utf-8"));
3002
+ data = JSON.parse(readFileSync6(rulesFile, "utf-8"));
2501
3003
  } catch {
2502
3004
  continue;
2503
3005
  }
@@ -2534,8 +3036,8 @@ function _scenarioRulesToDefaults() {
2534
3036
  function _agentRulesToDefaults() {
2535
3037
  const out = {};
2536
3038
  const _root = getRulesRoot();
2537
- const agentsDir = _root ? join5(_root, "agents") : existsSync6(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync6(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
2538
- if (!existsSync6(agentsDir)) return out;
3039
+ const agentsDir = _root ? join6(_root, "agents") : existsSync7(_AGENTS_DIR_PROD) ? _AGENTS_DIR_PROD : existsSync7(_AGENTS_DIR_DEV) ? _AGENTS_DIR_DEV : _AGENTS_DIR_PROD;
3040
+ if (!existsSync7(agentsDir)) return out;
2539
3041
  let entries;
2540
3042
  try {
2541
3043
  entries = readdirSync4(agentsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
@@ -2543,11 +3045,11 @@ function _agentRulesToDefaults() {
2543
3045
  return out;
2544
3046
  }
2545
3047
  for (const agentId of entries) {
2546
- const rulesFile = join5(agentsDir, agentId, "rules.json");
2547
- if (!existsSync6(rulesFile)) continue;
3048
+ const rulesFile = join6(agentsDir, agentId, "rules.json");
3049
+ if (!existsSync7(rulesFile)) continue;
2548
3050
  let data;
2549
3051
  try {
2550
- data = JSON.parse(readFileSync5(rulesFile, "utf-8"));
3052
+ data = JSON.parse(readFileSync6(rulesFile, "utf-8"));
2551
3053
  } catch {
2552
3054
  continue;
2553
3055
  }
@@ -3155,12 +3657,12 @@ function addCustomSkill(skillId, category, keywords, content) {
3155
3657
  );
3156
3658
  }
3157
3659
  const runtime = loadRuntimeConfig();
3158
- const skillsDir = join5(getRuntimeConfigDir(), "agent_moss_skills");
3660
+ const skillsDir = join6(getRuntimeConfigDir(), "agent_moss_skills");
3159
3661
  try {
3160
- mkdirSync(skillsDir, { recursive: true });
3662
+ mkdirSync2(skillsDir, { recursive: true });
3161
3663
  } catch {
3162
3664
  }
3163
- writeFileSync(join5(skillsDir, `${skillId}.md`), content, "utf-8");
3665
+ writeFileSync(join6(skillsDir, `${skillId}.md`), content, "utf-8");
3164
3666
  const l3 = runtime.L3_skills || (runtime.L3_skills = {});
3165
3667
  const cat = l3[category] || (l3[category] = { category_enabled: true, category_desc: "", skills: [] });
3166
3668
  (cat.skills || (cat.skills = [])).push({ id: skillId, keywords, enabled: true, builtin: false });
@@ -3175,9 +3677,9 @@ function deleteCustomSkill(skillId, category) {
3175
3677
  if (idx >= 0) {
3176
3678
  if (skills[idx].builtin) return null;
3177
3679
  skills.splice(idx, 1);
3178
- const skillPath = join5(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
3680
+ const skillPath = join6(getRuntimeConfigDir(), "agent_moss_skills", `${skillId}.md`);
3179
3681
  try {
3180
- if (existsSync6(skillPath)) unlinkSync(skillPath);
3682
+ if (existsSync7(skillPath)) unlinkSync(skillPath);
3181
3683
  } catch {
3182
3684
  }
3183
3685
  saveRuntimeConfig(runtime);
@@ -3381,10 +3883,10 @@ var init_runtime_config = __esm({
3381
3883
  init_rules_loader();
3382
3884
  init_runtime_config_bridge();
3383
3885
  init_rules_mode();
3384
- __dirname4 = dirname3(fileURLToPath4(import.meta.url));
3886
+ __dirname4 = dirname4(fileURLToPath4(import.meta.url));
3385
3887
  ENV_RUNTIME_CONFIG_PATH = "AGENT_MOSS_RUNTIME_CONFIG_PATH";
3386
3888
  RUNTIME_CONFIG_DIR = runtimeDir();
3387
- RUNTIME_CONFIG_PATH = join5(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
3889
+ RUNTIME_CONFIG_PATH = join6(RUNTIME_CONFIG_DIR, "agent_moss_runtime.json");
3388
3890
  LAYER_DISABLE_ENV = {
3389
3891
  L1_heuristic: "AGENT_MOSS_DISABLE_HEURISTIC",
3390
3892
  L2_logic_rules: "AGENT_MOSS_DISABLE_LOGIC_RULES",
@@ -3412,10 +3914,10 @@ var init_runtime_config = __esm({
3412
3914
  intent_consistency: ["intent_keywords", "reason"]
3413
3915
  };
3414
3916
  hasRuntimeConfigCache = /* @__PURE__ */ new Map();
3415
- _AGENTS_DIR_PROD = join5(__dirname4, "agents");
3416
- _AGENTS_DIR_DEV = join5(__dirname4, "..", "..", "..", "agent_moss", "agents");
3417
- _SCENARIOS_DIR_PROD = join5(__dirname4, "scenarios");
3418
- _SCENARIOS_DIR_DEV = join5(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
3917
+ _AGENTS_DIR_PROD = join6(__dirname4, "agents");
3918
+ _AGENTS_DIR_DEV = join6(__dirname4, "..", "..", "..", "agent_moss", "agents");
3919
+ _SCENARIOS_DIR_PROD = join6(__dirname4, "scenarios");
3920
+ _SCENARIOS_DIR_DEV = join6(__dirname4, "..", "..", "..", "agent_moss", "scenarios");
3419
3921
  _effectiveCache = /* @__PURE__ */ new WeakMap();
3420
3922
  SKILL_ID_RE = /^[A-Za-z0-9_-]+$/;
3421
3923
  registerRuntimeConfigBridge({
@@ -3753,18 +4255,23 @@ var init_config = __esm({
3753
4255
  });
3754
4256
 
3755
4257
  // src/agents/index.ts
3756
- import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
3757
- import { resolve as resolve4, dirname as dirname4, join as join6 } from "path";
4258
+ import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
4259
+ import { resolve as resolve5, dirname as dirname5, join as join7 } from "path";
3758
4260
  import { fileURLToPath as fileURLToPath6 } from "url";
3759
4261
  function resolveAgentsDir() {
3760
4262
  const root = getRulesRoot();
3761
- if (root) return join6(root, "agents");
3762
- if (existsSync7(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
3763
- if (existsSync7(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
4263
+ if (root) return join7(root, "agents");
4264
+ if (existsSync8(AGENTS_DIR_PROD)) return AGENTS_DIR_PROD;
4265
+ if (existsSync8(AGENTS_DIR_DEV_FALLBACK)) return AGENTS_DIR_DEV_FALLBACK;
3764
4266
  return AGENTS_DIR_PROD;
3765
4267
  }
3766
4268
  function getAgentExtensions(agentId) {
3767
4269
  if (!agentId) return null;
4270
+ if (!isSafeId(agentId)) {
4271
+ console.warn(`[AgentMoss agents] \u975E\u6CD5 agent_id \u5DF2\u5FFD\u7565\uFF08\u542B\u8DEF\u5F84\u5B57\u7B26\u6216\u8D85\u957F\uFF09: ${agentId.slice(0, 120)}`);
4272
+ return null;
4273
+ }
4274
+ if (cache.size >= CACHE_MAX) cache.clear();
3768
4275
  const fp = getRuleSourceFingerprint();
3769
4276
  const cached2 = cache.get(agentId);
3770
4277
  if (cached2 && cached2.fp === fp) return cached2.ext;
@@ -3777,13 +4284,13 @@ function getAgentExtensions(agentId) {
3777
4284
  }
3778
4285
  if (ext === null) {
3779
4286
  const agentsDir = resolveAgentsDir();
3780
- const rulesFile = resolve4(agentsDir, agentId, "rules.json");
3781
- if (!existsSync7(rulesFile)) {
4287
+ const rulesFile = resolve5(agentsDir, agentId, "rules.json");
4288
+ if (!existsSync8(rulesFile)) {
3782
4289
  cache.set(agentId, { fp, ext: null });
3783
4290
  return null;
3784
4291
  }
3785
4292
  try {
3786
- const raw = readFileSync6(rulesFile, "utf-8");
4293
+ const raw = readFileSync7(rulesFile, "utf-8");
3787
4294
  ext = JSON.parse(raw);
3788
4295
  if (!ext.agent_id) {
3789
4296
  console.warn(`[AgentMoss agents] rules.json \u7F3A\u5C11 agent_id \u5B57\u6BB5: ${rulesFile}`);
@@ -3817,7 +4324,7 @@ function mergeAgentExtensions(...exts) {
3817
4324
  extra_skills: nonNull.flatMap((e) => e.extra_skills || [])
3818
4325
  };
3819
4326
  }
3820
- var __filename4, __dirname5, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache;
4327
+ var __filename4, __dirname5, AGENTS_DIR_PROD, AGENTS_DIR_DEV_FALLBACK, cache, CACHE_MAX;
3821
4328
  var init_agents = __esm({
3822
4329
  "src/agents/index.ts"() {
3823
4330
  "use strict";
@@ -3825,26 +4332,32 @@ var init_agents = __esm({
3825
4332
  init_paths();
3826
4333
  init_runtime_config();
3827
4334
  __filename4 = fileURLToPath6(import.meta.url);
3828
- __dirname5 = dirname4(__filename4);
3829
- AGENTS_DIR_PROD = resolve4(__dirname5, "agents");
3830
- AGENTS_DIR_DEV_FALLBACK = resolve4(__dirname5, "../../../agent_moss/agents");
4335
+ __dirname5 = dirname5(__filename4);
4336
+ AGENTS_DIR_PROD = resolve5(__dirname5, "agents");
4337
+ AGENTS_DIR_DEV_FALLBACK = resolve5(__dirname5, "../../../agent_moss/agents");
3831
4338
  cache = /* @__PURE__ */ new Map();
4339
+ CACHE_MAX = 512;
3832
4340
  }
3833
4341
  });
3834
4342
 
3835
4343
  // src/scenarios/index.ts
3836
- import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
3837
- import { resolve as resolve5, dirname as dirname5, join as join7 } from "path";
4344
+ import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
4345
+ import { resolve as resolve6, dirname as dirname6, join as join8 } from "path";
3838
4346
  import { fileURLToPath as fileURLToPath7 } from "url";
3839
4347
  function resolveScenariosDir() {
3840
4348
  const root = getRulesRoot();
3841
- if (root) return join7(root, "scenarios");
3842
- if (existsSync8(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
3843
- if (existsSync8(SCENARIOS_DIR_DEV_FALLBACK)) return SCENARIOS_DIR_DEV_FALLBACK;
4349
+ if (root) return join8(root, "scenarios");
4350
+ if (existsSync9(SCENARIOS_DIR_PROD)) return SCENARIOS_DIR_PROD;
4351
+ if (existsSync9(SCENARIOS_DIR_DEV_FALLBACK)) return SCENARIOS_DIR_DEV_FALLBACK;
3844
4352
  return SCENARIOS_DIR_PROD;
3845
4353
  }
3846
4354
  function getScenarioExtensions(scenarioId) {
3847
4355
  if (!scenarioId) return null;
4356
+ if (!isSafeId(scenarioId)) {
4357
+ console.warn(`[AgentMoss scenarios] \u975E\u6CD5 scenario_id \u5DF2\u5FFD\u7565\uFF08\u542B\u8DEF\u5F84\u5B57\u7B26\u6216\u8D85\u957F\uFF09: ${scenarioId.slice(0, 120)}`);
4358
+ return null;
4359
+ }
4360
+ if (cache2.size >= CACHE_MAX2) cache2.clear();
3848
4361
  const fp = getRuleSourceFingerprint();
3849
4362
  const cached2 = cache2.get(scenarioId);
3850
4363
  if (cached2 && cached2.fp === fp) return cached2.ext;
@@ -3863,19 +4376,19 @@ function getScenarioExtensions(scenarioId) {
3863
4376
  }
3864
4377
  }
3865
4378
  if (ext === null) {
3866
- const builtinFile = resolve5(resolveScenariosDir(), scenarioId, "rules.json");
4379
+ const builtinFile = resolve6(resolveScenariosDir(), scenarioId, "rules.json");
3867
4380
  const rulesFile = overrideHit ?? builtinFile;
3868
4381
  if (overrideHit) {
3869
4382
  console.warn(
3870
4383
  `[AgentMoss] \u26A0\uFE0F \u573A\u666F ${scenarioId} \u89C4\u5219\u88AB\u8986\u76D6\u76EE\u5F55\u8986\u76D6: ${overrideHit}\uFF08\u5185\u7F6E\u9ED8\u8BA4 ${builtinFile} \u88AB\u5FFD\u7565\uFF09\u3002\u8BE5\u6587\u4EF6\u7531\u90E8\u7F72\u4FA7\u81EA\u884C\u7EF4\u62A4\uFF0C\u5185\u7F6E\u573A\u666F\u89C4\u5219\u66F4\u65B0\u4E0D\u4F1A\u53CD\u6620\u5230\u5B83\u4E0A\u9762\u3002`
3871
4384
  );
3872
4385
  }
3873
- if (!existsSync8(rulesFile)) {
4386
+ if (!existsSync9(rulesFile)) {
3874
4387
  cache2.set(scenarioId, { fp, ext: null });
3875
4388
  return null;
3876
4389
  }
3877
4390
  try {
3878
- const raw = readFileSync7(rulesFile, "utf-8");
4391
+ const raw = readFileSync8(rulesFile, "utf-8");
3879
4392
  const data = JSON.parse(raw);
3880
4393
  const id = data.scenario_id || data.agent_id || "";
3881
4394
  if (!id) {
@@ -3914,7 +4427,7 @@ function getScenarioExtensionsMulti(scenarioSpec) {
3914
4427
  if (nonNull.length === 0) return null;
3915
4428
  return mergeAgentExtensions(...nonNull);
3916
4429
  }
3917
- var __filename5, __dirname6, SCENARIOS_DIR_PROD, SCENARIOS_DIR_DEV_FALLBACK, cache2;
4430
+ var __filename5, __dirname6, SCENARIOS_DIR_PROD, SCENARIOS_DIR_DEV_FALLBACK, cache2, CACHE_MAX2;
3918
4431
  var init_scenarios = __esm({
3919
4432
  "src/scenarios/index.ts"() {
3920
4433
  "use strict";
@@ -3924,10 +4437,11 @@ var init_scenarios = __esm({
3924
4437
  init_runtime_config();
3925
4438
  init_rules_loader();
3926
4439
  __filename5 = fileURLToPath7(import.meta.url);
3927
- __dirname6 = dirname5(__filename5);
3928
- SCENARIOS_DIR_PROD = resolve5(__dirname6, "scenarios");
3929
- SCENARIOS_DIR_DEV_FALLBACK = resolve5(__dirname6, "../../../agent_moss/scenarios");
4440
+ __dirname6 = dirname6(__filename5);
4441
+ SCENARIOS_DIR_PROD = resolve6(__dirname6, "scenarios");
4442
+ SCENARIOS_DIR_DEV_FALLBACK = resolve6(__dirname6, "../../../agent_moss/scenarios");
3930
4443
  cache2 = /* @__PURE__ */ new Map();
4444
+ CACHE_MAX2 = 512;
3931
4445
  }
3932
4446
  });
3933
4447
 
@@ -4807,8 +5321,8 @@ var init_heuristic = __esm({
4807
5321
  });
4808
5322
 
4809
5323
  // src/engine/template-loader.ts
4810
- import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
4811
- import { resolve as resolve6, dirname as dirname6 } from "path";
5324
+ import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
5325
+ import { resolve as resolve7, dirname as dirname7 } from "path";
4812
5326
  import { fileURLToPath as fileURLToPath8 } from "url";
4813
5327
  function loadTemplates() {
4814
5328
  if (cachedTemplates) return cachedTemplates;
@@ -4820,10 +5334,10 @@ function loadTemplates() {
4820
5334
  return cachedTemplates;
4821
5335
  }
4822
5336
  for (const file of TEMPLATE_FILES) {
4823
- const filePath = resolve6(dir, file);
4824
- if (existsSync9(filePath)) {
5337
+ const filePath = resolve7(dir, file);
5338
+ if (existsSync10(filePath)) {
4825
5339
  const key = file.replace(".txt", "").replace("_template", "");
4826
- cachedTemplates[key] = readFileSync8(filePath, "utf-8");
5340
+ cachedTemplates[key] = readFileSync9(filePath, "utf-8");
4827
5341
  }
4828
5342
  }
4829
5343
  console.log(`[AgentMoss] Loaded ${Object.keys(cachedTemplates).length} templates from ${dir}`);
@@ -4836,15 +5350,15 @@ var init_template_loader = __esm({
4836
5350
  init_esm_shims();
4837
5351
  init_asset_dir();
4838
5352
  __filename6 = fileURLToPath8(import.meta.url);
4839
- __dirname7 = dirname6(__filename6);
5353
+ __dirname7 = dirname7(__filename6);
4840
5354
  TEMPLATE_FILES = ["prompt1_template.txt", "prompt2_template.txt", "security_judge_template.txt"];
4841
5355
  cachedTemplates = null;
4842
5356
  }
4843
5357
  });
4844
5358
 
4845
5359
  // src/engine/script-analyzer.ts
4846
- import { readFileSync as readFileSync9 } from "fs";
4847
- import { resolve as resolve7, join as join8 } from "path";
5360
+ import { readFileSync as readFileSync10 } from "fs";
5361
+ import { resolve as resolve8, join as join9 } from "path";
4848
5362
  function emptyResult() {
4849
5363
  return { scriptPath: "", scriptContent: "", matchedKeywords: [], lineCount: 0, isSuspicious: false, error: "", riskLevel: "", riskReason: "" };
4850
5364
  }
@@ -4897,8 +5411,8 @@ function assessKeywordCombinationRisk(matchedKeywords, scriptContent) {
4897
5411
  }
4898
5412
  function readScriptContent(scriptPath, maxLines = 500) {
4899
5413
  try {
4900
- const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ? join8(process.env.HOME || "/home/user", scriptPath.slice(1)) : resolve7(scriptPath);
4901
- const raw = readFileSync9(expandedPath, "utf-8");
5414
+ const expandedPath = scriptPath === "~" || scriptPath.startsWith("~/") ? join9(process.env.HOME || "/home/user", scriptPath.slice(1)) : resolve8(scriptPath);
5415
+ const raw = readFileSync10(expandedPath, "utf-8");
4902
5416
  const lines = raw.split("\n").slice(0, maxLines);
4903
5417
  return { content: lines.join("\n"), lineCount: lines.length, error: "" };
4904
5418
  } catch (e) {
@@ -5026,14 +5540,14 @@ function isSysArgv(node) {
5026
5540
  const prop = node.getChild("PropertyName");
5027
5541
  return prop !== null && srcSlice(prop) === "argv";
5028
5542
  }
5029
- function evalExpr(node, env) {
5543
+ function evalExpr(node, env2) {
5030
5544
  if (!node) return null;
5031
5545
  const name = node.name;
5032
5546
  if (name === "String") return stringValue(srcSlice(node));
5033
5547
  if (name === "Number") return srcSlice(node);
5034
5548
  if (name === "VariableName") {
5035
5549
  const id = srcSlice(node);
5036
- return id in env.vars ? env.vars[id] : null;
5550
+ return id in env2.vars ? env2.vars[id] : null;
5037
5551
  }
5038
5552
  if (name === "MemberExpression") {
5039
5553
  const bracket = node.getChild("[");
@@ -5043,13 +5557,13 @@ function evalExpr(node, env) {
5043
5557
  const idxNode = bracket.nextSibling;
5044
5558
  if (idxNode && idxNode.name === "Number") {
5045
5559
  const idx = parseInt(srcSlice(idxNode), 10);
5046
- if (idx >= 1 && idx <= env.argv.length) return env.argv[idx - 1];
5560
+ if (idx >= 1 && idx <= env2.argv.length) return env2.argv[idx - 1];
5047
5561
  return null;
5048
5562
  }
5049
5563
  }
5050
5564
  return null;
5051
5565
  }
5052
- if (isSysArgv(node)) return env.argv[0] ?? null;
5566
+ if (isSysArgv(node)) return env2.argv[0] ?? null;
5053
5567
  return null;
5054
5568
  }
5055
5569
  if (name === "CallExpression") {
@@ -5057,7 +5571,7 @@ function evalExpr(node, env) {
5057
5571
  if (func && func.name === "VariableName" && srcSlice(func) === "len") {
5058
5572
  const argList = node.getChild("ArgList");
5059
5573
  const inner = argList ? firstNamedChild(argList) : null;
5060
- if (inner && isSysArgv(inner)) return String(env.argv.length + 1);
5574
+ if (inner && isSysArgv(inner)) return String(env2.argv.length + 1);
5061
5575
  }
5062
5576
  return null;
5063
5577
  }
@@ -5069,16 +5583,16 @@ function evalExpr(node, env) {
5069
5583
  const condNode = ifKw.nextSibling;
5070
5584
  const orelseNode = elseKw.nextSibling;
5071
5585
  if (!thenNode || !condNode || !orelseNode) return null;
5072
- const cond = evalCondition(condNode, env);
5073
- if (cond === true) return evalExpr(thenNode, env);
5074
- if (cond === false) return evalExpr(orelseNode, env);
5586
+ const cond = evalCondition(condNode, env2);
5587
+ if (cond === true) return evalExpr(thenNode, env2);
5588
+ if (cond === false) return evalExpr(orelseNode, env2);
5075
5589
  return null;
5076
5590
  }
5077
5591
  if (name === "BinaryExpression") {
5078
5592
  const op = findOp(node);
5079
5593
  if (op === "+") {
5080
- const left = evalExpr(node.firstChild, env);
5081
- const right = evalExpr(lastNamedChild(node), env);
5594
+ const left = evalExpr(node.firstChild, env2);
5595
+ const right = evalExpr(lastNamedChild(node), env2);
5082
5596
  if (left !== null && right !== null) return left + right;
5083
5597
  }
5084
5598
  return null;
@@ -5111,7 +5625,7 @@ function findOp(node) {
5111
5625
  }
5112
5626
  return null;
5113
5627
  }
5114
- function evalCondition(node, env) {
5628
+ function evalCondition(node, env2) {
5115
5629
  if (!node) return null;
5116
5630
  const name = node.name;
5117
5631
  if (name === "Boolean") {
@@ -5122,7 +5636,7 @@ function evalCondition(node, env) {
5122
5636
  const op = findOp(node);
5123
5637
  if (op === "and" || op === "or") {
5124
5638
  const parts2 = namedChildren(node);
5125
- const vals = parts2.map((p) => evalCondition(p, env));
5639
+ const vals = parts2.map((p) => evalCondition(p, env2));
5126
5640
  if (op === "and") {
5127
5641
  if (vals.some((v) => v === false)) return false;
5128
5642
  if (vals.every((v) => v === true)) return true;
@@ -5136,13 +5650,13 @@ function evalCondition(node, env) {
5136
5650
  if (parts.length !== 3) return null;
5137
5651
  const [left, opNode, right] = parts;
5138
5652
  const opText = srcSlice(opNode).trim();
5139
- return evalCompare(left, opText, right, env);
5653
+ return evalCompare(left, opText, right, env2);
5140
5654
  }
5141
5655
  if (name === "UnaryExpression") {
5142
5656
  const kw = node.getChild("not");
5143
5657
  if (kw) {
5144
5658
  const operand = kw.nextSibling;
5145
- const v = evalCondition(operand, env);
5659
+ const v = evalCondition(operand, env2);
5146
5660
  return v === null ? null : !v;
5147
5661
  }
5148
5662
  return null;
@@ -5163,39 +5677,39 @@ function namedChildren(node) {
5163
5677
  }
5164
5678
  return out;
5165
5679
  }
5166
- function evalCompare(left, opText, right, env) {
5680
+ function evalCompare(left, opText, right, env2) {
5167
5681
  if (opText === "==" && left.name === "VariableName" && srcSlice(left) === "__name__") {
5168
- const rv = evalExpr(right, env);
5682
+ const rv = evalExpr(right, env2);
5169
5683
  if (rv !== null) return rv === "__main__";
5170
5684
  return null;
5171
5685
  }
5172
5686
  if (opText === "==" || opText === "!=") {
5173
- const lv = evalExpr(left, env);
5174
- const rv = evalExpr(right, env);
5687
+ const lv = evalExpr(left, env2);
5688
+ const rv = evalExpr(right, env2);
5175
5689
  if (lv !== null && rv !== null) {
5176
5690
  return opText === "==" ? lv === rv : lv !== rv;
5177
5691
  }
5178
5692
  return null;
5179
5693
  }
5180
5694
  if (opText === "in") {
5181
- const lv = evalExpr(left, env);
5695
+ const lv = evalExpr(left, env2);
5182
5696
  if (lv === null) return null;
5183
5697
  const rightName = right.name;
5184
5698
  if (rightName === "TupleExpression" || rightName === "ArrayExpression" || rightName === "SetExpression" || rightName === "ParenthesizedExpression") {
5185
5699
  const container = rightName === "ParenthesizedExpression" && right.firstChild && right.firstChild.name === "TupleExpression" ? right.firstChild : right;
5186
5700
  for (const elt of namedChildren(container)) {
5187
- const ev = evalExpr(elt, env);
5701
+ const ev = evalExpr(elt, env2);
5188
5702
  if (ev !== null && ev === lv) return true;
5189
5703
  }
5190
5704
  return false;
5191
5705
  }
5192
- const rv = evalExpr(right, env);
5706
+ const rv = evalExpr(right, env2);
5193
5707
  if (rv !== null) return rv.includes(lv);
5194
5708
  return null;
5195
5709
  }
5196
5710
  if (opText === ">" || opText === ">=" || opText === "<" || opText === "<=") {
5197
- const lv = evalExpr(left, env);
5198
- const rv = evalExpr(right, env);
5711
+ const lv = evalExpr(left, env2);
5712
+ const rv = evalExpr(right, env2);
5199
5713
  if (lv === null || rv === null) return null;
5200
5714
  const ln = parseInt(lv, 10);
5201
5715
  const rn = parseInt(rv, 10);
@@ -5207,71 +5721,71 @@ function evalCompare(left, opText, right, env) {
5207
5721
  }
5208
5722
  return null;
5209
5723
  }
5210
- function processStmts(stmts, env, lineStarts, lines, branches) {
5211
- for (const s of stmts) processStmt(s, env, lineStarts, lines, branches);
5724
+ function processStmts(stmts, env2, lineStarts, lines, branches) {
5725
+ for (const s of stmts) processStmt(s, env2, lineStarts, lines, branches);
5212
5726
  }
5213
- function invalidateEnvOnCalls(node, env) {
5727
+ function invalidateEnvOnCalls(node, env2) {
5214
5728
  const cursor = node.cursor();
5215
5729
  cursor.iterate((n) => {
5216
5730
  const nd = n.node;
5217
5731
  if (nd.name !== "CallExpression") return true;
5218
5732
  const func = nd.firstChild;
5219
5733
  if (func && func.name === "VariableName" && !PURE_BUILTINS.has(srcSlice(func))) {
5220
- for (const k of Object.keys(env.vars)) env.vars[k] = null;
5734
+ for (const k of Object.keys(env2.vars)) env2.vars[k] = null;
5221
5735
  return false;
5222
5736
  }
5223
5737
  return true;
5224
5738
  });
5225
5739
  }
5226
- function processStmt(stmt, env, lineStarts, lines, branches) {
5740
+ function processStmt(stmt, env2, lineStarts, lines, branches) {
5227
5741
  const name = stmt.name;
5228
5742
  if (name === "AssignStatement") {
5229
5743
  const target = stmt.firstChild;
5230
5744
  if (target && target.name === "VariableName") {
5231
5745
  const valNode = target.nextSibling ? target.nextSibling.nextSibling : null;
5232
- invalidateEnvOnCalls(stmt, env);
5233
- const val = evalExpr(valNode, env);
5234
- env.vars[srcSlice(target)] = val;
5746
+ invalidateEnvOnCalls(stmt, env2);
5747
+ const val = evalExpr(valNode, env2);
5748
+ env2.vars[srcSlice(target)] = val;
5235
5749
  }
5236
5750
  markNodeLines(stmt, lines, lineStarts);
5237
5751
  return;
5238
5752
  }
5239
5753
  if (name === "UpdateStatement") {
5240
5754
  const target = stmt.firstChild;
5241
- if (target && target.name === "VariableName") env.vars[srcSlice(target)] = null;
5242
- invalidateEnvOnCalls(stmt, env);
5755
+ if (target && target.name === "VariableName") env2.vars[srcSlice(target)] = null;
5756
+ invalidateEnvOnCalls(stmt, env2);
5243
5757
  markNodeLines(stmt, lines, lineStarts);
5244
5758
  return;
5245
5759
  }
5246
5760
  if (name === "IfStatement") {
5247
- processIf(stmt, env, lineStarts, lines, branches);
5761
+ processIf(stmt, env2, lineStarts, lines, branches);
5248
5762
  return;
5249
5763
  }
5250
5764
  if (name === "ForStatement" || name === "WhileStatement") {
5251
5765
  markNodeLines(stmt, lines, lineStarts);
5252
5766
  for (const body of stmt.getChildren("Body")) {
5253
- processStmts(bodyStmts(body), { argv: env.argv, vars: { ...env.vars } }, lineStarts, lines, branches);
5767
+ processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
5254
5768
  }
5255
5769
  return;
5256
5770
  }
5257
5771
  if (name === "TryStatement") {
5258
5772
  markNodeLines(stmt, lines, lineStarts);
5259
5773
  for (const body of stmt.getChildren("Body")) {
5260
- processStmts(bodyStmts(body), { argv: env.argv, vars: { ...env.vars } }, lineStarts, lines, branches);
5774
+ processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
5261
5775
  }
5262
5776
  return;
5263
5777
  }
5264
5778
  if (name === "WithStatement") {
5265
5779
  markNodeLines(stmt, lines, lineStarts);
5266
5780
  const body = stmt.getChild("Body");
5267
- if (body) processStmts(bodyStmts(body), { argv: env.argv, vars: { ...env.vars } }, lineStarts, lines, branches);
5781
+ if (body) processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
5268
5782
  return;
5269
5783
  }
5270
5784
  if (name === "MatchStatement") {
5271
5785
  markNodeLines(stmt, lines, lineStarts);
5272
5786
  for (const clause of stmt.getChildren("MatchClause")) {
5273
5787
  const body = clause.getChild("Body");
5274
- if (body) processStmts(bodyStmts(body), { argv: env.argv, vars: { ...env.vars } }, lineStarts, lines, branches);
5788
+ if (body) processStmts(bodyStmts(body), { argv: env2.argv, vars: { ...env2.vars } }, lineStarts, lines, branches);
5275
5789
  }
5276
5790
  return;
5277
5791
  }
@@ -5280,7 +5794,7 @@ function processStmt(stmt, env, lineStarts, lines, branches) {
5280
5794
  return;
5281
5795
  }
5282
5796
  if (name === "ExpressionStatement") {
5283
- invalidateEnvOnCalls(stmt, env);
5797
+ invalidateEnvOnCalls(stmt, env2);
5284
5798
  }
5285
5799
  markNodeLines(stmt, lines, lineStarts);
5286
5800
  }
@@ -5300,7 +5814,7 @@ function bodyStmts(body) {
5300
5814
  }
5301
5815
  return out;
5302
5816
  }
5303
- function processIf(stmt, env, lineStarts, lines, branches) {
5817
+ function processIf(stmt, env2, lineStarts, lines, branches) {
5304
5818
  const sections = [];
5305
5819
  let c = stmt.firstChild;
5306
5820
  while (c) {
@@ -5315,17 +5829,17 @@ function processIf(stmt, env, lineStarts, lines, branches) {
5315
5829
  }
5316
5830
  c = c.nextSibling;
5317
5831
  }
5318
- processIfSections(stmt, sections, 0, env, lineStarts, lines, branches);
5832
+ processIfSections(stmt, sections, 0, env2, lineStarts, lines, branches);
5319
5833
  }
5320
- function processIfSections(_stmt, sections, idx, env, lineStarts, lines, branches) {
5834
+ function processIfSections(_stmt, sections, idx, env2, lineStarts, lines, branches) {
5321
5835
  if (idx >= sections.length) return;
5322
5836
  const s = sections[idx];
5323
5837
  if (s.cond) markNodeLines(s.cond, lines, lineStarts);
5324
5838
  if (s.isElse) {
5325
- if (s.body) processStmts(bodyStmts(s.body), env, lineStarts, lines, branches);
5839
+ if (s.body) processStmts(bodyStmts(s.body), env2, lineStarts, lines, branches);
5326
5840
  return;
5327
5841
  }
5328
- const cond = s.cond ? evalCondition(s.cond, env) : null;
5842
+ const cond = s.cond ? evalCondition(s.cond, env2) : null;
5329
5843
  branches.push({
5330
5844
  kind: "if",
5331
5845
  label: s.cond ? srcSlice(s.cond) : "",
@@ -5333,18 +5847,18 @@ function processIfSections(_stmt, sections, idx, env, lineStarts, lines, branche
5333
5847
  reachable: cond !== false
5334
5848
  });
5335
5849
  if (cond === true) {
5336
- if (s.body) processStmts(bodyStmts(s.body), env, lineStarts, lines, branches);
5850
+ if (s.body) processStmts(bodyStmts(s.body), env2, lineStarts, lines, branches);
5337
5851
  return;
5338
5852
  }
5339
5853
  if (cond === false) {
5340
- processIfSections(_stmt, sections, idx + 1, env, lineStarts, lines, branches);
5854
+ processIfSections(_stmt, sections, idx + 1, env2, lineStarts, lines, branches);
5341
5855
  return;
5342
5856
  }
5343
- if (s.body) processStmts(bodyStmts(s.body), env, lineStarts, lines, branches);
5857
+ if (s.body) processStmts(bodyStmts(s.body), env2, lineStarts, lines, branches);
5344
5858
  for (let j = idx + 1; j < sections.length; j++) {
5345
5859
  const sj = sections[j];
5346
5860
  if (sj.cond) markNodeLines(sj.cond, lines, lineStarts);
5347
- if (sj.body) processStmts(bodyStmts(sj.body), env, lineStarts, lines, branches);
5861
+ if (sj.body) processStmts(bodyStmts(sj.body), env2, lineStarts, lines, branches);
5348
5862
  }
5349
5863
  }
5350
5864
  function parseShellString(s) {
@@ -5520,7 +6034,7 @@ var init_python_backend = __esm({
5520
6034
  SOURCE = c;
5521
6035
  const sourceLines = c.split("\n");
5522
6036
  const lineStarts = buildLineStarts(c);
5523
- const env = makeEnv(entryArgs || []);
6037
+ const env2 = makeEnv(entryArgs || []);
5524
6038
  const lines = /* @__PURE__ */ new Set();
5525
6039
  const branches = [];
5526
6040
  if (sourceLines.length > MAX_LINES) {
@@ -5547,7 +6061,7 @@ var init_python_backend = __esm({
5547
6061
  };
5548
6062
  }
5549
6063
  const stmts = bodyStmts(tree);
5550
- processStmts(stmts, env, lineStarts, lines, branches);
6064
+ processStmts(stmts, env2, lineStarts, lines, branches);
5551
6065
  const reachable = reconstruct(sourceLines, lines);
5552
6066
  let calls = extractCallsFromTree(tree, lines);
5553
6067
  if (calls.length === 0) calls = extractCallsRegex(c);
@@ -5775,7 +6289,7 @@ function tokenize(content) {
5775
6289
  }
5776
6290
  return stmts;
5777
6291
  }
5778
- function evalExpr2(text, env) {
6292
+ function evalExpr2(text, env2) {
5779
6293
  const isSingleQuoted = text.length >= 2 && text[0] === "'" && text[text.length - 1] === "'";
5780
6294
  if (text.length >= 2 && (text[0] === '"' || text[0] === "'") && text[text.length - 1] === text[0]) {
5781
6295
  text = text.slice(1, -1);
@@ -5791,19 +6305,19 @@ function evalExpr2(text, env) {
5791
6305
  const dm = inner.match(/^([A-Za-z_][A-Za-z0-9_]*|\d+):?-(.*)$/s);
5792
6306
  if (dm) {
5793
6307
  const key = /^\d+$/.test(dm[1]) ? `$${dm[1]}` : dm[1];
5794
- const bound = env.get(key);
6308
+ const bound = env2.get(key);
5795
6309
  if (bound && !(bound.kind === "concrete" && !bound.value)) {
5796
6310
  result.push(bound);
5797
6311
  } else {
5798
- result.push(evalExpr2(dm[2], env));
6312
+ result.push(evalExpr2(dm[2], env2));
5799
6313
  }
5800
6314
  continue;
5801
6315
  }
5802
6316
  if (/^\d+$/.test(inner)) {
5803
- const v = env.get(`$${inner}`);
6317
+ const v = env2.get(`$${inner}`);
5804
6318
  result.push(v || svalSym(`$${inner}`));
5805
6319
  } else {
5806
- const v = env.get(inner);
6320
+ const v = env2.get(inner);
5807
6321
  result.push(v || svalSym(inner));
5808
6322
  }
5809
6323
  continue;
@@ -5812,7 +6326,7 @@ function evalExpr2(text, env) {
5812
6326
  if (bare) {
5813
6327
  const varName = bare[1];
5814
6328
  const key = /^\d+$/.test(varName) ? `$${varName}` : varName;
5815
- const v = env.get(key);
6329
+ const v = env2.get(key);
5816
6330
  result.push(v || svalSym(p));
5817
6331
  continue;
5818
6332
  }
@@ -5822,13 +6336,13 @@ function evalExpr2(text, env) {
5822
6336
  return fold(svalExpr(result));
5823
6337
  }
5824
6338
  function initEnv(entryArgs) {
5825
- const env = /* @__PURE__ */ new Map();
6339
+ const env2 = /* @__PURE__ */ new Map();
5826
6340
  if (entryArgs && entryArgs.length) {
5827
- entryArgs.forEach((a, idx) => env.set(`$${idx + 1}`, svalConc(a)));
5828
- env.set("$@", svalConc(entryArgs.join(" ")));
5829
- env.set("$*", svalConc(entryArgs.join(" ")));
6341
+ entryArgs.forEach((a, idx) => env2.set(`$${idx + 1}`, svalConc(a)));
6342
+ env2.set("$@", svalConc(entryArgs.join(" ")));
6343
+ env2.set("$*", svalConc(entryArgs.join(" ")));
5830
6344
  }
5831
- return env;
6345
+ return env2;
5832
6346
  }
5833
6347
  function globToRegex(pattern) {
5834
6348
  let out = "^";
@@ -5886,12 +6400,12 @@ function evalNumericCompare(op, left, right) {
5886
6400
  return null;
5887
6401
  }
5888
6402
  }
5889
- function resolveVarSide(tok, env) {
6403
+ function resolveVarSide(tok, env2) {
5890
6404
  if (!tok.startsWith("$")) return tok;
5891
- const ev = evalExpr2(tok, env);
6405
+ const ev = evalExpr2(tok, env2);
5892
6406
  return ev.kind === "concrete" ? ev.value : null;
5893
6407
  }
5894
- function evalCondition2(cond, env) {
6408
+ function evalCondition2(cond, env2) {
5895
6409
  let inner = cond.trim().replace(/^\[\[?|\]\]?$/g, "").trim();
5896
6410
  if (/^-[A-Za-z]\s/.test(inner) || inner.startsWith("-d") || inner.startsWith("-f")) return null;
5897
6411
  const innerQ = inner.replace(/"/g, "").replace(/'/g, "");
@@ -5901,8 +6415,8 @@ function evalCondition2(cond, env) {
5901
6415
  if (!opm) continue;
5902
6416
  const left2 = innerQ.slice(0, opm.index).trim();
5903
6417
  const right2 = innerQ.slice(opm.index + opm[0].length).trim();
5904
- const lv2 = resolveVarSide(left2, env);
5905
- const rv2 = resolveVarSide(right2, env);
6418
+ const lv2 = resolveVarSide(left2, env2);
6419
+ const rv2 = resolveVarSide(right2, env2);
5906
6420
  if (lv2 === null || rv2 === null) return null;
5907
6421
  return evalNumericCompare(op2, lv2, rv2);
5908
6422
  }
@@ -5913,7 +6427,7 @@ function evalCondition2(cond, env) {
5913
6427
  let right = sm[3].trim();
5914
6428
  if (op === "=~") {
5915
6429
  if (right.startsWith("/") && right.endsWith("/")) right = right.slice(1, -1);
5916
- const lv2 = resolveVarSide(left, env);
6430
+ const lv2 = resolveVarSide(left, env2);
5917
6431
  if (lv2 === null) return null;
5918
6432
  try {
5919
6433
  return new RegExp(right).test(lv2);
@@ -5921,8 +6435,8 @@ function evalCondition2(cond, env) {
5921
6435
  return null;
5922
6436
  }
5923
6437
  }
5924
- const lv = resolveVarSide(left, env);
5925
- const rv = resolveVarSide(right, env);
6438
+ const lv = resolveVarSide(left, env2);
6439
+ const rv = resolveVarSide(right, env2);
5926
6440
  if (lv === null || rv === null) return null;
5927
6441
  if (op === "!=") return lv !== rv;
5928
6442
  return lv === rv;
@@ -5932,7 +6446,7 @@ function symbolicProcess(content, entryArgs) {
5932
6446
  const stmts = tokenize(content);
5933
6447
  if (!stmts.some((s) => s.kind === "case" || s.kind === "if" || s.kind === "assign")) return null;
5934
6448
  if (stmts.some((s) => !s.modelable)) return null;
5935
- const env = initEnv(entryArgs);
6449
+ const env2 = initEnv(entryArgs);
5936
6450
  let linesBudget = 0;
5937
6451
  let branchesBudget = 0;
5938
6452
  let modelable = false;
@@ -5942,9 +6456,9 @@ function symbolicProcess(content, entryArgs) {
5942
6456
  if (linesBudget > MAX_LINES2) return null;
5943
6457
  if (s.kind === "assign") {
5944
6458
  modelable = true;
5945
- if (env.size >= MAX_VARS) return null;
5946
- const v = evalExpr2(s.rhs, env);
5947
- env.set(s.name, fold(v));
6459
+ if (env2.size >= MAX_VARS) return null;
6460
+ const v = evalExpr2(s.rhs, env2);
6461
+ env2.set(s.name, fold(v));
5948
6462
  out.push(s.line);
5949
6463
  continue;
5950
6464
  }
@@ -5952,7 +6466,7 @@ function symbolicProcess(content, entryArgs) {
5952
6466
  modelable = true;
5953
6467
  branchesBudget += s.branches.length;
5954
6468
  if (branchesBudget > MAX_BRANCHES) return null;
5955
- const selVal = fold(evalExpr2(s.selector, env));
6469
+ const selVal = fold(evalExpr2(s.selector, env2));
5956
6470
  if (selVal.kind !== "concrete") return null;
5957
6471
  const concrete = selVal.value || "";
5958
6472
  let selected = null;
@@ -5979,7 +6493,7 @@ esac`);
5979
6493
  modelable = true;
5980
6494
  branchesBudget++;
5981
6495
  if (branchesBudget > MAX_BRANCHES) return null;
5982
- const truth = evalCondition2(s.cond, env);
6496
+ const truth = evalCondition2(s.cond, env2);
5983
6497
  if (truth === null) return null;
5984
6498
  const chosen = truth ? s.thenBody : s.elseBody;
5985
6499
  if (chosen === null) return null;
@@ -6512,9 +7026,9 @@ var init_tracker = __esm({
6512
7026
  });
6513
7027
 
6514
7028
  // src/engine/script-content-analyzer.ts
6515
- import { statSync as statSync4, existsSync as existsSync11 } from "fs";
6516
- import { resolve as resolve9, join as join10, isAbsolute as isAbsolute2 } from "path";
6517
- import { homedir as homedir3 } from "os";
7029
+ import { statSync as statSync4, existsSync as existsSync12 } from "fs";
7030
+ import { resolve as resolve10, join as join11, isAbsolute as isAbsolute3 } from "path";
7031
+ import { homedir as homedir4 } from "os";
6518
7032
  function emptyChain() {
6519
7033
  return {
6520
7034
  nodes: [],
@@ -6625,7 +7139,7 @@ function extractInlineScriptPath(actionDetail) {
6625
7139
  if (looksLikeScriptPath(tok)) {
6626
7140
  try {
6627
7141
  const resolved = resolvePath2(tok.replace(/^['"]|['"]$/g, ""));
6628
- if (existsSync11(resolved)) {
7142
+ if (existsSync12(resolved)) {
6629
7143
  const stat = statSync4(resolved);
6630
7144
  if (stat.isFile()) return resolved;
6631
7145
  }
@@ -6639,7 +7153,7 @@ function extractInlineScriptPath(actionDetail) {
6639
7153
  const p = m[1];
6640
7154
  try {
6641
7155
  const resolved = resolvePath2(p);
6642
- if (existsSync11(resolved)) {
7156
+ if (existsSync12(resolved)) {
6643
7157
  const stat = statSync4(resolved);
6644
7158
  if (stat.isFile()) return resolved;
6645
7159
  }
@@ -6655,7 +7169,7 @@ function extractInlineScriptPath(actionDetail) {
6655
7169
  let resolved;
6656
7170
  try {
6657
7171
  resolved = resolvePath2(p);
6658
- if (existsSync11(resolved)) {
7172
+ if (existsSync12(resolved)) {
6659
7173
  const stat = statSync4(resolved);
6660
7174
  if (stat.isFile()) return resolved;
6661
7175
  }
@@ -6696,9 +7210,9 @@ function shlexSplit(s) {
6696
7210
  function resolvePath2(p) {
6697
7211
  let expanded = p;
6698
7212
  if (expanded.startsWith("~/") || expanded === "~") {
6699
- expanded = join10(homedir3(), expanded.slice(1));
7213
+ expanded = join11(homedir4(), expanded.slice(1));
6700
7214
  }
6701
- return resolve9(expanded);
7215
+ return resolve10(expanded);
6702
7216
  }
6703
7217
  function scanExtensionPatterns(ext, content) {
6704
7218
  const pats = ext?.extra_command_patterns;
@@ -6819,8 +7333,8 @@ function analyzeScriptContent2(input, ext) {
6819
7333
  resolvedPath = inline;
6820
7334
  isInline = true;
6821
7335
  }
6822
- if (resolvedPath && !isAbsolute2(resolvedPath) && !resolvedPath.startsWith("~/")) {
6823
- resolvedPath = join10(input.cwd || process.cwd(), resolvedPath);
7336
+ if (resolvedPath && !isAbsolute3(resolvedPath) && !resolvedPath.startsWith("~/")) {
7337
+ resolvedPath = join11(input.cwd || process.cwd(), resolvedPath);
6824
7338
  }
6825
7339
  const l1Adapter = (content) => {
6826
7340
  const j = scanDangerousCommandPatterns("bash", content, input.osType);
@@ -6946,16 +7460,16 @@ __export(token_stats_exports, {
6946
7460
  recordTokenUsage: () => recordTokenUsage,
6947
7461
  resetTokenStats: () => resetTokenStats
6948
7462
  });
6949
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync2, existsSync as existsSync12, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
7463
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync2, existsSync as existsSync13, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2 } from "fs";
6950
7464
  function getStatsPath() {
6951
7465
  const envPath = process.env[ENV_STATS_PATH];
6952
7466
  if (envPath) return envPath;
6953
7467
  return getTokenStatsPath();
6954
7468
  }
6955
7469
  function loadAllRecords(statsPath) {
6956
- if (!existsSync12(statsPath)) return [];
7470
+ if (!existsSync13(statsPath)) return [];
6957
7471
  try {
6958
- const data = JSON.parse(readFileSync12(statsPath, "utf-8"));
7472
+ const data = JSON.parse(readFileSync13(statsPath, "utf-8"));
6959
7473
  if (Array.isArray(data)) return data;
6960
7474
  if (data && Array.isArray(data.records)) return data.records;
6961
7475
  return [];
@@ -6966,7 +7480,7 @@ function loadAllRecords(statsPath) {
6966
7480
  function saveRecords(statsPath, records) {
6967
7481
  try {
6968
7482
  const dir = statsPath.substring(0, statsPath.lastIndexOf("/"));
6969
- if (dir && !existsSync12(dir)) mkdirSync2(dir, { recursive: true });
7483
+ if (dir && !existsSync13(dir)) mkdirSync3(dir, { recursive: true });
6970
7484
  writeFileSync2(statsPath, JSON.stringify(records, null, 2), "utf-8");
6971
7485
  } catch (e) {
6972
7486
  }
@@ -7093,7 +7607,7 @@ function getTokenTrend(days = 0, startDate, endDate) {
7093
7607
  }
7094
7608
  function resetTokenStats() {
7095
7609
  const statsPath = getStatsPath();
7096
- if (existsSync12(statsPath)) {
7610
+ if (existsSync13(statsPath)) {
7097
7611
  try {
7098
7612
  unlinkSync2(statsPath);
7099
7613
  } catch {
@@ -7112,7 +7626,7 @@ var init_token_stats = __esm({
7112
7626
  });
7113
7627
 
7114
7628
  // src/engine/llm-analyzer.ts
7115
- import { appendFileSync } from "fs";
7629
+ import { openSync as openSync2, writeSync as writeSync2, closeSync as closeSync2, constants as fsConstants } from "fs";
7116
7630
  import OpenAI from "openai";
7117
7631
  async function runLLMAnalysis(input, previous) {
7118
7632
  if (!isLLMAvailable()) {
@@ -7128,7 +7642,15 @@ async function runLLMAnalysis(input, previous) {
7128
7642
  };
7129
7643
  }
7130
7644
  const globalConfig = getLLMConfig();
7131
- const config = input.llmConfig?.apiKey ? { ...globalConfig, ...input.llmConfig } : globalConfig;
7645
+ let perRequest = input.llmConfig;
7646
+ if (perRequest?.apiKey) {
7647
+ const [okUrl, why] = validateLlmBaseUrl(String(perRequest.baseUrl || ""));
7648
+ if (!okUrl) {
7649
+ console.warn("[AgentMoss] \u5FFD\u7565\u4E0D\u88AB\u5141\u8BB8\u7684 per-request llmConfig.baseUrl:", why);
7650
+ perRequest = { ...perRequest, baseUrl: void 0 };
7651
+ }
7652
+ }
7653
+ const config = perRequest?.apiKey ? { ...globalConfig, ...perRequest } : globalConfig;
7132
7654
  const rawDetail = input.aNext.actionDetail.replace(/^terminal-\w+:\s*command=([^,]+).*/, "$1").trim();
7133
7655
  const cacheKey = `${input.aNext.actionType}:${rawDetail || input.aNext.actionDetail}`;
7134
7656
  const cached2 = llmCache.get(cacheKey);
@@ -7157,6 +7679,10 @@ async function runLLMAnalysis(input, previous) {
7157
7679
  }
7158
7680
  function logPrompt(prompt, aNext, logPath) {
7159
7681
  if (!logPath) return;
7682
+ if (!pathWithin(logPath, logRoots())) {
7683
+ console.warn("[AgentMoss] \u5FFD\u7565\u8D8A\u754C\u7684 llm_log_path\uFF08\u4E0D\u5728\u5141\u8BB8\u6839\u76EE\u5F55\u5185\uFF09:", logPath);
7684
+ return;
7685
+ }
7160
7686
  try {
7161
7687
  const actionType = aNext.actionType || "unknown";
7162
7688
  const actionDetail = (aNext.actionDetail || "").slice(0, 100);
@@ -7164,7 +7690,14 @@ function logPrompt(prompt, aNext, logPath) {
7164
7690
  ${prompt}
7165
7691
  --- END PROMPT ---
7166
7692
  `;
7167
- appendFileSync(logPath, line, "utf-8");
7693
+ let flags = fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_APPEND;
7694
+ if (fsConstants.O_NOFOLLOW) flags |= fsConstants.O_NOFOLLOW;
7695
+ const fd = openSync2(logPath, flags, 384);
7696
+ try {
7697
+ writeSync2(fd, line);
7698
+ } finally {
7699
+ closeSync2(fd);
7700
+ }
7168
7701
  } catch (e) {
7169
7702
  console.warn("[AgentMoss] Failed to write LLM prompt to log:", e);
7170
7703
  }
@@ -7323,7 +7856,7 @@ async function analyzeWithLLM(prompt, config) {
7323
7856
  }
7324
7857
  if (attempt < maxRetries) {
7325
7858
  console.error(`[AgentMoss] LLM call failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${retryCfg.retryInterval}s:`, err.message);
7326
- await new Promise((resolve11) => setTimeout(resolve11, retryCfg.retryInterval * 1e3));
7859
+ await new Promise((resolve12) => setTimeout(resolve12, retryCfg.retryInterval * 1e3));
7327
7860
  } else {
7328
7861
  clearTimeout(totalTimeoutTimer);
7329
7862
  console.error("[AgentMoss] LLM call failed after all retries:", err.message);
@@ -7339,6 +7872,7 @@ var init_llm_analyzer = __esm({
7339
7872
  "src/engine/llm-analyzer.ts"() {
7340
7873
  "use strict";
7341
7874
  init_esm_shims();
7875
+ init_security_policy();
7342
7876
  init_config();
7343
7877
  init_skill_loader();
7344
7878
  init_template_loader();
@@ -7350,9 +7884,9 @@ var init_llm_analyzer = __esm({
7350
7884
  });
7351
7885
 
7352
7886
  // src/brain/rule-store.ts
7353
- import { existsSync as existsSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
7354
- import { join as join11 } from "path";
7355
- import { homedir as homedir4 } from "os";
7887
+ import { existsSync as existsSync14, readFileSync as readFileSync14, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "fs";
7888
+ import { join as join12 } from "path";
7889
+ import { homedir as homedir5 } from "os";
7356
7890
  function getRulesPath() {
7357
7891
  const envPath = process.env[ENV_RULES_PATH];
7358
7892
  if (envPath) return envPath;
@@ -7370,7 +7904,7 @@ var init_rule_store = __esm({
7370
7904
  "src/brain/rule-store.ts"() {
7371
7905
  "use strict";
7372
7906
  init_esm_shims();
7373
- DEFAULT_RULES_PATH = join11(homedir4(), ".agentmoss", "brain", "rules.json");
7907
+ DEFAULT_RULES_PATH = join12(homedir5(), ".agentmoss", "brain", "rules.json");
7374
7908
  ENV_RULES_PATH = "AGENT_MOSS_BRAIN_RULES_PATH";
7375
7909
  STATUS_DRAFT = "draft";
7376
7910
  STATUS_APPROVED = "approved";
@@ -7387,11 +7921,11 @@ var init_rule_store = __esm({
7387
7921
  this.data = this.load();
7388
7922
  }
7389
7923
  load() {
7390
- if (!existsSync13(this.path)) {
7924
+ if (!existsSync14(this.path)) {
7391
7925
  return { version: 1, rules: [], pattern_candidates: [] };
7392
7926
  }
7393
7927
  try {
7394
- const raw = JSON.parse(readFileSync13(this.path, "utf-8"));
7928
+ const raw = JSON.parse(readFileSync14(this.path, "utf-8"));
7395
7929
  return {
7396
7930
  version: raw.version || 1,
7397
7931
  rules: raw.rules || [],
@@ -7403,7 +7937,7 @@ var init_rule_store = __esm({
7403
7937
  }
7404
7938
  save() {
7405
7939
  const dir = this.path.substring(0, this.path.lastIndexOf("/"));
7406
- if (dir) mkdirSync3(dir, { recursive: true });
7940
+ if (dir) mkdirSync4(dir, { recursive: true });
7407
7941
  writeFileSync3(this.path, JSON.stringify(this.data, null, 2), "utf-8");
7408
7942
  }
7409
7943
  addRule(rule) {
@@ -7985,9 +8519,9 @@ var init_reviewer = __esm({
7985
8519
  });
7986
8520
 
7987
8521
  // src/brain/skill-manager.ts
7988
- import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync14, renameSync, writeFileSync as writeFileSync4 } from "fs";
7989
- import { homedir as homedir5 } from "os";
7990
- import { join as join12 } from "path";
8522
+ import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync, writeFileSync as writeFileSync4 } from "fs";
8523
+ import { homedir as homedir6 } from "os";
8524
+ import { join as join13 } from "path";
7991
8525
  import { randomUUID } from "crypto";
7992
8526
  function getMetaPath(override) {
7993
8527
  return override || process.env[ENV_SKILL_META_PATH] || DEFAULT_SKILL_META_PATH;
@@ -7997,14 +8531,14 @@ var init_skill_manager = __esm({
7997
8531
  "src/brain/skill-manager.ts"() {
7998
8532
  "use strict";
7999
8533
  init_esm_shims();
8000
- DEFAULT_SKILL_META_PATH = join12(homedir5(), ".agentmoss", "brain", "skill_meta.json");
8534
+ DEFAULT_SKILL_META_PATH = join13(homedir6(), ".agentmoss", "brain", "skill_meta.json");
8001
8535
  ENV_SKILL_META_PATH = "AGENT_MOSS_BRAIN_SKILL_META_PATH";
8002
8536
  BrainSkillManager = class {
8003
8537
  constructor(skillsDir, metaPath) {
8004
8538
  this.skillsDir = skillsDir;
8005
8539
  this.metaPath = getMetaPath(metaPath);
8006
8540
  try {
8007
- mkdirSync4(join12(this.metaPath, ".."), { recursive: true });
8541
+ mkdirSync5(join13(this.metaPath, ".."), { recursive: true });
8008
8542
  } catch {
8009
8543
  }
8010
8544
  this.load();
@@ -8014,9 +8548,9 @@ var init_skill_manager = __esm({
8014
8548
  skills = [];
8015
8549
  metaPath;
8016
8550
  load() {
8017
- if (!existsSync14(this.metaPath)) return;
8551
+ if (!existsSync15(this.metaPath)) return;
8018
8552
  try {
8019
- const data = JSON.parse(readFileSync14(this.metaPath, "utf-8"));
8553
+ const data = JSON.parse(readFileSync15(this.metaPath, "utf-8"));
8020
8554
  this.skills = data.skills || [];
8021
8555
  } catch {
8022
8556
  this.skills = [];
@@ -8039,7 +8573,7 @@ var init_skill_manager = __esm({
8039
8573
  let changed = false;
8040
8574
  for (const sk of this.skills) {
8041
8575
  if (sk.status !== "active") continue;
8042
- if (!existsSync14(join12(this.skillsDir, `${sk.name}.md`))) {
8576
+ if (!existsSync15(join13(this.skillsDir, `${sk.name}.md`))) {
8043
8577
  sk.status = "stale";
8044
8578
  changed = true;
8045
8579
  }
@@ -8050,9 +8584,9 @@ var init_skill_manager = __esm({
8050
8584
  addSkill(skill) {
8051
8585
  const id = skill.id || randomUUID();
8052
8586
  const name = skill.name || `brain_${id.slice(0, 8)}`;
8053
- if (!existsSync14(this.skillsDir)) return "";
8054
- const mdPath = join12(this.skillsDir, `${name}.md`);
8055
- if (existsSync14(mdPath)) return "";
8587
+ if (!existsSync15(this.skillsDir)) return "";
8588
+ const mdPath = join13(this.skillsDir, `${name}.md`);
8589
+ if (existsSync15(mdPath)) return "";
8056
8590
  try {
8057
8591
  writeFileSync4(mdPath, skill.content, "utf-8");
8058
8592
  } catch {
@@ -8101,12 +8635,12 @@ var init_skill_manager = __esm({
8101
8635
  const out = [];
8102
8636
  for (const sk of this.skills) {
8103
8637
  if (sk.origin_node !== "local" || sk.status !== "active") continue;
8104
- const mdPath = join12(this.skillsDir, `${sk.name}.md`);
8105
- if (!existsSync14(mdPath)) continue;
8638
+ const mdPath = join13(this.skillsDir, `${sk.name}.md`);
8639
+ if (!existsSync15(mdPath)) continue;
8106
8640
  out.push({
8107
8641
  name: sk.name,
8108
8642
  category: sk.category || "",
8109
- content: readFileSync14(mdPath, "utf-8"),
8643
+ content: readFileSync15(mdPath, "utf-8"),
8110
8644
  keywords: sk.keywords || []
8111
8645
  });
8112
8646
  }
@@ -8216,11 +8750,11 @@ __export(contributor_exports, {
8216
8750
  runGit: () => runGit,
8217
8751
  writeExportFiles: () => writeExportFiles
8218
8752
  });
8219
- import { mkdirSync as mkdirSync5, mkdtempSync, rmSync, writeFileSync as writeFileSync5 } from "fs";
8220
- import { tmpdir } from "os";
8221
- import { join as join13 } from "path";
8753
+ import { mkdirSync as mkdirSync6, mkdtempSync, rmSync, writeFileSync as writeFileSync5 } from "fs";
8754
+ import { tmpdir as tmpdir2 } from "os";
8755
+ import { join as join14 } from "path";
8222
8756
  import { spawnSync } from "child_process";
8223
- import { dirname as dirname9 } from "path";
8757
+ import { dirname as dirname10 } from "path";
8224
8758
  import { fileURLToPath as fileURLToPath9 } from "url";
8225
8759
  function collectRules(store) {
8226
8760
  return store.getActiveRules().filter((r) => EXPORT_RULE_SOURCES.includes(r.source)).map((r) => ({
@@ -8239,32 +8773,32 @@ function collectSkills(skillMgr) {
8239
8773
  }
8240
8774
  function writeExportFiles(outDir, rules, skills) {
8241
8775
  const written = [];
8242
- const rulesDir2 = join13(outDir, "rules");
8243
- mkdirSync5(rulesDir2, { recursive: true });
8776
+ const rulesDir2 = join14(outDir, "rules");
8777
+ mkdirSync6(rulesDir2, { recursive: true });
8244
8778
  const byLayer = /* @__PURE__ */ new Map();
8245
8779
  for (const r of rules) {
8246
8780
  if (!byLayer.has(r.layer)) byLayer.set(r.layer, []);
8247
8781
  byLayer.get(r.layer).push(r);
8248
8782
  }
8249
8783
  for (const layer of [...byLayer.keys()].sort((a, b) => a - b)) {
8250
- const path6 = join13(rulesDir2, `l${layer}_brain_rules.json`);
8784
+ const path6 = join14(rulesDir2, `l${layer}_brain_rules.json`);
8251
8785
  writeFileSync5(path6, JSON.stringify(byLayer.get(layer), null, 2), "utf-8");
8252
8786
  written.push(path6);
8253
8787
  }
8254
8788
  if (rules.length === 0) {
8255
- const path6 = join13(rulesDir2, "empty.txt");
8789
+ const path6 = join14(rulesDir2, "empty.txt");
8256
8790
  writeFileSync5(path6, "(no brain rules to contribute)", "utf-8");
8257
8791
  written.push(path6);
8258
8792
  }
8259
- const skillsDir = join13(outDir, "skills");
8260
- mkdirSync5(skillsDir, { recursive: true });
8793
+ const skillsDir = join14(outDir, "skills");
8794
+ mkdirSync6(skillsDir, { recursive: true });
8261
8795
  for (const sk of skills) {
8262
- const path6 = join13(skillsDir, `${sk.name}.md`);
8796
+ const path6 = join14(skillsDir, `${sk.name}.md`);
8263
8797
  writeFileSync5(path6, sk.content || "", "utf-8");
8264
8798
  written.push(path6);
8265
8799
  }
8266
8800
  if (skills.length === 0) {
8267
- const path6 = join13(skillsDir, "empty.txt");
8801
+ const path6 = join14(skillsDir, "empty.txt");
8268
8802
  writeFileSync5(path6, "(no brain skills to contribute)", "utf-8");
8269
8803
  written.push(path6);
8270
8804
  }
@@ -8394,7 +8928,7 @@ async function contribute(opts = {}) {
8394
8928
  console.log(`[Brain] push \u76EE\u6807: ${pushTarget}`);
8395
8929
  console.log(`[Brain] MR \u6307\u5411: ${DEFAULT_TARGET_REPO}`);
8396
8930
  const store = new BrainRuleStore();
8397
- const skillsDir = resolveAssetDir(__dirname8, "skills") ?? join13(__dirname8, "..", "..", "agent_moss", "skills");
8931
+ const skillsDir = resolveAssetDir(__dirname8, "skills") ?? join14(__dirname8, "..", "..", "agent_moss", "skills");
8398
8932
  const skillMgr = new BrainSkillManager(skillsDir);
8399
8933
  let rules;
8400
8934
  let skills;
@@ -8409,9 +8943,9 @@ async function contribute(opts = {}) {
8409
8943
  return 0;
8410
8944
  }
8411
8945
  console.log(`[Brain] \u5BFC\u51FA: ${rules.length} \u6761\u89C4\u5219, ${skills.length} \u4E2A Skill`);
8412
- const workdir = mkdtempSync(join13(tmpdir(), "agentmoss-contrib-"));
8946
+ const workdir = mkdtempSync(join14(tmpdir2(), "agentmoss-contrib-"));
8413
8947
  try {
8414
- writeExportFiles(join13(workdir, "agent_moss", "brain_contrib"), rules, skills);
8948
+ writeExportFiles(join14(workdir, "agent_moss", "brain_contrib"), rules, skills);
8415
8949
  console.log(`[Brain] \u63A2\u6D4B ${pushTarget} \u8BBF\u95EE\u6743\u9650 ...`);
8416
8950
  if (!probeAccess(pushTarget)) {
8417
8951
  const username = getCurrentUsername();
@@ -8461,7 +8995,7 @@ var init_contributor = __esm({
8461
8995
  init_rule_store();
8462
8996
  init_skill_manager();
8463
8997
  init_asset_dir();
8464
- __dirname8 = dirname9(fileURLToPath9(import.meta.url));
8998
+ __dirname8 = dirname10(fileURLToPath9(import.meta.url));
8465
8999
  DEFAULT_TARGET_REPO = "ssh://git@atomgit.com/kenhkl/AgentMoss.git";
8466
9000
  CONTACT_EMAIL = "kenhkl11@hotmail.com";
8467
9001
  TOKEN_ENV = "AGENT_MOSS_CONTRIBUTE_TOKEN";
@@ -8836,7 +9370,7 @@ function markIntegrity(actionDetail) {
8836
9370
  }
8837
9371
  return { integrity: "medium", isLowSource: false };
8838
9372
  }
8839
- function initNode(sessionId, seq, actionType, actionDetail) {
9373
+ function initNode(sessionId, seq, actionType, actionDetail, timestamp) {
8840
9374
  const { integrity, isLowSource } = markIntegrity(actionDetail);
8841
9375
  return {
8842
9376
  sessionId,
@@ -8846,7 +9380,8 @@ function initNode(sessionId, seq, actionType, actionDetail) {
8846
9380
  stage: markStage(actionDetail),
8847
9381
  resources: extractResources(actionDetail),
8848
9382
  integrity,
8849
- isLowSource
9383
+ isLowSource,
9384
+ timestamp: timestamp || void 0
8850
9385
  };
8851
9386
  }
8852
9387
  function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
@@ -8856,13 +9391,21 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
8856
9391
  const details = [];
8857
9392
  for (const a of actionHistory) {
8858
9393
  const ad = String(a.actionDetail || "");
8859
- if (ad) details.push({ actionType: String(a.actionType || ""), actionDetail: ad });
9394
+ if (ad) details.push({
9395
+ actionType: String(a.actionType || ""),
9396
+ actionDetail: ad,
9397
+ calledAt: a.calledAt || void 0
9398
+ });
8860
9399
  }
8861
9400
  if (aNext) {
8862
- details.push({ actionType: String(aNext.actionType || ""), actionDetail: String(aNext.actionDetail || "") });
9401
+ details.push({
9402
+ actionType: String(aNext.actionType || ""),
9403
+ actionDetail: String(aNext.actionDetail || ""),
9404
+ calledAt: aNext.calledAt || void 0
9405
+ });
8863
9406
  }
8864
9407
  details.forEach((d, seq) => {
8865
- const node = initNode(sessionId, seq, d.actionType, d.actionDetail);
9408
+ const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt);
8866
9409
  nodes.push(node);
8867
9410
  if (node.stage) stageFlags.add(node.stage);
8868
9411
  });
@@ -8888,7 +9431,8 @@ function buildMultiSessionGraph(sessions, aNext) {
8888
9431
  last.sessionId,
8889
9432
  last.nodes.length,
8890
9433
  String(aNext.actionType || ""),
8891
- String(aNext.actionDetail || "")
9434
+ String(aNext.actionDetail || ""),
9435
+ aNext.calledAt || void 0
8892
9436
  ));
8893
9437
  if (last.nodes.length > 1) {
8894
9438
  last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
@@ -8946,6 +9490,27 @@ var init_builder = __esm({
8946
9490
  });
8947
9491
 
8948
9492
  // src/engine/formal-engine/properties.ts
9493
+ function parseTs(ts) {
9494
+ if (!ts) return null;
9495
+ const t = Date.parse(String(ts).trim());
9496
+ return Number.isFinite(t) ? t : null;
9497
+ }
9498
+ function privescExfilWindowSec() {
9499
+ const raw = (process.env.AGENT_MOSS_PRIVESC_EXFIL_WINDOW_SEC || "").trim();
9500
+ if (!raw) return 1800;
9501
+ const n = parseInt(raw, 10);
9502
+ return Number.isFinite(n) ? Math.max(0, n) : 1800;
9503
+ }
9504
+ function privescInWindow(nodes, indexes, exfilIdx, windowSec) {
9505
+ const before = indexes.filter((i) => i < exfilIdx);
9506
+ if (windowSec <= 0) return before;
9507
+ const exfilTs = parseTs(nodes[exfilIdx].timestamp);
9508
+ if (exfilTs === null) return before;
9509
+ return before.filter((i) => {
9510
+ const ts = parseTs(nodes[i].timestamp);
9511
+ return ts === null || exfilTs - ts <= windowSec * 1e3;
9512
+ });
9513
+ }
8949
9514
  function loadCrossSessionChains() {
8950
9515
  if (CROSS_SESSION_CHAINS) return CROSS_SESSION_CHAINS;
8951
9516
  const combo = loadL2().combo_chains || {};
@@ -9137,6 +9702,7 @@ function checkNeverExfilAfterPrivesc(graph) {
9137
9702
  const nodes = allNodes(graph);
9138
9703
  if (nodes.length < 2) return violations;
9139
9704
  const witness = graph.witnessIdx;
9705
+ const windowSec = privescExfilWindowSec();
9140
9706
  if (witness !== void 0) {
9141
9707
  const evIdx = [];
9142
9708
  nodes.forEach((n, i) => {
@@ -9144,15 +9710,17 @@ function checkNeverExfilAfterPrivesc(graph) {
9144
9710
  });
9145
9711
  if (evIdx.length === 0) return violations;
9146
9712
  const firstEv = Math.min(...evIdx);
9147
- let pre = null;
9148
- for (let i = 0; i < firstEv; i++) {
9149
- if (isPrivescNode(nodes[i])) pre = nodes[i];
9150
- }
9151
- if (pre) {
9713
+ const privescIdx = [];
9714
+ nodes.forEach((n, i) => {
9715
+ if (isPrivescNode(n)) privescIdx.push(i);
9716
+ });
9717
+ const candidates = privescInWindow(nodes, privescIdx, firstEv, windowSec);
9718
+ if (candidates.length > 0) {
9719
+ const pre = nodes[candidates[candidates.length - 1]];
9152
9720
  const post = nodes[firstEv];
9153
9721
  return [{
9154
9722
  propertyId: "no-exfil-after-privesc",
9155
- reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\xAC\u25C7EXFIL)",
9723
+ reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
9156
9724
  riskLevel: "critical",
9157
9725
  path: [
9158
9726
  { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
@@ -9165,18 +9733,18 @@ function checkNeverExfilAfterPrivesc(graph) {
9165
9733
  for (let i = 0; i < nodes.length; i++) {
9166
9734
  if (!isPrivescNode(nodes[i])) continue;
9167
9735
  for (let j = i + 1; j < nodes.length; j++) {
9168
- if (isExfilNode(nodes[j])) {
9169
- violations.push({
9170
- propertyId: "no-exfil-after-privesc",
9171
- reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\xAC\u25C7EXFIL)",
9172
- riskLevel: "critical",
9173
- path: [
9174
- { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
9175
- { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
9176
- ]
9177
- });
9178
- return violations;
9179
- }
9736
+ if (!isExfilNode(nodes[j])) continue;
9737
+ if (privescInWindow(nodes, [i], j, windowSec).length === 0) continue;
9738
+ violations.push({
9739
+ propertyId: "no-exfil-after-privesc",
9740
+ reason: "\u63D0\u6743\u540E\u51FA\u73B0\u5916\u4F20\uFF1A\u63D0\u6743(\u524D) \u2192 \u5916\u4F20(\u540E)\uFF0C\u8FDD\u53CD \u25A1(PRIVESC\u2192\u25C7EXFIL) \u7A97\u53E3\u7EA6\u675F",
9741
+ riskLevel: "critical",
9742
+ path: [
9743
+ { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
9744
+ { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
9745
+ ]
9746
+ });
9747
+ return violations;
9180
9748
  }
9181
9749
  }
9182
9750
  return violations;
@@ -9339,6 +9907,10 @@ function checkCrossSessionReachability(graph) {
9339
9907
  }
9340
9908
  return violations;
9341
9909
  }
9910
+ function isDataSourceNode(n) {
9911
+ if (n.stage === "CRED" || n.isLowSource) return true;
9912
+ return DATA_SOURCE_READ_RE.test(n.actionDetail);
9913
+ }
9342
9914
  function checkSensitiveDataAggregation(graph) {
9343
9915
  const violations = [];
9344
9916
  const nodes = allNodes(graph);
@@ -9358,6 +9930,7 @@ function checkSensitiveDataAggregation(graph) {
9358
9930
  for (const r of n.resources) {
9359
9931
  for (const j of resIndex.get(r) || []) {
9360
9932
  if (j === exfilIdx) continue;
9933
+ if (!isDataSourceNode(nodes[j])) continue;
9361
9934
  srcs.add(`${nodes[j].sessionId}#${nodes[j].seq}`);
9362
9935
  }
9363
9936
  }
@@ -9380,7 +9953,7 @@ function checkSensitiveDataAggregation(graph) {
9380
9953
  }
9381
9954
  return violations;
9382
9955
  }
9383
- var CROSS_SESSION_CHAINS, CRED_FILE_RE, EXFIL_RE, EBPF_NET_RE, REMOTE_RE, EXFIL_ACTION_RE, CRED_STRICT_RE, INLINE_SHELL_RE, FORMAL_RULES, PKG_MGR, SHELLS, TOOL_REGISTRY, COMPUTE_RE, STRONG_COMPUTE_TYPES, DL_OR_LOW_RE, ALL_PROPERTIES;
9956
+ var CROSS_SESSION_CHAINS, CRED_FILE_RE, EXFIL_RE, EBPF_NET_RE, REMOTE_RE, EXFIL_ACTION_RE, CRED_STRICT_RE, INLINE_SHELL_RE, FORMAL_RULES, PKG_MGR, SHELLS, TOOL_REGISTRY, COMPUTE_RE, STRONG_COMPUTE_TYPES, DL_OR_LOW_RE, DATA_SOURCE_READ_RE, ALL_PROPERTIES;
9384
9957
  var init_properties = __esm({
9385
9958
  "src/engine/formal-engine/properties.ts"() {
9386
9959
  "use strict";
@@ -9404,6 +9977,7 @@ var init_properties = __esm({
9404
9977
  COMPUTE_RE = /subprocess|os\.system|shell\s*=\s*True|\beval\(|\bexec\(|bash\s+[^\s]|sh\s+-c|python3?\s+|node\s+|\btimeout\s+|nohup\s+|\b\.\/[\w./-]+/i;
9405
9978
  STRONG_COMPUTE_TYPES = new Set(FORMAL_RULES.compute_action_types ?? []);
9406
9979
  DL_OR_LOW_RE = /curl\s+.*-o\b|wget\s+.*\s+-O\b|curl\s+.*\|\s*(bash|sh|python)|wget\s+.*\|\s*(bash|sh|python)|git\s+clone|docker\s+pull|pip\s+install|npm\s+install|base64\s+-d/i;
9980
+ DATA_SOURCE_READ_RE = /\b(?:cat|head|tail|less|more|sed|awk|grep|strings|base64|xxd|od|dd)\b|\b(?:open|read_text|read_bytes)\s*\(/i;
9407
9981
  ALL_PROPERTIES = [
9408
9982
  { id: "cross-session-chain", check: checkCrossSessionChain, shortName: "P1" },
9409
9983
  { id: "sensitive-data-flow", check: checkSensitiveDataFlow, shortName: "P2" },
@@ -9926,10 +10500,10 @@ var init_stateful = __esm({
9926
10500
  import * as fs4 from "fs";
9927
10501
  import * as os2 from "os";
9928
10502
  import * as path4 from "path";
9929
- import { createHash as createHash2 } from "crypto";
10503
+ import { createHash as createHash3 } from "crypto";
9930
10504
  function defaultStateDbPath() {
9931
10505
  const dir = process.env.AGENT_MOSS_STATE_DIR || path4.join(os2.homedir(), ".agentmoss");
9932
- const tag = createHash2("sha1").update("agentmoss-stateful").digest("hex").slice(0, 8);
10506
+ const tag = createHash3("sha1").update("agentmoss-stateful").digest("hex").slice(0, 8);
9933
10507
  return path4.join(dir, `stateful_chains_${tag}.json`);
9934
10508
  }
9935
10509
  var ChainStateStore;
@@ -9937,6 +10511,7 @@ var init_chain_state = __esm({
9937
10511
  "src/engine/formal-engine/chain_state.ts"() {
9938
10512
  "use strict";
9939
10513
  init_esm_shims();
10514
+ init_security_policy();
9940
10515
  init_stateful();
9941
10516
  ChainStateStore = class {
9942
10517
  filePath;
@@ -9963,8 +10538,8 @@ var init_chain_state = __esm({
9963
10538
  this._timer = setTimeout(() => {
9964
10539
  this._timer = null;
9965
10540
  try {
9966
- fs4.mkdirSync(path4.dirname(this.filePath), { recursive: true });
9967
- fs4.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), "utf-8");
10541
+ ensurePrivateDir(path4.dirname(this.filePath));
10542
+ writePrivateText(this.filePath, JSON.stringify(this.data, null, 2));
9968
10543
  } catch {
9969
10544
  }
9970
10545
  }, this._flushDelayMs);
@@ -9976,8 +10551,8 @@ var init_chain_state = __esm({
9976
10551
  this._timer = null;
9977
10552
  }
9978
10553
  try {
9979
- fs4.mkdirSync(path4.dirname(this.filePath), { recursive: true });
9980
- fs4.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), "utf-8");
10554
+ ensurePrivateDir(path4.dirname(this.filePath));
10555
+ writePrivateText(this.filePath, JSON.stringify(this.data, null, 2));
9981
10556
  } catch {
9982
10557
  }
9983
10558
  }
@@ -10500,13 +11075,26 @@ var init_coordinator = __esm({
10500
11075
  });
10501
11076
 
10502
11077
  // src/infra/policy-cache.ts
10503
- import { createHash as createHash3 } from "crypto";
10504
- import { existsSync as existsSync16, mkdirSync as mkdirSync7, readdirSync as readdirSync5, readFileSync as readFileSync16, rmdirSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync7 } from "fs";
10505
- import { tmpdir as tmpdir2 } from "os";
10506
- import { join as join15 } from "path";
11078
+ import { createHash as createHash4 } from "crypto";
11079
+ import { existsSync as existsSync17, lstatSync as lstatSync2, readdirSync as readdirSync5, readFileSync as readFileSync17, rmdirSync, unlinkSync as unlinkSync3 } from "fs";
11080
+ import { tmpdir as tmpdir3 } from "os";
11081
+ import { join as join16 } from "path";
10507
11082
  function cacheDir() {
10508
11083
  return cacheDirOverride ?? CACHE_DIR;
10509
11084
  }
11085
+ function safeCacheDir() {
11086
+ const dir = cacheDir();
11087
+ if (ensurePrivateDir(dir)) return dir;
11088
+ try {
11089
+ const fallback = join16(getDataRoot(), "policy_cache");
11090
+ if (ensurePrivateDir(fallback)) {
11091
+ console.warn(`[AgentMoss] \u7F13\u5B58\u76EE\u5F55 ${dir} \u4E0D\u5B89\u5168\uFF08\u88AB\u5176\u4ED6\u7528\u6237\u62E5\u6709\u6216\u4E3A\u7B26\u53F7\u94FE\u63A5\uFF09\uFF0C\u5DF2\u56DE\u9000\u5230 ${fallback}`);
11092
+ return fallback;
11093
+ }
11094
+ } catch {
11095
+ }
11096
+ return dir;
11097
+ }
10510
11098
  function getDefaultCache() {
10511
11099
  if (!defaultCache) defaultCache = new PolicyCache(getCacheOptions());
10512
11100
  return defaultCache;
@@ -10516,8 +11104,10 @@ var init_policy_cache = __esm({
10516
11104
  "src/infra/policy-cache.ts"() {
10517
11105
  "use strict";
10518
11106
  init_esm_shims();
11107
+ init_security_policy();
11108
+ init_paths();
10519
11109
  init_config();
10520
- CACHE_DIR = join15(tmpdir2(), "agent_moss_policy_cache");
11110
+ CACHE_DIR = join16(tmpdir3(), "agent_moss_policy_cache");
10521
11111
  cacheDirOverride = null;
10522
11112
  PolicyCache = class {
10523
11113
  // Map 的插入序即 LRU 序(get 时重新 set 到末尾)
@@ -10530,9 +11120,9 @@ var init_policy_cache = __esm({
10530
11120
  return `${sessionId}::${promptSession}`;
10531
11121
  }
10532
11122
  cacheFilePath(sessionId, promptSession) {
10533
- const promptHash = createHash3("md5").update(promptSession).digest("hex").slice(0, 12);
10534
- const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
10535
- return join15(cacheDir(), safeSessionId, `${promptHash}.toml`);
11123
+ const promptHash = createHash4("md5").update(promptSession).digest("hex").slice(0, 12);
11124
+ const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_").replace(/\.\./g, "__");
11125
+ return join16(safeCacheDir(), safeSessionId, `${promptHash}.toml`);
10536
11126
  }
10537
11127
  /** 取缓存:内存 → 文件;未命中返回 null。 */
10538
11128
  get(sessionId, promptSession) {
@@ -10545,9 +11135,9 @@ var init_policy_cache = __esm({
10545
11135
  return hit;
10546
11136
  }
10547
11137
  const file = this.cacheFilePath(sessionId, promptSession);
10548
- if (existsSync16(file)) {
11138
+ if (existsSync17(file) && !lstatSync2(file).isSymbolicLink()) {
10549
11139
  try {
10550
- const policy = readFileSync16(file, "utf-8");
11140
+ const policy = readFileSync17(file, "utf-8");
10551
11141
  this.remember(key, policy);
10552
11142
  return policy;
10553
11143
  } catch {
@@ -10561,8 +11151,10 @@ var init_policy_cache = __esm({
10561
11151
  this.remember(this.cacheKey(sessionId, promptSession), policy);
10562
11152
  const file = this.cacheFilePath(sessionId, promptSession);
10563
11153
  try {
10564
- mkdirSync7(join15(cacheDir(), sessionId.replace(/\//g, "_").replace(/\\/g, "_")), { recursive: true });
10565
- writeFileSync7(file, policy, "utf-8");
11154
+ const dir = safeCacheDir();
11155
+ const sub = sessionId.replace(/\//g, "_").replace(/\\/g, "_").replace(/\.\./g, "__");
11156
+ if (!ensurePrivateDir(join16(dir, sub))) return;
11157
+ writePrivateText(file, policy);
10566
11158
  } catch {
10567
11159
  }
10568
11160
  }
@@ -10586,13 +11178,13 @@ var init_policy_cache = __esm({
10586
11178
  for (const k of [...this.memory.keys()]) {
10587
11179
  if (k.startsWith(`${sessionId}::`)) this.memory.delete(k);
10588
11180
  }
10589
- const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
10590
- const dir = join15(cacheDir(), safeSessionId);
11181
+ const safeSessionId = sessionId.replace(/\//g, "_").replace(/\\/g, "_").replace(/\.\./g, "__");
11182
+ const dir = join16(cacheDir(), safeSessionId);
10591
11183
  try {
10592
- if (existsSync16(dir)) {
11184
+ if (existsSync17(dir)) {
10593
11185
  for (const f of readdirSync5(dir)) {
10594
11186
  try {
10595
- unlinkSync3(join15(dir, f));
11187
+ unlinkSync3(join16(dir, f));
10596
11188
  } catch {
10597
11189
  }
10598
11190
  }
@@ -10604,11 +11196,11 @@ var init_policy_cache = __esm({
10604
11196
  clear() {
10605
11197
  this.memory.clear();
10606
11198
  try {
10607
- if (existsSync16(cacheDir())) {
11199
+ if (existsSync17(cacheDir())) {
10608
11200
  for (const sessionDir of readdirSync5(cacheDir())) {
10609
- const dir = join15(cacheDir(), sessionDir);
11201
+ const dir = join16(cacheDir(), sessionDir);
10610
11202
  try {
10611
- for (const f of readdirSync5(dir)) unlinkSync3(join15(dir, f));
11203
+ for (const f of readdirSync5(dir)) unlinkSync3(join16(dir, f));
10612
11204
  rmdirSync(dir);
10613
11205
  } catch {
10614
11206
  }
@@ -10704,9 +11296,9 @@ __export(analyzer_exports, {
10704
11296
  savePolicyToFile: () => savePolicyToFile,
10705
11297
  validateToml: () => validateToml
10706
11298
  });
10707
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from "fs";
10708
- import { tmpdir as tmpdir3 } from "os";
10709
- import { join as join16, resolve as resolve10 } from "path";
11299
+ import { existsSync as existsSync18, readFileSync as readFileSync18 } from "fs";
11300
+ import { tmpdir as tmpdir4 } from "os";
11301
+ import { join as join17, resolve as resolve11 } from "path";
10710
11302
  import { parse as parseToml } from "smol-toml";
10711
11303
  function _setPolicyOutputDirForTest(dir) {
10712
11304
  policyOutputDirOverride = dir;
@@ -10718,10 +11310,13 @@ function savePolicyToFile(sessionId, policyToml) {
10718
11310
  if (!isPolicySaveEnabled()) return null;
10719
11311
  try {
10720
11312
  const outDir = policyOutputDir();
10721
- mkdirSync8(outDir, { recursive: true });
10722
- const safeName = sessionId.replace(/\//g, "_").replace(/\\/g, "_");
10723
- const filepath = join16(outDir, `${safeName}.toml`);
10724
- writeFileSync8(filepath, policyToml, "utf-8");
11313
+ if (!ensurePrivateDir(outDir)) {
11314
+ console.warn(`[AgentMoss] Policy \u8F93\u51FA\u76EE\u5F55 ${outDir} \u4E0D\u5B89\u5168\uFF0C\u8DF3\u8FC7\u843D\u76D8\uFF08\u4E0D\u5F71\u54CD\u5224\u5B9A\uFF09`);
11315
+ return null;
11316
+ }
11317
+ const safeName = sessionId.replace(/\//g, "_").replace(/\\/g, "_").replace(/\.\./g, "__");
11318
+ const filepath = join17(outDir, `${safeName}.toml`);
11319
+ writePrivateText(filepath, policyToml);
10725
11320
  return filepath;
10726
11321
  } catch (err) {
10727
11322
  console.warn(`[AgentMoss] \u7B56\u7565\u6587\u4EF6\u843D\u76D8\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u5224\u5B9A\uFF09: ${policyOutputDir()}: ${String(err)}`);
@@ -10758,12 +11353,12 @@ function validateToml(tomlStr) {
10758
11353
  function loadPolicyMappingDoc() {
10759
11354
  const dir = resolveAssetDir(__dirname, "templates");
10760
11355
  if (dir) {
10761
- const p = resolve10(dir, "policy_mapping.md");
10762
- if (existsSync17(p)) return readFileSync17(p, "utf-8");
11356
+ const p = resolve11(dir, "policy_mapping.md");
11357
+ if (existsSync18(p)) return readFileSync18(p, "utf-8");
10763
11358
  }
10764
11359
  for (const cand of assetDirCandidates(__dirname, "templates")) {
10765
- const p = resolve10(cand, "policy_mapping.md");
10766
- if (existsSync17(p)) return readFileSync17(p, "utf-8");
11360
+ const p = resolve11(cand, "policy_mapping.md");
11361
+ if (existsSync18(p)) return readFileSync18(p, "utf-8");
10767
11362
  }
10768
11363
  return "";
10769
11364
  }
@@ -10926,6 +11521,7 @@ var init_analyzer = __esm({
10926
11521
  "src/engine/analyzer.ts"() {
10927
11522
  "use strict";
10928
11523
  init_esm_shims();
11524
+ init_security_policy();
10929
11525
  init_coordinator();
10930
11526
  init_policy_cache();
10931
11527
  init_config();
@@ -10933,7 +11529,7 @@ var init_analyzer = __esm({
10933
11529
  init_asset_dir();
10934
11530
  init_token_stats();
10935
11531
  init_logging();
10936
- POLICY_OUTPUT_DIR = join16(tmpdir3(), "agent_moss");
11532
+ POLICY_OUTPUT_DIR = join17(tmpdir4(), "agent_moss");
10937
11533
  policyOutputDirOverride = null;
10938
11534
  DEFAULT_READONLY_POLICY = `landlock_optional = false
10939
11535
  mount_isolation_fallback = false
@@ -11345,7 +11941,8 @@ var init_observable = __esm({
11345
11941
  });
11346
11942
 
11347
11943
  // src/routes.ts
11348
- import { join as join18, dirname as dirname11 } from "path";
11944
+ import { randomBytes as randomBytes2 } from "crypto";
11945
+ import { join as join19, dirname as dirname12 } from "path";
11349
11946
  import { fileURLToPath as fileURLToPath10 } from "url";
11350
11947
  async function healthRoute(c) {
11351
11948
  return c.json(HealthResponseSchema.parse({
@@ -11361,7 +11958,23 @@ async function analyzeRoute(c) {
11361
11958
  if (!osType) {
11362
11959
  osType = process.platform === "win32" ? "windows" : "linux";
11363
11960
  }
11364
- const agentId = req.agent_id || req.metadata?.agent_type || "";
11961
+ const _principal = principalOf(c.req.raw.headers, new URL(c.req.url).pathname);
11962
+ const [_boundOk, agentId] = bindAgent(_principal, req.agent_id || req.metadata?.agent_type || "");
11963
+ if (!_boundOk) {
11964
+ return c.json({ detail: `agent_id \u4E0E\u6240\u6301\u51ED\u8BC1\u4E0D\u5339\u914D: ${agentId}` }, 403);
11965
+ }
11966
+ const _sweepKey = _principal || `ip:${c.req.header("x-forwarded-for") || "local"}`;
11967
+ const [_sweepN, _sweepHit] = noteSessionAccess(_sweepKey, req.session_id);
11968
+ if (_sweepHit) {
11969
+ if (sessionSweepAction() === "deny") {
11970
+ return c.json({
11971
+ detail: `\u77ED\u65F6\u95F4\u5185\u8BBF\u95EE\u4E86 ${_sweepN} \u4E2A\u4E0D\u540C\u4F1A\u8BDD\uFF0C\u8D85\u51FA\u9608\u503C ${sessionSweepThreshold()}\uFF08\u7591\u4F3C\u5192\u5145/\u626B\u63CF\uFF09`
11972
+ }, 429);
11973
+ }
11974
+ console.warn(
11975
+ `[AgentMoss] \u26A0\uFE0F \u8DE8\u4F1A\u8BDD\u6279\u91CF\u8BBF\u95EE: key=${_sweepKey} \u7A97\u53E3\u5185 ${_sweepN} \u4E2A\u4E0D\u540C session\uFF08\u9608\u503C ${sessionSweepThreshold()}\uFF09`
11976
+ );
11977
+ }
11365
11978
  const result = await analyze({
11366
11979
  sessionId: req.session_id,
11367
11980
  promptSession: req.prompt_session,
@@ -11372,14 +11985,16 @@ async function analyzeRoute(c) {
11372
11985
  actionHistory: req.action_history.map((h) => ({
11373
11986
  actionType: h.action_type,
11374
11987
  actionDetail: h.action_detail,
11375
- output: h.output
11988
+ output: h.output,
11989
+ calledAt: h.called_at
11376
11990
  })),
11377
11991
  sessionHistory: (req.session_history || []).map((s) => ({
11378
11992
  sessionId: s.session_id,
11379
11993
  actionHistory: (s.action_history || []).map((h) => ({
11380
11994
  actionType: h.action_type,
11381
11995
  actionDetail: h.action_detail,
11382
- output: h.output
11996
+ output: h.output,
11997
+ calledAt: h.called_at
11383
11998
  }))
11384
11999
  })),
11385
12000
  // prompt_history 必须透传:L2 意图一致性(logic-rules.ts 的
@@ -11406,7 +12021,8 @@ async function analyzeRoute(c) {
11406
12021
  // agent_id:AgentGate V1 normalizer 历史上把 agent_type 塞 metadata,
11407
12022
  // 顶层为空时兜底(OpenDesk 的 agent-moss 需 agent_id 加载专属规则)。
11408
12023
  agentId,
11409
- llmConfig: req.metadata?.llm_config,
12024
+ // 严格模式:完全不接受 per-request LLM 配置(默认接受,保持原行为)
12025
+ llmConfig: perRequestLlmEnabled() ? req.metadata?.llm_config : void 0,
11410
12026
  llmLogPath: req.metadata?.llm_log_path
11411
12027
  });
11412
12028
  const durationMs = performance.now() - t0;
@@ -11454,7 +12070,8 @@ async function analyzeRoute(c) {
11454
12070
  violated_policy: "",
11455
12071
  confidence: 90,
11456
12072
  analysis_duration_ms: Math.round((performance.now() - t0) * 10) / 10,
11457
- violation_chain: judgment.violation_chain
12073
+ // 跨会话节点脱敏(时间线按 agent_id 共享,原样返回等于泄露别人的命令原文)
12074
+ violation_chain: redactChain(judgment.violation_chain, req.session_id)
11458
12075
  })
11459
12076
  );
11460
12077
  }
@@ -11478,14 +12095,19 @@ async function analyzeRoute(c) {
11478
12095
  })
11479
12096
  );
11480
12097
  } catch (err) {
11481
- console.error("[AgentMoss] Analysis error:", err);
11482
12098
  if (err instanceof SyntaxError) {
11483
12099
  return c.json({ detail: err.message, error: "Invalid JSON" }, 422);
11484
12100
  }
11485
12101
  if (err?.issues) {
11486
12102
  return c.json({ detail: err.issues, error: "Validation error" }, 422);
11487
12103
  }
11488
- return c.json({ detail: String(err), error: "Internal error" }, 500);
12104
+ if (errorDetailMode() === "full") {
12105
+ console.error("[AgentMoss] Analysis error:", err);
12106
+ return c.json({ detail: String(err), error: "Internal error" }, 500);
12107
+ }
12108
+ const ref = randomBytes2(4).toString("hex");
12109
+ console.error(`[AgentMoss] Analysis internal error (ref=${ref}):`, err);
12110
+ return c.json({ detail: `Internal server error (ref: ${ref})`, error: "Internal error" }, 500);
11489
12111
  }
11490
12112
  }
11491
12113
  function getChainStateStoreFor(_agentId) {
@@ -11523,6 +12145,11 @@ async function reviewRoute(c) {
11523
12145
  if (!tracker) {
11524
12146
  tracker = new StatefulChainTracker(agentId);
11525
12147
  }
12148
+ if (req.db_path && !pathWithin(req.db_path, reviewRoots())) {
12149
+ return c.json({
12150
+ detail: "db_path \u4E0D\u5728\u5141\u8BB8\u8BFB\u53D6\u7684\u76EE\u5F55\u5185\uFF1B\u6807\u51C6 AgentGate \u90E8\u7F72\u65E0\u9700\u914D\u7F6E\uFF0C\u81EA\u5B9A\u4E49\u8DEF\u5F84\u8BF7\u52A0\u5165 AGENT_MOSS_REVIEW_PATHS"
12151
+ }, 403);
12152
+ }
11526
12153
  let sessions = [];
11527
12154
  if (req.db_path) {
11528
12155
  sessions = loadAgentGateSessions(req.db_path);
@@ -11586,15 +12213,20 @@ async function reviewRoute(c) {
11586
12213
  discovered_patterns: discovered
11587
12214
  }));
11588
12215
  } catch (err) {
11589
- console.error("[AgentMoss] Review error:", err);
11590
- return c.json({ error: "Internal error", detail: String(err) }, 500);
12216
+ if (errorDetailMode() === "full") {
12217
+ console.error("[AgentMoss] Review error:", err);
12218
+ return c.json({ error: "Internal error", detail: String(err) }, 500);
12219
+ }
12220
+ const ref = randomBytes2(4).toString("hex");
12221
+ console.error(`[AgentMoss] Review internal error (ref=${ref}):`, err);
12222
+ return c.json({ error: "Internal error", detail: `Internal server error (ref: ${ref})` }, 500);
11591
12223
  }
11592
12224
  }
11593
12225
  function getBrainRuleStore() {
11594
12226
  return new BrainRuleStore();
11595
12227
  }
11596
12228
  function getBrainSkillManager() {
11597
- const dir = resolveAssetDir(__dirname9, "skills") ?? join18(__dirname9, "..", "..", "agent_moss", "skills");
12229
+ const dir = resolveAssetDir(__dirname9, "skills") ?? join19(__dirname9, "..", "..", "agent_moss", "skills");
11598
12230
  return new BrainSkillManager(dir);
11599
12231
  }
11600
12232
  async function brainRulesRoute(c) {
@@ -11690,6 +12322,7 @@ var init_routes = __esm({
11690
12322
  "src/routes.ts"() {
11691
12323
  "use strict";
11692
12324
  init_esm_shims();
12325
+ init_security_policy();
11693
12326
  init_models();
11694
12327
  init_analyzer();
11695
12328
  init_actrail();
@@ -11700,15 +12333,16 @@ var init_routes = __esm({
11700
12333
  init_rule_store();
11701
12334
  init_skill_manager();
11702
12335
  init_asset_dir();
11703
- __dirname9 = dirname11(fileURLToPath10(import.meta.url));
12336
+ __dirname9 = dirname12(fileURLToPath10(import.meta.url));
11704
12337
  reviewStore = null;
11705
12338
  }
11706
12339
  });
11707
12340
 
11708
12341
  // src/console/router.ts
11709
12342
  import { Hono } from "hono";
11710
- import { readFileSync as readFileSync19, existsSync as existsSync19, unlinkSync as unlinkSync4 } from "fs";
11711
- import { join as join19, dirname as dirname12 } from "path";
12343
+ import { timingSafeEqual as timingSafeEqual2 } from "crypto";
12344
+ import { readFileSync as readFileSync20, existsSync as existsSync20, unlinkSync as unlinkSync4 } from "fs";
12345
+ import { join as join20, dirname as dirname13 } from "path";
11712
12346
  import { fileURLToPath as fileURLToPath11 } from "url";
11713
12347
  function getConsoleToken() {
11714
12348
  return process.env[CONSOLE_TOKEN_ENV] || "";
@@ -11717,7 +12351,11 @@ function verifyAuth(req) {
11717
12351
  const token = getConsoleToken();
11718
12352
  if (!token) return true;
11719
12353
  const auth = req.headers.get("Authorization") || "";
11720
- if (auth.startsWith("Bearer ")) return auth.slice(7) === token;
12354
+ if (auth.startsWith("Bearer ")) {
12355
+ const a = Buffer.from(auth.slice(7), "utf-8");
12356
+ const b = Buffer.from(token, "utf-8");
12357
+ return a.length === b.length && timingSafeEqual2(a, b);
12358
+ }
11721
12359
  return false;
11722
12360
  }
11723
12361
  function authMiddleware() {
@@ -11746,9 +12384,9 @@ function createConsoleRouter() {
11746
12384
  console2.use("/api/*", authMiddleware());
11747
12385
  console2.use("/api/*", builtinOnlyReadOnlyMiddleware());
11748
12386
  const serveIndex = (c) => {
11749
- const indexFile = join19(STATIC_DIR, "index.html");
11750
- if (existsSync19(indexFile)) {
11751
- return c.html(readFileSync19(indexFile, "utf-8"));
12387
+ const indexFile = join20(STATIC_DIR, "index.html");
12388
+ if (existsSync20(indexFile)) {
12389
+ return c.html(readFileSync20(indexFile, "utf-8"));
11752
12390
  }
11753
12391
  return c.html("<h1>AgentMoss Policy Console</h1><p>Frontend not built. Use /console/api/* endpoints.</p>");
11754
12392
  };
@@ -11943,15 +12581,15 @@ function createConsoleRouter() {
11943
12581
  if (!/^[A-Za-z0-9_-]+$/.test(skillId)) {
11944
12582
  return c.json({ detail: "\u975E\u6CD5 skill id" }, 400);
11945
12583
  }
11946
- const userPath = join19(dirname12(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
12584
+ const userPath = join20(dirname13(getRuntimeConfigPath()), "agent_moss_skills", `${skillId}.md`);
11947
12585
  const builtinDir = resolveAssetDir(__dirname10, "skills");
11948
12586
  const candidates = [
11949
12587
  ...isRuntimeConfigDisabled() ? [] : [userPath],
11950
- builtinDir ? join19(builtinDir, `${skillId}.md`) : ""
12588
+ builtinDir ? join20(builtinDir, `${skillId}.md`) : ""
11951
12589
  ].filter(Boolean);
11952
12590
  for (const p of candidates) {
11953
- if (existsSync19(p)) {
11954
- return c.json({ skill_id: skillId, path: p, content: readFileSync19(p, "utf-8") });
12591
+ if (existsSync20(p)) {
12592
+ return c.json({ skill_id: skillId, path: p, content: readFileSync20(p, "utf-8") });
11955
12593
  }
11956
12594
  }
11957
12595
  return c.json({ detail: `skill ${skillId} \u4E0D\u5B58\u5728` }, 404);
@@ -12048,7 +12686,7 @@ function createConsoleRouter() {
12048
12686
  const defaults = generateSourceDefaults();
12049
12687
  try {
12050
12688
  const p = getRuntimeConfigPath();
12051
- if (existsSync19(p)) unlinkSync4(p);
12689
+ if (existsSync20(p)) unlinkSync4(p);
12052
12690
  } catch {
12053
12691
  }
12054
12692
  return c.json(withL22View(defaults));
@@ -12084,8 +12722,8 @@ var init_router = __esm({
12084
12722
  init_rules_loader();
12085
12723
  init_asset_dir();
12086
12724
  init_rules_mode();
12087
- __dirname10 = dirname12(fileURLToPath11(import.meta.url));
12088
- STATIC_DIR = join19(__dirname10, "static");
12725
+ __dirname10 = dirname13(fileURLToPath11(import.meta.url));
12726
+ STATIC_DIR = join20(__dirname10, "static");
12089
12727
  CONSOLE_TOKEN_ENV = "AGENT_MOSS_CONSOLE_TOKEN";
12090
12728
  }
12091
12729
  });
@@ -12112,7 +12750,7 @@ import { Hono as Hono2 } from "hono";
12112
12750
  import { cors } from "hono/cors";
12113
12751
  import { logger } from "hono/logger";
12114
12752
  import net from "net";
12115
- import { existsSync as existsSync20 } from "fs";
12753
+ import { existsSync as existsSync21 } from "fs";
12116
12754
  function logDeploymentSelfCheck() {
12117
12755
  try {
12118
12756
  const mode = getRulesMode();
@@ -12139,7 +12777,7 @@ function logDeploymentSelfCheck() {
12139
12777
  console.log("\u5C42\u7EA7\u5F00\u5173: " + layerKeys.map((k) => `${k}=${isLayerEnabled(k) ? "on" : "OFF"}`).join(" "));
12140
12778
  if (!builtinOnly) {
12141
12779
  try {
12142
- const dirs = rulesDirs().filter((d) => existsSync20(d));
12780
+ const dirs = rulesDirs().filter((d) => existsSync21(d));
12143
12781
  if (dirs.length > 0) {
12144
12782
  console.log(`\u89C4\u5219\u76EE\u5F55: ${dirs.join(", ")}\uFF08\u53EA\u8BFB\u5176\u4E2D ${OVERRIDE_FILENAME} / ${EXTRA_FILENAME}\uFF09`);
12145
12783
  }
@@ -12179,9 +12817,91 @@ function logDeploymentSelfCheck() {
12179
12817
  }
12180
12818
  function createApp() {
12181
12819
  enforceRulesContract();
12820
+ hardenExistingPaths();
12182
12821
  const app = new Hono2();
12183
- app.use("*", cors({ origin: "*", allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], allowHeaders: ["*"] }));
12822
+ app.use("*", cors({
12823
+ origin: (o) => allowedOrigins().includes(o) ? o : null,
12824
+ allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
12825
+ allowHeaders: ["*"]
12826
+ }));
12184
12827
  app.use("*", logger());
12828
+ const pools = new PooledLimiter();
12829
+ const limiter = new RateLimiter(rateLimitQps());
12830
+ app.use("*", async (c, next) => {
12831
+ const path6 = new URL(c.req.url).pathname;
12832
+ const method = c.req.method.toUpperCase();
12833
+ if (method === "OPTIONS") return next();
12834
+ const maxBody = maxBodyBytes();
12835
+ const cl = c.req.header("content-length");
12836
+ if (cl && maxBody > 0) {
12837
+ const n = Number.parseInt(cl, 10);
12838
+ if (Number.isFinite(n) && n > maxBody) {
12839
+ return c.json({ detail: `\u8BF7\u6C42\u4F53\u8D85\u8FC7\u4E0A\u9650 ${maxBody} \u5B57\u8282` }, 413);
12840
+ }
12841
+ }
12842
+ if (originCheckEnabled()) {
12843
+ const origin = c.req.header("origin") || "";
12844
+ if (origin && !originAllowed(origin, c.req.header("host") || "")) {
12845
+ console.warn(`[AgentMoss] \u5DF2\u62E6\u622A\u8DE8\u6E90\u8BF7\u6C42: origin=${origin} path=${path6}`);
12846
+ return c.json({ detail: `\u8DE8\u6E90\u6765\u6E90\u672A\u88AB\u5141\u8BB8: ${origin}` }, 403);
12847
+ }
12848
+ }
12849
+ const [okAuth, principal] = checkRequestAuth(c.req.raw.headers, path6);
12850
+ const isPublicPage = !path6.startsWith("/console/api") && (path6 === "/console" || path6.startsWith("/console/"));
12851
+ if (!okAuth && !isPublicPage) {
12852
+ return c.json({ detail: "Unauthorized: invalid or missing Bearer token" }, 401);
12853
+ }
12854
+ if (limiter.enabled && path6.startsWith("/api/")) {
12855
+ const key = principal ? "p:" + principalHash(principal) : "c:" + (c.req.header("x-forwarded-for") || "local");
12856
+ if (!limiter.allow(key)) {
12857
+ return c.json({ detail: "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5" }, 429);
12858
+ }
12859
+ }
12860
+ const ip = c.req.header("x-forwarded-for") || "local";
12861
+ const keys = [];
12862
+ const take = (key, limit) => {
12863
+ if (pools.tryAcquire(key, limit)) {
12864
+ keys.push(key);
12865
+ return true;
12866
+ }
12867
+ return false;
12868
+ };
12869
+ const busy = (detail, status = 503) => {
12870
+ c.header("Retry-After", "1");
12871
+ c.header("Connection", "close");
12872
+ return c.json({ detail }, status);
12873
+ };
12874
+ try {
12875
+ if (!principal && authEnabled()) {
12876
+ if (!take("unauth:total", unauthInflight())) {
12877
+ return busy("\u672A\u8BA4\u8BC1\u8BF7\u6C42\u5E76\u53D1\u8D85\u9650\uFF0C\u8BF7\u6301\u51ED\u8BC1\u8BBF\u95EE");
12878
+ }
12879
+ if (!take(`unauth:ip:${ip}`, unauthPerIp())) {
12880
+ return busy("\u8BE5\u6765\u6E90\u7684\u672A\u8BA4\u8BC1\u8BF7\u6C42\u5E76\u53D1\u8D85\u9650");
12881
+ }
12882
+ } else {
12883
+ const bound = boundAgentOf(principal);
12884
+ if (bound && !take(`agent:${bound}`, agentInflight())) {
12885
+ return busy(`agent '${bound}' \u7684\u5E76\u53D1\u8D85\u9650\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5`, 429);
12886
+ }
12887
+ }
12888
+ const pool = poolOf(path6);
12889
+ if (!take(`pool:${pool}`, poolLimit(pool))) {
12890
+ const qkey = principal ? "p:" + principalHash(principal) : "c:local";
12891
+ const [qn, abusive] = noteQueueReject(qkey);
12892
+ if (abusive && queueAbuseAction() === "deny") {
12893
+ return busy("\u5E76\u53D1\u62D2\u7EDD\u8FC7\u4E8E\u9891\u7E41\uFF0C\u7591\u4F3C\u6076\u610F\u8BF7\u6C42", 429);
12894
+ }
12895
+ if (abusive) {
12896
+ console.warn(`[AgentMoss] \u26A0\uFE0F \u7591\u4F3C\u6076\u610F\u5E76\u53D1\u5360\u7528: key=${qkey} \u7A97\u53E3\u5185\u88AB\u62D2 ${qn} \u6B21`);
12897
+ }
12898
+ return busy("\u670D\u52A1\u7E41\u5FD9\uFF08\u5E76\u53D1\u5DF2\u6EE1\uFF09\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
12899
+ }
12900
+ return await next();
12901
+ } finally {
12902
+ while (keys.length) pools.release(keys.pop());
12903
+ }
12904
+ });
12185
12905
  app.get("/api/v1/health", healthRoute);
12186
12906
  app.post("/api/v1/analyze", analyzeRoute);
12187
12907
  app.post("/api/v1/review", reviewRoute);
@@ -12199,12 +12919,12 @@ function createApp() {
12199
12919
  }
12200
12920
  async function findFreePort(startPort, host = "127.0.0.1", scan = 100) {
12201
12921
  for (let port = startPort; port < startPort + scan; port++) {
12202
- const free = await new Promise((resolve11) => {
12922
+ const free = await new Promise((resolve12) => {
12203
12923
  const server = net.createServer();
12204
12924
  server.listen(port, host, () => {
12205
- server.close(() => resolve11(true));
12925
+ server.close(() => resolve12(true));
12206
12926
  });
12207
- server.on("error", () => resolve11(false));
12927
+ server.on("error", () => resolve12(false));
12208
12928
  });
12209
12929
  if (free) {
12210
12930
  if (port !== startPort) {
@@ -12241,6 +12961,7 @@ async function warmUpEngine() {
12241
12961
  }
12242
12962
  }
12243
12963
  async function startServer(host = "127.0.0.1", port = 9090) {
12964
+ failFastOnAuthError();
12244
12965
  const actualPort = await findFreePort(port, host);
12245
12966
  const app = createApp();
12246
12967
  return {
@@ -12250,7 +12971,12 @@ async function startServer(host = "127.0.0.1", port = 9090) {
12250
12971
  logDeploymentSelfCheck();
12251
12972
  await warmUpEngine();
12252
12973
  const { serve: honoServe } = await import("@hono/node-server");
12253
- honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
12974
+ const srv = honoServe({ fetch: app.fetch, hostname: host, port: actualPort });
12975
+ try {
12976
+ srv.maxConnections = maxConnections();
12977
+ srv.keepAliveTimeout = keepAliveSec() * 1e3;
12978
+ } catch {
12979
+ }
12254
12980
  }
12255
12981
  };
12256
12982
  }
@@ -12261,28 +12987,29 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
12261
12987
  socketPath,
12262
12988
  close: async () => {
12263
12989
  if (!server) return;
12264
- await new Promise((resolve11) => {
12265
- server.close(() => resolve11());
12990
+ await new Promise((resolve12) => {
12991
+ server.close(() => resolve12());
12266
12992
  });
12267
12993
  server = null;
12268
12994
  },
12269
12995
  serve: async () => {
12270
- const { mkdirSync: mkdirSync10, existsSync: existsSync22, unlinkSync: unlinkSync5 } = await import("fs");
12271
- const { dirname: dirname14 } = await import("path");
12272
- const dir = dirname14(socketPath);
12996
+ failFastOnAuthError();
12997
+ const { mkdirSync: mkdirSync10, existsSync: existsSync23, unlinkSync: unlinkSync5 } = await import("fs");
12998
+ const { dirname: dirname15 } = await import("path");
12999
+ const dir = dirname15(socketPath);
12273
13000
  if (dir) {
12274
13001
  try {
12275
13002
  mkdirSync10(dir, { recursive: true });
12276
13003
  } catch {
12277
13004
  }
12278
13005
  }
12279
- if (existsSync22(socketPath)) {
13006
+ if (existsSync23(socketPath)) {
12280
13007
  const { connect } = await import("net");
12281
- const inUse = await new Promise((resolve11) => {
13008
+ const inUse = await new Promise((resolve12) => {
12282
13009
  const probe = connect(socketPath);
12283
13010
  const done = (v) => {
12284
13011
  probe.destroy();
12285
- resolve11(v);
13012
+ resolve12(v);
12286
13013
  };
12287
13014
  probe.once("connect", () => done(true));
12288
13015
  probe.once("error", () => done(false));
@@ -12305,25 +13032,79 @@ async function startUnixSocketServer(socketPath = "/var/run/agent_moss/agent_mos
12305
13032
  const { getRequestListener } = await import("@hono/node-server");
12306
13033
  const { createServer } = await import("http");
12307
13034
  server = createServer(getRequestListener(app.fetch));
12308
- await new Promise((resolve11, reject) => {
13035
+ try {
13036
+ server.maxConnections = maxConnections();
13037
+ server.keepAliveTimeout = keepAliveSec() * 1e3;
13038
+ } catch {
13039
+ }
13040
+ await new Promise((resolve12, reject) => {
12309
13041
  server.once("error", reject);
12310
- server.listen(socketPath, () => resolve11());
13042
+ server.listen(socketPath, () => resolve12());
12311
13043
  });
13044
+ try {
13045
+ const { chmodSync: chmodSync2 } = await import("fs");
13046
+ chmodSync2(socketPath, socketMode());
13047
+ console.log(`[AgentMoss] socket \u6743\u9650: 0${socketMode().toString(8)}\uFF08AGENT_MOSS_SOCKET_MODE\uFF09`);
13048
+ } catch (e) {
13049
+ console.warn(`[AgentMoss] \u8BBE\u7F6E socket \u6743\u9650\u5931\u8D25: ${String(e)}`);
13050
+ }
12312
13051
  console.log(`AgentMoss HTTP server on unix socket: ${socketPath}`);
12313
13052
  }
12314
13053
  };
12315
13054
  }
12316
- var WARMUP_COMMANDS;
13055
+ var PooledLimiter, RateLimiter, WARMUP_COMMANDS;
12317
13056
  var init_server = __esm({
12318
13057
  "src/server.ts"() {
12319
13058
  "use strict";
12320
13059
  init_esm_shims();
12321
13060
  init_rules_contract();
13061
+ init_security_policy();
12322
13062
  init_routes();
12323
13063
  init_console();
12324
13064
  init_runtime_config();
12325
13065
  init_rules_mode();
12326
13066
  init_rules_loader();
13067
+ PooledLimiter = class {
13068
+ counts = /* @__PURE__ */ new Map();
13069
+ tryAcquire(key, limit) {
13070
+ if (limit <= 0) return true;
13071
+ const cur = this.counts.get(key) || 0;
13072
+ if (cur >= limit) return false;
13073
+ this.counts.set(key, cur + 1);
13074
+ return true;
13075
+ }
13076
+ release(key) {
13077
+ const cur = this.counts.get(key) || 0;
13078
+ if (cur <= 1) this.counts.delete(key);
13079
+ else this.counts.set(key, cur - 1);
13080
+ }
13081
+ };
13082
+ RateLimiter = class {
13083
+ constructor(qps, burst) {
13084
+ this.qps = qps;
13085
+ this.burst = burst;
13086
+ }
13087
+ qps;
13088
+ burst;
13089
+ buckets = /* @__PURE__ */ new Map();
13090
+ get enabled() {
13091
+ return this.qps > 0;
13092
+ }
13093
+ allow(key) {
13094
+ if (!this.enabled) return true;
13095
+ const burst = this.burst ?? Math.max(this.qps, 1) * 2;
13096
+ const now = Date.now() / 1e3;
13097
+ const [tokens, last] = this.buckets.get(key) ?? [burst, now];
13098
+ const next = Math.min(burst, tokens + (now - last) * this.qps);
13099
+ if (next < 1) {
13100
+ this.buckets.set(key, [next, now]);
13101
+ return false;
13102
+ }
13103
+ this.buckets.set(key, [next - 1, now]);
13104
+ if (this.buckets.size > 1e4) this.buckets.clear();
13105
+ return true;
13106
+ }
13107
+ };
12327
13108
  WARMUP_COMMANDS = {
12328
13109
  linux: "whoami",
12329
13110
  macos: "whoami",
@@ -12342,26 +13123,26 @@ __export(rules_tools_exports, {
12342
13123
  rulesStatusText: () => rulesStatusText,
12343
13124
  templateSourceDir: () => templateSourceDir
12344
13125
  });
12345
- import { existsSync as existsSync21, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync20, writeFileSync as writeFileSync9 } from "fs";
12346
- import { dirname as dirname13, join as join20 } from "path";
13126
+ import { existsSync as existsSync22, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync21, writeFileSync as writeFileSync7 } from "fs";
13127
+ import { dirname as dirname14, join as join21 } from "path";
12347
13128
  import { fileURLToPath as fileURLToPath12 } from "url";
12348
13129
  function initRulesDir(targetDir, opts = {}) {
12349
13130
  mkdirSync9(targetDir, { recursive: true });
12350
13131
  const written = [];
12351
- const extraPath = join20(targetDir, EXTRA_FILENAME);
12352
- if (opts.force || !existsSync21(extraPath)) {
12353
- writeFileSync9(extraPath, JSON.stringify(EXTRA_TEMPLATE, null, 2), "utf-8");
13132
+ const extraPath = join21(targetDir, EXTRA_FILENAME);
13133
+ if (opts.force || !existsSync22(extraPath)) {
13134
+ writeFileSync7(extraPath, JSON.stringify(EXTRA_TEMPLATE, null, 2), "utf-8");
12354
13135
  written.push(extraPath);
12355
13136
  }
12356
- const samplePath = join20(targetDir, OVERRIDE_FILENAME);
12357
- if (opts.force || !existsSync21(samplePath)) {
13137
+ const samplePath = join21(targetDir, OVERRIDE_FILENAME);
13138
+ if (opts.force || !existsSync22(samplePath)) {
12358
13139
  let payload = OVERRIDE_TEMPLATE;
12359
13140
  if (opts.withBuiltin) {
12360
13141
  let data = {};
12361
13142
  const builtin = resolveRulesFile("l1_rules.json");
12362
- if (builtin && existsSync21(builtin)) {
13143
+ if (builtin && existsSync22(builtin)) {
12363
13144
  try {
12364
- data = JSON.parse(readFileSync20(builtin, "utf-8")) || {};
13145
+ data = JSON.parse(readFileSync21(builtin, "utf-8")) || {};
12365
13146
  } catch {
12366
13147
  data = {};
12367
13148
  }
@@ -12369,20 +13150,20 @@ function initRulesDir(targetDir, opts = {}) {
12369
13150
  payload = { _\u8BF4\u660E: OVERRIDE_BUILTIN_NOTE };
12370
13151
  for (const k of L1_KEYS) payload[k] = data[k] || [];
12371
13152
  }
12372
- writeFileSync9(samplePath, JSON.stringify(payload, null, 2), "utf-8");
13153
+ writeFileSync7(samplePath, JSON.stringify(payload, null, 2), "utf-8");
12373
13154
  written.push(samplePath);
12374
13155
  }
12375
- const readmeSrc = join20(templateSourceDir(), "README.md");
12376
- const readmeDst = join20(targetDir, "README.md");
12377
- if (existsSync21(readmeSrc) && (opts.force || !existsSync21(readmeDst))) {
12378
- writeFileSync9(readmeDst, readFileSync20(readmeSrc, "utf-8"), "utf-8");
13156
+ const readmeSrc = join21(templateSourceDir(), "README.md");
13157
+ const readmeDst = join21(targetDir, "README.md");
13158
+ if (existsSync22(readmeSrc) && (opts.force || !existsSync22(readmeDst))) {
13159
+ writeFileSync7(readmeDst, readFileSync21(readmeSrc, "utf-8"), "utf-8");
12379
13160
  written.push(readmeDst);
12380
13161
  }
12381
13162
  return written;
12382
13163
  }
12383
13164
  function templateSourceDir() {
12384
- const cands = [join20(__dirname11, "data", "rules_template"), join20(__dirname11, "..", "..", "agent_moss", "data", "rules_template")];
12385
- for (const c of cands) if (existsSync21(c)) return c;
13165
+ const cands = [join21(__dirname11, "data", "rules_template"), join21(__dirname11, "..", "..", "agent_moss", "data", "rules_template")];
13166
+ for (const c of cands) if (existsSync22(c)) return c;
12386
13167
  return cands[0];
12387
13168
  }
12388
13169
  function templateFiles(dirs) {
@@ -12390,7 +13171,7 @@ function templateFiles(dirs) {
12390
13171
  for (const d of dirs) {
12391
13172
  try {
12392
13173
  for (const name of readdirSync7(d)) {
12393
- if (name.endsWith(".json.\u793A\u4F8B")) out.push(join20(d, name));
13174
+ if (name.endsWith(".json.\u793A\u4F8B")) out.push(join21(d, name));
12394
13175
  }
12395
13176
  } catch {
12396
13177
  }
@@ -12399,7 +13180,7 @@ function templateFiles(dirs) {
12399
13180
  }
12400
13181
  function rulesStatusText() {
12401
13182
  const lines = [];
12402
- const existing = rulesDirs().filter((d) => existsSync21(d));
13183
+ const existing = rulesDirs().filter((d) => existsSync22(d));
12403
13184
  lines.push("\u89C4\u5219\u76EE\u5F55: " + (existing.length > 0 ? existing.join(", ") : "\uFF08\u672A\u8BBE\u7F6E / \u4E0D\u5B58\u5728\uFF09"));
12404
13185
  if (existing.length > 0) {
12405
13186
  lines.push(` \uFF08\u53EA\u8BFB\u5176\u4E2D\u7684 ${OVERRIDE_FILENAME} \u4E0E ${EXTRA_FILENAME}\uFF1B\u5176\u4ED6\u6587\u4EF6\u540D\u4E00\u5F8B\u4E0D\u8BFB\uFF09`);
@@ -12446,7 +13227,7 @@ var init_rules_tools = __esm({
12446
13227
  "use strict";
12447
13228
  init_esm_shims();
12448
13229
  init_rules_loader();
12449
- __dirname11 = dirname13(fileURLToPath12(import.meta.url));
13230
+ __dirname11 = dirname14(fileURLToPath12(import.meta.url));
12450
13231
  EXTRA_TEMPLATE = {
12451
13232
  _\u8BF4\u660E: [
12452
13233
  "\u8FFD\u52A0\u89C4\u5219\uFF1A\u5185\u7F6E\u89C4\u5219\u5168\u90E8\u4FDD\u7559\uFF0C\u4F60\u5199\u5728\u8FD9\u91CC\u7684\u89C4\u5219\u52A0\u5728\u5185\u7F6E\u4E4B\u540E\u4E00\u8D77\u5224\u5B9A\u3002",
@@ -12490,7 +13271,7 @@ init_esm_shims();
12490
13271
  init_server();
12491
13272
  init_package_json();
12492
13273
  import { fileURLToPath as fileURLToPath13 } from "url";
12493
- import { realpathSync } from "fs";
13274
+ import { realpathSync as realpathSync2 } from "fs";
12494
13275
  function printHelp() {
12495
13276
  console.log(`AgentMoss security analysis engine (TypeScript v${version})
12496
13277
 
@@ -12794,13 +13575,13 @@ async function main() {
12794
13575
  const otherInstances = [];
12795
13576
  for (let port = 9090; port <= 9095; port++) {
12796
13577
  try {
12797
- const data = await new Promise((resolve11, reject) => {
13578
+ const data = await new Promise((resolve12, reject) => {
12798
13579
  const req = http.get(`http://127.0.0.1:${port}/api/v1/health`, { timeout: 2e3 }, (res) => {
12799
13580
  let body = "";
12800
13581
  res.on("data", (chunk) => body += chunk);
12801
13582
  res.on("end", () => {
12802
13583
  try {
12803
- resolve11(JSON.parse(body));
13584
+ resolve12(JSON.parse(body));
12804
13585
  } catch {
12805
13586
  reject();
12806
13587
  }
@@ -12832,13 +13613,13 @@ async function main() {
12832
13613
  console.log(` Systemd: \u672A\u6CE8\u518C\uFF08\u8FD0\u884C agent-moss install --enable \u5B89\u88C5\uFF09`);
12833
13614
  }
12834
13615
  try {
12835
- const data = await new Promise((resolve11, reject) => {
13616
+ const data = await new Promise((resolve12, reject) => {
12836
13617
  const req = http.get(`http://127.0.0.1:${foundPort}/console/api/config`, { timeout: 3e3 }, (res) => {
12837
13618
  let body = "";
12838
13619
  res.on("data", (chunk) => body += chunk);
12839
13620
  res.on("end", () => {
12840
13621
  try {
12841
- resolve11(JSON.parse(body));
13622
+ resolve12(JSON.parse(body));
12842
13623
  } catch {
12843
13624
  reject();
12844
13625
  }
@@ -12893,8 +13674,8 @@ async function main() {
12893
13674
  var isMain = (() => {
12894
13675
  try {
12895
13676
  if (!process.argv[1]) return false;
12896
- const thisFile = realpathSync(fileURLToPath13(import.meta.url));
12897
- const argv1 = realpathSync(
13677
+ const thisFile = realpathSync2(fileURLToPath13(import.meta.url));
13678
+ const argv1 = realpathSync2(
12898
13679
  process.argv[1].startsWith("file://") ? fileURLToPath13(process.argv[1]) : process.argv[1]
12899
13680
  );
12900
13681
  return thisFile === argv1;