# @dsh-enhanced/assistant-policy

个人助理的统一授权边界：默认拒绝、单调工具 guard、策略自有硬预算、持久审批提案、紧急停止和脱敏审计。后续 Memory、Wiki、Automation、Channel 与 Credential 插件只通过 `ctx.assistantPolicy` 请求授权，不应各自实现一套可绕过的规则。

关闭资源词表把 `preference` 与 `memory` 分开：前者只表示可衰减、可回滚的偏好假设和低风险 overlay，后者才是 owner-confirmed 长期事实。允许前者不会隐式允许写入后者。

## 安装

```sh
dsh plugin --profile web add @dsh-enhanced/assistant-policy
dsh --profile web --dump-config
```

默认配置把数据库放在 `$DSH_HOME/assistant-policy/policy.sqlite`，并且 `rules` 为空、`toolDefaultEffect` 为 `deny`。因此插件安装后会拒绝所有经过 DSH `tools.guard()` 的工具调用，直到 profile 的 `cordis.patch.yml` 明确配置 allow 规则。这是预期的 fail-closed 行为。把 `toolDefaultEffect` 设为 `allow` 只会放行没有匹配规则的工具执行；显式 deny、紧急停止、身份缺失和已耗尽预算始终优先，不能被“完全访问”或自动审核绕过。

## 最小配置

下面只允许 `primary` preset 在指定 workspace 的前台会话读取文件；`bash` 和后台任务仍会被拒绝：

```yaml
- id: dsh-enhanced-assistant-policy
  name: '@dsh-enhanced/assistant-policy'
  config:
    databasePath: !!js dshHomePath('assistant-policy/policy.sqlite')
    proposalMaintenanceIntervalMs: 15000
    toolDefaultEffect: deny
    autoReview:
      enabled: true
      timeoutMs: 30000
      maxTokens: 512
    rules:
      - id: allow-primary-workspace-read
        effect: allow
        subject:
          kind: agent
          id: primary
          workspace: /absolute/path/to/workspace
        actions: [execute]
        resource:
          kind: tool
          id: read
        context:
          initiators: [foreground]
    budgets: []
```

匹配支持完整字符串中的 `*` 通配符。任意匹配的 deny 都优先于 allow；同 effect 先选更具体的规则，再按声明顺序决定。工具身份来自 DSH 的 `Agent.session.header.agentPreset` 与绝对 `cwd`，缺少 agent、preset 或 workspace 时直接拒绝，调用方参数不能伪造这些字段。

## 三档即时审批

当前适配 DSH `0.2.0-rc.2`：`auto` 是上游保留档位，由 Policy 在权限服务就绪期间通过 `permissionPresets.registerAuto()` 动态注册；meta-bundle 不配置同名静态项。定向测试覆盖注册、服务换代、撤销及 Session format v4；完整部署仍需独立验收。

生产三档由标准 `permission/preset`、`sandbox/mode` 与 `approval/policy` 共同决定：官方 id `workspace-write` / `auto` / `danger-full-access` 分别映射 reviewer `user` / `auto-review` / `none`，而且日志中更晚的官方选择会覆盖更早的旧 reviewer 事件。`assistant-policy/approval-reviewer` 仍保留为旧 session、单 workspace preset 或第三方动态 id 的兼容事实；无论意图来自哪一种事件，执行状态仍逐项核验：

- **请求批准**：`sandbox/mode: workspace-write` + `approval/policy: ask` + reviewer `user`。低风险精确 allowlist 直接继续，其余动作交给人工。
- **帮我批准**：DSH `0.2.0-rc.2` 的保留 Auto bundle 使用 `danger-full-access + ask`，由 Policy 的实时注册控制；只有 format v4 且有效 preset 为 `auto` 才识别该组合。format v3 中残留的 `auto` 选择不能授权 full-access 自动审核，未知会话格式也不会启用自动审核。内置只读网络工具、workspace 内文件读写、非凭据本地文件只读、单个命名 Skill 的加载、普通前台 Bash（含测试、构建、非敏感管道/重定向和依赖安装）以及专用 `ask_user_question` 直接继续，不再消耗 reviewer 或打断用户；未知插件工具仍进入隔离 reviewer。凭据、后台进程、破坏性命令、提权、workspace 外写入、任意 `run_code` / PowerShell，以及显式原生 sandbox escalation 仍交人工。
- **完全访问权限**：只有显式且同时生效的 `sandbox/mode: danger-full-access` + `approval/policy: never` + reviewer `none` 才会绕过参数级即时风险门。单独的 `never`、缺少 sandbox 事件、事件畸形或三者暂时不一致都会失败关闭，绝不会把 headless 的“不询问”误解成 full；此时敏感工具直接返回稳定的 `[approval-disabled]` 诊断，并明确说明没有向用户展示审批，不再制造 `the user rejected tool` 的错误归因。`tools.guard()` 的显式 deny、紧急停止、身份和预算检查仍然单调生效；若希望未匹配工具也可执行，需要显式配置 `toolDefaultEffect: allow`。

包根导出 `approvalReviewerOf(events)`、`getApprovalReviewer(session)` 和兼容 setter `setApprovalReviewer(session, reviewer)`；setter 只接受 `user | auto-review | none`。auto 与 full 都要求 preset 意图、显式 approval 和显式 sandbox 完整一致；缺项、畸形事件或切换中的中间状态只会折回 `user`。因此 Web 的 full→auto、ask→auto，以及 Delivery auto→Web ask 都由最新官方 preset 直接串行折叠，不再依赖另写 custom reviewer。

canonical `danger-full-access` 现在直接折叠为 reviewer `none`，不需要迁移写入。仅对旧版/第三方的非 canonical full preset，插件仍在 live session 扫描、`session/created` 和工具执行屏障做严格兼容迁移；它要求显式 full bundle、sandbox 与 approval 完全一致且没有 reviewer，失败则收窄并拒绝本次工具。

`assistant-policy/approval-reviewer` 是 required session event。当前 DSH 尚无公开事件注册 API，而且独立安装/软链接插件时，插件和 DSH Host 可能各自加载一份 `@deepseek-ai/dsh-session`。本包会同步校验当前 `0.2.0-rc.2` 的 format v4（仍识别旧 v3），并在插件本地目录与真实 Host reader 使用的目录不同时做精确、进程生命周期内单调注册；npm/pnpm bin 会先解析软链接，不依赖固定安装路径。注册不能在插件卸载或 HMR 时删除，否则 persistence 的关机 drain 和已有 session 冷恢复可能再次把事件判为 unknown。若无法从真实入口解析 Host copy，或已解析到的 copy 在格式、导出形态、同步加载能力上不兼容，插件都会 fail fast，拒绝仅修改本地目录并要求同步更新兼容层。

参数级风险门不是宽泛的字符串前缀判断，也不声称实现了完整 shell parser：

Auto 还会直接执行一种严格限定的公共 HTTP GET/HEAD `curl` 读取：第一个参数必须是 `-q` 或 `--disable`，禁用隐式 `.curlrc`；只接收一个完整 HTTP(S) 域名 URL，支持单/双引号包裹查询参数以及常用只读选项。跟随重定向必须显式限制协议，例如 `curl -q -fsSL --proto-redir '=http,https' --max-time 30 https://example.com/article`。输出只到标准输出；认证、header/cookie/proxy/config、上传、文件输出、管道、重定向和 shell 展开仍请求人工批准。IP 字面量、已知本地域名和不明选项也保持人工审批；此分类不提供 DNS 或重定向目标的网络隔离。请求批准档继续询问此类网络读取，Policy 的身份、规则和预算仍然生效。

限定读取之外，动态或带引号/转义拼接的命令头（包括管道和顺序命令后的命令头）、shell 控制结构，以及命令/进程替换均保守交人工，不能落入 Auto 的普通 Bash 放行路径。`time` / `timeout` / `nice` / `ionice` / `stdbuf`、`find`、`busybox` 和 `toybox` 的参数或谓词可包含另一条待执行命令，目前整条调用请求人工批准；`find -exec` / `-execdir` 和这些选项的拼接形态不会自动执行，`xargs` / `env` / `exec` 仍保持人工审批；简单与复杂 Bash 路径都会检查可见的敏感命令。普通参数中的非敏感 `$name` / `${name}` 和单引号字面文本保留原处理；引号内 Python/JS 脚本的普通赋值不会被误判为 shell 环境变量注入，因此抓取文章后在工作目录解析 JSON 可沿用 Auto 常规执行。此拒绝扫描不实现完整 shell 语义。

- 命令辨识支持完整的 `command -v <name...>`、`type -p <name...>` 和 `type -P <name...>`，每次 1–8 个字面命令名；这只查询可执行名称，不运行被查询的命令。选项、路径、插值、命令替换、复合 shell、敏感字段和越界 workdir 不适用；`command curl ...` 等执行包装器不会因此获得权限；

- `read` / `read_image` 只有一个可证明位于 workspace 内且不带 credential-sensitive 标记的本地路径时可继续；`glob` / `grep` 只接受已知参数形态，并对 search root、glob/include 做同样的词法范围检查。workspace 外、`.env`、`.ssh`、`.codex/auth.json` 等敏感目标、URL、缺失路径或未知参数一律交人工；
- `pwd`、受限 `ls`、`git status --short|--porcelain`、`git rev-parse --show-toplevel|--abbrev-ref HEAD`、`git diff --no-ext-diff` 的 `--stat|--stat --cached|--name-only`、`git log --oneline -n 10|20`，以及 `node/npm/pnpm/python3/cargo/rustc/tsc/git --version`、`node -v`、`go version`、`uname -s|-m` 等少量精确 argv 形态可继续。允许的条件是只读、不触网、不读 credential 路径且不接受可逃出 workspace 的操作数：带操作数的命令（如 `ls`）由独立分类器做范围检查，`git log --oneline -n 50`、`uname -a`、`go get`、`npm install`、`cargo install` 等相邻形态不在名单内。其 `workdir` 与 `ls` 路径操作数仍必须位于非 credential 的 workspace 范围内，未知或畸形 bash 参数失败关闭；
- 简单但未分类的前台 Bash（例如 `pnpm test`）先归为 `ask-review`；显式 `ask` 档仍提示人工，默认 `auto` 档则直接继续。未知插件工具保持 `ask-review` 并在 auto 中交隔离 reviewer，不因本次放宽自动执行。`run_code` 例外：当前 worker runtime 是 bash-equivalent 的便利执行环境，不是 OS 安全边界，因此在 ask/auto 档始终进入 `ask-human`；
- 内置只读 `web_search` / `web_fetch` 与专用 `ask_user_question` 确定性放行。上述限定读取之外的 `curl`、`ssh`、`git push` 等网络命令，以及 credential 痕迹、外部路径中的 credential 关键词搜索、后台执行、破坏性操作、提权、workspace 外写入仍进入 `ask-human`。普通外部本地只读与 Skill 加载只在默认 `auto` 直放，显式 `ask` 仍会询问。复杂 shell 会先做高召回敏感扫描；未命中时仅在默认 auto 直接继续，在显式 ask 仍展示授权。`npx`、`npm exec`、`pnpm/yarn dlx` 及依赖安装属于默认 auto 的普通操作；`git submodule update` 仍按潜在远端执行归入 `ask-human`；
- `pwsh` 在具备独立严格解析器前一律进入 `ask-human`；
- `bash` / `pwsh` / `write` / `edit` 已携带合法的 `sandbox_permissions: workspace-write | danger-full-access` 和非空 `justification` 时交还工具自身的原生审批，避免弹两次；Codex 风格的 `require_escalated` 或其他畸形升级参数仍交人工。

workspace 路径判断是保守的词法检查，不替代宿主对 symlink、文件权限和进程的最终约束。`workspace-write` sandbox 不能被当作网络或子进程隔离；这里的风险门只负责授权路由，不是 OS 安全边界。

### 隔离自动 reviewer

`autoReview.enabled` 默认开启，但只有 session 明确选择 `auto-review` 且动作是 `ask-review` 时才调用。`provider` / `model` 可选；省略时沿用当前 request header 的 provider/model，因此 Codex、TraeX 或其他已选 route 都能供应 reviewer，也可把两者固定到独立 route。请求由插件手工构造，使用独立 system prompt、`tools: []`，不继承 Agent 工具，也不写入主会话。

Reviewer 只接收当前 open turn 中唯一、尚未结算的 exact `callId` 参数，以及最近的真实 user-role 文本意图；只信任核心 `user` 与可选 Delivery 的 `delivery` source，`plugin` / `tool` 或未知扩展来源不能充当授权。可信消息只要还含图片等非文本 block 就整体转人工，不会丢掉上下文后让 reviewer 猜测。调用 LLM 前会快照 `permission/preset`、sandbox、approval、reviewer 四类最新事件的 seq/identity、open `turn/start` seq、exact call/settlement event seq、可信 intent event seq 与精确内容；LLM await 返回后以及签发 grant 的同步边界会再次校验 signal 与完整快照。权限或事实漂移（包括先改走再改回的 ABA）绝不会得到 `allowed-once`：仍为一致 auto 档时降级人工，否则返回失败关闭结果。参数和意图都有硬字节上限；发生截断、secret 脱敏、参数 prompt injection、找不到 exact call、重复或已结算 call、route 缺失、超时、provider 错误或非严格 JSON 时，都会交给人工，绝不静默允许。只有严格返回 `outcome=allow`、`riskLevel=low` 且 `authorization` 至少为 `medium` 才可能签发一次性 `allowed-once`；rationale 不进入主模型会话。

发送 reviewer 前的本地 secret-like 检查是失败关闭的：除 password/passphrase/passwd/secret/token 等敏感字段名与常见 token 前缀外，也拦截对应自然语言赋值、URI userinfo、PEM private key、JWT，以及 `AKIA` / `ASIA` AWS access key。任何命中都会保持 reviewer 请求数为零并转人工；这是避免跨 provider 外发的最后本地边界，不应把 reviewer provider 当成秘密扫描器。

人工渠道通过 Host-only `registerHumanApprovalAnswerer(answerer)` 注册，并在自己的 Cordis effect 中释放。Policy 的原生 `approval/request` 前置监听器先做风险/自动审核，再把**同一个请求对象**交给这些渠道；只有未被精确 owner 渠道认领的请求才到原生 Web Remote。这样不依赖 Web/Delivery 插件启动先后，也不会让 headless 飞书请求被无浏览器的 Remote 提前拿走。路由没有自己的审批账本或执行权限；每个渠道仍须验证当前 owner、调用、参数、权限和期限。卸载、取消及晚到允许不能形成新 grant。

路由只依赖原生 Approval 服务；LLM 是独立的、生命周期绑定的可选 provider。缺少、卸载或替换 LLM 时，符合 auto 档的待审请求立即转人工，不等待 provider 恢复，也不接受卸载后的旧模型判定。Web 保留原生批准/拒绝控件；Delivery 必须和提供此接口的 Policy 成套升级。

包根的 `isAutoReviewEscalation(request)` 是 auto reviewer 与人工 answerer 的对象级交接信号：它只在同一个 `ApprovalRequest` 被明确升级并执行下游 `next()` 的期间为真，并在 `finally` 中清除。Reviewer listener 使用 waterfall 的 `prepend` 注册，即使 LLM service 晚于人工 listener 到达也先完成自动判定。人工渠道在 auto 档只应认领这个信号标记的请求；其他插件的自定义 approval reason 不会被本插件标记或抢占。

## 硬预算

额度定义属于 policy 配置，消费者只能引用 `budgetId`，不能传入或抬高 `limit`、`periodMs`：

```yaml
rules:
  - id: allow-budgeted-background-tool
    effect: allow
    actions: [execute]
    resource: { kind: tool, id: wiki_* }
    context: { initiators: [background] }
    budget: { id: background-tool-calls, amount: 1 }
budgets:
  - id: background-tool-calls
    metric: tool-calls
    limit: 100
    periodMs: 86400000
    scope: subject
```

每次带预算的授权必须提供幂等键。策略先在 SQLite 短事务中预留，再立即计入固定收费；进程在两步之间崩溃最多造成保守占额，不会超额。同一业务操作即使跨过预算周期仍重放原 reservation，不会重复扣费；换 scope、metric、period 或 amount 继续失败关闭。可变成本消费者使用 `reserve()`，完成后调用 `finalize()`，失败调用 `release()`。`scope` 可为 `global`、`subject` 或 `workspace`，同样由配置决定。

## 服务 API

注入名为 `assistantPolicy`。持久账本只属于 service 内部，不从包根导出；消费者不能直接打开数据库或绕过 service 规则。主要方法为：

- `evaluate(request)`：只评估，不写审计；紧急停止优先于所有规则。
- `getEmergencyStop()`：只读返回当前硬门状态，不做 authorize、不写审计且不消耗预算；Delivery 等最终授权方可在提交外部批准前再次复核。
- `inspectHostConfiguration()`：仅供受信 Host 的只读快照，返回当前 rules、tool default effect、budgets 与 auto-review 配置，用于将实际装载的完整 Policy 配置和冻结计划核对；不暴露账本、审计、维护配置或任何授权凭据，也不授予调用者额外权限。
- `authorize(request, options)`：执行预算检查并追加脱敏审计。
- `reserve` / `finalize` / `release`：策略配置约束下的可变成本预算。
- `propose` / `decideProposal`：绑定 principal、版本 CAS、TTL 与幂等键的持久审批提案。所有提案永久保存 diff SHA-256；只有携带 `dispatch` 的 pending 提案会临时保存 immutable diff 原文，路由与提案在同一个 SQLite 事务中落盘。路由 principal 必须与提案 principal 完全一致、workspace 必须为绝对路径。无路由提案和 v1 legacy 提案只凭 hash 安全重放，绝不会在重放时补建路由。
- `getProposal`：只读地返回某个提案的当前状态（含终态与决定者）。`getProposalByIdempotencyKey` 用完整的 requester、principal、action 和 resource scope 做只读诊断；它绝不创建、重放或延长提案。
- `recoverOrCreateProposal`：业务域跨 SQLite 恢复时使用的原子入口。调用方提交不可移动的绝对 `notAfter`；Policy 在 `BEGIN IMMEDIATE` 中选择“恢复 exact 既有提案 / 创建一次 / 过期后永久 tombstone 幂等键”。tombstone 的 SHA-256 intent hash 固定绑定 deadline、requester、principal、action、resource、diff、summary 以及 dispatch presence/route，但不保存 diff 明文；v4 遗留 tombstone 缺少可验证 identity，迁移后只会阻止复用，任何 recovery 都失败关闭。因此多个进程不能在 lookup 与 create 之间制造孤儿审批卡，也不能借重启获得新 TTL。账本维护和恢复不会反向调用业务域；业务域读取 Policy 决定后，再通过自己的 gate 提交。
- `runOwnerRequestedChange(input, commit)`：受信 Memory/Wiki 工具提交入口。它从当前会话固定 provider/model，以独立无工具审查器判断经过 Delivery 验证的当前 owner 原文是否明确要求该精确 add/replace/create/update；replace/update 还要求 Host 提供当前目标快照，并在审查后重验。审查返回严格 `execute` 且流中恰有一个末尾 `finish/stop` 才会在同步 `commit` 内给一个 policy 幂等键、requester、principal、action 和 v2 dispatch route 一次临时许可。模型响应的 adapter 私有 replay 元数据不参与授权；工具调用、缺失或重复终态、终态后任何 chunk、异常结束和非严格 JSON 仍走普通提案。Policy 在同一 SQLite 事务中创建并终结 approved；既有提案只精确重放，不升级 pending/rejected/expired。审查不确定、模型不可用、禁用、目标缺失或超限时仍走普通提案；取消、owner 收据/参数/目标/模型或权限配置变化、LLM 换代和紧急停止状态变化会中止旧请求。每次入口调用追加一条 `owner-request.review` 分类审计，用 call 身份哈希及合法收据摘要关联；分类区分上下文/配置回退、模型 ask/格式/超时/流错误和同步 scope 不匹配，不保存 owner 原文、工具参数或模型输出。批准以 Policy 事务提交为生效点；此后发生崩溃时，域沿原有精确决定和版本 CAS 恢复，不重新审批，也不扩大变更。
- `listPendingApprovalDispatches` / `markApprovalDispatchEnqueued`：向 Delivery 提供有界、可 CAS 的 durable approval outbox。展示用的 summary、diff、action、resource、版本与过期时间只能从 policy 已持久化的 canonical proposal 派生，调用方不能替换卡片文案。终态或已过期提案不会被列出。
- `listProposalStatesForRoute(route, limit = 10)`：Host 只读查询当前 owner 的精确 v2 dispatch 绑定链路，最多返回 20 条最近提案及 `truncated`。结果含 proposal ID、resource、action、状态、版本及时间，不返回 diff、summary 或拒绝理由；通用 `resource.id` 和 `action` 是调用方提供的元数据，可能包含正文，消费方仍须限制其来源、编码和字节数。拒绝及批准等终态仍可见，旧 v1 或其他 binding、workspace、principal、owner record 均不可见。到期但账本仍为 pending 时只在查询结果中显示 expired，不写库、不增加版本或虚构决定时间。approved 只表示 Policy 批准，不保证 Memory/Wiki 已写入；调用方必须用当前可信 owner 路由，不能让模型提供 route。
- `validateApprovalSettlement(snapshot, expectation)`：跨 Memory / Wiki / Automations / Evolution 共用的纯函数提交门。它逐字段核对 immutable proposal、重新计算 diff SHA-256，并要求终态版本恰好为原版本 `+1`；approved/rejected 必须由绑定 principal 决定，expired 必须由 `system:expiry` 决定。缺失或冲突会抛出稳定的 `ApprovalSettlementConflict`，业务域应 fail closed。
- `setEmergencyStop` / `getEmergencyStop`：持久紧急停止。
- `queryAudit`：按 sequence 有界分页，单次最多 100 条。
- `bindInitiator(agent, initiator, principal?)`：供受信调度器在 Agent 生命周期内绑定 initiator；外部调度器同时传入 canonical principal 后，`authorizeAgent()` 与工具 guard 会把它作为 Agent subject 的精确 principal。释放后恢复前一层绑定。

DSH 原生 `user-approval` 仍只负责 open turn 内的即时询问；本插件的 proposal 是跨 turn、可重启恢复的审批状态，不把两者混为一个授权凭证。

## 数据与权限

- 文件系统：创建一个 SQLite 数据库及 WAL/SHM 辅助文件；新目录权限 `0700`，主数据库 `0600`。数据库使用 WAL、`busy_timeout`、外键、FULL synchronous 和前向 schema 版本。
- 网络：插件不直接打开 socket；启用隔离自动 reviewer 时会调用宿主 `dsh-llm`，所选 provider route 可能使用网络。provider 的认证与传输仍由宿主拥有，本插件不直接读取其凭据。
- 子进程：无。
- 凭据：运行时的 Policy service 不读取或保存凭据值。审计按敏感 key/value、shell `command`、授权头、token、password、path/cwd 做脱敏，资源 id 仅保存 SHA-256。自动 reviewer 输入只要需要任何 secret 脱敏就失去自动放行资格并转人工。包另附带两个安装期 CLI（`dsh-permission-setup`、`dsh-model-setup`），仅供安装器/部署者手动调用，不在 Cordis 运行时加载。安装器始终传 `--profile <name>`：两个 CLI 先校验所选 DSH Host 并读取目标 profile 的原生 `--dump-config`，再原子改写其 `cordis.patch.yml`，分别写入 `permission.defaultPreset` 与 `agent-default-model`（自定义网关还写入 `llm-pi-ai.providers.<route>`），保留原生有效整段配置中的其它预设、网关、注释和 `!!js`。读取或组合失败时拒绝写入。若省略 `--profile`，CLI 仍写旧版全局 `settings.yaml`；DSH 0.2 在下次启动时把它一次性导入第一个启动的 profile，不适合多 profile 设置。目标配置对应区段尚留在 `settings.yaml` 时，显式 `--profile` 拒绝写入，须先把旧设置迁移至目标 profile，避免首启覆盖。显式 `--store-key` 时 API Key 仅从环境变量读取，原子写入 `$DSH_HOME/.credentials.yaml`（`0600`，目录 `0700`），密钥绝不作为命令行参数、缺失时 fail-closed。agent route（如 `traex-agent`）不涉及 API Key；`--enable-in-profile` 与 `--profile` 同时使用时必须同名。

自动发现本机 agent 时可调用 `dsh-model-setup --profile web --provider traex-agent --enable-in-profile web --enable-only [--agent-command /absolute/path/to/traex]`：仅启用目标 profile 的 route，不改模型选择。若希望在没有显式选择时把它设为该 profile 的默认模型，改用 `--default-if-absent`；CLI 用 YAML AST 检查待导入的 `settings.yaml`、home patch 和目标 profile patch，已有 `agent-default-model` 就保留，三处都没有才在目标 profile patch 添加默认模型行。两个模式互斥且仅适用于带 `--enable-in-profile` 的 agent route；启用 route 时在目标行同时写 `disabled: false` 和 `config.enabled: true`，保留 profile 的已有配置；没有 profile 配置时继承 home patch 最后生效的整段配置，并保留 `!!js` 表达式。`--agent-command` 必须是绝对路径，只填缺失的 `config.command`，保留已有 `command` 和 `cwd`。显式 model setup 传 `--profile` 时只设置目标 profile；不传时保留旧版 `settings.yaml` 行为。
- 浏览器：无。
- 安装脚本：无 npm 生命周期脚本；上述 CLI 仅在被显式调用时运行。

审批展示预算由包根的 frozen `APPROVAL_DISPLAY_BUDGET` 统一公开：Delivery 默认文本上限 64 KiB，summary 最多 120 UTF-8 bytes，diff 最多 60 KiB，预留 4 KiB 给渠道渲染。原始 diff 只在 routed dispatch 仍为 pending 时存在；成功 enqueue、批准、拒绝或过期都会在同一状态事务中清除，v2→v3 迁移也会清除无路由、已 enqueue 和终态记录的遗留原文。业务域仍不得把 credential、token 或其他秘密写进 diff。`proposalMaintenanceIntervalMs` 默认每 15 秒有界地过期 stale proposal，设为 `0` 可关闭，最大值为 `2147483647`。审计是 append-only；当前版本不会自动删除记录，部署者应根据自己的隐私与合规周期备份或轮换整个数据库。SQLite 提供单机多连接并发安全，不承诺多主机共享文件系统上的一致性。

## 兼容性

本包已在安装的 DeepSeek Harness `0.2.0-rc.2` 上通过类型检查及包测试；完整宿主安装和外部模型/渠道仍按仓库[兼容性基线](../../docs/compatibility.md)验收。

### Finite action broker preauthorization

`evaluateAgent` checks live identity and rules without consuming budgets or recording authorization; the broker charges `authorizeAgent` once before credential acquisition and dispatch with a durable action ID as its idempotency key. Polling must use the read-only method. `registerPreauthorizedTool` accepts only exact definitions from their owning trusted Fibers: the approved GitHub action tools from `dsh-enhanced-assistant-actions`, `isolation_run`/`isolation_grants` from Isolation, the approved Goals tools from Goals, and `skill_run`/`skill_status` from Skills. The registration expires with the caller scope; same-name scoped shadows cannot borrow it. This skips native per-call approval only when the owned predicate remains valid. Monotonic policy denials, cancellation, revocation and other tool middleware still apply. It is a trusted Host integration contract, not isolation against malicious plugins in the same Host process.

`assistant-isolation` may also register only its exact `isolation_run` ToolDefinition for finite-grant risk preauthorization. This suppresses a redundant approval prompt, while native tool Policy denials and call budgets remain effective. Isolation separately authorizes `tool:isolation:<grant-id>` once per new job with a persistent idempotency key; polling and result replay do not consume that grant-operation budget. A separately configured `tool:isolation_run` budget continues to count tool calls. See the [Isolation authorization contract](../assistant-isolation/README.md).

`personal-memory` and `personal-wiki` may register only their exact `memory_manage` and `wiki_upsert` definitions from their own mounted `PersonalMemoryService` / `PersonalWikiService` Fibers (or their package-named mount). This skips duplicate native tool approval; Policy guard, budgets, and the separate durable proposal decision still apply.

`assistant-goals` can register only its exact `goal_checkpoint` definition when the separate checkpoint capability is present. Its predicate is read-only and requires a live authenticated owner, the owning Session/native Goal, the current record version, and a valid same-owner dependency graph; the execution path repeats Policy and transactional CAS checks. A caller Fiber disposal, cancellation, policy denial, stale version, or definition mismatch fails closed.

For native skill benchmark cells, `delegatedSkillToolPreauthorizationVersion() === 1` allows only the actual Skills Fiber to register its exact `skill_run` and `skill_status` definitions. Their predicate requires a live owner-bound delegated arm whose steps are all `isolation_run`; ordinary skills, different versions, revoked mounts and foreign scopes do not gain this authorization. Native Policy, call budgets and each nested tool check remain active. See [native skill reuse](../../docs/native-skill-reuse.md).
