@kenhkl/agent-moss 0.12.0 → 0.12.2

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.
@@ -17,6 +17,15 @@ interface AnalysisInput {
17
17
  actionType: string;
18
18
  actionDetail: string;
19
19
  }[];
20
+ /** 完整的交错历史 prompt(可选),每轮 {text, actions},按时间序。
21
+ * 供 L2 意图一致性与 L3 语义分析使用;空则回退单条 promptSession。 */
22
+ promptHistory?: {
23
+ text: string;
24
+ actions: {
25
+ actionType: string;
26
+ actionDetail: string;
27
+ }[];
28
+ }[];
20
29
  aNext: {
21
30
  actionType: string;
22
31
  actionDetail: string;
@@ -672,6 +672,32 @@ function matchDangerousAction(actionDetail, action) {
672
672
  const re = new RegExp(`(?<![-\\w])${escaped}\\b`, "i");
673
673
  return re.test(actionDetail);
674
674
  }
675
+ function collectIntentTexts(promptSession, promptHistory) {
676
+ const texts = [];
677
+ if (promptHistory) {
678
+ for (const turn of promptHistory) {
679
+ if (typeof turn === "string") {
680
+ const t2 = turn.trim();
681
+ if (t2) texts.push(t2.toLowerCase());
682
+ continue;
683
+ }
684
+ const t = (turn?.text || "").trim();
685
+ if (t) texts.push(t.toLowerCase());
686
+ }
687
+ }
688
+ if (promptSession && promptSession.trim()) {
689
+ texts.push(promptSession.toLowerCase());
690
+ }
691
+ const seen = /* @__PURE__ */ new Set();
692
+ const deduped = [];
693
+ for (const t of texts) {
694
+ if (!seen.has(t)) {
695
+ seen.add(t);
696
+ deduped.push(t);
697
+ }
698
+ }
699
+ return deduped;
700
+ }
675
701
  function runLogicCheck(input) {
676
702
  const { actionHistory, aNext, cwd } = input;
677
703
  const actionDetail = aNext.actionDetail.toLowerCase();
@@ -681,25 +707,24 @@ function runLogicCheck(input) {
681
707
  const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
682
708
  const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd);
683
709
  if (rbwResult) return rbwResult;
684
- if (input.promptSession && input.promptSession.trim()) {
685
- if (!isExcludedType) {
686
- if (INLINE_SCRIPT_RE.test(actionDetail)) {
687
- } else {
688
- const promptLower = input.promptSession.toLowerCase();
689
- for (const pattern of INTENT_DEVIATION_PATTERNS) {
690
- const intentMatched = pattern.intentKeywords.some((kw) => promptLower.includes(kw));
691
- const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
692
- if (intentMatched && dangerousMatched) {
693
- return {
694
- allowed: false,
695
- reason: pattern.reason,
696
- riskLevel: "high",
697
- riskType: "intent_deviation",
698
- confidence: 85,
699
- source: "logic_rule",
700
- violatedLayers: ["2"]
701
- };
702
- }
710
+ if (!isExcludedType) {
711
+ if (!INLINE_SCRIPT_RE.test(actionDetail)) {
712
+ const intentTexts = collectIntentTexts(input.promptSession, input.promptHistory);
713
+ for (const pattern of INTENT_DEVIATION_PATTERNS) {
714
+ const intentMatched = intentTexts.some(
715
+ (text) => pattern.intentKeywords.some((kw) => text.includes(kw))
716
+ );
717
+ const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
718
+ if (intentMatched && dangerousMatched) {
719
+ return {
720
+ allowed: false,
721
+ reason: pattern.reason,
722
+ riskLevel: "high",
723
+ riskType: "intent_deviation",
724
+ confidence: 85,
725
+ source: "logic_rule",
726
+ violatedLayers: ["2"]
727
+ };
703
728
  }
704
729
  }
705
730
  }
@@ -2264,7 +2289,7 @@ function runHeuristicCheck(input) {
2264
2289
  } catch {
2265
2290
  }
2266
2291
  }
2267
- const scanDetail = stripLiteralTextSegments(input.aNext.actionDetail);
2292
+ const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
2268
2293
  const scanCommand = `${input.aNext.actionType}: ${scanDetail}`;
2269
2294
  let topRiskType = "";
2270
2295
  for (const rule of DANGEROUS_PATTERNS) {
@@ -2375,6 +2400,28 @@ function stripHeredocContent(actionDetail) {
2375
2400
  "<<heredoc>>\n"
2376
2401
  );
2377
2402
  }
2403
+ function normalizePathSegments(text) {
2404
+ const protTokens = [];
2405
+ let t = text.replace(/\b(https?|ftp|file):\/\//gi, (m) => {
2406
+ const scheme = m.slice(0, m.indexOf(":"));
2407
+ protTokens.push("://");
2408
+ return `${scheme}__PROT${protTokens.length - 1}__`;
2409
+ });
2410
+ const quoteTokens = [];
2411
+ t = t.replace(/'[^']*'|"[^"]*"/g, (m) => {
2412
+ quoteTokens.push(m);
2413
+ return `__Q${quoteTokens.length - 1}__`;
2414
+ });
2415
+ t = t.replace(/\/{2,}/g, "/");
2416
+ t = t.replace(/\/\.\//g, "/");
2417
+ for (let i = 0; i < quoteTokens.length; i++) {
2418
+ t = t.replace(`__Q${i}__`, quoteTokens[i]);
2419
+ }
2420
+ for (let i = 0; i < protTokens.length; i++) {
2421
+ t = t.replace(`__PROT${i}__`, protTokens[i]);
2422
+ }
2423
+ return t;
2424
+ }
2378
2425
  function splitCommandSegments(actionDetail) {
2379
2426
  const segments = [];
2380
2427
  let cur = "";
@@ -2427,7 +2474,7 @@ function isSearchActionType(actionType) {
2427
2474
  function checkSensitivePath(command, cwd, agentExt) {
2428
2475
  const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
2429
2476
  const isWriteOpFlag = isWriteOperation("", command);
2430
- const pathScanText = stripLiteralTextSegments(command);
2477
+ const pathScanText = normalizePathSegments(stripLiteralTextSegments(command));
2431
2478
  const pathMatches = (text, path3) => {
2432
2479
  const parts = path3.split(/(\(\?!.*?\))/);
2433
2480
  const escaped = parts.map((part) => {
@@ -3711,11 +3758,12 @@ ${prompt}
3711
3758
  function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
3712
3759
  const skillsText = formatSkillsForPrompt(matchedSkills);
3713
3760
  const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
3761
+ const promptHistoryText = formatPromptHistory(input.promptHistory);
3714
3762
  const scriptAnalysisText = scriptChainText ? scriptChainText : scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
3715
3763
  const hintsText = formatHints(previous);
3716
3764
  const securityJudgeTemplate = templates["security_judge"];
3717
3765
  if (securityJudgeTemplate) {
3718
- return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{{", "{").replaceAll("}}", "}");
3766
+ return securityJudgeTemplate.replaceAll("{prompt_session}", input.promptSession || "\uFF08\u672A\u63D0\u4F9B\uFF09").replaceAll("{prompt_history_text}", promptHistoryText).replaceAll("{action_history}", historyStr).replaceAll("{action_type}", input.aNext.actionType).replaceAll("{action_detail}", input.aNext.actionDetail).replaceAll("{reason}", input.reason || "\u672A\u8BF4\u660E").replaceAll("{skills_text}", skillsText).replaceAll("{hints_text}", hintsText).replaceAll("{script_analysis_text}", scriptAnalysisText).replaceAll("{skip_l3_hints}", skipL3Hints).replaceAll("{{", "{").replaceAll("}}", "}");
3719
3767
  }
3720
3768
  console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
3721
3769
  return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
@@ -3745,6 +3793,27 @@ ${scriptAnalysisText}` : ""}
3745
3793
  \u8F93\u51FA\u683C\u5F0F:
3746
3794
  {"allowed": true/false, "reason": "\u5224\u65AD\u7406\u7531", "riskLevel": "low|medium|high|critical", "riskType": "\u98CE\u9669\u7C7B\u578B", "confidence": 0-100}`;
3747
3795
  }
3796
+ function formatPromptHistory(promptHistory, maxTextChars = 500) {
3797
+ if (!promptHistory || promptHistory.length === 0) {
3798
+ return "\uFF08\u672A\u63D0\u4F9B\u5386\u53F2\u610F\u56FE\uFF09";
3799
+ }
3800
+ const lines = [];
3801
+ for (let i = 0; i < promptHistory.length; i++) {
3802
+ const turn = promptHistory[i];
3803
+ const text = (turn?.text || "").trim();
3804
+ if (!text) continue;
3805
+ let line = ` \u7B2C${i + 1}\u8F6E\u7528\u6237\u610F\u56FE: ${text.slice(0, maxTextChars)}`;
3806
+ const actions = turn?.actions;
3807
+ if (actions && actions.length > 0) {
3808
+ const types = actions.map((a) => a?.actionType).filter((t) => typeof t === "string" && t.length > 0);
3809
+ if (types.length > 0) {
3810
+ line += ` \u2192 \u52A8\u4F5C: ${types.join(", ")}`;
3811
+ }
3812
+ }
3813
+ lines.push(line);
3814
+ }
3815
+ return lines.length > 0 ? lines.join("\n") : "\uFF08\u672A\u63D0\u4F9B\u5386\u53F2\u610F\u56FE\uFF09";
3816
+ }
3748
3817
  function formatHints(previous) {
3749
3818
  const hints = [];
3750
3819
  if (previous.heuristic) {