dsh-rule-engine 0.5.11 → 0.5.13
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/README.md +30 -99
- package/lib/core/authorization.js +517 -513
- package/lib/core/config.js +73 -66
- package/lib/core/contract.js +41 -0
- package/lib/core/guard-core.js +754 -724
- package/lib/core/intent.js +11 -3
- package/lib/core/label-fingerprint.js +145 -0
- package/lib/core/lexicon.js +72 -72
- package/lib/core/parser.js +111 -103
- package/lib/core/patterns.js +42 -3
- package/lib/core/state.js +324 -301
- package/lib/core/understander.js +221 -150
- package/lib/index.js +1788 -1663
- package/lib/service.js +17 -2
- package/package.json +1 -1
- package/scripts/publish-aptitude-check.mjs +144 -0
- package/scripts/verify-all.mjs +4 -0
package/lib/core/config.js
CHANGED
|
@@ -1,66 +1,73 @@
|
|
|
1
|
-
// config.js - 插件配置加载。
|
|
2
|
-
// 配置缺失时使用默认值;损坏时也回退默认并记录错误,避免裸奔。
|
|
3
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { configFilePath } from "./paths.js";
|
|
5
|
-
|
|
6
|
-
export const DEFAULT_CONFIG = {
|
|
7
|
-
enabled: true,
|
|
8
|
-
correctInject: true,
|
|
9
|
-
selfProtect: true,
|
|
10
|
-
injectLimitPerRulePerSession: 3,
|
|
11
|
-
taskContractEnabled: false,
|
|
12
|
-
askEnabled: false,
|
|
13
|
-
taskContractMode: "observe",
|
|
14
|
-
taskContractDefaults: {
|
|
15
|
-
agentBudget: 0,
|
|
16
|
-
hashPolicy: "deny",
|
|
17
|
-
dependencyPolicy: "ask",
|
|
18
|
-
allowedPaths: null
|
|
19
|
-
},
|
|
20
|
-
llmIntent: {
|
|
21
|
-
enabled: true,
|
|
22
|
-
dailyLimitPerSession: 50,
|
|
23
|
-
thresholdHigh: 0.8,
|
|
24
|
-
thresholdLow: 0.5,
|
|
25
|
-
cacheSize: 500
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
ok:
|
|
42
|
-
error:
|
|
43
|
-
...DEFAULT_CONFIG
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
1
|
+
// config.js - 插件配置加载。
|
|
2
|
+
// 配置缺失时使用默认值;损坏时也回退默认并记录错误,避免裸奔。
|
|
3
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { configFilePath } from "./paths.js";
|
|
5
|
+
|
|
6
|
+
export const DEFAULT_CONFIG = {
|
|
7
|
+
enabled: true,
|
|
8
|
+
correctInject: true,
|
|
9
|
+
selfProtect: true,
|
|
10
|
+
injectLimitPerRulePerSession: 3,
|
|
11
|
+
taskContractEnabled: false,
|
|
12
|
+
askEnabled: false,
|
|
13
|
+
taskContractMode: "observe",
|
|
14
|
+
taskContractDefaults: {
|
|
15
|
+
agentBudget: 0,
|
|
16
|
+
hashPolicy: "deny",
|
|
17
|
+
dependencyPolicy: "ask",
|
|
18
|
+
allowedPaths: null
|
|
19
|
+
},
|
|
20
|
+
llmIntent: {
|
|
21
|
+
enabled: true,
|
|
22
|
+
dailyLimitPerSession: 50,
|
|
23
|
+
thresholdHigh: 0.8,
|
|
24
|
+
thresholdLow: 0.5,
|
|
25
|
+
cacheSize: 500
|
|
26
|
+
},
|
|
27
|
+
// T3 通用化(2026-08-31):声明式 handler 绑定覆盖表 { ruleId: handlerName }
|
|
28
|
+
// 可选键,缺省 {}——不写即走默认偏好表(HANDLER_BY_RULE),行为零变化;
|
|
29
|
+
// 通用化通道:陌生用户可在规则正文声明 <!-- handler: xxx --> 或在此配置集中覆盖。
|
|
30
|
+
handlerOverrides: {},
|
|
31
|
+
// 残余1 剥离(2026-08-31):本机默认偏好表下沉配置——{ ruleId: handlerName }
|
|
32
|
+
// 通用发布=空(代码零本机编号);本机在此写入 17 条偏好,引擎升级不丢。
|
|
33
|
+
handlerDefaultMap: {}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function loadPluginConfig() {
|
|
37
|
+
try {
|
|
38
|
+
const raw = readFileSync(configFilePath(), "utf8");
|
|
39
|
+
const data = JSON.parse(raw);
|
|
40
|
+
return {
|
|
41
|
+
ok: true,
|
|
42
|
+
error: null,
|
|
43
|
+
...DEFAULT_CONFIG,
|
|
44
|
+
...(data && typeof data === "object" ? data : {})
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
error: `配置读取失败,已用默认配置:${error instanceof Error ? error.message : String(error)}`,
|
|
50
|
+
...DEFAULT_CONFIG
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** 合并并保存插件配置(设置页调用;UTF-8 无 BOM) */
|
|
56
|
+
export function savePluginConfig(partial) {
|
|
57
|
+
const current = loadPluginConfig();
|
|
58
|
+
const next = {
|
|
59
|
+
...current,
|
|
60
|
+
...(partial || {}),
|
|
61
|
+
taskContractDefaults: {
|
|
62
|
+
...DEFAULT_CONFIG.taskContractDefaults,
|
|
63
|
+
...((current.taskContractDefaults || {})),
|
|
64
|
+
...((partial?.taskContractDefaults) || {})
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
// 规范化
|
|
68
|
+
next.taskContractEnabled = next.taskContractEnabled === true;
|
|
69
|
+
next.askEnabled = next.askEnabled === true;
|
|
70
|
+
next.taskContractMode = next.taskContractMode === "armed" ? "armed" : "observe";
|
|
71
|
+
writeFileSync(configFilePath(), JSON.stringify(next, null, 2) + "\n", "utf8");
|
|
72
|
+
return next;
|
|
73
|
+
}
|
package/lib/core/contract.js
CHANGED
|
@@ -17,10 +17,38 @@ export function defaultContract() {
|
|
|
17
17
|
hashPolicy: "deny",
|
|
18
18
|
allowedPaths: null,
|
|
19
19
|
dependencyPolicy: "ask",
|
|
20
|
+
// 0.5.12(F5 批次 3):类别白名单——null=不限制类别(走既有判定);数组=仅放行该类别集合。
|
|
21
|
+
// 破坏类(delete/move/rm 等)永不可入 categories(由 isDestructiveCategory 结构性排除)。
|
|
22
|
+
categories: null,
|
|
23
|
+
// 0.5.12(F5 批次 3):完成自动降级——契约内命令成功后(exit 0 + 产出物),将此信号记录
|
|
24
|
+
// 至 completesAfter,后续契约命中自动降级为普通判定(TTL 不再续期)。
|
|
25
|
+
completesAfter: null,
|
|
20
26
|
source: "default"
|
|
21
27
|
};
|
|
22
28
|
}
|
|
23
29
|
|
|
30
|
+
/** 非破坏动作类别(F5 类别白名单的可选集;破坏类永不可入) */
|
|
31
|
+
export const NON_DESTRUCTIVE_CATEGORIES = new Set(["install", "build", "test", "audit", "analyze", "naming", "sync", "restore"]);
|
|
32
|
+
/** 破坏类类别(任何契约类别白名单都必须排除) */
|
|
33
|
+
export const DESTRUCTIVE_CATEGORIES = new Set(["delete", "move", "replace", "purge"]);
|
|
34
|
+
|
|
35
|
+
/** 从命令文本推断动作类别(纯函数;未知→null 走普通判定)。破坏性词返回破坏类。 */
|
|
36
|
+
export function categoryOfCommand(command) {
|
|
37
|
+
const s = String(command || "").toLowerCase();
|
|
38
|
+
if (/remove-item|\brm(?:dir)?\b|del(?:ete)?\b|move-item|\bmv\b|rename-item|\bren\b|clear-content|clean|purge|rm\s+-/.test(s)) return "delete";
|
|
39
|
+
if (/install|add\s+--save|pnpm\s+add|npm\s+install|yarn\s+add/.test(s)) return "install";
|
|
40
|
+
if (/build|tsc|tsdown|vite\s+build|webpack|rollup|compile|bundle/.test(s)) return "build";
|
|
41
|
+
if (/test|jest|vitest|playwright|npm\s+test|pnpm\s+test|check-plugin-load|verify/.test(s)) return "test";
|
|
42
|
+
if (/audit|mount|consistency|dump-config|inspect/.test(s)) return "audit";
|
|
43
|
+
if (/analyze|analysis|review|read|grep|search/.test(s)) return "analyze";
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 类别是否属于破坏类(结构性排除:破坏类永不因契约类别白名单放行) */
|
|
48
|
+
export function isDestructiveCategory(cat) {
|
|
49
|
+
return cat === "delete" || cat === "move" || cat === "replace" || cat === "purge";
|
|
50
|
+
}
|
|
51
|
+
|
|
24
52
|
/**
|
|
25
53
|
* 会话契约初始化(批次 4,阶段 3 默认武装):
|
|
26
54
|
* 优先级:defaults 显式(用户配置)> global 模板(用户 /guard mode|budget 写入且非默认)> armed 推导 > 系统默认。
|
|
@@ -270,6 +298,19 @@ export function decideContractAction({ contract, action, config = {} }) {
|
|
|
270
298
|
return { outcome: "deny", family: "S", reasonCode: "DEPENDENCY_NOT_AUTHORIZED", reason: "检测到添加依赖操作,当前 deps=deny", nextStep: "使用 deps=allow 或说明必要性" };
|
|
271
299
|
}
|
|
272
300
|
|
|
301
|
+
// 0.5.12(F5 批次 3):类别白名单——契约配置了 categories 时:
|
|
302
|
+
// ① 破坏类句子结构性拒绝(不因契约放行——最坏情况缓解);
|
|
303
|
+
// ② 已知类别不在白名单 → deny;未知类别(null)→ 普通判定(不放大契约)。
|
|
304
|
+
if (Array.isArray(contract.categories) && contract.categories.length > 0) {
|
|
305
|
+
const cat = categoryOfCommand(action.commandText || action.cmd || "");
|
|
306
|
+
if (cat && isDestructiveCategory(cat)) {
|
|
307
|
+
return { outcome: "deny", family: "S", reasonCode: "DESTRUCTIVE_NOT_ALLOWED", reason: `动作类别 ${cat} 属破坏类,任务契约永不放行`, nextStep: "改用非破坏操作,或退出任务契约" };
|
|
308
|
+
}
|
|
309
|
+
if (cat && !contract.categories.includes(cat)) {
|
|
310
|
+
return { outcome: "deny", family: "S", reasonCode: "CATEGORY_NOT_IN_CONTRACT", reason: `动作类别 ${cat} 不在契约白名单 [${contract.categories.join(",")}]`, nextStep: `更新契约类别(如 /guard contract categories build,test)` };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
273
314
|
if (action.mutability === "delegate" && action.unboundedDelegation) {
|
|
274
315
|
return { outcome: "deny", family: "S", reasonCode: "UNBOUNDED_DELEGATION", reason: "该委托可能无界启动子代理,无法满足 agents=N", nextStep: "使用显式子代理调用,或关闭任务契约" };
|
|
275
316
|
}
|