@lytharalab/dsh-cons-auto-review 1.2.1 → 1.2.3

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 CHANGED
@@ -1,3 +1,15 @@
1
+ # 1.2.3
2
+
3
+ - 适配 DSH **0.2.2-rc.1**,保留此前四个版本的支持。CI 在 Node.js 22 和 24 上分别验证五个 DSH 版本。
4
+ - 适配 Cordis 4.1 的权限配置保存与 DSH 新版 Agent 初始化。CAutoR 默认值继续保存只读守护项,子 Agent 在服务重载后保留 `never` 审批策略。
5
+ - 模型审查和审批说明翻译都携带当前会话 ID 与 `auto-review` 用途,满足新版 OpenCode 路由的请求要求。
6
+
7
+ # 1.2.2
8
+
9
+ - 适配 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 版本。
10
+ - 核对新版发布包后,现有审查逻辑可继续复用;无需修改宿主或用户权限配置。
11
+ - 新增真实模型服务回归:能力变化后只选择当前明确提供的低推理档位;安全写入不查询模型,能力查询失败转中文审批且不执行工具。
12
+
1
13
  # 1.2.1
2
14
 
3
15
  - 适配 DSH **0.2.1-rc.2**,保留 **0.1.7-rc.2** 与 **0.2.0-rc.2** 支持;测试依赖升级,CI 扩展为 Node.js 22 / 24 × 三个 DSH 版本。
package/README.md CHANGED
@@ -1,16 +1,16 @@
1
- # Con's 自动审查 — DCAR 1.2.1
1
+ # Con's 自动审查 — DCAR 1.2.3
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@lytharalab/dsh-cons-auto-review.svg)](https://www.npmjs.com/package/@lytharalab/dsh-cons-auto-review)
4
4
  [![CI](https://github.com/LytharaLab/DSH-Cons-Auto-Review/actions/workflows/ci.yml/badge.svg)](https://github.com/LytharaLab/DSH-Cons-Auto-Review/actions/workflows/ci.yml)
5
5
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](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`, `0.2.0-rc.2` and `0.2.1-rc.2`. The detailed documentation below is in Chinese.
7
+ > **English.** DCAR (`@lytharalab/dsh-cons-auto-review`) adds a **CAutoR** preset to the DeepSeek Harness permission menu. Deterministic rules approve safe workspace operations first. Other operations go to the LLM for review. Install with `dsh plugin --profile desktop add @lytharalab/dsh-cons-auto-review`. Requires Node.js 22.19+ or 24+ and supports DSH `0.1.7-rc.2`, `0.2.0-rc.2`, `0.2.1-rc.2`, `0.2.1-rc.5`, and `0.2.2-rc.1`.
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**、**0.2.1-rc.2**。DSH 的插件 API 仍在变化,其他版本不作为 1.2.1 的兼容承诺。
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** 和 **0.2.2-rc.1**。
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.1.tgz"
23
+ dsh plugin --profile desktop add "L:\Projects\DSH-Cons-Auto-Review\lytharalab-dsh-cons-auto-review-1.2.3.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.3 · DSH 0.2.2-rc.1 适配
70
+
71
+ 适配新版的会话权限初始化、默认权限保存和工具执行器。审查请求携带当前会话 ID 与用途,供 OpenCode 路由识别。CI 在 Node.js 22 和 24 上验证五个受支持的 DSH 版本。
72
+
73
+ ## 1.2.2 · DSH 0.2.1-rc.5 适配
74
+
75
+ 支持 DSH **0.2.1-rc.5**,保留此前三个版本的兼容性。测试依赖使用新版真实服务,CI 覆盖 Node.js 22 / 24 × 四个 DSH 版本;新增模型能力动态变化及能力查询失败的真实服务回归验证。
76
+
69
77
  ## 1.2.1 · DSH 0.2.1 适配
70
78
 
71
79
  支持 DSH **0.2.1-rc.2** 的会话工作目录切换:相对文件路径、搜索路径、Shell 的 `workdir` 和审查输入均使用宿主当前目录。自动放行的工作区边界仍是会话初始工作区,切换目录不会扩大它;配置中的相对额外根目录与审计日志路径也仍以初始工作区为基准。当前目录消失时跟随宿主恢复;无法确定目录时按 `review.onError` 处理。旧 DSH 继续使用原有目录逻辑。
@@ -2,17 +2,21 @@
2
2
 
3
3
  ## 支持版本
4
4
 
5
- DCAR 1.2.1 固定支持 DSH **0.1.7-rc.2**、**0.2.0-rc.2** 与 **0.2.1-rc.2**。关键路径使用官方扩展点 `tools/pre-execute`、权限 Session 投影、命令注册表、文件系统解析和 `ctx.llm.stream`。
5
+ DCAR 1.2.3 固定支持 DSH **0.1.7-rc.2**、**0.2.0-rc.2**、**0.2.1-rc.2**、**0.2.1-rc.5** 和 **0.2.2-rc.1**。关键路径使用官方扩展点 `tools/pre-execute`、权限 Session 投影、命令注册表、文件系统解析和 `ctx.llm.stream`。
6
+
7
+ DSH 0.2.2-rc.1 的权限初始化移到 Agent 发布阶段,DCAR 在同一阶段设置 CAutoR 默认值并读取子 Agent 已捕获的委派权限。默认值持久化适配 Cordis 4.1,审查与说明翻译都附带宿主要求的会话 ID 与请求用途。新版集成测试使用 DSH 原生工具执行器。[DSH 0.2.2-rc.1 源码基线](https://github.com/deepseek-ai/deepseek-harness/tree/66491b05e3f05ad35bcdd4f6cd7ed26041d9e3db)
6
8
 
7
9
  源码基线:DeepSeek Harness `639ed015397290b3745d163aafe02ffee4aa3f84`(`dsh-v0.2.0-rc.2`);旧版本基线为 `477b4f420553e8a52c2fbccc464d7561b239c443`(`dsh-v0.1.7-rc.2`)。两版使用的 reviewer、permission service、containment 与文件路径解析源码一致。
8
10
 
9
11
  新增基线:[DeepSeek Harness `687f282e8625c6b8d11ff58211a90524be2e4317`](https://github.com/deepseek-ai/deepseek-harness/tree/687f282e8625c6b8d11ff58211a90524be2e4317)(`dsh-v0.2.1-rc.2`)。新版将当前执行目录与原始工作区分开;DCAR 按宿主当前目录检查相对路径并构造审查环境,保留原工作区作为程序批准边界。当前目录变化通知只作为历史事实,不构成授权。旧版通过能力探测沿用初始目录,无需安装新版目录服务。
10
12
 
13
+ 新增基线:[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)逐字节一致,当前适配逻辑可直接复用。新增真实服务测试验证动态模型能力与查询失败路径;这不替代真实远端模型目录及网络的验收。
14
+
11
15
  ## CAutoR 集成方式
12
16
 
13
17
  官方权限服务只提供固定 `registerAuto()` 动态接口,通用自定义模式依赖静态预设表。1.0.2 由主插件在激活时给已存在的权限服务实例附加动态模式注册能力;安装补丁只插入 `dcar`,不替换宿主的权限服务行。
14
18
 
15
- 运行时适配覆盖该服务实例的 `names/specOf/derive/apply`,复用原始 Session 事件、沙箱和审批 setter、`/permission`、Remote `catalog()`、投影及官方 Auto 注册。审查入口安装后注册 CAutoR;停用时取消审查、回退已加载会话,再撤销审查注册并还原服务原始属性;已保存的默认设置保留只读守护项。它需要官方类中 `specOf/derive/apply/emitCatalogChanged` 的运行时方法;这些不是通用稳定第三方扩展接口,因此依赖版本明确固定为以上三个版本,并在加载时检查方法可用性。插件不修改 DSH 安装目录中的源码,也不注册第二个权限服务。
19
+ 运行时适配覆盖该服务实例的 `names/specOf/derive/apply`,复用原始 Session 事件、沙箱和审批 setter、`/permission`、Remote `catalog()`、投影及官方 Auto 注册。审查入口安装后注册 CAutoR;停用时取消审查、回退已加载会话,再撤销审查注册并还原服务原始属性;已保存的默认设置保留只读守护项。由于适配依赖权限服务的运行时方法,插件明确列出支持的 DSH 版本,并在加载时检查方法可用性。
16
20
 
17
21
  DSH Web/desktop 的权限 UI 从该 `catalog()` 读取选项,自定义标签按服务端 `name` 显示,CAutoR 无需修改前端 bundle。
18
22
 
@@ -26,7 +30,7 @@ DSH Web/desktop 的权限 UI 从该 `catalog()` 读取选项,自定义标签
26
30
 
27
31
  ## 已运行的检查
28
32
 
29
- 见 `TEST_REPORT.md` 与 `test-results/`:三个发布版本分别安装对应的真实 DSH npm 包,运行完整插件测试;旧版显式跳过新版独有的目录服务场景。测试使用真实 Cordis、权限目录、Session、Session 投影、工具执行管线、原生文件读写工具、原生审批和命令服务;模型响应由本地测试 Adapter 提供。
33
+ 见 `TEST_REPORT.md` 与 `test-results/`。五个版本分别安装对应的 DSH npm 包并运行插件测试。最早两个版本跳过三个依赖会话目录服务的用例。测试使用真实 Cordis、权限目录、Session、Session 投影、工具执行管线、原生文件读写工具、原生审批和命令服务,模型响应由本地测试 Adapter 提供。
30
34
 
31
35
  覆盖本地零模型写入、按需升级、拒绝不执行、询问只在批准后执行、符号链接和目录前缀、Shell 混合语法、PTC 内部调用、子 Agent 与创建竞态、卸载回退、缺失审查提供者、配置验证、模型路由、重试、超时、并发、缓存和默认日志参数省略。
32
36
 
@@ -36,7 +40,7 @@ DSH Web/desktop 的权限 UI 从该 `catalog()` 读取选项,自定义标签
36
40
 
37
41
  ## 验证范围
38
42
 
39
- 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 矩阵仍需推送后运行。
43
+ CI 在 Linux 上使用 Node.js 22 和 24 运行兼容性矩阵。自动化验证通过宿主服务检查权限行为,覆盖范围包括 PTC 内层调用的审查,但未启动 PTC VM、真实远端模型 API、Desktop 窗口或 OS 级沙箱进程。
40
44
 
41
45
  默认设置测试使用真实 Settings、Loader 和权限服务,并将配置保存到测试 JSON 文件后重新加载。测试存储替代 DSH 的完整多层 YAML 配置编辑器;未启动实际 Desktop 设置页面。
42
46
 
package/docs/RELEASING.md CHANGED
@@ -64,10 +64,10 @@
64
64
 
65
65
  ## 工作流的几个约定
66
66
 
67
- - 打标签(`v*`)触发真正的发布;手动 `workflow_dispatch` 只会跑校验与 `npm publish --dry-run`,不会真的上传。
67
+ - 推送 `v*` 标签触发发布。手动 `workflow_dispatch` 运行校验和 `npm pack --dry-run`。
68
68
  - 只有 `publish.yml` 会执行 `npm publish`。npm 的 trusted publisher 绑定的是 workflow 文件名,出现第二个能发布的 workflow 会互相干扰。
69
69
  - 发布步骤使用 `--access public --provenance`;`actions/setup-node` 故意**不设置** `registry-url`,避免生成依赖 `NODE_AUTH_TOKEN` 的 `.npmrc`,发布目标由 `publishConfig.registry` 决定。
70
- - `.github/workflows/ci.yml` 在每次推送与 PR 上跑 Node 22/24 × DSH `0.1.7-rc.2` / `0.2.0-rc.2` 矩阵,另外单独校验 npm 包内容:必需文件齐全、`tests/`、`test-results/`、`.github/` 不得进入发布包。
70
+ - `.github/workflows/ci.yml` 在分支推送与 PR 上用 Node.js 22 和 24 验证[受支持的 DSH 版本](COMPATIBILITY.md),并单独检查 npm 包内容。必需文件须齐全,测试、测试记录及工作流仅留在仓库。
71
71
 
72
72
  ## 发布包内容
73
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lytharalab/dsh-cons-auto-review",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
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,36 +68,38 @@
68
68
  },
69
69
  "peerDependencies": {
70
70
  "@deepseek-ai/cordis": "^4.0.0",
71
- "@deepseek-ai/dsh-llm": "0.1.7-rc.2 || 0.2.0-rc.2 || 0.2.1-rc.2",
72
- "@deepseek-ai/dsh-permission-presets": "0.1.7-rc.2 || 0.2.0-rc.2 || 0.2.1-rc.2",
73
- "@deepseek-ai/dsh-sandbox": "0.1.7-rc.2 || 0.2.0-rc.2 || 0.2.1-rc.2",
74
- "@deepseek-ai/dsh-util-values": "0.1.7-rc.2 || 0.2.0-rc.2 || 0.2.1-rc.2",
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 || 0.2.2-rc.1",
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 || 0.2.2-rc.1",
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 || 0.2.2-rc.1",
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 || 0.2.2-rc.1",
75
75
  "@deepseek-ai/schemastery": "*",
76
- "@deepseek-ai/dsh-user-approval": "0.1.7-rc.2 || 0.2.0-rc.2 || 0.2.1-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 || 0.2.2-rc.1"
77
77
  },
78
78
  "devDependencies": {
79
- "@deepseek-ai/cordis": "4.0.4",
80
- "@deepseek-ai/cordis-plugin-loader": "1.0.5",
81
- "@deepseek-ai/dsh-attachment": "0.2.1-rc.2",
82
- "@deepseek-ai/dsh-commands": "0.2.1-rc.2",
83
- "@deepseek-ai/dsh-fs": "0.2.1-rc.2",
84
- "@deepseek-ai/dsh-fs-local": "0.2.1-rc.2",
85
- "@deepseek-ai/dsh-llm": "0.2.1-rc.2",
86
- "@deepseek-ai/dsh-permission-presets": "0.2.1-rc.2",
87
- "@deepseek-ai/dsh-sandbox": "0.2.1-rc.2",
88
- "@deepseek-ai/dsh-sandbox-policy": "0.2.1-rc.2",
89
- "@deepseek-ai/dsh-scope": "0.2.1-rc.2",
90
- "@deepseek-ai/dsh-session": "0.2.1-rc.2",
91
- "@deepseek-ai/dsh-session-projection": "0.2.1-rc.2",
92
- "@deepseek-ai/dsh-settings": "0.2.1-rc.2",
93
- "@deepseek-ai/dsh-shell": "0.2.1-rc.2",
94
- "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.2",
95
- "@deepseek-ai/dsh-tool-fs": "0.2.1-rc.2",
96
- "@deepseek-ai/dsh-tools": "0.2.1-rc.2",
97
- "@deepseek-ai/dsh-user-approval": "0.2.1-rc.2",
98
- "@deepseek-ai/dsh-util-values": "0.2.1-rc.2",
99
- "@deepseek-ai/dsh-working-directory": "0.2.1-rc.2",
100
- "@deepseek-ai/schemastery": "3.18.4",
79
+ "@deepseek-ai/cordis": "4.1.0",
80
+ "@deepseek-ai/cordis-plugin-loader": "1.1.0",
81
+ "@deepseek-ai/dsh-agent": "0.2.2-rc.1",
82
+ "@deepseek-ai/dsh-attachment": "0.2.2-rc.1",
83
+ "@deepseek-ai/dsh-commands": "0.2.2-rc.1",
84
+ "@deepseek-ai/dsh-fs": "0.2.2-rc.1",
85
+ "@deepseek-ai/dsh-fs-local": "0.2.2-rc.1",
86
+ "@deepseek-ai/dsh-llm": "0.2.2-rc.1",
87
+ "@deepseek-ai/dsh-permission-presets": "0.2.2-rc.1",
88
+ "@deepseek-ai/dsh-sandbox": "0.2.2-rc.1",
89
+ "@deepseek-ai/dsh-sandbox-policy": "0.2.2-rc.1",
90
+ "@deepseek-ai/dsh-scope": "0.2.2-rc.1",
91
+ "@deepseek-ai/dsh-session": "0.2.2-rc.1",
92
+ "@deepseek-ai/dsh-session-projection": "0.2.2-rc.1",
93
+ "@deepseek-ai/dsh-settings": "0.2.2-rc.1",
94
+ "@deepseek-ai/dsh-shell": "0.2.2-rc.1",
95
+ "@deepseek-ai/dsh-system-prompt": "0.2.2-rc.1",
96
+ "@deepseek-ai/dsh-tool-executor-native": "0.2.2-rc.1",
97
+ "@deepseek-ai/dsh-tool-fs": "0.2.2-rc.1",
98
+ "@deepseek-ai/dsh-tools": "0.2.2-rc.1",
99
+ "@deepseek-ai/dsh-user-approval": "0.2.2-rc.1",
100
+ "@deepseek-ai/dsh-util-values": "0.2.2-rc.1",
101
+ "@deepseek-ai/dsh-working-directory": "0.2.2-rc.1",
102
+ "@deepseek-ai/schemastery": "3.19.0",
101
103
  "yaml": "2.8.3"
102
104
  },
103
105
  "icon": "./icon.svg",
package/src/engine.js CHANGED
@@ -113,6 +113,8 @@ export class ReviewEngine {
113
113
  const options = {
114
114
  provider: c.review.provider || snapshot.provider,
115
115
  model: c.review.model || snapshot.model,
116
+ sessionId: exec.agent.session.id,
117
+ purpose: "auto-review",
116
118
  system:
117
119
  REVIEW_POLICY +
118
120
  (c.review.allowAsk
package/src/index.js CHANGED
@@ -109,6 +109,16 @@ export function apply(ctx, input = {}) {
109
109
  if (!accepting || !config.enabled) throw new Error("DCAR is disabled or closing");
110
110
  };
111
111
  const getConfig = (session) => overrides.get(session) ?? config;
112
+ // Hosts that seed permissions at agent creation forbid Session writes
113
+ // during session/created, which can still veto publication.
114
+ const defaultOnAgentCreated = presets.constructor.inject?.includes("agents");
115
+ function applyFreshDefault(session) {
116
+ const state = ctx.sessionProjections.stateOf(session, "permissions");
117
+ if (!session.header.parentSession && state?.preset === null &&
118
+ state.sandbox === null && state.approval === null && !state.seeded &&
119
+ presets.defaultPreset === PRESET)
120
+ presets.set(session, config.enabled ? PRESET : config.lifecycle.fallbackPreset);
121
+ }
112
122
  function selected(session) {
113
123
  if (presets.current(session) === PRESET) return true;
114
124
  const state = ctx.sessionProjections.stateOf(session, "permissions");
@@ -144,19 +154,19 @@ export function apply(ctx, input = {}) {
144
154
  (session) => {
145
155
  if (!accepting) return;
146
156
  markInherited(session);
147
- const state = ctx.sessionProjections.stateOf(session, "permissions");
148
- if (!session.header.parentSession && state?.preset === null &&
149
- state.sandbox === null && state.approval === null && !state.seeded &&
150
- presets.defaultPreset === PRESET)
151
- presets.set(session, accepting && config.enabled ? PRESET : config.lifecycle.fallbackPreset);
157
+ if (!defaultOnAgentCreated) applyFreshDefault(session);
152
158
  },
153
159
  { prepend: true },
154
160
  );
155
161
  yield stopAgentCreated = ctx.on("agent/created", ({ agent }) => {
156
162
  if (!accepting) return;
163
+ // Newer hosts append delegation overrides after session/created, before
164
+ // agent/created. Read them before the host fills in a missing preset.
165
+ markInherited(agent.session);
166
+ if (defaultOnAgentCreated) applyFreshDefault(agent.session);
157
167
  if (inherited.has(agent.session))
158
168
  agent.session.append("permission/preset", { preset: PRESET });
159
- });
169
+ }, { prepend: true });
160
170
  yield stopGate = ctx.on(
161
171
  "tools/pre-execute",
162
172
  async (exec, next) => {
@@ -210,7 +220,12 @@ export function apply(ctx, input = {}) {
210
220
  const pending = handoff.get(session);
211
221
  handoff.delete(session);
212
222
  if (!config.enabled || !pending || pending.seq !== session.seq) continue;
213
- presets.apply(session, PRESET, (policy) => setApprovalPolicy(session, pending.approval === "never" ? "never" : policy));
223
+ const keepNever = pending.approval === "never";
224
+ if (keepNever && ctx.approval.overrideOf(session) !== "never")
225
+ setApprovalPolicy(session, "never");
226
+ presets.apply(session, PRESET, (policy) => {
227
+ if (!keepNever) setApprovalPolicy(session, policy);
228
+ });
214
229
  if (pending.inherited) inherited.add(session);
215
230
  }
216
231
  // A session created while the persisted default's provider was absent starts
@@ -42,8 +42,10 @@ export function registerReviewPreset(service, entries, name, spec, admit) {
42
42
  /** Persist a confined CAutoR entry through the host's normal config editor. */
43
43
  const persistenceKey = Symbol.for("dcar.default-persistence");
44
44
  export function attachDefaultPersistence(editor, service, admit) {
45
- const provider = Object.getOwnPropertyDescriptor(service, "ctx")?.value?.fiber;
46
- if (!provider) return () => {};
45
+ // Cordis can expose different views of one fiber through the service and
46
+ // loader. Within this editor's registry both views share a unique uid.
47
+ const providerId = Object.getOwnPropertyDescriptor(service, "ctx")?.value?.fiber.uid;
48
+ if (providerId == null) return () => {};
47
49
  let registry = Object.getOwnPropertyDescriptor(editor, persistenceKey)?.value;
48
50
  if (!registry) {
49
51
  const previous = Object.getOwnPropertyDescriptor(editor, "edit");
@@ -56,7 +58,7 @@ export function attachDefaultPersistence(editor, service, admit) {
56
58
  if (previous && !previous.configurable) throw new Error("DCAR cannot extend this config editor instance");
57
59
  const handlers = new Map();
58
60
  const wrapper = function (entry, change) {
59
- const handler = handlers.get(entry.fiber);
61
+ const handler = handlers.get(entry.fiber?.uid);
60
62
  if (!handler) return original.call(this, entry, change);
61
63
  return original.call(this, entry, (current, inherited) => {
62
64
  const next = change(current, inherited);
@@ -71,10 +73,10 @@ export function attachDefaultPersistence(editor, service, admit) {
71
73
  Object.defineProperty(editor, persistenceKey, { value: registry, configurable: true });
72
74
  }
73
75
  const handler = { service, admit };
74
- registry.handlers.set(provider, handler);
76
+ registry.handlers.set(providerId, handler);
75
77
  return () => {
76
- if (registry.handlers.get(provider) !== handler) return;
77
- registry.handlers.delete(provider);
78
+ if (registry.handlers.get(providerId) !== handler) return;
79
+ registry.handlers.delete(providerId);
78
80
  if (registry.handlers.size) return;
79
81
  if (Object.getOwnPropertyDescriptor(editor, "edit")?.value === registry.wrapper) {
80
82
  if (registry.previous) Object.defineProperty(editor, "edit", registry.previous);