@lytharalab/dsh-cons-auto-review 1.2.0 → 1.2.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/CHANGELOG.md +14 -0
- package/README.md +15 -4
- package/THIRD_PARTY_NOTICES.md +2 -0
- package/docs/COMPATIBILITY.md +8 -4
- package/package.json +25 -24
- package/src/audit.js +2 -3
- package/src/deterministic.js +6 -5
- package/src/engine.js +6 -1
- package/src/index.d.ts +1 -1
- package/src/index.js +3 -2
- package/src/paths.js +5 -5
- package/src/permission-adapter.js +1 -1
- package/src/permission-presets.js +2 -2
- package/src/shell.js +6 -3
- package/src/upstream-reviewer.js +1 -2
- package/src/working-directory.js +26 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# 1.2.2
|
|
2
|
+
|
|
3
|
+
- 适配 DSH **0.2.1-rc.5**,保留 **0.1.7-rc.2**、**0.2.0-rc.2** 与 **0.2.1-rc.2** 支持;测试依赖升级,CI 扩展为 Node.js 22 / 24 × 四个 DSH 版本。
|
|
4
|
+
- 核对新版发布包后,现有审查逻辑可继续复用;无需修改宿主或用户权限配置。
|
|
5
|
+
- 新增真实模型服务回归:能力变化后只选择当前明确提供的低推理档位;安全写入不查询模型,能力查询失败转中文审批且不执行工具。
|
|
6
|
+
|
|
7
|
+
# 1.2.1
|
|
8
|
+
|
|
9
|
+
- 适配 DSH **0.2.1-rc.2**,保留 **0.1.7-rc.2** 与 **0.2.0-rc.2** 支持;测试依赖升级,CI 扩展为 Node.js 22 / 24 × 三个 DSH 版本。
|
|
10
|
+
- 修复会话切换工作目录后,程序检查及模型审查仍使用初始目录的问题:文件、搜索、自定义路径工具及 Shell 相对路径与宿主实际执行目录保持一致。
|
|
11
|
+
- 切换目录不扩大自动放行边界;相对额外可写根与审计日志仍锚定原工作区。无初始目录的会话使用宿主默认目录,目录恢复遵循宿主行为;目录解析失败不自动批准。
|
|
12
|
+
- `/dcar` 显示版本跟随包版本,避免升级后仍显示旧版本号。
|
|
13
|
+
- 增加工作目录变化、外部写入拦截、受保护路径、目录恢复、默认目录、审查环境与日志位置的回归验证。
|
|
14
|
+
|
|
1
15
|
# 1.2.0
|
|
2
16
|
|
|
3
17
|
- 修复长会话下「授权上下文超过 180000 字符的配置上限,自动审查未完成」导致**每个**需要审查的操作都退回人工授权的问题:会话变长后历史会持续增长,此前一旦整体超限就直接放弃审查,模型根本没有被调用。现在历史与项目指令各自有独立字符预算,超出时从最旧条目开始省略,并在审查输入里留下 `omitted-context` 说明——模型可见,不是静默截断。
|
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
# Con's 自动审查 — DCAR 1.2.
|
|
1
|
+
# Con's 自动审查 — DCAR 1.2.2
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@lytharalab/dsh-cons-auto-review)
|
|
4
4
|
[](https://github.com/LytharaLab/DSH-Cons-Auto-Review/actions/workflows/ci.yml)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
|
|
7
|
-
> **English.** DCAR (`@lytharalab/dsh-cons-auto-review`) adds a **CAutoR** preset to the DeepSeek Harness permission menu. Deterministic rules approve safe operations first — workspace writes, declared temp/external roots, literal read-only shell commands — and only what a program cannot approve is escalated to the LLM auto-review. The deterministic layer returns `allow` or `escalate`, never a silent denial. Install with `dsh plugin --profile desktop add @lytharalab/dsh-cons-auto-review`; Node.js 22.19+ or 24+, tested against DSH `0.1.7-rc.2` and `0.2.
|
|
7
|
+
> **English.** DCAR (`@lytharalab/dsh-cons-auto-review`) adds a **CAutoR** preset to the DeepSeek Harness permission menu. Deterministic rules approve safe operations first — workspace writes, declared temp/external roots, literal read-only shell commands — and only what a program cannot approve is escalated to the LLM auto-review. The deterministic layer returns `allow` or `escalate`, never a silent denial. Install with `dsh plugin --profile desktop add @lytharalab/dsh-cons-auto-review`; Node.js 22.19+ or 24+, tested against DSH `0.1.7-rc.2`, `0.2.0-rc.2`, `0.2.1-rc.2` and `0.2.1-rc.5`. The detailed documentation below is in Chinese.
|
|
8
8
|
|
|
9
9
|
**`@lytharalab/dsh-cons-auto-review`** 为 DeepSeek Harness 的「权限」菜单添加 **CAutoR** 模式:先用 DSH「工作区内修改」的确定性路径规则自动放行安全操作,仅将无法由程序自动批准的操作交给 LLM Auto Review。第一层只返回 `allow` 或 `escalate`。
|
|
10
10
|
|
|
11
11
|
## 安装与启用
|
|
12
12
|
|
|
13
|
-
包已发布到 npm:[`@lytharalab/dsh-cons-auto-review`](https://www.npmjs.com/package/@lytharalab/dsh-cons-auto-review)。要求 Node.js 22.19+ 或 24+;兼容并测试 DSH **0.1.7-rc.2**、**0.2.0-rc.2**。DSH 的插件 API 仍在变化,其他版本不作为 1.2.
|
|
13
|
+
包已发布到 npm:[`@lytharalab/dsh-cons-auto-review`](https://www.npmjs.com/package/@lytharalab/dsh-cons-auto-review)。要求 Node.js 22.19+ 或 24+;兼容并测试 DSH **0.1.7-rc.2**、**0.2.0-rc.2**、**0.2.1-rc.2**、**0.2.1-rc.5**。DSH 的插件 API 仍在变化,其他版本不作为 1.2.2 的兼容承诺。
|
|
14
14
|
|
|
15
15
|
```powershell
|
|
16
16
|
dsh plugin --profile desktop add @lytharalab/dsh-cons-auto-review
|
|
@@ -20,7 +20,7 @@ Web 使用 `--profile web`。也可以离线安装解压的项目目录或仓库
|
|
|
20
20
|
|
|
21
21
|
```powershell
|
|
22
22
|
dsh plugin --profile desktop add "L:\Projects\DSH-Cons-Auto-Review"
|
|
23
|
-
dsh plugin --profile desktop add "L:\Projects\DSH-Cons-Auto-Review\lytharalab-dsh-cons-auto-review-1.2.
|
|
23
|
+
dsh plugin --profile desktop add "L:\Projects\DSH-Cons-Auto-Review\lytharalab-dsh-cons-auto-review-1.2.2.tgz"
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
**从旧包名升级:**包在首次公开发布时由 `dsh-cons-auto-review` 改名为 `@lytharalab/dsh-cons-auto-review`。关闭 Desktop,先移除旧包再装新包,然后重新打开:
|
|
@@ -66,6 +66,14 @@ profile 配置里若残留 `name: dsh-cons-auto-review` 的插件行或覆盖片
|
|
|
66
66
|
|
|
67
67
|
CAutoR 的执行策略是 `danger-full-access`。它复用原模式的路径审查逻辑,并以程序检查及 LLM 作为授权入口;不使用原执行沙箱隔离进程。
|
|
68
68
|
|
|
69
|
+
## 1.2.2 · DSH 0.2.1-rc.5 适配
|
|
70
|
+
|
|
71
|
+
支持 DSH **0.2.1-rc.5**,保留此前三个版本的兼容性。测试依赖使用新版真实服务,CI 覆盖 Node.js 22 / 24 × 四个 DSH 版本;新增模型能力动态变化及能力查询失败的真实服务回归验证。
|
|
72
|
+
|
|
73
|
+
## 1.2.1 · DSH 0.2.1 适配
|
|
74
|
+
|
|
75
|
+
支持 DSH **0.2.1-rc.2** 的会话工作目录切换:相对文件路径、搜索路径、Shell 的 `workdir` 和审查输入均使用宿主当前目录。自动放行的工作区边界仍是会话初始工作区,切换目录不会扩大它;配置中的相对额外根目录与审计日志路径也仍以初始工作区为基准。当前目录消失时跟随宿主恢复;无法确定目录时按 `review.onError` 处理。旧 DSH 继续使用原有目录逻辑。
|
|
76
|
+
|
|
69
77
|
## 1.2.0 长会话上下文预算
|
|
70
78
|
|
|
71
79
|
会话变长后,审查输入里的历史事实会持续增长。1.1.0 及更早版本一旦整体超过 `maxInputChars`(默认 180000)就放弃本次审查并退回人工授权——于是**每个**需要审查的操作都要手动确认,而模型根本没有被调用。1.2.0 把长上下文拆成两段独立预算来处理:
|
|
@@ -142,3 +150,6 @@ DCAR 放宽的是「哪些操作还需要人工确认」,不是隔离边界:
|
|
|
142
150
|
## 许可
|
|
143
151
|
|
|
144
152
|
[MIT](LICENSE)。复制和改编的 DeepSeek MIT 代码见 [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md) 与 [THIRD_PARTY_LICENSE.txt](THIRD_PARTY_LICENSE.txt)。
|
|
153
|
+
|
|
154
|
+
## CON?
|
|
155
|
+
- 目前此项目由 [Con89524](https://github.com/Convex89524) 单人维护
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -8,3 +8,5 @@ This project includes adapted code from [DeepSeek Harness](https://github.com/de
|
|
|
8
8
|
- `src/permission-presets.js`: subclasses the host DSH permission service, adding effect-scoped custom review presets and an absent-provider guard; the upstream base implementation stays in the host's installed package.
|
|
9
9
|
|
|
10
10
|
The project's npm peer and development dependencies keep their own license terms. No dependency source or `node_modules` is included in the project ZIP or npm package.
|
|
11
|
+
|
|
12
|
+
DCAR 1.2.1 also follows the working-directory snapshot change in [DeepSeek Harness 687f282e8625c6b8d11ff58211a90524be2e4317](https://github.com/deepseek-ai/deepseek-harness/tree/687f282e8625c6b8d11ff58211a90524be2e4317), under the same MIT License.
|
package/docs/COMPATIBILITY.md
CHANGED
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
## 支持版本
|
|
4
4
|
|
|
5
|
-
DCAR 1.2.
|
|
5
|
+
DCAR 1.2.2 固定支持 DSH **0.1.7-rc.2**、**0.2.0-rc.2**、**0.2.1-rc.2** 与 **0.2.1-rc.5**。关键路径使用官方扩展点 `tools/pre-execute`、权限 Session 投影、命令注册表、文件系统解析和 `ctx.llm.stream`。
|
|
6
6
|
|
|
7
7
|
源码基线:DeepSeek Harness `639ed015397290b3745d163aafe02ffee4aa3f84`(`dsh-v0.2.0-rc.2`);旧版本基线为 `477b4f420553e8a52c2fbccc464d7561b239c443`(`dsh-v0.1.7-rc.2`)。两版使用的 reviewer、permission service、containment 与文件路径解析源码一致。
|
|
8
8
|
|
|
9
|
+
新增基线:[DeepSeek Harness `687f282e8625c6b8d11ff58211a90524be2e4317`](https://github.com/deepseek-ai/deepseek-harness/tree/687f282e8625c6b8d11ff58211a90524be2e4317)(`dsh-v0.2.1-rc.2`)。新版将当前执行目录与原始工作区分开;DCAR 按宿主当前目录检查相对路径并构造审查环境,保留原工作区作为程序批准边界。当前目录变化通知只作为历史事实,不构成授权。旧版通过能力探测沿用初始目录,无需安装新版目录服务。
|
|
10
|
+
|
|
11
|
+
新增基线:[DeepSeek Harness `b830ec950faba0a46dda564be3e2d854afed94f1`](https://github.com/deepseek-ai/deepseek-harness/tree/b830ec950faba0a46dda564be3e2d854afed94f1)(`dsh-v0.2.1-rc.5`)。已核对 GitHub 标签、版本间提交及新版权限服务源码,并比较 rc.2 / rc.5 对应测试依赖发布包:19 个直接 DSH 依赖的 `lib/` 运行时代码与类型声明(不含 source map)逐字节一致,当前适配逻辑可直接复用。新增真实服务测试验证动态模型能力与查询失败路径;这不替代真实远端模型目录及网络的验收。
|
|
12
|
+
|
|
9
13
|
## CAutoR 集成方式
|
|
10
14
|
|
|
11
15
|
官方权限服务只提供固定 `registerAuto()` 动态接口,通用自定义模式依赖静态预设表。1.0.2 由主插件在激活时给已存在的权限服务实例附加动态模式注册能力;安装补丁只插入 `dcar`,不替换宿主的权限服务行。
|
|
12
16
|
|
|
13
|
-
运行时适配覆盖该服务实例的 `names/specOf/derive/apply`,复用原始 Session 事件、沙箱和审批 setter、`/permission`、Remote `catalog()`、投影及官方 Auto 注册。审查入口安装后注册 CAutoR;停用时取消审查、回退已加载会话,再撤销审查注册并还原服务原始属性;已保存的默认设置保留只读守护项。它需要官方类中 `specOf/derive/apply/emitCatalogChanged`
|
|
17
|
+
运行时适配覆盖该服务实例的 `names/specOf/derive/apply`,复用原始 Session 事件、沙箱和审批 setter、`/permission`、Remote `catalog()`、投影及官方 Auto 注册。审查入口安装后注册 CAutoR;停用时取消审查、回退已加载会话,再撤销审查注册并还原服务原始属性;已保存的默认设置保留只读守护项。它需要官方类中 `specOf/derive/apply/emitCatalogChanged` 的运行时方法;这些不是通用稳定第三方扩展接口,因此依赖版本明确固定为以上四个版本,并在加载时检查方法可用性。插件不修改 DSH 安装目录中的源码,也不注册第二个权限服务。
|
|
14
18
|
|
|
15
19
|
DSH Web/desktop 的权限 UI 从该 `catalog()` 读取选项,自定义标签按服务端 `name` 显示,CAutoR 无需修改前端 bundle。
|
|
16
20
|
|
|
@@ -24,7 +28,7 @@ DSH Web/desktop 的权限 UI 从该 `catalog()` 读取选项,自定义标签
|
|
|
24
28
|
|
|
25
29
|
## 已运行的检查
|
|
26
30
|
|
|
27
|
-
见 `TEST_REPORT.md` 与 `test-results
|
|
31
|
+
见 `TEST_REPORT.md` 与 `test-results/`:四个发布版本分别安装对应的真实 DSH npm 包,运行完整插件测试;旧版显式跳过新版独有的目录服务场景。测试使用真实 Cordis、权限目录、Session、Session 投影、工具执行管线、原生文件读写工具、原生审批和命令服务;模型响应由本地测试 Adapter 提供。
|
|
28
32
|
|
|
29
33
|
覆盖本地零模型写入、按需升级、拒绝不执行、询问只在批准后执行、符号链接和目录前缀、Shell 混合语法、PTC 内部调用、子 Agent 与创建竞态、卸载回退、缺失审查提供者、配置验证、模型路由、重试、超时、并发、缓存和默认日志参数省略。
|
|
30
34
|
|
|
@@ -34,7 +38,7 @@ DSH Web/desktop 的权限 UI 从该 `catalog()` 读取选项,自定义标签
|
|
|
34
38
|
|
|
35
39
|
## 验证范围
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
1.2.2 与 1.2.1 验证环境为 Windows、Node.js 24.13.0;历史验证环境为 Linux、Node.js 24.19.0。未实际运行 Desktop 窗口、真实模型 API 或 OS 级沙箱进程。这里的 PTC 检查使用真实工具管线的 nested dispatch 输入,未启动 PTC VM。Node.js 22 / 24 的 Linux CI 矩阵仍需推送后运行。
|
|
38
42
|
|
|
39
43
|
默认设置测试使用真实 Settings、Loader 和权限服务,并将配置保存到测试 JSON 文件后重新加载。测试存储替代 DSH 的完整多层 YAML 配置编辑器;未启动实际 Desktop 设置页面。
|
|
40
44
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lytharalab/dsh-cons-auto-review",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Con's 自动审查(DCAR):确定性规则优先、LLM 按需升级的 DeepSeek Harness 工具审查插件,提供 CAutoR 权限模式。Deterministic-first auto review for DeepSeek Harness.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -68,34 +68,35 @@
|
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"@deepseek-ai/cordis": "^4.0.0",
|
|
71
|
-
"@deepseek-ai/dsh-llm": "
|
|
72
|
-
"@deepseek-ai/dsh-permission-presets": "0.1.7-rc.2 || 0.2.0-rc.2",
|
|
73
|
-
"@deepseek-ai/dsh-sandbox": "
|
|
74
|
-
"@deepseek-ai/dsh-util-values": "
|
|
71
|
+
"@deepseek-ai/dsh-llm": "0.1.7-rc.2 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.5",
|
|
72
|
+
"@deepseek-ai/dsh-permission-presets": "0.1.7-rc.2 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.5",
|
|
73
|
+
"@deepseek-ai/dsh-sandbox": "0.1.7-rc.2 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.5",
|
|
74
|
+
"@deepseek-ai/dsh-util-values": "0.1.7-rc.2 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.5",
|
|
75
75
|
"@deepseek-ai/schemastery": "*",
|
|
76
|
-
"@deepseek-ai/dsh-user-approval": "0.1.7-rc.2 || 0.2.0-rc.2"
|
|
76
|
+
"@deepseek-ai/dsh-user-approval": "0.1.7-rc.2 || 0.2.0-rc.2 || 0.2.1-rc.2 || 0.2.1-rc.5"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@deepseek-ai/cordis": "4.0.4",
|
|
80
80
|
"@deepseek-ai/cordis-plugin-loader": "1.0.5",
|
|
81
|
-
"@deepseek-ai/dsh-attachment": "0.2.
|
|
82
|
-
"@deepseek-ai/dsh-commands": "0.2.
|
|
83
|
-
"@deepseek-ai/dsh-fs": "0.2.
|
|
84
|
-
"@deepseek-ai/dsh-fs-local": "0.2.
|
|
85
|
-
"@deepseek-ai/dsh-llm": "0.2.
|
|
86
|
-
"@deepseek-ai/dsh-permission-presets": "0.2.
|
|
87
|
-
"@deepseek-ai/dsh-sandbox": "0.2.
|
|
88
|
-
"@deepseek-ai/dsh-sandbox-policy": "0.2.
|
|
89
|
-
"@deepseek-ai/dsh-scope": "0.2.
|
|
90
|
-
"@deepseek-ai/dsh-session": "0.2.
|
|
91
|
-
"@deepseek-ai/dsh-session-projection": "0.2.
|
|
92
|
-
"@deepseek-ai/dsh-settings": "0.2.
|
|
93
|
-
"@deepseek-ai/dsh-shell": "0.2.
|
|
94
|
-
"@deepseek-ai/dsh-system-prompt": "0.2.
|
|
95
|
-
"@deepseek-ai/dsh-tool-fs": "0.2.
|
|
96
|
-
"@deepseek-ai/dsh-tools": "0.2.
|
|
97
|
-
"@deepseek-ai/dsh-user-approval": "0.2.
|
|
98
|
-
"@deepseek-ai/dsh-util-values": "0.2.
|
|
81
|
+
"@deepseek-ai/dsh-attachment": "0.2.1-rc.5",
|
|
82
|
+
"@deepseek-ai/dsh-commands": "0.2.1-rc.5",
|
|
83
|
+
"@deepseek-ai/dsh-fs": "0.2.1-rc.5",
|
|
84
|
+
"@deepseek-ai/dsh-fs-local": "0.2.1-rc.5",
|
|
85
|
+
"@deepseek-ai/dsh-llm": "0.2.1-rc.5",
|
|
86
|
+
"@deepseek-ai/dsh-permission-presets": "0.2.1-rc.5",
|
|
87
|
+
"@deepseek-ai/dsh-sandbox": "0.2.1-rc.5",
|
|
88
|
+
"@deepseek-ai/dsh-sandbox-policy": "0.2.1-rc.5",
|
|
89
|
+
"@deepseek-ai/dsh-scope": "0.2.1-rc.5",
|
|
90
|
+
"@deepseek-ai/dsh-session": "0.2.1-rc.5",
|
|
91
|
+
"@deepseek-ai/dsh-session-projection": "0.2.1-rc.5",
|
|
92
|
+
"@deepseek-ai/dsh-settings": "0.2.1-rc.5",
|
|
93
|
+
"@deepseek-ai/dsh-shell": "0.2.1-rc.5",
|
|
94
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.1-rc.5",
|
|
95
|
+
"@deepseek-ai/dsh-tool-fs": "0.2.1-rc.5",
|
|
96
|
+
"@deepseek-ai/dsh-tools": "0.2.1-rc.5",
|
|
97
|
+
"@deepseek-ai/dsh-user-approval": "0.2.1-rc.5",
|
|
98
|
+
"@deepseek-ai/dsh-util-values": "0.2.1-rc.5",
|
|
99
|
+
"@deepseek-ai/dsh-working-directory": "0.2.1-rc.5",
|
|
99
100
|
"@deepseek-ai/schemastery": "3.18.4",
|
|
100
101
|
"yaml": "2.8.3"
|
|
101
102
|
},
|
package/src/audit.js
CHANGED
|
@@ -32,7 +32,7 @@ export class Audit {
|
|
|
32
32
|
request(id) {
|
|
33
33
|
this.state(id).llmRequests++;
|
|
34
34
|
}
|
|
35
|
-
record(exec, event, c) {
|
|
35
|
+
record(exec, event, c, workspaceRoot = exec.agent.session.header.cwd) {
|
|
36
36
|
const id = String(exec.agent.session.id),
|
|
37
37
|
s = this.state(id);
|
|
38
38
|
s.total++;
|
|
@@ -74,10 +74,9 @@ export class Audit {
|
|
|
74
74
|
if (s.history.length > c.audit.historyLimit)
|
|
75
75
|
s.history.splice(0, s.history.length - c.audit.historyLimit);
|
|
76
76
|
if (c.audit.file) {
|
|
77
|
-
const cwd = exec.agent.session.header.cwd;
|
|
78
77
|
const file = isAbsolute(c.audit.file)
|
|
79
78
|
? c.audit.file
|
|
80
|
-
: resolve(
|
|
79
|
+
: resolve(workspaceRoot, c.audit.file);
|
|
81
80
|
this.tail = this.tail
|
|
82
81
|
.then(async () => {
|
|
83
82
|
await mkdir(dirname(file), { recursive: true });
|
package/src/deterministic.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Layer one: a positive authorization rule or escalation; never a final deny. */
|
|
2
2
|
import { checkPaths, allow, escalate } from "./paths.js";
|
|
3
3
|
import { checkShell } from "./shell.js";
|
|
4
|
+
import { executionDirectories } from "./working-directory.js";
|
|
4
5
|
function field(a, path) {
|
|
5
6
|
return path
|
|
6
7
|
.split(".")
|
|
@@ -28,18 +29,18 @@ export async function deterministic(exec, ctx, c, signal = exec.signal) {
|
|
|
28
29
|
return escalate("INVALID_ARGUMENTS", "Expected object arguments");
|
|
29
30
|
if (c.shell.tools.includes(name)) return checkShell(exec, ctx, c, signal);
|
|
30
31
|
const custom = c.rules.customTools.find((r) => r.name === name);
|
|
31
|
-
const cwd = exec
|
|
32
|
+
const { cwd, workspaceRoot } = await executionDirectories(exec, ctx, signal);
|
|
32
33
|
if (custom) {
|
|
33
34
|
const paths = custom.pathFields.flatMap((p) => {
|
|
34
35
|
const v = field(a, p);
|
|
35
36
|
return Array.isArray(v) ? v : [v];
|
|
36
37
|
});
|
|
37
|
-
return checkPaths(paths, custom.effect, cwd, ctx, c, signal);
|
|
38
|
+
return checkPaths(paths, custom.effect, cwd, ctx, c, signal, workspaceRoot);
|
|
38
39
|
}
|
|
39
40
|
if (c.rules.readTools.includes(name))
|
|
40
|
-
return checkPaths([a.file_path], "read", cwd, ctx, c, signal);
|
|
41
|
+
return checkPaths([a.file_path], "read", cwd, ctx, c, signal, workspaceRoot);
|
|
41
42
|
if (c.rules.writeTools.includes(name))
|
|
42
|
-
return checkPaths([a.file_path], "write", cwd, ctx, c, signal);
|
|
43
|
+
return checkPaths([a.file_path], "write", cwd, ctx, c, signal, workspaceRoot);
|
|
43
44
|
if (c.rules.searchTools.includes(name)) {
|
|
44
45
|
const path = a.path ?? a.cwd ?? cwd;
|
|
45
46
|
// An absolute/traversing glob can search beyond its supplied root; do not approve it by its cwd.
|
|
@@ -49,7 +50,7 @@ export async function deterministic(exec, ctx, c, signal = exec.signal) {
|
|
|
49
50
|
/(^|[\\/])\.\.([\\/]|$)|^[\\/]|^[a-z]:/i.test(a.pattern))
|
|
50
51
|
)
|
|
51
52
|
return escalate("SEARCH_SCOPE", "Search pattern needs review");
|
|
52
|
-
return checkPaths([path], "read", cwd, ctx, c, signal);
|
|
53
|
+
return checkPaths([path], "read", cwd, ctx, c, signal, workspaceRoot);
|
|
53
54
|
}
|
|
54
55
|
return escalate(
|
|
55
56
|
"UNKNOWN_TOOL",
|
package/src/engine.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Deterministic-first authorization, configurable LLM fallback, and audit. */
|
|
2
2
|
import { deterministic } from "./deterministic.js";
|
|
3
|
+
import { executionDirectories, workspaceDirectory } from "./working-directory.js";
|
|
3
4
|
import {
|
|
4
5
|
snapshotAutoReview,
|
|
5
6
|
budgetReviewText,
|
|
@@ -86,7 +87,10 @@ export class ReviewEngine {
|
|
|
86
87
|
c.review.maxQueued,
|
|
87
88
|
reviewSignal,
|
|
88
89
|
);
|
|
89
|
-
const
|
|
90
|
+
const { cwd } = await abortable(
|
|
91
|
+
executionDirectories(exec, this.ctx, reviewSignal), reviewSignal,
|
|
92
|
+
);
|
|
93
|
+
const snapshot = this.snapshot(exec.agent, exec, cwd);
|
|
90
94
|
const budgeted = budgetReviewText(snapshot, c.review);
|
|
91
95
|
const text = budgeted.text;
|
|
92
96
|
trace.contextOmitted =
|
|
@@ -286,6 +290,7 @@ export class ReviewEngine {
|
|
|
286
290
|
displayReason,
|
|
287
291
|
},
|
|
288
292
|
c,
|
|
293
|
+
workspaceDirectory(exec, this.ctx),
|
|
289
294
|
);
|
|
290
295
|
return result;
|
|
291
296
|
}
|
package/src/index.d.ts
CHANGED
package/src/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** CAutoR permission preset and deterministic-first tool gate. */
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
|
+
import pkg from "../package.json" with { type: "json" };
|
|
3
4
|
import { DEFAULTS, resolveConfig, patchConfig } from "./config.js";
|
|
4
5
|
import { ReviewEngine } from "./engine.js";
|
|
5
6
|
import { attachPermissionAdapter } from "./permission-adapter.js";
|
|
@@ -359,7 +360,7 @@ export function apply(ctx, input = {}) {
|
|
|
359
360
|
if (cmd === "" || cmd === "status")
|
|
360
361
|
return {
|
|
361
362
|
kind: "success",
|
|
362
|
-
text: `DCAR
|
|
363
|
+
text: `DCAR ${pkg.version} | 当前权限:${presets.current(session)} | CAutoR 审查:${selected(session) || inherited.has(session) ? "启用" : "未启用"}\n/dcar on · off · stats · history · config · reset · clear-cache · check`,
|
|
363
364
|
};
|
|
364
365
|
return {
|
|
365
366
|
kind: "error",
|
package/src/paths.js
CHANGED
|
@@ -38,17 +38,17 @@ export function protectedPath(path, config) {
|
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
/** Check every path, including aliases and missing targets, without changing sandbox policy. */
|
|
41
|
-
export async function checkPaths(paths, effect, cwd, ctx, config, signal) {
|
|
41
|
+
export async function checkPaths(paths, effect, cwd, ctx, config, signal, workspaceRoot = cwd) {
|
|
42
42
|
if (!paths.length)
|
|
43
43
|
return escalate("INVALID_PATH", "No target paths supplied");
|
|
44
|
-
if (!isAbsolute(cwd))
|
|
44
|
+
if (!isAbsolute(cwd) || !isAbsolute(workspaceRoot))
|
|
45
45
|
return escalate("WORKSPACE_UNAVAILABLE", "No absolute session workspace");
|
|
46
46
|
const roots = config.rules.includeTempRoots
|
|
47
|
-
? writableRoots({ mode: "workspace-write", workspaceRoot
|
|
48
|
-
: [
|
|
47
|
+
? writableRoots({ mode: "workspace-write", workspaceRoot })
|
|
48
|
+
: [workspaceRoot];
|
|
49
49
|
roots.push(
|
|
50
50
|
...config.rules.additionalWritableRoots.map((p) =>
|
|
51
|
-
isAbsolute(p) ? p : resolve(
|
|
51
|
+
isAbsolute(p) ? p : resolve(workspaceRoot, p),
|
|
52
52
|
),
|
|
53
53
|
);
|
|
54
54
|
for (const path of paths) {
|
|
@@ -24,7 +24,7 @@ export function attachPermissionAdapter(service) {
|
|
|
24
24
|
if (typeof names !== "function" ||
|
|
25
25
|
Object.values(original).some((method) => typeof method !== "function") ||
|
|
26
26
|
typeof service.emitCatalogChanged !== "function")
|
|
27
|
-
throw new Error("DCAR
|
|
27
|
+
throw new Error("DCAR 所需的 DSH 权限服务接口不可用,无法启用自动审查;请参阅 docs/COMPATIBILITY.md");
|
|
28
28
|
if (!Object.isExtensible(service) || [...own.values()].some((d) => d && !d.configurable))
|
|
29
29
|
throw new Error("DCAR cannot extend this permission service instance");
|
|
30
30
|
const entries = new Map();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Permission service adapter for the DSH releases listed in docs/COMPATIBILITY.md.
|
|
3
3
|
* Adds effect-scoped review presets without changing DSH's built-in knob setters,
|
|
4
4
|
* /permission command, session projection, catalog Remote, or official Auto integration.
|
|
5
5
|
*/
|
|
@@ -13,7 +13,7 @@ export default class DCARPermissionPresetService extends PermissionPresetService
|
|
|
13
13
|
for (const method of ["specOf", "derive", "apply", "emitCatalogChanged"])
|
|
14
14
|
if (typeof PermissionPresetService.prototype[method] !== "function")
|
|
15
15
|
throw new Error(
|
|
16
|
-
`DCAR
|
|
16
|
+
`DCAR 所需的 DSH 权限服务接口 ${method} 不可用,无法启用自动审查;请参阅 docs/COMPATIBILITY.md`,
|
|
17
17
|
);
|
|
18
18
|
super(ctx, config);
|
|
19
19
|
this.dcarReviewPresets = new Map();
|
package/src/shell.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Conservative command grammar for unconfined execution. Unrecognized syntax escalates. */
|
|
2
2
|
import { checkPaths, allow, escalate } from "./paths.js";
|
|
3
|
+
import { executionDirectories } from "./working-directory.js";
|
|
3
4
|
/** Parse literal argv only; never evaluate a command or use a prefix permission match. */
|
|
4
5
|
export function literalArgv(command) {
|
|
5
6
|
if (
|
|
@@ -61,9 +62,9 @@ export async function checkShell(exec, ctx, c, signal) {
|
|
|
61
62
|
"Expansion, compound syntax or scripts require LLM review",
|
|
62
63
|
);
|
|
63
64
|
const [exe, ...args] = argv;
|
|
64
|
-
const cwd = exec
|
|
65
|
+
const { cwd, workspaceRoot } = await executionDirectories(exec, ctx, signal);
|
|
65
66
|
if (a.workdir !== undefined) {
|
|
66
|
-
const checked = await checkPaths([a.workdir], "write", cwd, ctx, c, signal);
|
|
67
|
+
const checked = await checkPaths([a.workdir], "write", cwd, ctx, c, signal, workspaceRoot);
|
|
67
68
|
if (checked.kind !== "allow") return checked;
|
|
68
69
|
}
|
|
69
70
|
const base =
|
|
@@ -109,6 +110,7 @@ export async function checkShell(exec, ctx, c, signal) {
|
|
|
109
110
|
ctx,
|
|
110
111
|
c,
|
|
111
112
|
signal,
|
|
113
|
+
workspaceRoot,
|
|
112
114
|
);
|
|
113
115
|
}
|
|
114
116
|
if (exe === "pwd")
|
|
@@ -181,6 +183,7 @@ export async function checkShell(exec, ctx, c, signal) {
|
|
|
181
183
|
ctx,
|
|
182
184
|
c,
|
|
183
185
|
signal,
|
|
186
|
+
workspaceRoot,
|
|
184
187
|
);
|
|
185
188
|
}
|
|
186
189
|
if (c.shell.workspaceMutationCommands.includes(exe)) {
|
|
@@ -191,7 +194,7 @@ export async function checkShell(exec, ctx, c, signal) {
|
|
|
191
194
|
);
|
|
192
195
|
if (!paths.length || paths.some((x) => x.startsWith("-")))
|
|
193
196
|
return escalate("SHELL_OPTIONS", "Unsupported mutation options");
|
|
194
|
-
return checkPaths(paths, "write", base, ctx, c, signal);
|
|
197
|
+
return checkPaths(paths, "write", base, ctx, c, signal, workspaceRoot);
|
|
195
198
|
}
|
|
196
199
|
return escalate("UNKNOWN_COMMAND", `Command ${exe} requires semantic review`);
|
|
197
200
|
}
|
package/src/upstream-reviewer.js
CHANGED
|
@@ -221,7 +221,7 @@ function ptcAction(exec, start, visibleParentKeys) {
|
|
|
221
221
|
arguments: exec.arguments,
|
|
222
222
|
};
|
|
223
223
|
}
|
|
224
|
-
function snapshotAutoReview(agent, exec) {
|
|
224
|
+
function snapshotAutoReview(agent, exec, cwd = agent.session.header.cwd) {
|
|
225
225
|
const { session } = agent;
|
|
226
226
|
const events = session.snapshotEvents();
|
|
227
227
|
const nodes = [...session.surface.nodes];
|
|
@@ -235,7 +235,6 @@ function snapshotAutoReview(agent, exec) {
|
|
|
235
235
|
"auto-review: no complete request-header route is available",
|
|
236
236
|
);
|
|
237
237
|
}
|
|
238
|
-
const cwd = session.header.cwd;
|
|
239
238
|
if (cwd === void 0 || cwd.length === 0) {
|
|
240
239
|
throw new Error("auto-review: the session has no working directory");
|
|
241
240
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Resolve paths where the host executes them, without moving the permission root. */
|
|
2
|
+
import { isAbsolute } from "node:path";
|
|
3
|
+
|
|
4
|
+
function directoryService(ctx) {
|
|
5
|
+
return typeof ctx.get === "function"
|
|
6
|
+
? ctx.get("workingDirectory")
|
|
7
|
+
: ctx.workingDirectory;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function workspaceDirectory(exec, ctx) {
|
|
11
|
+
return exec.agent.session.header.cwd ?? directoryService(ctx)?.defaultDirectory;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function executionDirectories(exec, ctx, signal) {
|
|
15
|
+
const service = directoryService(ctx);
|
|
16
|
+
const original = exec.agent.session.header.cwd;
|
|
17
|
+
// Older DSH releases have no mutable working-directory service.
|
|
18
|
+
const cwd = service === undefined
|
|
19
|
+
? original
|
|
20
|
+
: await service.ensure(exec.agent, signal);
|
|
21
|
+
const workspaceRoot = original ?? service?.defaultDirectory;
|
|
22
|
+
if (typeof cwd !== "string" || !isAbsolute(cwd) ||
|
|
23
|
+
typeof workspaceRoot !== "string" || !isAbsolute(workspaceRoot))
|
|
24
|
+
throw new Error("DCAR 无法确定当前工作目录和原始工作区,不能据此自动放行");
|
|
25
|
+
return { cwd, workspaceRoot };
|
|
26
|
+
}
|