@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.
package/dist/cli.js CHANGED
@@ -683,6 +683,32 @@ function matchDangerousAction(actionDetail, action) {
683
683
  const re = new RegExp(`(?<![-\\w])${escaped}\\b`, "i");
684
684
  return re.test(actionDetail);
685
685
  }
686
+ function collectIntentTexts(promptSession, promptHistory) {
687
+ const texts = [];
688
+ if (promptHistory) {
689
+ for (const turn of promptHistory) {
690
+ if (typeof turn === "string") {
691
+ const t2 = turn.trim();
692
+ if (t2) texts.push(t2.toLowerCase());
693
+ continue;
694
+ }
695
+ const t = (turn?.text || "").trim();
696
+ if (t) texts.push(t.toLowerCase());
697
+ }
698
+ }
699
+ if (promptSession && promptSession.trim()) {
700
+ texts.push(promptSession.toLowerCase());
701
+ }
702
+ const seen = /* @__PURE__ */ new Set();
703
+ const deduped = [];
704
+ for (const t of texts) {
705
+ if (!seen.has(t)) {
706
+ seen.add(t);
707
+ deduped.push(t);
708
+ }
709
+ }
710
+ return deduped;
711
+ }
686
712
  function runLogicCheck(input) {
687
713
  const { actionHistory, aNext, cwd } = input;
688
714
  const actionDetail = aNext.actionDetail.toLowerCase();
@@ -692,25 +718,24 @@ function runLogicCheck(input) {
692
718
  const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
693
719
  const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd);
694
720
  if (rbwResult) return rbwResult;
695
- if (input.promptSession && input.promptSession.trim()) {
696
- if (!isExcludedType) {
697
- if (INLINE_SCRIPT_RE.test(actionDetail)) {
698
- } else {
699
- const promptLower = input.promptSession.toLowerCase();
700
- for (const pattern of INTENT_DEVIATION_PATTERNS) {
701
- const intentMatched = pattern.intentKeywords.some((kw) => promptLower.includes(kw));
702
- const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
703
- if (intentMatched && dangerousMatched) {
704
- return {
705
- allowed: false,
706
- reason: pattern.reason,
707
- riskLevel: "high",
708
- riskType: "intent_deviation",
709
- confidence: 85,
710
- source: "logic_rule",
711
- violatedLayers: ["2"]
712
- };
713
- }
721
+ if (!isExcludedType) {
722
+ if (!INLINE_SCRIPT_RE.test(actionDetail)) {
723
+ const intentTexts = collectIntentTexts(input.promptSession, input.promptHistory);
724
+ for (const pattern of INTENT_DEVIATION_PATTERNS) {
725
+ const intentMatched = intentTexts.some(
726
+ (text) => pattern.intentKeywords.some((kw) => text.includes(kw))
727
+ );
728
+ const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
729
+ if (intentMatched && dangerousMatched) {
730
+ return {
731
+ allowed: false,
732
+ reason: pattern.reason,
733
+ riskLevel: "high",
734
+ riskType: "intent_deviation",
735
+ confidence: 85,
736
+ source: "logic_rule",
737
+ violatedLayers: ["2"]
738
+ };
714
739
  }
715
740
  }
716
741
  }
@@ -2275,7 +2300,7 @@ function runHeuristicCheck(input) {
2275
2300
  } catch {
2276
2301
  }
2277
2302
  }
2278
- const scanDetail = stripLiteralTextSegments(input.aNext.actionDetail);
2303
+ const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
2279
2304
  const scanCommand = `${input.aNext.actionType}: ${scanDetail}`;
2280
2305
  let topRiskType = "";
2281
2306
  for (const rule of DANGEROUS_PATTERNS) {
@@ -2386,6 +2411,28 @@ function stripHeredocContent(actionDetail) {
2386
2411
  "<<heredoc>>\n"
2387
2412
  );
2388
2413
  }
2414
+ function normalizePathSegments(text) {
2415
+ const protTokens = [];
2416
+ let t = text.replace(/\b(https?|ftp|file):\/\//gi, (m) => {
2417
+ const scheme = m.slice(0, m.indexOf(":"));
2418
+ protTokens.push("://");
2419
+ return `${scheme}__PROT${protTokens.length - 1}__`;
2420
+ });
2421
+ const quoteTokens = [];
2422
+ t = t.replace(/'[^']*'|"[^"]*"/g, (m) => {
2423
+ quoteTokens.push(m);
2424
+ return `__Q${quoteTokens.length - 1}__`;
2425
+ });
2426
+ t = t.replace(/\/{2,}/g, "/");
2427
+ t = t.replace(/\/\.\//g, "/");
2428
+ for (let i = 0; i < quoteTokens.length; i++) {
2429
+ t = t.replace(`__Q${i}__`, quoteTokens[i]);
2430
+ }
2431
+ for (let i = 0; i < protTokens.length; i++) {
2432
+ t = t.replace(`__PROT${i}__`, protTokens[i]);
2433
+ }
2434
+ return t;
2435
+ }
2389
2436
  function splitCommandSegments(actionDetail) {
2390
2437
  const segments = [];
2391
2438
  let cur = "";
@@ -2438,7 +2485,7 @@ function isSearchActionType(actionType) {
2438
2485
  function checkSensitivePath(command, cwd, agentExt) {
2439
2486
  const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
2440
2487
  const isWriteOpFlag = isWriteOperation("", command);
2441
- const pathScanText = stripLiteralTextSegments(command);
2488
+ const pathScanText = normalizePathSegments(stripLiteralTextSegments(command));
2442
2489
  const pathMatches = (text, path3) => {
2443
2490
  const parts = path3.split(/(\(\?!.*?\))/);
2444
2491
  const escaped = parts.map((part) => {
@@ -3727,11 +3774,12 @@ ${prompt}
3727
3774
  function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
3728
3775
  const skillsText = formatSkillsForPrompt(matchedSkills);
3729
3776
  const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
3777
+ const promptHistoryText = formatPromptHistory(input.promptHistory);
3730
3778
  const scriptAnalysisText = scriptChainText ? scriptChainText : scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
3731
3779
  const hintsText = formatHints(previous);
3732
3780
  const securityJudgeTemplate = templates["security_judge"];
3733
3781
  if (securityJudgeTemplate) {
3734
- 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("}}", "}");
3782
+ 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("}}", "}");
3735
3783
  }
3736
3784
  console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
3737
3785
  return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
@@ -3761,6 +3809,27 @@ ${scriptAnalysisText}` : ""}
3761
3809
  \u8F93\u51FA\u683C\u5F0F:
3762
3810
  {"allowed": true/false, "reason": "\u5224\u65AD\u7406\u7531", "riskLevel": "low|medium|high|critical", "riskType": "\u98CE\u9669\u7C7B\u578B", "confidence": 0-100}`;
3763
3811
  }
3812
+ function formatPromptHistory(promptHistory, maxTextChars = 500) {
3813
+ if (!promptHistory || promptHistory.length === 0) {
3814
+ return "\uFF08\u672A\u63D0\u4F9B\u5386\u53F2\u610F\u56FE\uFF09";
3815
+ }
3816
+ const lines = [];
3817
+ for (let i = 0; i < promptHistory.length; i++) {
3818
+ const turn = promptHistory[i];
3819
+ const text = (turn?.text || "").trim();
3820
+ if (!text) continue;
3821
+ let line = ` \u7B2C${i + 1}\u8F6E\u7528\u6237\u610F\u56FE: ${text.slice(0, maxTextChars)}`;
3822
+ const actions = turn?.actions;
3823
+ if (actions && actions.length > 0) {
3824
+ const types = actions.map((a) => a?.actionType).filter((t) => typeof t === "string" && t.length > 0);
3825
+ if (types.length > 0) {
3826
+ line += ` \u2192 \u52A8\u4F5C: ${types.join(", ")}`;
3827
+ }
3828
+ }
3829
+ lines.push(line);
3830
+ }
3831
+ return lines.length > 0 ? lines.join("\n") : "\uFF08\u672A\u63D0\u4F9B\u5386\u53F2\u610F\u56FE\uFF09";
3832
+ }
3764
3833
  function formatHints(previous) {
3765
3834
  const hints = [];
3766
3835
  if (previous.heuristic) {
@@ -5030,9 +5099,14 @@ var ActionItemSchema = z.object({
5030
5099
  action_type: z.string().describe("Action type, e.g. bash, file_read, file_write"),
5031
5100
  action_detail: z.string().describe("Action detail / command content")
5032
5101
  });
5102
+ var PromptTurnSchema = z.object({
5103
+ text: z.string().default("").describe("User prompt text at this turn"),
5104
+ actions: z.array(ActionItemSchema).default([]).describe("Actions triggered by this prompt, in order, until the next prompt")
5105
+ });
5033
5106
  var AnalyzeRequestSchema = z.object({
5034
5107
  session_id: z.string().describe("Session / agent unique identifier"),
5035
- prompt_session: z.string().default("").describe("User original prompt"),
5108
+ prompt_session: z.string().default("").describe("User original prompt (most recent user message; backward compat)"),
5109
+ prompt_history: z.array(PromptTurnSchema).default([]).describe("Full interleaved prompt history (ordered turns, each with its triggered actions). Optional; empty = fall back to prompt_session only."),
5036
5110
  action_history: z.array(ActionItemSchema).default([]).describe("Historical action sequence"),
5037
5111
  a_next: ActionItemSchema.describe("Next action to be executed"),
5038
5112
  reason: z.string().default("").describe("Reason for executing this action"),