@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 +97 -23
- package/dist/cli.js.map +1 -1
- package/dist/engine/coordinator.d.ts +9 -0
- package/dist/engine/coordinator.js +91 -22
- package/dist/engine/coordinator.js.map +1 -1
- package/dist/server.js +97 -23
- package/dist/server.js.map +1 -1
- package/dist/static/index.html +282 -245
- package/dist/templates/security_judge_template.txt +7 -1
- package/package.json +1 -1
|
@@ -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 (
|
|
685
|
-
if (!
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
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) {
|