@kenhkl/agent-moss 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -682,6 +682,32 @@ function matchDangerousAction(actionDetail, action) {
682
682
  const re = new RegExp(`(?<![-\\w])${escaped}\\b`, "i");
683
683
  return re.test(actionDetail);
684
684
  }
685
+ function collectIntentTexts(promptSession, promptHistory) {
686
+ const texts = [];
687
+ if (promptHistory) {
688
+ for (const turn of promptHistory) {
689
+ if (typeof turn === "string") {
690
+ const t2 = turn.trim();
691
+ if (t2) texts.push(t2.toLowerCase());
692
+ continue;
693
+ }
694
+ const t = (turn?.text || "").trim();
695
+ if (t) texts.push(t.toLowerCase());
696
+ }
697
+ }
698
+ if (promptSession && promptSession.trim()) {
699
+ texts.push(promptSession.toLowerCase());
700
+ }
701
+ const seen = /* @__PURE__ */ new Set();
702
+ const deduped = [];
703
+ for (const t of texts) {
704
+ if (!seen.has(t)) {
705
+ seen.add(t);
706
+ deduped.push(t);
707
+ }
708
+ }
709
+ return deduped;
710
+ }
685
711
  function runLogicCheck(input) {
686
712
  const { actionHistory, aNext, cwd } = input;
687
713
  const actionDetail = aNext.actionDetail.toLowerCase();
@@ -691,25 +717,24 @@ function runLogicCheck(input) {
691
717
  const isExcludedType = EXCLUDED_PREFIXES.some((p) => actionType.startsWith(p));
692
718
  const rbwResult = checkReadBeforeWrite(actionType, actionDetail, actionHistory, input.reason, resolvedCwd);
693
719
  if (rbwResult) return rbwResult;
694
- if (input.promptSession && input.promptSession.trim()) {
695
- if (!isExcludedType) {
696
- if (INLINE_SCRIPT_RE.test(actionDetail)) {
697
- } else {
698
- const promptLower = input.promptSession.toLowerCase();
699
- for (const pattern of INTENT_DEVIATION_PATTERNS) {
700
- const intentMatched = pattern.intentKeywords.some((kw) => promptLower.includes(kw));
701
- const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
702
- if (intentMatched && dangerousMatched) {
703
- return {
704
- allowed: false,
705
- reason: pattern.reason,
706
- riskLevel: "high",
707
- riskType: "intent_deviation",
708
- confidence: 85,
709
- source: "logic_rule",
710
- violatedLayers: ["2"]
711
- };
712
- }
720
+ if (!isExcludedType) {
721
+ if (!INLINE_SCRIPT_RE.test(actionDetail)) {
722
+ const intentTexts = collectIntentTexts(input.promptSession, input.promptHistory);
723
+ for (const pattern of INTENT_DEVIATION_PATTERNS) {
724
+ const intentMatched = intentTexts.some(
725
+ (text) => pattern.intentKeywords.some((kw) => text.includes(kw))
726
+ );
727
+ const dangerousMatched = pattern.dangerousActions.some((kw) => matchDangerousAction(actionDetail, kw));
728
+ if (intentMatched && dangerousMatched) {
729
+ return {
730
+ allowed: false,
731
+ reason: pattern.reason,
732
+ riskLevel: "high",
733
+ riskType: "intent_deviation",
734
+ confidence: 85,
735
+ source: "logic_rule",
736
+ violatedLayers: ["2"]
737
+ };
713
738
  }
714
739
  }
715
740
  }
@@ -2274,7 +2299,7 @@ function runHeuristicCheck(input) {
2274
2299
  } catch {
2275
2300
  }
2276
2301
  }
2277
- const scanDetail = stripLiteralTextSegments(input.aNext.actionDetail);
2302
+ const scanDetail = normalizePathSegments(stripLiteralTextSegments(input.aNext.actionDetail));
2278
2303
  const scanCommand = `${input.aNext.actionType}: ${scanDetail}`;
2279
2304
  let topRiskType = "";
2280
2305
  for (const rule of DANGEROUS_PATTERNS) {
@@ -2385,6 +2410,28 @@ function stripHeredocContent(actionDetail) {
2385
2410
  "<<heredoc>>\n"
2386
2411
  );
2387
2412
  }
2413
+ function normalizePathSegments(text) {
2414
+ const protTokens = [];
2415
+ let t = text.replace(/\b(https?|ftp|file):\/\//gi, (m) => {
2416
+ const scheme = m.slice(0, m.indexOf(":"));
2417
+ protTokens.push("://");
2418
+ return `${scheme}__PROT${protTokens.length - 1}__`;
2419
+ });
2420
+ const quoteTokens = [];
2421
+ t = t.replace(/'[^']*'|"[^"]*"/g, (m) => {
2422
+ quoteTokens.push(m);
2423
+ return `__Q${quoteTokens.length - 1}__`;
2424
+ });
2425
+ t = t.replace(/\/{2,}/g, "/");
2426
+ t = t.replace(/\/\.\//g, "/");
2427
+ for (let i = 0; i < quoteTokens.length; i++) {
2428
+ t = t.replace(`__Q${i}__`, quoteTokens[i]);
2429
+ }
2430
+ for (let i = 0; i < protTokens.length; i++) {
2431
+ t = t.replace(`__PROT${i}__`, protTokens[i]);
2432
+ }
2433
+ return t;
2434
+ }
2388
2435
  function splitCommandSegments(actionDetail) {
2389
2436
  const segments = [];
2390
2437
  let cur = "";
@@ -2437,7 +2484,7 @@ function isSearchActionType(actionType) {
2437
2484
  function checkSensitivePath(command, cwd, agentExt) {
2438
2485
  const expandedCwd = cwd.replace(/^~/, process.env.HOME || "/home/user");
2439
2486
  const isWriteOpFlag = isWriteOperation("", command);
2440
- const pathScanText = stripLiteralTextSegments(command);
2487
+ const pathScanText = normalizePathSegments(stripLiteralTextSegments(command));
2441
2488
  const pathMatches = (text, path3) => {
2442
2489
  const parts = path3.split(/(\(\?!.*?\))/);
2443
2490
  const escaped = parts.map((part) => {
@@ -3716,9 +3763,14 @@ var ActionItemSchema = z.object({
3716
3763
  action_type: z.string().describe("Action type, e.g. bash, file_read, file_write"),
3717
3764
  action_detail: z.string().describe("Action detail / command content")
3718
3765
  });
3766
+ var PromptTurnSchema = z.object({
3767
+ text: z.string().default("").describe("User prompt text at this turn"),
3768
+ actions: z.array(ActionItemSchema).default([]).describe("Actions triggered by this prompt, in order, until the next prompt")
3769
+ });
3719
3770
  var AnalyzeRequestSchema = z.object({
3720
3771
  session_id: z.string().describe("Session / agent unique identifier"),
3721
- prompt_session: z.string().default("").describe("User original prompt"),
3772
+ prompt_session: z.string().default("").describe("User original prompt (most recent user message; backward compat)"),
3773
+ 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."),
3722
3774
  action_history: z.array(ActionItemSchema).default([]).describe("Historical action sequence"),
3723
3775
  a_next: ActionItemSchema.describe("Next action to be executed"),
3724
3776
  reason: z.string().default("").describe("Reason for executing this action"),
@@ -4567,11 +4619,12 @@ ${prompt}
4567
4619
  function buildPrompt(input, previous, matchedSkills, templates, scriptAnalysis, skipL3Hints = "", scriptChainText = "") {
4568
4620
  const skillsText = formatSkillsForPrompt(matchedSkills);
4569
4621
  const historyStr = input.actionHistory.map((h, i) => `${i + 1}. ${h.actionType}: ${h.actionDetail}`).join("\n") || "(\u65E0\u5386\u53F2\u64CD\u4F5C)";
4622
+ const promptHistoryText = formatPromptHistory(input.promptHistory);
4570
4623
  const scriptAnalysisText = scriptChainText ? scriptChainText : scriptAnalysis ? formatScriptAnalysisForPrompt(scriptAnalysis) : "";
4571
4624
  const hintsText = formatHints(previous);
4572
4625
  const securityJudgeTemplate = templates["security_judge"];
4573
4626
  if (securityJudgeTemplate) {
4574
- 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("}}", "}");
4627
+ 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("}}", "}");
4575
4628
  }
4576
4629
  console.warn("[AgentMoss] security_judge template missing, using sparse fallback prompt");
4577
4630
  return `## \u5F53\u524D\u64CD\u4F5C\u4E0A\u4E0B\u6587
@@ -4601,6 +4654,27 @@ ${scriptAnalysisText}` : ""}
4601
4654
  \u8F93\u51FA\u683C\u5F0F:
4602
4655
  {"allowed": true/false, "reason": "\u5224\u65AD\u7406\u7531", "riskLevel": "low|medium|high|critical", "riskType": "\u98CE\u9669\u7C7B\u578B", "confidence": 0-100}`;
4603
4656
  }
4657
+ function formatPromptHistory(promptHistory, maxTextChars = 500) {
4658
+ if (!promptHistory || promptHistory.length === 0) {
4659
+ return "\uFF08\u672A\u63D0\u4F9B\u5386\u53F2\u610F\u56FE\uFF09";
4660
+ }
4661
+ const lines = [];
4662
+ for (let i = 0; i < promptHistory.length; i++) {
4663
+ const turn = promptHistory[i];
4664
+ const text = (turn?.text || "").trim();
4665
+ if (!text) continue;
4666
+ let line = ` \u7B2C${i + 1}\u8F6E\u7528\u6237\u610F\u56FE: ${text.slice(0, maxTextChars)}`;
4667
+ const actions = turn?.actions;
4668
+ if (actions && actions.length > 0) {
4669
+ const types = actions.map((a) => a?.actionType).filter((t) => typeof t === "string" && t.length > 0);
4670
+ if (types.length > 0) {
4671
+ line += ` \u2192 \u52A8\u4F5C: ${types.join(", ")}`;
4672
+ }
4673
+ }
4674
+ lines.push(line);
4675
+ }
4676
+ return lines.length > 0 ? lines.join("\n") : "\uFF08\u672A\u63D0\u4F9B\u5386\u53F2\u610F\u56FE\uFF09";
4677
+ }
4604
4678
  function formatHints(previous) {
4605
4679
  const hints = [];
4606
4680
  if (previous.heuristic) {