@kenhkl/agent-moss 0.17.5 → 0.17.7

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
@@ -1340,6 +1340,12 @@ function agentInflight() {
1340
1340
  function boundAgentOf(principal) {
1341
1341
  return principal.startsWith("agent:") ? principal.slice("agent:".length) : "";
1342
1342
  }
1343
+ function chainScope() {
1344
+ return (env("AGENT_MOSS_CHAIN_SCOPE", "system") || "system").toLowerCase();
1345
+ }
1346
+ function chainBucket(agentId = "") {
1347
+ return chainScope() === "agent" ? agentId || "default" : "__system__";
1348
+ }
1343
1349
  function rateLimitQps() {
1344
1350
  return envInt("AGENT_MOSS_RATE_LIMIT_QPS", 100);
1345
1351
  }
@@ -1411,6 +1417,8 @@ var init_models = __esm({
1411
1417
  action_history: z.array(ActionItemSchema).default([]).describe("Historical action sequence"),
1412
1418
  session_history: z.array(z.object({
1413
1419
  session_id: z.string(),
1420
+ /** 来源 agent(AgentGate 逐会话带上):记录在链路径里供告警/取证,不参与链匹配。 */
1421
+ agent_id: z.string().optional(),
1414
1422
  action_history: z.array(ActionItemSchema).default([])
1415
1423
  })).default([]).describe("Multi-session historical actions (optional): for L2.2 formal engine cross-session combo-chain / sensitive-data-flow verification. Provided by caller (incl. future agentgate)."),
1416
1424
  a_next: ActionItemSchema.describe("Next action to be executed"),
@@ -2095,12 +2103,19 @@ function hasUserConsent(actionHistory) {
2095
2103
  return t === "ask_user_question" && String(h.output || "").trim().length > 0;
2096
2104
  });
2097
2105
  }
2106
+ function hasWeakConsent(actionHistory) {
2107
+ return actionHistory.some((h) => {
2108
+ if (String(h.actionType || "").toLowerCase() !== "ask_user_question") return false;
2109
+ return CONSENT_TOPIC.test(String(h.actionDetail || ""));
2110
+ });
2111
+ }
2098
2112
  function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
2099
2113
  if (actionType !== "bash") return null;
2100
2114
  const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
2101
2115
  const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
2102
2116
  if (!isPasswordModify) return null;
2103
2117
  if (hasUserConsent(actionHistory)) return null;
2118
+ if (hasWeakConsent(actionHistory)) return null;
2104
2119
  return {
2105
2120
  allowed: false,
2106
2121
  reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u5BC6\u7801\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
@@ -2117,6 +2132,7 @@ function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osTyp
2117
2132
  const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
2118
2133
  if (!isUserDeletion) return null;
2119
2134
  if (hasUserConsent(actionHistory)) return null;
2135
+ if (hasWeakConsent(actionHistory)) return null;
2120
2136
  return {
2121
2137
  allowed: false,
2122
2138
  reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
@@ -2218,7 +2234,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
2218
2234
  violatedLayers: ["L2.1"]
2219
2235
  };
2220
2236
  }
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;
2237
+ 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, CONSENT_TOPIC, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
2222
2238
  var init_logic_rules = __esm({
2223
2239
  "src/engine/logic-rules.ts"() {
2224
2240
  "use strict";
@@ -2305,6 +2321,7 @@ var init_logic_rules = __esm({
2305
2321
  reason: String(c.reason || "\u68C0\u6D4B\u5230\u7EC4\u5408\u653B\u51FB\u94FE")
2306
2322
  }));
2307
2323
  })();
2324
+ CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
2308
2325
  INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
2309
2326
  DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
2310
2327
  _IFA = loadLogicRules().indirect_file_access ?? {};
@@ -2990,7 +3007,7 @@ function _scenarioRulesToDefaults() {
2990
3007
  if (!existsSync7(scenDir)) return out;
2991
3008
  let entries;
2992
3009
  try {
2993
- entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
3010
+ entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("__")).map((d) => d.name);
2994
3011
  } catch {
2995
3012
  return out;
2996
3013
  }
@@ -9370,7 +9387,7 @@ function markIntegrity(actionDetail) {
9370
9387
  }
9371
9388
  return { integrity: "medium", isLowSource: false };
9372
9389
  }
9373
- function initNode(sessionId, seq, actionType, actionDetail, timestamp) {
9390
+ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "") {
9374
9391
  const { integrity, isLowSource } = markIntegrity(actionDetail);
9375
9392
  return {
9376
9393
  sessionId,
@@ -9378,13 +9395,14 @@ function initNode(sessionId, seq, actionType, actionDetail, timestamp) {
9378
9395
  actionType,
9379
9396
  actionDetail,
9380
9397
  stage: markStage(actionDetail),
9398
+ agentId: agentId || void 0,
9381
9399
  resources: extractResources(actionDetail),
9382
9400
  integrity,
9383
9401
  isLowSource,
9384
9402
  timestamp: timestamp || void 0
9385
9403
  };
9386
9404
  }
9387
- function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
9405
+ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "") {
9388
9406
  const nodes = [];
9389
9407
  const edges = [];
9390
9408
  const stageFlags = /* @__PURE__ */ new Set();
@@ -9394,18 +9412,20 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
9394
9412
  if (ad) details.push({
9395
9413
  actionType: String(a.actionType || ""),
9396
9414
  actionDetail: ad,
9397
- calledAt: a.calledAt || void 0
9415
+ calledAt: a.calledAt || void 0,
9416
+ agentId: a.agentId || agentId || void 0
9398
9417
  });
9399
9418
  }
9400
9419
  if (aNext) {
9401
9420
  details.push({
9402
9421
  actionType: String(aNext.actionType || ""),
9403
9422
  actionDetail: String(aNext.actionDetail || ""),
9404
- calledAt: aNext.calledAt || void 0
9423
+ calledAt: aNext.calledAt || void 0,
9424
+ agentId: aNext.agentId || agentId || void 0
9405
9425
  });
9406
9426
  }
9407
9427
  details.forEach((d, seq) => {
9408
- const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt);
9428
+ const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "");
9409
9429
  nodes.push(node);
9410
9430
  if (node.stage) stageFlags.add(node.stage);
9411
9431
  });
@@ -9422,7 +9442,7 @@ function buildMultiSessionGraph(sessions, aNext) {
9422
9442
  }
9423
9443
  for (const s of sessions) {
9424
9444
  const sid = String(s.sessionId || "s");
9425
- mg.sessions.push(buildSessionGraph(s.actionHistory || [], void 0, sid));
9445
+ mg.sessions.push(buildSessionGraph(s.actionHistory || [], void 0, sid, s.agentId || ""));
9426
9446
  }
9427
9447
  if (aNext) {
9428
9448
  const last = mg.sessions[mg.sessions.length - 1];
@@ -9432,7 +9452,8 @@ function buildMultiSessionGraph(sessions, aNext) {
9432
9452
  last.nodes.length,
9433
9453
  String(aNext.actionType || ""),
9434
9454
  String(aNext.actionDetail || ""),
9435
- aNext.calledAt || void 0
9455
+ aNext.calledAt || void 0,
9456
+ aNext.agentId || ""
9436
9457
  ));
9437
9458
  if (last.nodes.length > 1) {
9438
9459
  last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
@@ -10219,8 +10240,8 @@ var init_stateful = __esm({
10219
10240
  }
10220
10241
  /** 增量推进:把当前动作追加到时间线 → 链指针推进 → 判定。 */
10221
10242
  advance(args) {
10222
- const { aNext, sessionId = "current", isError = false } = args;
10223
- const node = this.appendNode(aNext, sessionId, isError);
10243
+ const { aNext, sessionId = "current", isError = false, agentId = "" } = args;
10244
+ const node = this.appendNode(aNext, sessionId, isError, agentId);
10224
10245
  if (!node) {
10225
10246
  return { seq: this.seqNext, node: null, matched: null, pendingPrefixes: this.pendingPrefixes, exfil: [], shouldBlock: false };
10226
10247
  }
@@ -10240,7 +10261,7 @@ var init_stateful = __esm({
10240
10261
  }
10241
10262
  };
10242
10263
  }
10243
- appendNode(action, sessionId, isError = false) {
10264
+ appendNode(action, sessionId, isError = false, agentId = "") {
10244
10265
  const ad = String(action.actionDetail || "");
10245
10266
  const at = String(action.actionType || "");
10246
10267
  if (!ad) return null;
@@ -10257,6 +10278,7 @@ var init_stateful = __esm({
10257
10278
  resources: extractResources2(ad),
10258
10279
  instances: extractInstances(ad, stage),
10259
10280
  isError,
10281
+ agentId: String(action.agentId || agentId || ""),
10260
10282
  lifecycle: lifecycleFor(ad, stage, isError),
10261
10283
  timestamp: typeof action.calledAt === "string" ? action.calledAt : _StatefulChainTracker.now()
10262
10284
  };
@@ -10394,7 +10416,13 @@ var init_stateful = __esm({
10394
10416
  let want = stages[0];
10395
10417
  for (const n of this.nodes_) {
10396
10418
  if (n.stage === want) {
10397
- path6.push({ sessionId: n.sessionId, seq: n.seq, stage: n.stage, timestamp: n.timestamp });
10419
+ path6.push({
10420
+ sessionId: n.sessionId,
10421
+ seq: n.seq,
10422
+ stage: n.stage,
10423
+ timestamp: n.timestamp,
10424
+ agentId: n.agentId
10425
+ });
10398
10426
  const idx = stages.indexOf(n.stage);
10399
10427
  if (idx + 1 < stages.length) {
10400
10428
  want = stages[idx + 1];
@@ -10419,7 +10447,7 @@ var init_stateful = __esm({
10419
10447
  }
10420
10448
  if (taintedRes.size > 0) {
10421
10449
  this.tainted.add(idx);
10422
- for (const r of taintedRes) this.taintedResources.add(r);
10450
+ for (const r of node.resources) this.taintedResources.add(r);
10423
10451
  }
10424
10452
  }
10425
10453
  resourceTainted(resource) {
@@ -10468,6 +10496,7 @@ var init_stateful = __esm({
10468
10496
  this.nodes_.push({
10469
10497
  seq: Number(raw.seq || 0),
10470
10498
  sessionId: String(raw.sessionId || ""),
10499
+ agentId: String(raw.agentId || ""),
10471
10500
  actionType: String(raw.actionType || ""),
10472
10501
  actionDetail: String(raw.actionDetail || ""),
10473
10502
  stage: String(raw.stage || ""),
@@ -10574,9 +10603,9 @@ var init_chain_state = __esm({
10574
10603
  delete this.data[agentId];
10575
10604
  this._scheduleWrite();
10576
10605
  }
10577
- applyAction(agentId, aNext, sessionId, isError = false) {
10606
+ applyAction(agentId, aNext, sessionId, isError = false, sourceAgent = "") {
10578
10607
  const t = this.load(agentId) || new StatefulChainTracker(agentId);
10579
- const r = t.advance({ aNext, sessionId, isError });
10608
+ const r = t.advance({ aNext, sessionId, isError, agentId: sourceAgent });
10580
10609
  this.save(t);
10581
10610
  return r;
10582
10611
  }
@@ -10891,10 +10920,14 @@ async function runAnalysis(input) {
10891
10920
  const formalSessions = [];
10892
10921
  if (input.sessionHistory) {
10893
10922
  for (const s of input.sessionHistory) {
10894
- formalSessions.push({ sessionId: s.sessionId || "s", actionHistory: s.actionHistory || [] });
10923
+ formalSessions.push({
10924
+ sessionId: s.sessionId || "s",
10925
+ ...s.agentId ? { agentId: s.agentId } : {},
10926
+ actionHistory: s.actionHistory || []
10927
+ });
10895
10928
  }
10896
10929
  }
10897
- formalSessions.push({ sessionId: "current", actionHistory: input.actionHistory });
10930
+ formalSessions.push({ sessionId: "current", agentId: input.agentId, actionHistory: input.actionHistory });
10898
10931
  const formalResult = formalEngine.verify(formalSessions, input.aNext);
10899
10932
  if (!formalResult.verified) {
10900
10933
  const v = formalResult.violations[0];
@@ -11990,6 +12023,8 @@ async function analyzeRoute(c) {
11990
12023
  })),
11991
12024
  sessionHistory: (req.session_history || []).map((s) => ({
11992
12025
  sessionId: s.session_id,
12026
+ // 来源 agent(AgentGate 逐会话带上,用于链路径取证展示;不参与链匹配)
12027
+ ...s.agent_id ? { agentId: s.agent_id } : {},
11993
12028
  actionHistory: (s.action_history || []).map((h) => ({
11994
12029
  actionType: h.action_type,
11995
12030
  actionDetail: h.action_detail,
@@ -12048,10 +12083,14 @@ async function analyzeRoute(c) {
12048
12083
  if (incEnabled === "1" || incEnabled === "true" || incEnabled === "yes" || incEnabled === "on") {
12049
12084
  try {
12050
12085
  const store = getChainStateStoreFor(agentId);
12051
- const inc = store.applyAction(agentId, {
12052
- actionType: req.a_next.action_type,
12053
- actionDetail: req.a_next.action_detail
12054
- }, req.session_id, Boolean(meta.is_error));
12086
+ const inc = store.applyAction(
12087
+ chainBucket(agentId),
12088
+ { actionType: req.a_next.action_type, actionDetail: req.a_next.action_detail },
12089
+ req.session_id,
12090
+ Boolean(meta.is_error),
12091
+ agentId
12092
+ // 来源:真实 agent(桶 key 可能是 __system__)
12093
+ );
12055
12094
  const judgment = incMatchedToDeny(inc);
12056
12095
  if (judgment) {
12057
12096
  const layers = [...result.violated_layers];
@@ -12116,7 +12155,7 @@ function getChainStateStoreFor(_agentId) {
12116
12155
  }
12117
12156
  function incMatchedToDeny(inc) {
12118
12157
  if (inc.matched) {
12119
- const chain = (inc.matched.path || []).map((p) => [p.sessionId, p.seq, p.stage, p.timestamp]);
12158
+ const chain = (inc.matched.path || []).map((p) => [p.sessionId, p.seq, p.stage, p.timestamp, p.agentId ?? null]);
12120
12159
  return {
12121
12160
  property_id: "cross-" + inc.matched.chainId,
12122
12161
  reason: inc.matched.reason,
@@ -12141,9 +12180,10 @@ async function reviewRoute(c) {
12141
12180
  const req = ReviewRequestSchema.parse(body);
12142
12181
  const agentId = req.agent_id || "default";
12143
12182
  const store = getChainStateStoreFor(agentId);
12144
- let tracker = store.load(agentId);
12183
+ const bucket = chainBucket(agentId);
12184
+ let tracker = store.load(bucket);
12145
12185
  if (!tracker) {
12146
- tracker = new StatefulChainTracker(agentId);
12186
+ tracker = new StatefulChainTracker(bucket);
12147
12187
  }
12148
12188
  if (req.db_path && !pathWithin(req.db_path, reviewRoots())) {
12149
12189
  return c.json({
@@ -1345,12 +1345,19 @@ function hasUserConsent(actionHistory) {
1345
1345
  return t === "ask_user_question" && String(h.output || "").trim().length > 0;
1346
1346
  });
1347
1347
  }
1348
+ function hasWeakConsent(actionHistory) {
1349
+ return actionHistory.some((h) => {
1350
+ if (String(h.actionType || "").toLowerCase() !== "ask_user_question") return false;
1351
+ return CONSENT_TOPIC.test(String(h.actionDetail || ""));
1352
+ });
1353
+ }
1348
1354
  function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
1349
1355
  if (actionType !== "bash") return null;
1350
1356
  const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
1351
1357
  const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
1352
1358
  if (!isPasswordModify) return null;
1353
1359
  if (hasUserConsent(actionHistory)) return null;
1360
+ if (hasWeakConsent(actionHistory)) return null;
1354
1361
  return {
1355
1362
  allowed: false,
1356
1363
  reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u5BC6\u7801\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
@@ -1367,6 +1374,7 @@ function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osTyp
1367
1374
  const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
1368
1375
  if (!isUserDeletion) return null;
1369
1376
  if (hasUserConsent(actionHistory)) return null;
1377
+ if (hasWeakConsent(actionHistory)) return null;
1370
1378
  return {
1371
1379
  allowed: false,
1372
1380
  reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
@@ -1468,7 +1476,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
1468
1476
  violatedLayers: ["L2.1"]
1469
1477
  };
1470
1478
  }
1471
- 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;
1479
+ 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, CONSENT_TOPIC, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
1472
1480
  var init_logic_rules = __esm({
1473
1481
  "src/engine/logic-rules.ts"() {
1474
1482
  "use strict";
@@ -1555,6 +1563,7 @@ var init_logic_rules = __esm({
1555
1563
  reason: String(c.reason || "\u68C0\u6D4B\u5230\u7EC4\u5408\u653B\u51FB\u94FE")
1556
1564
  }));
1557
1565
  })();
1566
+ CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
1558
1567
  INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
1559
1568
  DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
1560
1569
  _IFA = loadLogicRules().indirect_file_access ?? {};
@@ -2240,7 +2249,7 @@ function _scenarioRulesToDefaults() {
2240
2249
  if (!existsSync5(scenDir)) return out;
2241
2250
  let entries;
2242
2251
  try {
2243
- entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
2252
+ entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("__")).map((d) => d.name);
2244
2253
  } catch {
2245
2254
  return out;
2246
2255
  }
@@ -8360,7 +8369,7 @@ function markIntegrity(actionDetail) {
8360
8369
  }
8361
8370
  return { integrity: "medium", isLowSource: false };
8362
8371
  }
8363
- function initNode(sessionId, seq, actionType, actionDetail, timestamp) {
8372
+ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "") {
8364
8373
  const { integrity, isLowSource } = markIntegrity(actionDetail);
8365
8374
  return {
8366
8375
  sessionId,
@@ -8368,13 +8377,14 @@ function initNode(sessionId, seq, actionType, actionDetail, timestamp) {
8368
8377
  actionType,
8369
8378
  actionDetail,
8370
8379
  stage: markStage(actionDetail),
8380
+ agentId: agentId || void 0,
8371
8381
  resources: extractResources(actionDetail),
8372
8382
  integrity,
8373
8383
  isLowSource,
8374
8384
  timestamp: timestamp || void 0
8375
8385
  };
8376
8386
  }
8377
- function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
8387
+ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "") {
8378
8388
  const nodes = [];
8379
8389
  const edges = [];
8380
8390
  const stageFlags = /* @__PURE__ */ new Set();
@@ -8384,18 +8394,20 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
8384
8394
  if (ad) details.push({
8385
8395
  actionType: String(a.actionType || ""),
8386
8396
  actionDetail: ad,
8387
- calledAt: a.calledAt || void 0
8397
+ calledAt: a.calledAt || void 0,
8398
+ agentId: a.agentId || agentId || void 0
8388
8399
  });
8389
8400
  }
8390
8401
  if (aNext) {
8391
8402
  details.push({
8392
8403
  actionType: String(aNext.actionType || ""),
8393
8404
  actionDetail: String(aNext.actionDetail || ""),
8394
- calledAt: aNext.calledAt || void 0
8405
+ calledAt: aNext.calledAt || void 0,
8406
+ agentId: aNext.agentId || agentId || void 0
8395
8407
  });
8396
8408
  }
8397
8409
  details.forEach((d, seq) => {
8398
- const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt);
8410
+ const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "");
8399
8411
  nodes.push(node);
8400
8412
  if (node.stage) stageFlags.add(node.stage);
8401
8413
  });
@@ -8412,7 +8424,7 @@ function buildMultiSessionGraph(sessions, aNext) {
8412
8424
  }
8413
8425
  for (const s of sessions) {
8414
8426
  const sid = String(s.sessionId || "s");
8415
- mg.sessions.push(buildSessionGraph(s.actionHistory || [], void 0, sid));
8427
+ mg.sessions.push(buildSessionGraph(s.actionHistory || [], void 0, sid, s.agentId || ""));
8416
8428
  }
8417
8429
  if (aNext) {
8418
8430
  const last = mg.sessions[mg.sessions.length - 1];
@@ -8422,7 +8434,8 @@ function buildMultiSessionGraph(sessions, aNext) {
8422
8434
  last.nodes.length,
8423
8435
  String(aNext.actionType || ""),
8424
8436
  String(aNext.actionDetail || ""),
8425
- aNext.calledAt || void 0
8437
+ aNext.calledAt || void 0,
8438
+ aNext.agentId || ""
8426
8439
  ));
8427
8440
  if (last.nodes.length > 1) {
8428
8441
  last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
@@ -9316,10 +9329,14 @@ async function runAnalysis(input) {
9316
9329
  const formalSessions = [];
9317
9330
  if (input.sessionHistory) {
9318
9331
  for (const s of input.sessionHistory) {
9319
- formalSessions.push({ sessionId: s.sessionId || "s", actionHistory: s.actionHistory || [] });
9332
+ formalSessions.push({
9333
+ sessionId: s.sessionId || "s",
9334
+ ...s.agentId ? { agentId: s.agentId } : {},
9335
+ actionHistory: s.actionHistory || []
9336
+ });
9320
9337
  }
9321
9338
  }
9322
- formalSessions.push({ sessionId: "current", actionHistory: input.actionHistory });
9339
+ formalSessions.push({ sessionId: "current", agentId: input.agentId, actionHistory: input.actionHistory });
9323
9340
  const formalResult = formalEngine.verify(formalSessions, input.aNext);
9324
9341
  if (!formalResult.verified) {
9325
9342
  const v = formalResult.violations[0];
package/dist/server.js CHANGED
@@ -1186,6 +1186,12 @@ function agentInflight() {
1186
1186
  function boundAgentOf(principal) {
1187
1187
  return principal.startsWith("agent:") ? principal.slice("agent:".length) : "";
1188
1188
  }
1189
+ function chainScope() {
1190
+ return (env("AGENT_MOSS_CHAIN_SCOPE", "system") || "system").toLowerCase();
1191
+ }
1192
+ function chainBucket(agentId = "") {
1193
+ return chainScope() === "agent" ? agentId || "default" : "__system__";
1194
+ }
1189
1195
  function rateLimitQps() {
1190
1196
  return envInt("AGENT_MOSS_RATE_LIMIT_QPS", 100);
1191
1197
  }
@@ -1845,12 +1851,19 @@ function hasUserConsent(actionHistory) {
1845
1851
  return t === "ask_user_question" && String(h.output || "").trim().length > 0;
1846
1852
  });
1847
1853
  }
1854
+ function hasWeakConsent(actionHistory) {
1855
+ return actionHistory.some((h) => {
1856
+ if (String(h.actionType || "").toLowerCase() !== "ask_user_question") return false;
1857
+ return CONSENT_TOPIC.test(String(h.actionDetail || ""));
1858
+ });
1859
+ }
1848
1860
  function checkPasswordConsent(actionType, actionDetail, actionHistory, osType) {
1849
1861
  if (actionType !== "bash") return null;
1850
1862
  const pwdPatterns = runtimeConfigBridge.current?.effectivePasswordPatterns(osType) ?? PASSWORD_MODIFY_PATTERNS;
1851
1863
  const isPasswordModify = pwdPatterns.some((p) => p.test(actionDetail));
1852
1864
  if (!isPasswordModify) return null;
1853
1865
  if (hasUserConsent(actionHistory)) return null;
1866
+ if (hasWeakConsent(actionHistory)) return null;
1854
1867
  return {
1855
1868
  allowed: false,
1856
1869
  reason: "\u6267\u884C\u975E\u4EA4\u4E92\u5F0F\u5BC6\u7801\u4FEE\u6539\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u5BC6\u7801\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u5BC6\u7801\u4FEE\u6539\u98CE\u9669",
@@ -1867,6 +1880,7 @@ function checkUserDeletionConsent(actionType, actionDetail, actionHistory, osTyp
1867
1880
  const isUserDeletion = udPatterns.some((p) => p.test(actionDetail));
1868
1881
  if (!isUserDeletion) return null;
1869
1882
  if (hasUserConsent(actionHistory)) return null;
1883
+ if (hasWeakConsent(actionHistory)) return null;
1870
1884
  return {
1871
1885
  allowed: false,
1872
1886
  reason: "\u6267\u884C\u5220\u9664\u7CFB\u7EDF\u7528\u6237\u547D\u4EE4\uFF0C\u4F46\u5386\u53F2\u6267\u884C\u5E8F\u5217\u4E2D\u672A\u53D1\u73B0\u5411\u7528\u6237\u786E\u8BA4\u7684 ask_user_question \u64CD\u4F5C\uFF0C\u53EF\u80FD\u5B58\u5728\u672A\u6388\u6743\u7684\u7528\u6237\u5220\u9664\u98CE\u9669",
@@ -1968,7 +1982,7 @@ function checkIndirectFileAccess(actionType, actionDetail, cwd, osType) {
1968
1982
  violatedLayers: ["L2.1"]
1969
1983
  };
1970
1984
  }
1971
- 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;
1985
+ 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, CONSENT_TOPIC, INLINE_SCRIPT_RE, DIR_TRAVERSAL_KW, _IFA, IFA_READ_KEYWORDS, IFA_WRITE_KEYWORDS, IFA_EXEC_KEYWORDS, IFA_OPEN_KEYWORDS;
1972
1986
  var init_logic_rules = __esm({
1973
1987
  "src/engine/logic-rules.ts"() {
1974
1988
  "use strict";
@@ -2055,6 +2069,7 @@ var init_logic_rules = __esm({
2055
2069
  reason: String(c.reason || "\u68C0\u6D4B\u5230\u7EC4\u5408\u653B\u51FB\u94FE")
2056
2070
  }));
2057
2071
  })();
2072
+ CONSENT_TOPIC = /密码|口令|passwd|password|凭据|凭证|用户|账户|账号|userdel|deluser|groupdel/i;
2058
2073
  INLINE_SCRIPT_RE = /(?:^|\s)(?:python|python3|python3\.\d+|perl|perl5|ruby|jruby|mruby|node|php|lua)\s+(?:-[a-zA-Z]*[cer][a-zA-Z]*\s+)/i;
2059
2074
  DIR_TRAVERSAL_KW = loadInline().dir_traversal_keywords ?? [];
2060
2075
  _IFA = loadLogicRules().indirect_file_access ?? {};
@@ -2740,7 +2755,7 @@ function _scenarioRulesToDefaults() {
2740
2755
  if (!existsSync7(scenDir)) return out;
2741
2756
  let entries;
2742
2757
  try {
2743
- entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
2758
+ entries = readdirSync4(scenDir, { withFileTypes: true }).filter((d) => d.isDirectory() && !d.name.startsWith("__")).map((d) => d.name);
2744
2759
  } catch {
2745
2760
  return out;
2746
2761
  }
@@ -8855,7 +8870,7 @@ function markIntegrity(actionDetail) {
8855
8870
  }
8856
8871
  return { integrity: "medium", isLowSource: false };
8857
8872
  }
8858
- function initNode(sessionId, seq, actionType, actionDetail, timestamp) {
8873
+ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "") {
8859
8874
  const { integrity, isLowSource } = markIntegrity(actionDetail);
8860
8875
  return {
8861
8876
  sessionId,
@@ -8863,13 +8878,14 @@ function initNode(sessionId, seq, actionType, actionDetail, timestamp) {
8863
8878
  actionType,
8864
8879
  actionDetail,
8865
8880
  stage: markStage(actionDetail),
8881
+ agentId: agentId || void 0,
8866
8882
  resources: extractResources(actionDetail),
8867
8883
  integrity,
8868
8884
  isLowSource,
8869
8885
  timestamp: timestamp || void 0
8870
8886
  };
8871
8887
  }
8872
- function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
8888
+ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "") {
8873
8889
  const nodes = [];
8874
8890
  const edges = [];
8875
8891
  const stageFlags = /* @__PURE__ */ new Set();
@@ -8879,18 +8895,20 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current") {
8879
8895
  if (ad) details.push({
8880
8896
  actionType: String(a.actionType || ""),
8881
8897
  actionDetail: ad,
8882
- calledAt: a.calledAt || void 0
8898
+ calledAt: a.calledAt || void 0,
8899
+ agentId: a.agentId || agentId || void 0
8883
8900
  });
8884
8901
  }
8885
8902
  if (aNext) {
8886
8903
  details.push({
8887
8904
  actionType: String(aNext.actionType || ""),
8888
8905
  actionDetail: String(aNext.actionDetail || ""),
8889
- calledAt: aNext.calledAt || void 0
8906
+ calledAt: aNext.calledAt || void 0,
8907
+ agentId: aNext.agentId || agentId || void 0
8890
8908
  });
8891
8909
  }
8892
8910
  details.forEach((d, seq) => {
8893
- const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt);
8911
+ const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "");
8894
8912
  nodes.push(node);
8895
8913
  if (node.stage) stageFlags.add(node.stage);
8896
8914
  });
@@ -8907,7 +8925,7 @@ function buildMultiSessionGraph(sessions, aNext) {
8907
8925
  }
8908
8926
  for (const s of sessions) {
8909
8927
  const sid = String(s.sessionId || "s");
8910
- mg.sessions.push(buildSessionGraph(s.actionHistory || [], void 0, sid));
8928
+ mg.sessions.push(buildSessionGraph(s.actionHistory || [], void 0, sid, s.agentId || ""));
8911
8929
  }
8912
8930
  if (aNext) {
8913
8931
  const last = mg.sessions[mg.sessions.length - 1];
@@ -8917,7 +8935,8 @@ function buildMultiSessionGraph(sessions, aNext) {
8917
8935
  last.nodes.length,
8918
8936
  String(aNext.actionType || ""),
8919
8937
  String(aNext.actionDetail || ""),
8920
- aNext.calledAt || void 0
8938
+ aNext.calledAt || void 0,
8939
+ aNext.agentId || ""
8921
8940
  ));
8922
8941
  if (last.nodes.length > 1) {
8923
8942
  last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
@@ -9704,8 +9723,8 @@ var init_stateful = __esm({
9704
9723
  }
9705
9724
  /** 增量推进:把当前动作追加到时间线 → 链指针推进 → 判定。 */
9706
9725
  advance(args) {
9707
- const { aNext, sessionId = "current", isError = false } = args;
9708
- const node = this.appendNode(aNext, sessionId, isError);
9726
+ const { aNext, sessionId = "current", isError = false, agentId = "" } = args;
9727
+ const node = this.appendNode(aNext, sessionId, isError, agentId);
9709
9728
  if (!node) {
9710
9729
  return { seq: this.seqNext, node: null, matched: null, pendingPrefixes: this.pendingPrefixes, exfil: [], shouldBlock: false };
9711
9730
  }
@@ -9725,7 +9744,7 @@ var init_stateful = __esm({
9725
9744
  }
9726
9745
  };
9727
9746
  }
9728
- appendNode(action, sessionId, isError = false) {
9747
+ appendNode(action, sessionId, isError = false, agentId = "") {
9729
9748
  const ad = String(action.actionDetail || "");
9730
9749
  const at = String(action.actionType || "");
9731
9750
  if (!ad) return null;
@@ -9742,6 +9761,7 @@ var init_stateful = __esm({
9742
9761
  resources: extractResources2(ad),
9743
9762
  instances: extractInstances(ad, stage),
9744
9763
  isError,
9764
+ agentId: String(action.agentId || agentId || ""),
9745
9765
  lifecycle: lifecycleFor(ad, stage, isError),
9746
9766
  timestamp: typeof action.calledAt === "string" ? action.calledAt : _StatefulChainTracker.now()
9747
9767
  };
@@ -9879,7 +9899,13 @@ var init_stateful = __esm({
9879
9899
  let want = stages[0];
9880
9900
  for (const n of this.nodes_) {
9881
9901
  if (n.stage === want) {
9882
- path6.push({ sessionId: n.sessionId, seq: n.seq, stage: n.stage, timestamp: n.timestamp });
9902
+ path6.push({
9903
+ sessionId: n.sessionId,
9904
+ seq: n.seq,
9905
+ stage: n.stage,
9906
+ timestamp: n.timestamp,
9907
+ agentId: n.agentId
9908
+ });
9883
9909
  const idx = stages.indexOf(n.stage);
9884
9910
  if (idx + 1 < stages.length) {
9885
9911
  want = stages[idx + 1];
@@ -9904,7 +9930,7 @@ var init_stateful = __esm({
9904
9930
  }
9905
9931
  if (taintedRes.size > 0) {
9906
9932
  this.tainted.add(idx);
9907
- for (const r of taintedRes) this.taintedResources.add(r);
9933
+ for (const r of node.resources) this.taintedResources.add(r);
9908
9934
  }
9909
9935
  }
9910
9936
  resourceTainted(resource) {
@@ -9953,6 +9979,7 @@ var init_stateful = __esm({
9953
9979
  this.nodes_.push({
9954
9980
  seq: Number(raw.seq || 0),
9955
9981
  sessionId: String(raw.sessionId || ""),
9982
+ agentId: String(raw.agentId || ""),
9956
9983
  actionType: String(raw.actionType || ""),
9957
9984
  actionDetail: String(raw.actionDetail || ""),
9958
9985
  stage: String(raw.stage || ""),
@@ -10059,9 +10086,9 @@ var init_chain_state = __esm({
10059
10086
  delete this.data[agentId];
10060
10087
  this._scheduleWrite();
10061
10088
  }
10062
- applyAction(agentId, aNext, sessionId, isError = false) {
10089
+ applyAction(agentId, aNext, sessionId, isError = false, sourceAgent = "") {
10063
10090
  const t = this.load(agentId) || new StatefulChainTracker(agentId);
10064
- const r = t.advance({ aNext, sessionId, isError });
10091
+ const r = t.advance({ aNext, sessionId, isError, agentId: sourceAgent });
10065
10092
  this.save(t);
10066
10093
  return r;
10067
10094
  }
@@ -10376,10 +10403,14 @@ async function runAnalysis(input) {
10376
10403
  const formalSessions = [];
10377
10404
  if (input.sessionHistory) {
10378
10405
  for (const s of input.sessionHistory) {
10379
- formalSessions.push({ sessionId: s.sessionId || "s", actionHistory: s.actionHistory || [] });
10406
+ formalSessions.push({
10407
+ sessionId: s.sessionId || "s",
10408
+ ...s.agentId ? { agentId: s.agentId } : {},
10409
+ actionHistory: s.actionHistory || []
10410
+ });
10380
10411
  }
10381
10412
  }
10382
- formalSessions.push({ sessionId: "current", actionHistory: input.actionHistory });
10413
+ formalSessions.push({ sessionId: "current", agentId: input.agentId, actionHistory: input.actionHistory });
10383
10414
  const formalResult = formalEngine.verify(formalSessions, input.aNext);
10384
10415
  if (!formalResult.verified) {
10385
10416
  const v = formalResult.violations[0];
@@ -10747,6 +10778,8 @@ var AnalyzeRequestSchema = z.object({
10747
10778
  action_history: z.array(ActionItemSchema).default([]).describe("Historical action sequence"),
10748
10779
  session_history: z.array(z.object({
10749
10780
  session_id: z.string(),
10781
+ /** 来源 agent(AgentGate 逐会话带上):记录在链路径里供告警/取证,不参与链匹配。 */
10782
+ agent_id: z.string().optional(),
10750
10783
  action_history: z.array(ActionItemSchema).default([])
10751
10784
  })).default([]).describe("Multi-session historical actions (optional): for L2.2 formal engine cross-session combo-chain / sensitive-data-flow verification. Provided by caller (incl. future agentgate)."),
10752
10785
  a_next: ActionItemSchema.describe("Next action to be executed"),
@@ -11661,6 +11694,8 @@ async function analyzeRoute(c) {
11661
11694
  })),
11662
11695
  sessionHistory: (req.session_history || []).map((s) => ({
11663
11696
  sessionId: s.session_id,
11697
+ // 来源 agent(AgentGate 逐会话带上,用于链路径取证展示;不参与链匹配)
11698
+ ...s.agent_id ? { agentId: s.agent_id } : {},
11664
11699
  actionHistory: (s.action_history || []).map((h) => ({
11665
11700
  actionType: h.action_type,
11666
11701
  actionDetail: h.action_detail,
@@ -11719,10 +11754,14 @@ async function analyzeRoute(c) {
11719
11754
  if (incEnabled === "1" || incEnabled === "true" || incEnabled === "yes" || incEnabled === "on") {
11720
11755
  try {
11721
11756
  const store = getChainStateStoreFor(agentId);
11722
- const inc = store.applyAction(agentId, {
11723
- actionType: req.a_next.action_type,
11724
- actionDetail: req.a_next.action_detail
11725
- }, req.session_id, Boolean(meta.is_error));
11757
+ const inc = store.applyAction(
11758
+ chainBucket(agentId),
11759
+ { actionType: req.a_next.action_type, actionDetail: req.a_next.action_detail },
11760
+ req.session_id,
11761
+ Boolean(meta.is_error),
11762
+ agentId
11763
+ // 来源:真实 agent(桶 key 可能是 __system__)
11764
+ );
11726
11765
  const judgment = incMatchedToDeny(inc);
11727
11766
  if (judgment) {
11728
11767
  const layers = [...result.violated_layers];
@@ -11788,7 +11827,7 @@ function getChainStateStoreFor(_agentId) {
11788
11827
  }
11789
11828
  function incMatchedToDeny(inc) {
11790
11829
  if (inc.matched) {
11791
- const chain = (inc.matched.path || []).map((p) => [p.sessionId, p.seq, p.stage, p.timestamp]);
11830
+ const chain = (inc.matched.path || []).map((p) => [p.sessionId, p.seq, p.stage, p.timestamp, p.agentId ?? null]);
11792
11831
  return {
11793
11832
  property_id: "cross-" + inc.matched.chainId,
11794
11833
  reason: inc.matched.reason,
@@ -11813,9 +11852,10 @@ async function reviewRoute(c) {
11813
11852
  const req = ReviewRequestSchema.parse(body);
11814
11853
  const agentId = req.agent_id || "default";
11815
11854
  const store = getChainStateStoreFor(agentId);
11816
- let tracker = store.load(agentId);
11855
+ const bucket = chainBucket(agentId);
11856
+ let tracker = store.load(bucket);
11817
11857
  if (!tracker) {
11818
- tracker = new StatefulChainTracker(agentId);
11858
+ tracker = new StatefulChainTracker(bucket);
11819
11859
  }
11820
11860
  if (req.db_path && !pathWithin(req.db_path, reviewRoots())) {
11821
11861
  return c.json({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenhkl/agent-moss",
3
- "version": "0.17.5",
3
+ "version": "0.17.7",
4
4
  "description": "Multi-layer security analysis engine for AI agents (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",