@kenhkl/agent-moss 0.17.8 → 0.17.11

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
@@ -1246,31 +1246,42 @@ function hardenExistingPaths() {
1246
1246
  function chainDetailMode() {
1247
1247
  return (env("AGENT_MOSS_CHAIN_DETAIL", "redacted") || "redacted").toLowerCase();
1248
1248
  }
1249
- function redactChain(chain, currentSessionId) {
1249
+ function redactChain(chain, currentInstanceId = "", currentSessionId = "") {
1250
1250
  if (!chain || chain.length === 0) return [];
1251
1251
  if (chainDetailMode() === "full") return [...chain];
1252
1252
  return chain.map((step) => {
1253
1253
  if (Array.isArray(step) && step.length >= 3) {
1254
1254
  const [sid, seq, , ts] = step;
1255
- if (String(sid) === String(currentSessionId)) return [...step];
1255
+ const inst = step.length > 5 ? String(step[5] ?? "") : "";
1256
+ const sameInstance = !!(inst && currentInstanceId && inst === String(currentInstanceId));
1257
+ const sameSessionFallback = !inst && !!currentSessionId && String(sid) === String(currentSessionId);
1258
+ if (sameInstance || sameSessionFallback) return [...step];
1256
1259
  return ["<other-session>", seq, "<\u5DF2\u9690\u85CF>", ts ?? ""];
1257
1260
  }
1258
1261
  return step;
1259
1262
  });
1260
1263
  }
1261
- function redactGraph(graph, currentSessionId) {
1264
+ function redactGraph(graph, currentInstanceId = "", currentSessionId = "") {
1262
1265
  if (!graph) return null;
1263
1266
  if (chainDetailMode() === "full") return graph;
1264
1267
  const nodes = (graph.nodes || []).map((n) => {
1265
1268
  const m = { ...n };
1266
- if (String(m.session_id ?? "") !== String(currentSessionId)) {
1269
+ const inst = String(m.instance_id ?? "") || "";
1270
+ const sid = String(m.session_id ?? "") || "";
1271
+ const sameInstance = !!(inst && currentInstanceId && inst === String(currentInstanceId));
1272
+ const sameSessionFallback = !inst && !!currentSessionId && sid === String(currentSessionId);
1273
+ if (!(sameInstance || sameSessionFallback)) {
1267
1274
  m.session_id = "<other-session>";
1268
1275
  m.action_detail = "<\u5DF2\u9690\u85CF>";
1269
1276
  m.resources = [];
1270
1277
  }
1271
1278
  return m;
1272
1279
  });
1273
- return { nodes, edges: graph.edges || [] };
1280
+ const out = { nodes, edges: graph.edges || [] };
1281
+ for (const key of ["property_id", "property_short", "property_name", "property_expression"]) {
1282
+ if (graph[key] !== void 0) out[key] = graph[key];
1283
+ }
1284
+ return out;
1274
1285
  }
1275
1286
  function sessionSweepThreshold() {
1276
1287
  return envInt("AGENT_MOSS_SESSION_SWEEP_THRESHOLD", 50);
@@ -1433,6 +1444,8 @@ var init_models = __esm({
1433
1444
  session_id: z.string(),
1434
1445
  /** 来源 agent(AgentGate 逐会话带上):记录在链路径里供告警/取证,不参与链匹配。 */
1435
1446
  agent_id: z.string().optional(),
1447
+ /** 来源实例:仅用于展示层脱敏(同实例不隐藏),不参与链匹配。 */
1448
+ instance_id: z.string().optional(),
1436
1449
  action_history: z.array(ActionItemSchema).default([])
1437
1450
  })).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)."),
1438
1451
  a_next: ActionItemSchema.describe("Next action to be executed"),
@@ -1440,6 +1453,7 @@ var init_models = __esm({
1440
1453
  os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
1441
1454
  cwd: z.string().default("").describe("Current working directory"),
1442
1455
  agent_id: z.string().default("").describe("Caller agent identifier, e.g. 'xiaoo' / 'opencode' / 'opendesk'. Empty = generic mode (no agent-specific rules)."),
1456
+ instance_id: z.string().default("").describe("Agent installation/instance identifier. Used only for display-layer redaction: nodes from the same instance are returned unredacted. Does not affect detection."),
1443
1457
  metadata: z.record(z.unknown()).optional().describe("Extended metadata")
1444
1458
  });
1445
1459
  AnalyzeResponseSchema = z.object({
@@ -1477,7 +1491,9 @@ var init_models = __esm({
1477
1491
  // 节点在性质中的角色:source/carrier/sink(P2)或阶段名(P1)
1478
1492
  role: z.string().optional(),
1479
1493
  // 是否为违例见证节点(当前动作补全链 / 触发外传)
1480
- witness: z.boolean().optional()
1494
+ witness: z.boolean().optional(),
1495
+ // 来源实例(仅展示层脱敏:同实例不隐藏)
1496
+ instance_id: z.string().optional()
1481
1497
  })
1482
1498
  ),
1483
1499
  edges: z.array(
@@ -9436,7 +9452,7 @@ function markIntegrity(actionDetail) {
9436
9452
  }
9437
9453
  return { integrity: "medium", isLowSource: false };
9438
9454
  }
9439
- function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "") {
9455
+ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "", instanceId = "") {
9440
9456
  const { integrity, isLowSource } = markIntegrity(actionDetail);
9441
9457
  return {
9442
9458
  sessionId,
@@ -9445,13 +9461,14 @@ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId =
9445
9461
  actionDetail,
9446
9462
  stage: markStage(actionDetail),
9447
9463
  agentId: agentId || void 0,
9464
+ instanceId: instanceId || void 0,
9448
9465
  resources: extractResources(actionDetail),
9449
9466
  integrity,
9450
9467
  isLowSource,
9451
9468
  timestamp: timestamp || void 0
9452
9469
  };
9453
9470
  }
9454
- function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "") {
9471
+ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "", instanceId = "") {
9455
9472
  const nodes = [];
9456
9473
  const edges = [];
9457
9474
  const stageFlags = /* @__PURE__ */ new Set();
@@ -9462,7 +9479,8 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
9462
9479
  actionType: String(a.actionType || ""),
9463
9480
  actionDetail: ad,
9464
9481
  calledAt: a.calledAt || void 0,
9465
- agentId: a.agentId || agentId || void 0
9482
+ agentId: a.agentId || agentId || void 0,
9483
+ instanceId: a.instanceId || instanceId || void 0
9466
9484
  });
9467
9485
  }
9468
9486
  if (aNext) {
@@ -9470,11 +9488,12 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
9470
9488
  actionType: String(aNext.actionType || ""),
9471
9489
  actionDetail: String(aNext.actionDetail || ""),
9472
9490
  calledAt: aNext.calledAt || void 0,
9473
- agentId: aNext.agentId || agentId || void 0
9491
+ agentId: aNext.agentId || agentId || void 0,
9492
+ instanceId: aNext.instanceId || instanceId || void 0
9474
9493
  });
9475
9494
  }
9476
9495
  details.forEach((d, seq) => {
9477
- const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "");
9496
+ const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "", d.instanceId || "");
9478
9497
  nodes.push(node);
9479
9498
  if (node.stage) stageFlags.add(node.stage);
9480
9499
  });
@@ -9486,12 +9505,18 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
9486
9505
  function buildMultiSessionGraph(sessions, aNext) {
9487
9506
  const mg = { sessions: [], crossEdges: [] };
9488
9507
  if (!sessions || sessions.length === 0) {
9489
- mg.sessions.push(buildSessionGraph([], aNext, "current"));
9508
+ mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext?.instanceId || ""));
9490
9509
  return mg;
9491
9510
  }
9492
9511
  for (const s of sessions) {
9493
9512
  const sid = String(s.sessionId || "s");
9494
- mg.sessions.push(buildSessionGraph(s.actionHistory || [], void 0, sid, s.agentId || ""));
9513
+ mg.sessions.push(buildSessionGraph(
9514
+ s.actionHistory || [],
9515
+ void 0,
9516
+ sid,
9517
+ s.agentId || "",
9518
+ s.instanceId || ""
9519
+ ));
9495
9520
  }
9496
9521
  if (aNext) {
9497
9522
  const last = mg.sessions[mg.sessions.length - 1];
@@ -9502,13 +9527,14 @@ function buildMultiSessionGraph(sessions, aNext) {
9502
9527
  String(aNext.actionType || ""),
9503
9528
  String(aNext.actionDetail || ""),
9504
9529
  aNext.calledAt || void 0,
9505
- aNext.agentId || ""
9530
+ aNext.agentId || "",
9531
+ aNext.instanceId || ""
9506
9532
  ));
9507
9533
  if (last.nodes.length > 1) {
9508
9534
  last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
9509
9535
  }
9510
9536
  } else {
9511
- mg.sessions.push(buildSessionGraph([], aNext, "current"));
9537
+ mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext.instanceId || ""));
9512
9538
  }
9513
9539
  }
9514
9540
  addCrossDataEdges(mg);
@@ -9572,7 +9598,8 @@ function graphFromPath(nodes, path6, edgeType = "next", propertyMeta, roles, wit
9572
9598
  seq: item.seq,
9573
9599
  stage: n ? n.stage : "",
9574
9600
  action_detail: n ? n.actionDetail : item.detail,
9575
- resources: n ? [...n.resources].sort() : []
9601
+ resources: n ? [...n.resources].sort() : [],
9602
+ instance_id: n ? n.instanceId || "" : ""
9576
9603
  });
9577
9604
  if (gNodes.length > 1) {
9578
9605
  gEdges.push({ from: gNodes.length - 2, to: gNodes.length - 1, type: edgeType });
@@ -9740,7 +9767,8 @@ function checkSensitiveDataFlow(graph) {
9740
9767
  stage: n.stage,
9741
9768
  action_detail: n.actionDetail,
9742
9769
  resources: [...n.resources].sort(),
9743
- role
9770
+ role,
9771
+ instance_id: n.instanceId || ""
9744
9772
  });
9745
9773
  if (k > 0) {
9746
9774
  const prev = chain[k - 1];
@@ -9890,14 +9918,23 @@ function checkNeverExfilAfterPrivesc(graph) {
9890
9918
  if (candidates.length > 0) {
9891
9919
  const pre = nodes[candidates[candidates.length - 1]];
9892
9920
  const post = nodes[firstEv];
9921
+ const path6 = [
9922
+ { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
9923
+ { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
9924
+ ];
9893
9925
  return [{
9894
9926
  propertyId: "no-exfil-after-privesc",
9895
9927
  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",
9896
9928
  riskLevel: "critical",
9897
- path: [
9898
- { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
9899
- { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
9900
- ]
9929
+ path: path6,
9930
+ graph: graphFromPath(nodes, path6, "next", {
9931
+ property_id: "no-exfil-after-privesc",
9932
+ property_short: "P4",
9933
+ property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
9934
+ }, ["privesc", "sink"], true, [
9935
+ { token: "PRIVESC", node_index: 0 },
9936
+ { token: "EXFIL", node_index: 1 }
9937
+ ])
9901
9938
  }];
9902
9939
  }
9903
9940
  return violations;
@@ -9907,14 +9944,23 @@ function checkNeverExfilAfterPrivesc(graph) {
9907
9944
  for (let j = i + 1; j < nodes.length; j++) {
9908
9945
  if (!isExfilNode(nodes[j])) continue;
9909
9946
  if (privescInWindow(nodes, [i], j, windowSec).length === 0) continue;
9947
+ const path6 = [
9948
+ { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
9949
+ { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
9950
+ ];
9910
9951
  violations.push({
9911
9952
  propertyId: "no-exfil-after-privesc",
9912
9953
  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",
9913
9954
  riskLevel: "critical",
9914
- path: [
9915
- { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
9916
- { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
9917
- ]
9955
+ path: path6,
9956
+ graph: graphFromPath(nodes, path6, "next", {
9957
+ property_id: "no-exfil-after-privesc",
9958
+ property_short: "P4",
9959
+ property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
9960
+ }, ["privesc", "sink"], true, [
9961
+ { token: "PRIVESC", node_index: 0 },
9962
+ { token: "EXFIL", node_index: 1 }
9963
+ ])
9918
9964
  });
9919
9965
  return violations;
9920
9966
  }
@@ -9939,14 +9985,23 @@ function checkNeverExfilAfterCred(graph) {
9939
9985
  }
9940
9986
  if (pre) {
9941
9987
  const post = nodes[firstEv];
9988
+ const path6 = [
9989
+ { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
9990
+ { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
9991
+ ];
9942
9992
  return [{
9943
9993
  propertyId: "no-exfil-after-credential",
9944
9994
  reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
9945
9995
  riskLevel: "critical",
9946
- path: [
9947
- { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
9948
- { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
9949
- ]
9996
+ path: path6,
9997
+ graph: graphFromPath(nodes, path6, "data", {
9998
+ property_id: "no-exfil-after-credential",
9999
+ property_short: "P5",
10000
+ property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
10001
+ }, ["source", "sink"], true, [
10002
+ { token: "CRED", node_index: 0 },
10003
+ { token: "EXFIL", node_index: 1 }
10004
+ ])
9950
10005
  }];
9951
10006
  }
9952
10007
  return violations;
@@ -9955,14 +10010,23 @@ function checkNeverExfilAfterCred(graph) {
9955
10010
  if (!isCredNode(nodes[i])) continue;
9956
10011
  for (let j = i + 1; j < nodes.length; j++) {
9957
10012
  if (isExfilNode(nodes[j])) {
10013
+ const path6 = [
10014
+ { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
10015
+ { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
10016
+ ];
9958
10017
  violations.push({
9959
10018
  propertyId: "no-exfil-after-credential",
9960
10019
  reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
9961
10020
  riskLevel: "critical",
9962
- path: [
9963
- { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
9964
- { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
9965
- ]
10021
+ path: path6,
10022
+ graph: graphFromPath(nodes, path6, "data", {
10023
+ property_id: "no-exfil-after-credential",
10024
+ property_short: "P5",
10025
+ property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
10026
+ }, ["source", "sink"], true, [
10027
+ { token: "CRED", node_index: 0 },
10028
+ { token: "EXFIL", node_index: 1 }
10029
+ ])
9966
10030
  });
9967
10031
  return violations;
9968
10032
  }
@@ -10114,11 +10178,19 @@ function checkSensitiveDataAggregation(graph) {
10114
10178
  if (!isExfilNode(n)) continue;
10115
10179
  const srcs = sourcesFor(i);
10116
10180
  if (srcs.size >= 2) {
10181
+ const path6 = [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }];
10117
10182
  violations.push({
10118
10183
  propertyId: "sensitive-data-aggregation",
10119
10184
  reason: `\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20\uFF1Aegress \u805A\u5408 ${srcs.size} \u4E2A\u4E8B\u4EF6\u6E90\u6570\u636E\uFF08Rule 12\uFF09`,
10120
10185
  riskLevel: "critical",
10121
- path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
10186
+ path: path6,
10187
+ graph: graphFromPath(nodes, path6, "next", {
10188
+ property_id: "sensitive-data-aggregation",
10189
+ property_short: "P8",
10190
+ property_name: "\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20"
10191
+ }, ["sink"], true, [
10192
+ { token: "EXFIL", node_index: 0 }
10193
+ ])
10122
10194
  });
10123
10195
  if (witness !== void 0) return violations;
10124
10196
  }
@@ -10383,7 +10455,7 @@ var init_stateful = __esm({
10383
10455
  for (const s of sessions || []) {
10384
10456
  const sid = String(s.sessionId || "s");
10385
10457
  for (const a of s.actionHistory || []) {
10386
- const node = this.appendNode(a, sid);
10458
+ const node = this.appendNode(a, sid, false, s.agentId || "", s.instanceId || "");
10387
10459
  if (!node) {
10388
10460
  continue;
10389
10461
  }
@@ -10397,8 +10469,8 @@ var init_stateful = __esm({
10397
10469
  }
10398
10470
  /** 增量推进:把当前动作追加到时间线 → 链指针推进 → 判定。 */
10399
10471
  advance(args) {
10400
- const { aNext, sessionId = "current", isError = false, agentId = "" } = args;
10401
- const node = this.appendNode(aNext, sessionId, isError, agentId);
10472
+ const { aNext, sessionId = "current", isError = false, agentId = "", instanceId = "" } = args;
10473
+ const node = this.appendNode(aNext, sessionId, isError, agentId, instanceId);
10402
10474
  if (!node) {
10403
10475
  return { seq: this.seqNext, node: null, matched: null, pendingPrefixes: this.pendingPrefixes, exfil: [], shouldBlock: false };
10404
10476
  }
@@ -10418,7 +10490,7 @@ var init_stateful = __esm({
10418
10490
  }
10419
10491
  };
10420
10492
  }
10421
- appendNode(action, sessionId, isError = false, agentId = "") {
10493
+ appendNode(action, sessionId, isError = false, agentId = "", instanceId = "") {
10422
10494
  const ad = String(action.actionDetail || "");
10423
10495
  const at = String(action.actionType || "");
10424
10496
  if (!ad) return null;
@@ -10436,6 +10508,7 @@ var init_stateful = __esm({
10436
10508
  instances: extractInstances(ad, stage),
10437
10509
  isError,
10438
10510
  agentId: String(action.agentId || agentId || ""),
10511
+ instanceId: String(action.instanceId || instanceId || ""),
10439
10512
  lifecycle: lifecycleFor(ad, stage, isError),
10440
10513
  timestamp: typeof action.calledAt === "string" ? action.calledAt : _StatefulChainTracker.now()
10441
10514
  };
@@ -10579,7 +10652,8 @@ var init_stateful = __esm({
10579
10652
  seq: n.seq,
10580
10653
  stage: n.stage,
10581
10654
  timestamp: n.timestamp,
10582
- agentId: n.agentId
10655
+ agentId: n.agentId,
10656
+ instanceId: n.instanceId
10583
10657
  });
10584
10658
  const idx = stages.indexOf(n.stage);
10585
10659
  if (idx + 1 < stages.length) {
@@ -10602,7 +10676,8 @@ var init_stateful = __esm({
10602
10676
  stage: p.stage,
10603
10677
  action_detail: n ? n.actionDetail : "",
10604
10678
  resources: n ? [...n.resources].sort() : [],
10605
- role: stages[i] || ""
10679
+ role: stages[i] || "",
10680
+ instance_id: p.instanceId || ""
10606
10681
  };
10607
10682
  });
10608
10683
  const gEdges = [];
@@ -10670,7 +10745,7 @@ var init_stateful = __esm({
10670
10745
  risk_level: "critical",
10671
10746
  path: chain.map((j) => {
10672
10747
  const n = this.nodes_[j];
10673
- return [n.sessionId, n.seq, n.actionDetail.slice(0, 60), n.timestamp];
10748
+ return [n.sessionId, n.seq, n.actionDetail.slice(0, 60), n.timestamp, null, n.instanceId];
10674
10749
  }),
10675
10750
  graph: this.taintGraph(chain)
10676
10751
  }];
@@ -10702,7 +10777,8 @@ var init_stateful = __esm({
10702
10777
  stage: n.stage,
10703
10778
  action_detail: n.actionDetail,
10704
10779
  resources: [...n.resources].sort(),
10705
- role
10780
+ role,
10781
+ instance_id: n.instanceId
10706
10782
  });
10707
10783
  if (k > 0) {
10708
10784
  const prev = chain[k - 1];
@@ -10774,6 +10850,8 @@ var init_stateful = __esm({
10774
10850
  seq: Number(raw.seq || 0),
10775
10851
  sessionId: String(raw.sessionId || ""),
10776
10852
  agentId: String(raw.agentId || ""),
10853
+ instanceId: String(raw.instanceId || ""),
10854
+ // 旧快照无此字段 → 空
10777
10855
  actionType: String(raw.actionType || ""),
10778
10856
  actionDetail: String(raw.actionDetail || ""),
10779
10857
  stage: String(raw.stage || ""),
@@ -10918,9 +10996,9 @@ var init_chain_state = __esm({
10918
10996
  delete this.data[agentId];
10919
10997
  this._scheduleWrite();
10920
10998
  }
10921
- applyAction(agentId, aNext, sessionId, isError = false, sourceAgent = "") {
10999
+ applyAction(agentId, aNext, sessionId, isError = false, sourceAgent = "", instanceId = "") {
10922
11000
  const t = this.load(agentId) || new StatefulChainTracker(agentId);
10923
- const r = t.advance({ aNext, sessionId, isError, agentId: sourceAgent });
11001
+ const r = t.advance({ aNext, sessionId, isError, agentId: sourceAgent, instanceId });
10924
11002
  this.save(t);
10925
11003
  return r;
10926
11004
  }
@@ -11238,12 +11316,21 @@ async function runAnalysis(input) {
11238
11316
  formalSessions.push({
11239
11317
  sessionId: s.sessionId || "s",
11240
11318
  ...s.agentId ? { agentId: s.agentId } : {},
11319
+ ...s.instanceId ? { instanceId: s.instanceId } : {},
11241
11320
  actionHistory: s.actionHistory || []
11242
11321
  });
11243
11322
  }
11244
11323
  }
11245
- formalSessions.push({ sessionId: "current", agentId: input.agentId, actionHistory: input.actionHistory });
11246
- const formalResult = formalEngine.verify(formalSessions, input.aNext);
11324
+ formalSessions.push({
11325
+ sessionId: "current",
11326
+ agentId: input.agentId,
11327
+ instanceId: input.instanceId || "",
11328
+ actionHistory: input.actionHistory
11329
+ });
11330
+ const formalResult = formalEngine.verify(formalSessions, {
11331
+ ...input.aNext,
11332
+ instanceId: input.instanceId || ""
11333
+ });
11247
11334
  if (!formalResult.verified) {
11248
11335
  const v = formalResult.violations[0];
11249
11336
  if (isFormalObserveMode()) {
@@ -12327,6 +12414,9 @@ async function analyzeRoute(c) {
12327
12414
  `[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`
12328
12415
  );
12329
12416
  }
12417
+ const instanceId = String(
12418
+ req.instance_id || req.metadata?.instance_id || ""
12419
+ );
12330
12420
  const result = await analyze({
12331
12421
  sessionId: req.session_id,
12332
12422
  promptSession: req.prompt_session,
@@ -12342,8 +12432,9 @@ async function analyzeRoute(c) {
12342
12432
  })),
12343
12433
  sessionHistory: (req.session_history || []).map((s) => ({
12344
12434
  sessionId: s.session_id,
12345
- // 来源 agent(AgentGate 逐会话带上,用于链路径取证展示;不参与链匹配)
12435
+ // 来源 agent/实例(AgentGate 逐会话带上,用于链路径取证/展示脱敏;不参与链匹配)
12346
12436
  ...s.agent_id ? { agentId: s.agent_id } : {},
12437
+ ...s.instance_id ? { instanceId: s.instance_id } : {},
12347
12438
  actionHistory: (s.action_history || []).map((h) => ({
12348
12439
  actionType: h.action_type,
12349
12440
  actionDetail: h.action_detail,
@@ -12367,11 +12458,13 @@ async function analyzeRoute(c) {
12367
12458
  })),
12368
12459
  aNext: {
12369
12460
  actionType: req.a_next.action_type,
12370
- actionDetail: req.a_next.action_detail
12461
+ actionDetail: req.a_next.action_detail,
12462
+ instanceId
12371
12463
  },
12372
12464
  reason: req.reason,
12373
12465
  osType,
12374
12466
  cwd: req.cwd,
12467
+ instanceId,
12375
12468
  // agent_id:AgentGate V1 normalizer 历史上把 agent_type 塞 metadata,
12376
12469
  // 顶层为空时兜底(OpenDesk 的 agent-moss 需 agent_id 加载专属规则)。
12377
12470
  agentId,
@@ -12407,8 +12500,9 @@ async function analyzeRoute(c) {
12407
12500
  { actionType: req.a_next.action_type, actionDetail: req.a_next.action_detail },
12408
12501
  req.session_id,
12409
12502
  Boolean(meta.is_error),
12410
- agentId
12503
+ agentId,
12411
12504
  // 来源:真实 agent(桶 key 可能是 __system__)
12505
+ instanceId
12412
12506
  );
12413
12507
  const judgment = incMatchedToDeny(inc);
12414
12508
  if (judgment) {
@@ -12429,8 +12523,8 @@ async function analyzeRoute(c) {
12429
12523
  confidence: 90,
12430
12524
  analysis_duration_ms: Math.round((performance.now() - t0) * 10) / 10,
12431
12525
  // 跨会话节点脱敏(时间线按 agent_id 共享,原样返回等于泄露别人的命令原文)
12432
- violation_chain: redactChain(judgment.violation_chain, req.session_id),
12433
- violation_graph: redactGraph(judgment.violation_graph, req.session_id)
12526
+ violation_chain: redactChain(judgment.violation_chain, instanceId, req.session_id),
12527
+ violation_graph: redactGraph(judgment.violation_graph, instanceId, req.session_id)
12434
12528
  })
12435
12529
  );
12436
12530
  }
@@ -12452,7 +12546,7 @@ async function analyzeRoute(c) {
12452
12546
  confidence: result.confidence,
12453
12547
  analysis_duration_ms: Math.round(durationMs * 10) / 10,
12454
12548
  violation_chain: result.violation_chain ?? [],
12455
- violation_graph: redactGraph(result.violation_graph, req.session_id)
12549
+ violation_graph: redactGraph(result.violation_graph, instanceId, req.session_id)
12456
12550
  })
12457
12551
  );
12458
12552
  } catch (err) {
@@ -81,6 +81,7 @@ interface AnalysisInput {
81
81
  * 由调用方(含未来 agentgate 非侵入式采集)提供。 */
82
82
  sessionHistory?: {
83
83
  sessionId?: string;
84
+ instanceId?: string;
84
85
  actionHistory: {
85
86
  actionType: string;
86
87
  actionDetail: string;
@@ -90,12 +91,15 @@ interface AnalysisInput {
90
91
  aNext: {
91
92
  actionType: string;
92
93
  actionDetail: string;
94
+ instanceId?: string;
93
95
  };
94
96
  reason: string;
95
97
  osType: string;
96
98
  cwd: string;
97
99
  /** 调用方 agent 标识(如 'xiaoo' / 'opencode' / 'opendesk')。空 = 通用模式。 */
98
100
  agentId: string;
101
+ /** 调用方实例标识(安装级)。仅用于展示层脱敏:同实例节点不隐藏。 */
102
+ instanceId?: string;
99
103
  /** 调用方场景标识(如 'bank'),用于加载场景定制规则(客户沙箱的目录访问边界/禁 ssh 等)。
100
104
  * 空时兜底环境变量 AGENT_MOSS_SCENARIO(部署级,客户沙箱设一次,所有请求生效)。 */
101
105
  scenarioId?: string;
@@ -8369,7 +8369,7 @@ function markIntegrity(actionDetail) {
8369
8369
  }
8370
8370
  return { integrity: "medium", isLowSource: false };
8371
8371
  }
8372
- function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "") {
8372
+ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "", instanceId = "") {
8373
8373
  const { integrity, isLowSource } = markIntegrity(actionDetail);
8374
8374
  return {
8375
8375
  sessionId,
@@ -8378,13 +8378,14 @@ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId =
8378
8378
  actionDetail,
8379
8379
  stage: markStage(actionDetail),
8380
8380
  agentId: agentId || void 0,
8381
+ instanceId: instanceId || void 0,
8381
8382
  resources: extractResources(actionDetail),
8382
8383
  integrity,
8383
8384
  isLowSource,
8384
8385
  timestamp: timestamp || void 0
8385
8386
  };
8386
8387
  }
8387
- function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "") {
8388
+ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "", instanceId = "") {
8388
8389
  const nodes = [];
8389
8390
  const edges = [];
8390
8391
  const stageFlags = /* @__PURE__ */ new Set();
@@ -8395,7 +8396,8 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
8395
8396
  actionType: String(a.actionType || ""),
8396
8397
  actionDetail: ad,
8397
8398
  calledAt: a.calledAt || void 0,
8398
- agentId: a.agentId || agentId || void 0
8399
+ agentId: a.agentId || agentId || void 0,
8400
+ instanceId: a.instanceId || instanceId || void 0
8399
8401
  });
8400
8402
  }
8401
8403
  if (aNext) {
@@ -8403,11 +8405,12 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
8403
8405
  actionType: String(aNext.actionType || ""),
8404
8406
  actionDetail: String(aNext.actionDetail || ""),
8405
8407
  calledAt: aNext.calledAt || void 0,
8406
- agentId: aNext.agentId || agentId || void 0
8408
+ agentId: aNext.agentId || agentId || void 0,
8409
+ instanceId: aNext.instanceId || instanceId || void 0
8407
8410
  });
8408
8411
  }
8409
8412
  details.forEach((d, seq) => {
8410
- const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "");
8413
+ const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "", d.instanceId || "");
8411
8414
  nodes.push(node);
8412
8415
  if (node.stage) stageFlags.add(node.stage);
8413
8416
  });
@@ -8419,12 +8422,18 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
8419
8422
  function buildMultiSessionGraph(sessions, aNext) {
8420
8423
  const mg = { sessions: [], crossEdges: [] };
8421
8424
  if (!sessions || sessions.length === 0) {
8422
- mg.sessions.push(buildSessionGraph([], aNext, "current"));
8425
+ mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext?.instanceId || ""));
8423
8426
  return mg;
8424
8427
  }
8425
8428
  for (const s of sessions) {
8426
8429
  const sid = String(s.sessionId || "s");
8427
- mg.sessions.push(buildSessionGraph(s.actionHistory || [], void 0, sid, s.agentId || ""));
8430
+ mg.sessions.push(buildSessionGraph(
8431
+ s.actionHistory || [],
8432
+ void 0,
8433
+ sid,
8434
+ s.agentId || "",
8435
+ s.instanceId || ""
8436
+ ));
8428
8437
  }
8429
8438
  if (aNext) {
8430
8439
  const last = mg.sessions[mg.sessions.length - 1];
@@ -8435,13 +8444,14 @@ function buildMultiSessionGraph(sessions, aNext) {
8435
8444
  String(aNext.actionType || ""),
8436
8445
  String(aNext.actionDetail || ""),
8437
8446
  aNext.calledAt || void 0,
8438
- aNext.agentId || ""
8447
+ aNext.agentId || "",
8448
+ aNext.instanceId || ""
8439
8449
  ));
8440
8450
  if (last.nodes.length > 1) {
8441
8451
  last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
8442
8452
  }
8443
8453
  } else {
8444
- mg.sessions.push(buildSessionGraph([], aNext, "current"));
8454
+ mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext.instanceId || ""));
8445
8455
  }
8446
8456
  }
8447
8457
  addCrossDataEdges(mg);
@@ -8495,7 +8505,8 @@ function graphFromPath(nodes, path5, edgeType = "next", propertyMeta, roles, wit
8495
8505
  seq: item.seq,
8496
8506
  stage: n ? n.stage : "",
8497
8507
  action_detail: n ? n.actionDetail : item.detail,
8498
- resources: n ? [...n.resources].sort() : []
8508
+ resources: n ? [...n.resources].sort() : [],
8509
+ instance_id: n ? n.instanceId || "" : ""
8499
8510
  });
8500
8511
  if (gNodes.length > 1) {
8501
8512
  gEdges.push({ from: gNodes.length - 2, to: gNodes.length - 1, type: edgeType });
@@ -8666,7 +8677,8 @@ function checkSensitiveDataFlow(graph) {
8666
8677
  stage: n.stage,
8667
8678
  action_detail: n.actionDetail,
8668
8679
  resources: [...n.resources].sort(),
8669
- role
8680
+ role,
8681
+ instance_id: n.instanceId || ""
8670
8682
  });
8671
8683
  if (k > 0) {
8672
8684
  const prev = chain[k - 1];
@@ -8824,14 +8836,23 @@ function checkNeverExfilAfterPrivesc(graph) {
8824
8836
  if (candidates.length > 0) {
8825
8837
  const pre = nodes[candidates[candidates.length - 1]];
8826
8838
  const post = nodes[firstEv];
8839
+ const path5 = [
8840
+ { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
8841
+ { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
8842
+ ];
8827
8843
  return [{
8828
8844
  propertyId: "no-exfil-after-privesc",
8829
8845
  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",
8830
8846
  riskLevel: "critical",
8831
- path: [
8832
- { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
8833
- { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
8834
- ]
8847
+ path: path5,
8848
+ graph: graphFromPath(nodes, path5, "next", {
8849
+ property_id: "no-exfil-after-privesc",
8850
+ property_short: "P4",
8851
+ property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
8852
+ }, ["privesc", "sink"], true, [
8853
+ { token: "PRIVESC", node_index: 0 },
8854
+ { token: "EXFIL", node_index: 1 }
8855
+ ])
8835
8856
  }];
8836
8857
  }
8837
8858
  return violations;
@@ -8841,14 +8862,23 @@ function checkNeverExfilAfterPrivesc(graph) {
8841
8862
  for (let j = i + 1; j < nodes.length; j++) {
8842
8863
  if (!isExfilNode(nodes[j])) continue;
8843
8864
  if (privescInWindow(nodes, [i], j, windowSec).length === 0) continue;
8865
+ const path5 = [
8866
+ { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
8867
+ { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
8868
+ ];
8844
8869
  violations.push({
8845
8870
  propertyId: "no-exfil-after-privesc",
8846
8871
  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",
8847
8872
  riskLevel: "critical",
8848
- path: [
8849
- { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
8850
- { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
8851
- ]
8873
+ path: path5,
8874
+ graph: graphFromPath(nodes, path5, "next", {
8875
+ property_id: "no-exfil-after-privesc",
8876
+ property_short: "P4",
8877
+ property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
8878
+ }, ["privesc", "sink"], true, [
8879
+ { token: "PRIVESC", node_index: 0 },
8880
+ { token: "EXFIL", node_index: 1 }
8881
+ ])
8852
8882
  });
8853
8883
  return violations;
8854
8884
  }
@@ -8873,14 +8903,23 @@ function checkNeverExfilAfterCred(graph) {
8873
8903
  }
8874
8904
  if (pre) {
8875
8905
  const post = nodes[firstEv];
8906
+ const path5 = [
8907
+ { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
8908
+ { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
8909
+ ];
8876
8910
  return [{
8877
8911
  propertyId: "no-exfil-after-credential",
8878
8912
  reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
8879
8913
  riskLevel: "critical",
8880
- path: [
8881
- { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
8882
- { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
8883
- ]
8914
+ path: path5,
8915
+ graph: graphFromPath(nodes, path5, "data", {
8916
+ property_id: "no-exfil-after-credential",
8917
+ property_short: "P5",
8918
+ property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
8919
+ }, ["source", "sink"], true, [
8920
+ { token: "CRED", node_index: 0 },
8921
+ { token: "EXFIL", node_index: 1 }
8922
+ ])
8884
8923
  }];
8885
8924
  }
8886
8925
  return violations;
@@ -8889,14 +8928,23 @@ function checkNeverExfilAfterCred(graph) {
8889
8928
  if (!isCredNode(nodes[i])) continue;
8890
8929
  for (let j = i + 1; j < nodes.length; j++) {
8891
8930
  if (isExfilNode(nodes[j])) {
8931
+ const path5 = [
8932
+ { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
8933
+ { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
8934
+ ];
8892
8935
  violations.push({
8893
8936
  propertyId: "no-exfil-after-credential",
8894
8937
  reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
8895
8938
  riskLevel: "critical",
8896
- path: [
8897
- { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
8898
- { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
8899
- ]
8939
+ path: path5,
8940
+ graph: graphFromPath(nodes, path5, "data", {
8941
+ property_id: "no-exfil-after-credential",
8942
+ property_short: "P5",
8943
+ property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
8944
+ }, ["source", "sink"], true, [
8945
+ { token: "CRED", node_index: 0 },
8946
+ { token: "EXFIL", node_index: 1 }
8947
+ ])
8900
8948
  });
8901
8949
  return violations;
8902
8950
  }
@@ -9053,11 +9101,19 @@ function checkSensitiveDataAggregation(graph) {
9053
9101
  if (!isExfilNode(n)) continue;
9054
9102
  const srcs = sourcesFor(i);
9055
9103
  if (srcs.size >= 2) {
9104
+ const path5 = [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }];
9056
9105
  violations.push({
9057
9106
  propertyId: "sensitive-data-aggregation",
9058
9107
  reason: `\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20\uFF1Aegress \u805A\u5408 ${srcs.size} \u4E2A\u4E8B\u4EF6\u6E90\u6570\u636E\uFF08Rule 12\uFF09`,
9059
9108
  riskLevel: "critical",
9060
- path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
9109
+ path: path5,
9110
+ graph: graphFromPath(nodes, path5, "next", {
9111
+ property_id: "sensitive-data-aggregation",
9112
+ property_short: "P8",
9113
+ property_name: "\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20"
9114
+ }, ["sink"], true, [
9115
+ { token: "EXFIL", node_index: 0 }
9116
+ ])
9061
9117
  });
9062
9118
  if (witness !== void 0) return violations;
9063
9119
  }
@@ -9434,12 +9490,21 @@ async function runAnalysis(input) {
9434
9490
  formalSessions.push({
9435
9491
  sessionId: s.sessionId || "s",
9436
9492
  ...s.agentId ? { agentId: s.agentId } : {},
9493
+ ...s.instanceId ? { instanceId: s.instanceId } : {},
9437
9494
  actionHistory: s.actionHistory || []
9438
9495
  });
9439
9496
  }
9440
9497
  }
9441
- formalSessions.push({ sessionId: "current", agentId: input.agentId, actionHistory: input.actionHistory });
9442
- const formalResult = formalEngine.verify(formalSessions, input.aNext);
9498
+ formalSessions.push({
9499
+ sessionId: "current",
9500
+ agentId: input.agentId,
9501
+ instanceId: input.instanceId || "",
9502
+ actionHistory: input.actionHistory
9503
+ });
9504
+ const formalResult = formalEngine.verify(formalSessions, {
9505
+ ...input.aNext,
9506
+ instanceId: input.instanceId || ""
9507
+ });
9443
9508
  if (!formalResult.verified) {
9444
9509
  const v = formalResult.violations[0];
9445
9510
  if (isFormalObserveMode()) {
package/dist/server.js CHANGED
@@ -1092,31 +1092,42 @@ function hardenExistingPaths() {
1092
1092
  function chainDetailMode() {
1093
1093
  return (env("AGENT_MOSS_CHAIN_DETAIL", "redacted") || "redacted").toLowerCase();
1094
1094
  }
1095
- function redactChain(chain, currentSessionId) {
1095
+ function redactChain(chain, currentInstanceId = "", currentSessionId = "") {
1096
1096
  if (!chain || chain.length === 0) return [];
1097
1097
  if (chainDetailMode() === "full") return [...chain];
1098
1098
  return chain.map((step) => {
1099
1099
  if (Array.isArray(step) && step.length >= 3) {
1100
1100
  const [sid, seq, , ts] = step;
1101
- if (String(sid) === String(currentSessionId)) return [...step];
1101
+ const inst = step.length > 5 ? String(step[5] ?? "") : "";
1102
+ const sameInstance = !!(inst && currentInstanceId && inst === String(currentInstanceId));
1103
+ const sameSessionFallback = !inst && !!currentSessionId && String(sid) === String(currentSessionId);
1104
+ if (sameInstance || sameSessionFallback) return [...step];
1102
1105
  return ["<other-session>", seq, "<\u5DF2\u9690\u85CF>", ts ?? ""];
1103
1106
  }
1104
1107
  return step;
1105
1108
  });
1106
1109
  }
1107
- function redactGraph(graph, currentSessionId) {
1110
+ function redactGraph(graph, currentInstanceId = "", currentSessionId = "") {
1108
1111
  if (!graph) return null;
1109
1112
  if (chainDetailMode() === "full") return graph;
1110
1113
  const nodes = (graph.nodes || []).map((n) => {
1111
1114
  const m = { ...n };
1112
- if (String(m.session_id ?? "") !== String(currentSessionId)) {
1115
+ const inst = String(m.instance_id ?? "") || "";
1116
+ const sid = String(m.session_id ?? "") || "";
1117
+ const sameInstance = !!(inst && currentInstanceId && inst === String(currentInstanceId));
1118
+ const sameSessionFallback = !inst && !!currentSessionId && sid === String(currentSessionId);
1119
+ if (!(sameInstance || sameSessionFallback)) {
1113
1120
  m.session_id = "<other-session>";
1114
1121
  m.action_detail = "<\u5DF2\u9690\u85CF>";
1115
1122
  m.resources = [];
1116
1123
  }
1117
1124
  return m;
1118
1125
  });
1119
- return { nodes, edges: graph.edges || [] };
1126
+ const out = { nodes, edges: graph.edges || [] };
1127
+ for (const key of ["property_id", "property_short", "property_name", "property_expression"]) {
1128
+ if (graph[key] !== void 0) out[key] = graph[key];
1129
+ }
1130
+ return out;
1120
1131
  }
1121
1132
  function sessionSweepThreshold() {
1122
1133
  return envInt("AGENT_MOSS_SESSION_SWEEP_THRESHOLD", 50);
@@ -8884,7 +8895,7 @@ function markIntegrity(actionDetail) {
8884
8895
  }
8885
8896
  return { integrity: "medium", isLowSource: false };
8886
8897
  }
8887
- function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "") {
8898
+ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId = "", instanceId = "") {
8888
8899
  const { integrity, isLowSource } = markIntegrity(actionDetail);
8889
8900
  return {
8890
8901
  sessionId,
@@ -8893,13 +8904,14 @@ function initNode(sessionId, seq, actionType, actionDetail, timestamp, agentId =
8893
8904
  actionDetail,
8894
8905
  stage: markStage(actionDetail),
8895
8906
  agentId: agentId || void 0,
8907
+ instanceId: instanceId || void 0,
8896
8908
  resources: extractResources(actionDetail),
8897
8909
  integrity,
8898
8910
  isLowSource,
8899
8911
  timestamp: timestamp || void 0
8900
8912
  };
8901
8913
  }
8902
- function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "") {
8914
+ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId = "", instanceId = "") {
8903
8915
  const nodes = [];
8904
8916
  const edges = [];
8905
8917
  const stageFlags = /* @__PURE__ */ new Set();
@@ -8910,7 +8922,8 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
8910
8922
  actionType: String(a.actionType || ""),
8911
8923
  actionDetail: ad,
8912
8924
  calledAt: a.calledAt || void 0,
8913
- agentId: a.agentId || agentId || void 0
8925
+ agentId: a.agentId || agentId || void 0,
8926
+ instanceId: a.instanceId || instanceId || void 0
8914
8927
  });
8915
8928
  }
8916
8929
  if (aNext) {
@@ -8918,11 +8931,12 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
8918
8931
  actionType: String(aNext.actionType || ""),
8919
8932
  actionDetail: String(aNext.actionDetail || ""),
8920
8933
  calledAt: aNext.calledAt || void 0,
8921
- agentId: aNext.agentId || agentId || void 0
8934
+ agentId: aNext.agentId || agentId || void 0,
8935
+ instanceId: aNext.instanceId || instanceId || void 0
8922
8936
  });
8923
8937
  }
8924
8938
  details.forEach((d, seq) => {
8925
- const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "");
8939
+ const node = initNode(sessionId, seq, d.actionType, d.actionDetail, d.calledAt, d.agentId || "", d.instanceId || "");
8926
8940
  nodes.push(node);
8927
8941
  if (node.stage) stageFlags.add(node.stage);
8928
8942
  });
@@ -8934,12 +8948,18 @@ function buildSessionGraph(actionHistory, aNext, sessionId = "current", agentId
8934
8948
  function buildMultiSessionGraph(sessions, aNext) {
8935
8949
  const mg = { sessions: [], crossEdges: [] };
8936
8950
  if (!sessions || sessions.length === 0) {
8937
- mg.sessions.push(buildSessionGraph([], aNext, "current"));
8951
+ mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext?.instanceId || ""));
8938
8952
  return mg;
8939
8953
  }
8940
8954
  for (const s of sessions) {
8941
8955
  const sid = String(s.sessionId || "s");
8942
- mg.sessions.push(buildSessionGraph(s.actionHistory || [], void 0, sid, s.agentId || ""));
8956
+ mg.sessions.push(buildSessionGraph(
8957
+ s.actionHistory || [],
8958
+ void 0,
8959
+ sid,
8960
+ s.agentId || "",
8961
+ s.instanceId || ""
8962
+ ));
8943
8963
  }
8944
8964
  if (aNext) {
8945
8965
  const last = mg.sessions[mg.sessions.length - 1];
@@ -8950,13 +8970,14 @@ function buildMultiSessionGraph(sessions, aNext) {
8950
8970
  String(aNext.actionType || ""),
8951
8971
  String(aNext.actionDetail || ""),
8952
8972
  aNext.calledAt || void 0,
8953
- aNext.agentId || ""
8973
+ aNext.agentId || "",
8974
+ aNext.instanceId || ""
8954
8975
  ));
8955
8976
  if (last.nodes.length > 1) {
8956
8977
  last.edges.push({ src: last.nodes.length - 2, dst: last.nodes.length - 1, kind: "next" });
8957
8978
  }
8958
8979
  } else {
8959
- mg.sessions.push(buildSessionGraph([], aNext, "current"));
8980
+ mg.sessions.push(buildSessionGraph([], aNext, "current", "", aNext.instanceId || ""));
8960
8981
  }
8961
8982
  }
8962
8983
  addCrossDataEdges(mg);
@@ -9020,7 +9041,8 @@ function graphFromPath(nodes, path6, edgeType = "next", propertyMeta, roles, wit
9020
9041
  seq: item.seq,
9021
9042
  stage: n ? n.stage : "",
9022
9043
  action_detail: n ? n.actionDetail : item.detail,
9023
- resources: n ? [...n.resources].sort() : []
9044
+ resources: n ? [...n.resources].sort() : [],
9045
+ instance_id: n ? n.instanceId || "" : ""
9024
9046
  });
9025
9047
  if (gNodes.length > 1) {
9026
9048
  gEdges.push({ from: gNodes.length - 2, to: gNodes.length - 1, type: edgeType });
@@ -9188,7 +9210,8 @@ function checkSensitiveDataFlow(graph) {
9188
9210
  stage: n.stage,
9189
9211
  action_detail: n.actionDetail,
9190
9212
  resources: [...n.resources].sort(),
9191
- role
9213
+ role,
9214
+ instance_id: n.instanceId || ""
9192
9215
  });
9193
9216
  if (k > 0) {
9194
9217
  const prev = chain[k - 1];
@@ -9338,14 +9361,23 @@ function checkNeverExfilAfterPrivesc(graph) {
9338
9361
  if (candidates.length > 0) {
9339
9362
  const pre = nodes[candidates[candidates.length - 1]];
9340
9363
  const post = nodes[firstEv];
9364
+ const path6 = [
9365
+ { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
9366
+ { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
9367
+ ];
9341
9368
  return [{
9342
9369
  propertyId: "no-exfil-after-privesc",
9343
9370
  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",
9344
9371
  riskLevel: "critical",
9345
- path: [
9346
- { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
9347
- { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
9348
- ]
9372
+ path: path6,
9373
+ graph: graphFromPath(nodes, path6, "next", {
9374
+ property_id: "no-exfil-after-privesc",
9375
+ property_short: "P4",
9376
+ property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
9377
+ }, ["privesc", "sink"], true, [
9378
+ { token: "PRIVESC", node_index: 0 },
9379
+ { token: "EXFIL", node_index: 1 }
9380
+ ])
9349
9381
  }];
9350
9382
  }
9351
9383
  return violations;
@@ -9355,14 +9387,23 @@ function checkNeverExfilAfterPrivesc(graph) {
9355
9387
  for (let j = i + 1; j < nodes.length; j++) {
9356
9388
  if (!isExfilNode(nodes[j])) continue;
9357
9389
  if (privescInWindow(nodes, [i], j, windowSec).length === 0) continue;
9390
+ const path6 = [
9391
+ { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
9392
+ { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
9393
+ ];
9358
9394
  violations.push({
9359
9395
  propertyId: "no-exfil-after-privesc",
9360
9396
  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",
9361
9397
  riskLevel: "critical",
9362
- path: [
9363
- { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
9364
- { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
9365
- ]
9398
+ path: path6,
9399
+ graph: graphFromPath(nodes, path6, "next", {
9400
+ property_id: "no-exfil-after-privesc",
9401
+ property_short: "P4",
9402
+ property_name: "\u63D0\u6743\u540E\u7981\u6B62\u5916\u4F20"
9403
+ }, ["privesc", "sink"], true, [
9404
+ { token: "PRIVESC", node_index: 0 },
9405
+ { token: "EXFIL", node_index: 1 }
9406
+ ])
9366
9407
  });
9367
9408
  return violations;
9368
9409
  }
@@ -9387,14 +9428,23 @@ function checkNeverExfilAfterCred(graph) {
9387
9428
  }
9388
9429
  if (pre) {
9389
9430
  const post = nodes[firstEv];
9431
+ const path6 = [
9432
+ { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
9433
+ { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
9434
+ ];
9390
9435
  return [{
9391
9436
  propertyId: "no-exfil-after-credential",
9392
9437
  reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
9393
9438
  riskLevel: "critical",
9394
- path: [
9395
- { sessionId: pre.sessionId, seq: pre.seq, detail: pre.actionDetail.slice(0, 60) },
9396
- { sessionId: post.sessionId, seq: post.seq, detail: post.actionDetail.slice(0, 60) }
9397
- ]
9439
+ path: path6,
9440
+ graph: graphFromPath(nodes, path6, "data", {
9441
+ property_id: "no-exfil-after-credential",
9442
+ property_short: "P5",
9443
+ property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
9444
+ }, ["source", "sink"], true, [
9445
+ { token: "CRED", node_index: 0 },
9446
+ { token: "EXFIL", node_index: 1 }
9447
+ ])
9398
9448
  }];
9399
9449
  }
9400
9450
  return violations;
@@ -9403,14 +9453,23 @@ function checkNeverExfilAfterCred(graph) {
9403
9453
  if (!isCredNode(nodes[i])) continue;
9404
9454
  for (let j = i + 1; j < nodes.length; j++) {
9405
9455
  if (isExfilNode(nodes[j])) {
9456
+ const path6 = [
9457
+ { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
9458
+ { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
9459
+ ];
9406
9460
  violations.push({
9407
9461
  propertyId: "no-exfil-after-credential",
9408
9462
  reason: "\u8BFB\u53D6\u51ED\u636E\u540E\u51FA\u73B0\u5916\u4F20\uFF0C\u8FDD\u53CD \u25A1(CRED\u2192\xAC\u25C7EXFIL)",
9409
9463
  riskLevel: "critical",
9410
- path: [
9411
- { sessionId: nodes[i].sessionId, seq: nodes[i].seq, detail: nodes[i].actionDetail.slice(0, 60) },
9412
- { sessionId: nodes[j].sessionId, seq: nodes[j].seq, detail: nodes[j].actionDetail.slice(0, 60) }
9413
- ]
9464
+ path: path6,
9465
+ graph: graphFromPath(nodes, path6, "data", {
9466
+ property_id: "no-exfil-after-credential",
9467
+ property_short: "P5",
9468
+ property_name: "\u8BFB\u53D6\u51ED\u636E\u540E\u7981\u6B62\u5916\u4F20"
9469
+ }, ["source", "sink"], true, [
9470
+ { token: "CRED", node_index: 0 },
9471
+ { token: "EXFIL", node_index: 1 }
9472
+ ])
9414
9473
  });
9415
9474
  return violations;
9416
9475
  }
@@ -9562,11 +9621,19 @@ function checkSensitiveDataAggregation(graph) {
9562
9621
  if (!isExfilNode(n)) continue;
9563
9622
  const srcs = sourcesFor(i);
9564
9623
  if (srcs.size >= 2) {
9624
+ const path6 = [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }];
9565
9625
  violations.push({
9566
9626
  propertyId: "sensitive-data-aggregation",
9567
9627
  reason: `\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20\uFF1Aegress \u805A\u5408 ${srcs.size} \u4E2A\u4E8B\u4EF6\u6E90\u6570\u636E\uFF08Rule 12\uFF09`,
9568
9628
  riskLevel: "critical",
9569
- path: [{ sessionId: n.sessionId, seq: n.seq, detail: n.actionDetail.slice(0, 60) }]
9629
+ path: path6,
9630
+ graph: graphFromPath(nodes, path6, "next", {
9631
+ property_id: "sensitive-data-aggregation",
9632
+ property_short: "P8",
9633
+ property_name: "\u654F\u611F\u6570\u636E\u805A\u5408\u5916\u4F20"
9634
+ }, ["sink"], true, [
9635
+ { token: "EXFIL", node_index: 0 }
9636
+ ])
9570
9637
  });
9571
9638
  if (witness !== void 0) return violations;
9572
9639
  }
@@ -9831,7 +9898,7 @@ var init_stateful = __esm({
9831
9898
  for (const s of sessions || []) {
9832
9899
  const sid = String(s.sessionId || "s");
9833
9900
  for (const a of s.actionHistory || []) {
9834
- const node = this.appendNode(a, sid);
9901
+ const node = this.appendNode(a, sid, false, s.agentId || "", s.instanceId || "");
9835
9902
  if (!node) {
9836
9903
  continue;
9837
9904
  }
@@ -9845,8 +9912,8 @@ var init_stateful = __esm({
9845
9912
  }
9846
9913
  /** 增量推进:把当前动作追加到时间线 → 链指针推进 → 判定。 */
9847
9914
  advance(args) {
9848
- const { aNext, sessionId = "current", isError = false, agentId = "" } = args;
9849
- const node = this.appendNode(aNext, sessionId, isError, agentId);
9915
+ const { aNext, sessionId = "current", isError = false, agentId = "", instanceId = "" } = args;
9916
+ const node = this.appendNode(aNext, sessionId, isError, agentId, instanceId);
9850
9917
  if (!node) {
9851
9918
  return { seq: this.seqNext, node: null, matched: null, pendingPrefixes: this.pendingPrefixes, exfil: [], shouldBlock: false };
9852
9919
  }
@@ -9866,7 +9933,7 @@ var init_stateful = __esm({
9866
9933
  }
9867
9934
  };
9868
9935
  }
9869
- appendNode(action, sessionId, isError = false, agentId = "") {
9936
+ appendNode(action, sessionId, isError = false, agentId = "", instanceId = "") {
9870
9937
  const ad = String(action.actionDetail || "");
9871
9938
  const at = String(action.actionType || "");
9872
9939
  if (!ad) return null;
@@ -9884,6 +9951,7 @@ var init_stateful = __esm({
9884
9951
  instances: extractInstances(ad, stage),
9885
9952
  isError,
9886
9953
  agentId: String(action.agentId || agentId || ""),
9954
+ instanceId: String(action.instanceId || instanceId || ""),
9887
9955
  lifecycle: lifecycleFor(ad, stage, isError),
9888
9956
  timestamp: typeof action.calledAt === "string" ? action.calledAt : _StatefulChainTracker.now()
9889
9957
  };
@@ -10027,7 +10095,8 @@ var init_stateful = __esm({
10027
10095
  seq: n.seq,
10028
10096
  stage: n.stage,
10029
10097
  timestamp: n.timestamp,
10030
- agentId: n.agentId
10098
+ agentId: n.agentId,
10099
+ instanceId: n.instanceId
10031
10100
  });
10032
10101
  const idx = stages.indexOf(n.stage);
10033
10102
  if (idx + 1 < stages.length) {
@@ -10050,7 +10119,8 @@ var init_stateful = __esm({
10050
10119
  stage: p.stage,
10051
10120
  action_detail: n ? n.actionDetail : "",
10052
10121
  resources: n ? [...n.resources].sort() : [],
10053
- role: stages[i] || ""
10122
+ role: stages[i] || "",
10123
+ instance_id: p.instanceId || ""
10054
10124
  };
10055
10125
  });
10056
10126
  const gEdges = [];
@@ -10118,7 +10188,7 @@ var init_stateful = __esm({
10118
10188
  risk_level: "critical",
10119
10189
  path: chain.map((j) => {
10120
10190
  const n = this.nodes_[j];
10121
- return [n.sessionId, n.seq, n.actionDetail.slice(0, 60), n.timestamp];
10191
+ return [n.sessionId, n.seq, n.actionDetail.slice(0, 60), n.timestamp, null, n.instanceId];
10122
10192
  }),
10123
10193
  graph: this.taintGraph(chain)
10124
10194
  }];
@@ -10150,7 +10220,8 @@ var init_stateful = __esm({
10150
10220
  stage: n.stage,
10151
10221
  action_detail: n.actionDetail,
10152
10222
  resources: [...n.resources].sort(),
10153
- role
10223
+ role,
10224
+ instance_id: n.instanceId
10154
10225
  });
10155
10226
  if (k > 0) {
10156
10227
  const prev = chain[k - 1];
@@ -10222,6 +10293,8 @@ var init_stateful = __esm({
10222
10293
  seq: Number(raw.seq || 0),
10223
10294
  sessionId: String(raw.sessionId || ""),
10224
10295
  agentId: String(raw.agentId || ""),
10296
+ instanceId: String(raw.instanceId || ""),
10297
+ // 旧快照无此字段 → 空
10225
10298
  actionType: String(raw.actionType || ""),
10226
10299
  actionDetail: String(raw.actionDetail || ""),
10227
10300
  stage: String(raw.stage || ""),
@@ -10366,9 +10439,9 @@ var init_chain_state = __esm({
10366
10439
  delete this.data[agentId];
10367
10440
  this._scheduleWrite();
10368
10441
  }
10369
- applyAction(agentId, aNext, sessionId, isError = false, sourceAgent = "") {
10442
+ applyAction(agentId, aNext, sessionId, isError = false, sourceAgent = "", instanceId = "") {
10370
10443
  const t = this.load(agentId) || new StatefulChainTracker(agentId);
10371
- const r = t.advance({ aNext, sessionId, isError, agentId: sourceAgent });
10444
+ const r = t.advance({ aNext, sessionId, isError, agentId: sourceAgent, instanceId });
10372
10445
  this.save(t);
10373
10446
  return r;
10374
10447
  }
@@ -10686,12 +10759,21 @@ async function runAnalysis(input) {
10686
10759
  formalSessions.push({
10687
10760
  sessionId: s.sessionId || "s",
10688
10761
  ...s.agentId ? { agentId: s.agentId } : {},
10762
+ ...s.instanceId ? { instanceId: s.instanceId } : {},
10689
10763
  actionHistory: s.actionHistory || []
10690
10764
  });
10691
10765
  }
10692
10766
  }
10693
- formalSessions.push({ sessionId: "current", agentId: input.agentId, actionHistory: input.actionHistory });
10694
- const formalResult = formalEngine.verify(formalSessions, input.aNext);
10767
+ formalSessions.push({
10768
+ sessionId: "current",
10769
+ agentId: input.agentId,
10770
+ instanceId: input.instanceId || "",
10771
+ actionHistory: input.actionHistory
10772
+ });
10773
+ const formalResult = formalEngine.verify(formalSessions, {
10774
+ ...input.aNext,
10775
+ instanceId: input.instanceId || ""
10776
+ });
10695
10777
  if (!formalResult.verified) {
10696
10778
  const v = formalResult.violations[0];
10697
10779
  if (isFormalObserveMode()) {
@@ -11062,6 +11144,8 @@ var AnalyzeRequestSchema = z.object({
11062
11144
  session_id: z.string(),
11063
11145
  /** 来源 agent(AgentGate 逐会话带上):记录在链路径里供告警/取证,不参与链匹配。 */
11064
11146
  agent_id: z.string().optional(),
11147
+ /** 来源实例:仅用于展示层脱敏(同实例不隐藏),不参与链匹配。 */
11148
+ instance_id: z.string().optional(),
11065
11149
  action_history: z.array(ActionItemSchema).default([])
11066
11150
  })).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)."),
11067
11151
  a_next: ActionItemSchema.describe("Next action to be executed"),
@@ -11069,6 +11153,7 @@ var AnalyzeRequestSchema = z.object({
11069
11153
  os_type: z.string().default("").describe("OS type: 'linux', 'windows', or empty for auto-detect"),
11070
11154
  cwd: z.string().default("").describe("Current working directory"),
11071
11155
  agent_id: z.string().default("").describe("Caller agent identifier, e.g. 'xiaoo' / 'opencode' / 'opendesk'. Empty = generic mode (no agent-specific rules)."),
11156
+ instance_id: z.string().default("").describe("Agent installation/instance identifier. Used only for display-layer redaction: nodes from the same instance are returned unredacted. Does not affect detection."),
11072
11157
  metadata: z.record(z.unknown()).optional().describe("Extended metadata")
11073
11158
  });
11074
11159
  var AnalyzeResponseSchema = z.object({
@@ -11106,7 +11191,9 @@ var AnalyzeResponseSchema = z.object({
11106
11191
  // 节点在性质中的角色:source/carrier/sink(P2)或阶段名(P1)
11107
11192
  role: z.string().optional(),
11108
11193
  // 是否为违例见证节点(当前动作补全链 / 触发外传)
11109
- witness: z.boolean().optional()
11194
+ witness: z.boolean().optional(),
11195
+ // 来源实例(仅展示层脱敏:同实例不隐藏)
11196
+ instance_id: z.string().optional()
11110
11197
  })
11111
11198
  ),
11112
11199
  edges: z.array(
@@ -11998,6 +12085,9 @@ async function analyzeRoute(c) {
11998
12085
  `[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`
11999
12086
  );
12000
12087
  }
12088
+ const instanceId = String(
12089
+ req.instance_id || req.metadata?.instance_id || ""
12090
+ );
12001
12091
  const result = await analyze({
12002
12092
  sessionId: req.session_id,
12003
12093
  promptSession: req.prompt_session,
@@ -12013,8 +12103,9 @@ async function analyzeRoute(c) {
12013
12103
  })),
12014
12104
  sessionHistory: (req.session_history || []).map((s) => ({
12015
12105
  sessionId: s.session_id,
12016
- // 来源 agent(AgentGate 逐会话带上,用于链路径取证展示;不参与链匹配)
12106
+ // 来源 agent/实例(AgentGate 逐会话带上,用于链路径取证/展示脱敏;不参与链匹配)
12017
12107
  ...s.agent_id ? { agentId: s.agent_id } : {},
12108
+ ...s.instance_id ? { instanceId: s.instance_id } : {},
12018
12109
  actionHistory: (s.action_history || []).map((h) => ({
12019
12110
  actionType: h.action_type,
12020
12111
  actionDetail: h.action_detail,
@@ -12038,11 +12129,13 @@ async function analyzeRoute(c) {
12038
12129
  })),
12039
12130
  aNext: {
12040
12131
  actionType: req.a_next.action_type,
12041
- actionDetail: req.a_next.action_detail
12132
+ actionDetail: req.a_next.action_detail,
12133
+ instanceId
12042
12134
  },
12043
12135
  reason: req.reason,
12044
12136
  osType,
12045
12137
  cwd: req.cwd,
12138
+ instanceId,
12046
12139
  // agent_id:AgentGate V1 normalizer 历史上把 agent_type 塞 metadata,
12047
12140
  // 顶层为空时兜底(OpenDesk 的 agent-moss 需 agent_id 加载专属规则)。
12048
12141
  agentId,
@@ -12078,8 +12171,9 @@ async function analyzeRoute(c) {
12078
12171
  { actionType: req.a_next.action_type, actionDetail: req.a_next.action_detail },
12079
12172
  req.session_id,
12080
12173
  Boolean(meta.is_error),
12081
- agentId
12174
+ agentId,
12082
12175
  // 来源:真实 agent(桶 key 可能是 __system__)
12176
+ instanceId
12083
12177
  );
12084
12178
  const judgment = incMatchedToDeny(inc);
12085
12179
  if (judgment) {
@@ -12100,8 +12194,8 @@ async function analyzeRoute(c) {
12100
12194
  confidence: 90,
12101
12195
  analysis_duration_ms: Math.round((performance.now() - t0) * 10) / 10,
12102
12196
  // 跨会话节点脱敏(时间线按 agent_id 共享,原样返回等于泄露别人的命令原文)
12103
- violation_chain: redactChain(judgment.violation_chain, req.session_id),
12104
- violation_graph: redactGraph(judgment.violation_graph, req.session_id)
12197
+ violation_chain: redactChain(judgment.violation_chain, instanceId, req.session_id),
12198
+ violation_graph: redactGraph(judgment.violation_graph, instanceId, req.session_id)
12105
12199
  })
12106
12200
  );
12107
12201
  }
@@ -12123,7 +12217,7 @@ async function analyzeRoute(c) {
12123
12217
  confidence: result.confidence,
12124
12218
  analysis_duration_ms: Math.round(durationMs * 10) / 10,
12125
12219
  violation_chain: result.violation_chain ?? [],
12126
- violation_graph: redactGraph(result.violation_graph, req.session_id)
12220
+ violation_graph: redactGraph(result.violation_graph, instanceId, req.session_id)
12127
12221
  })
12128
12222
  );
12129
12223
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenhkl/agent-moss",
3
- "version": "0.17.8",
3
+ "version": "0.17.11",
4
4
  "description": "Multi-layer security analysis engine for AI agents (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",