@klarkxy/dsh-safe-auto 0.1.0-rc.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/LICENSE +40 -0
- package/README.md +147 -0
- package/README.zh-CN.md +129 -0
- package/cordis.patch.yml +3 -0
- package/docs/ADR-0001.md +65 -0
- package/docs/ADR-0002.md +76 -0
- package/docs/ADR-0003.md +39 -0
- package/dsh.plugin.json +8 -0
- package/package.json +51 -0
- package/src/config.js +63 -0
- package/src/escalation.js +118 -0
- package/src/gate.js +64 -0
- package/src/index.js +198 -0
- package/src/model-route.js +110 -0
- package/src/policy.js +97 -0
- package/src/reviewer.js +125 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
The Star And Thank Author License (SATA)
|
|
2
|
+
Version 2.1, September 2026
|
|
3
|
+
|
|
4
|
+
Copyright © 2026 klarkxy
|
|
5
|
+
|
|
6
|
+
Project Url: https://github.com/klarkxy/dsh-plugins
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
10
|
+
in the Software without restriction, including without limitation the rights
|
|
11
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
13
|
+
furnished to do so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in
|
|
16
|
+
all copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
And wait, one more thing — an invitation, not a condition: it would be best if
|
|
19
|
+
you star/+1/like the project(s) in the project url section above, and thank the
|
|
20
|
+
author(s) in the Copyright section. You may, and arguably you should — but the
|
|
21
|
+
permissions above hold either way, star or no star.
|
|
22
|
+
|
|
23
|
+
Here are some suggested ways:
|
|
24
|
+
|
|
25
|
+
- Email the authors a thank-you letter, and make friends with him/her/them.
|
|
26
|
+
- Report bugs or issues.
|
|
27
|
+
- Tell friends what a wonderful project this is.
|
|
28
|
+
- And, sure, you can just express thanks in your mind without telling the world.
|
|
29
|
+
|
|
30
|
+
Contributors of this project by forking have the option to add his/her name and
|
|
31
|
+
forked project url at copyright and project url sections, but shall not delete
|
|
32
|
+
or modify anything else in these two sections.
|
|
33
|
+
|
|
34
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
35
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
36
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
37
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
38
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
39
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
40
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# DSH Safe Auto (release candidate)
|
|
2
|
+
|
|
3
|
+
[中文](README.zh-CN.md) · [Preflight](docs/ADR-0001.md) · [One-shot escalation](docs/ADR-0002.md) · [Reviewer routing](docs/ADR-0003.md)
|
|
4
|
+
|
|
5
|
+
Budgeted preflight and opt-in automatic approval of one native sandbox escalation at a time. **The reviewer follows the current conversation model by default, and can be configured independently.** The plugin reuses DSH's model adapters, tool loop and approval service. It never switches the standing session to Full Access and never lets a model enlarge the operator's configured envelope.
|
|
6
|
+
|
|
7
|
+
This is a local POSIX/native-tool candidate, not an independently audited security boundary. Start with `shadow`; retain native sandbox providers, human approval, isolation and backups. DSH `workspace-write` governs file effects, not network egress or all secret reads. An approved `danger-full-access` call genuinely bypasses the DSH file sandbox; exact rules are not a finer OS sandbox.
|
|
8
|
+
|
|
9
|
+
## Install and configure
|
|
10
|
+
|
|
11
|
+
Use a disposable profile from a checked repository checkout:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm install --frozen-lockfile
|
|
15
|
+
pnpm --filter @klarkxy/dsh-safe-auto test
|
|
16
|
+
dsh plugin --profile web add ./plugins/dsh-safe-auto
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A PR is not an npm release. The bundle inserts the `dsh-safe-auto` profile row. Configure its `config` through the profile patch mechanism:
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
id: dsh-safe-auto
|
|
23
|
+
name: '@klarkxy/dsh-safe-auto'
|
|
24
|
+
config:
|
|
25
|
+
mode: shadow
|
|
26
|
+
workspaceRoots:
|
|
27
|
+
- /absolute/canonical/project
|
|
28
|
+
shellCandidates:
|
|
29
|
+
- git status --short
|
|
30
|
+
# No endpoint or model override: follow this conversation's DSH provider/model.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Select native Workspace Write separately. The enrolled canonical root must exactly match session cwd and the resolved sandbox root. Config is strictly validated and immutable per plugin instance; reload after edits. This release exposes profile configuration, not a graphical model picker or generated settings form.
|
|
34
|
+
|
|
35
|
+
## Choose the approval model
|
|
36
|
+
|
|
37
|
+
### Default: follow the conversation
|
|
38
|
+
|
|
39
|
+
Leave `endpoint`, `fastProvider` and `fastModel` unset or empty. Each eligible review resolves the provider/model from the requesting session's accepted `requestHeader().config`. If no accepted header exists, it uses that same agent's `options`. A malformed header never falls back to another route. A new conversation request using a different model changes subsequent reviews; concurrent sessions do not share a global model selection.
|
|
40
|
+
|
|
41
|
+
This reuses DSH's configured provider and credentials. No additional API key or endpoint is required. **Only the model route is inherited**, not the conversation history, main-agent prompt, tools, replay state, reasoning setting or output budget. The reviewer is a separate tool-free one-shot with its own prompt and caps. A model requiring more reasoning tokens may need a larger reviewer cap; unsupported options fail closed rather than dropping limits.
|
|
42
|
+
|
|
43
|
+
### Independent DSH model
|
|
44
|
+
|
|
45
|
+
Set both fields in the same `config`, using IDs from your DSH model configuration:
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
fastProvider: your-review-provider
|
|
49
|
+
fastModel: your-small-review-model
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The model can differ from the conversation in both provider and model. It stays fixed when the conversation switches models. Clearing both fields restores following. Invalid/missing providers or models do not trigger a silent fallback to the conversation, another provider, or HTTP.
|
|
53
|
+
|
|
54
|
+
The optional deep stage is independently configurable, including while the primary reviewer follows the conversation:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
deepProvider: your-deep-review-provider
|
|
58
|
+
deepModel: your-deep-review-model
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Only a fast `review` decision invokes it. Both deep fields empty means no deep model; uncertainty then asks a human or denies in unattended mode. There is no second implicit call to the conversation model. Native provider/model fields must be configured in pairs.
|
|
62
|
+
|
|
63
|
+
### Independent HTTP endpoint (RC2-compatible)
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
endpoint: https://your-trusted-gateway.example/v1/chat/completions
|
|
67
|
+
fastModel: your-http-review-model
|
|
68
|
+
# deepModel: your-http-deep-model
|
|
69
|
+
apiKeyEnv: DSH_SAFE_AUTO_API_KEY
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`endpoint` is the complete OpenAI-compatible Chat Completions URL. This explicit endpoint uses the existing HTTP transport, including compatible lapp gateways; omit `fastProvider` and `deepProvider`. Combining native providers with an HTTP endpoint is rejected rather than guessed. Only this mode reads the named API key environment variable. HTTPS is required except loopback HTTP; redirects, URL credentials, query and fragment are rejected. To restore following, clear the endpoint and its fast model, not just the endpoint.
|
|
73
|
+
|
|
74
|
+
Both ordinary preflight and one-shot escalation use the selected reviewer. Routes are snapshotted for each review. A change during an outstanding native review invalidates automatic allowance; final guards recheck the route. Removing the model service leaves the policy guards installed. Switching models does not reset budgets. Explicit denials remain denials even if the route changes.
|
|
75
|
+
|
|
76
|
+
## Policy and one-shot escalation
|
|
77
|
+
|
|
78
|
+
| Action | Smart behavior |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| Ordinary `read/read_image/write/edit` in an enrolled workspace | Deterministic pass to existing policy; no reviewer call |
|
|
81
|
+
| Protected credentials/security configuration or selected dangerous programs | Hard denial, including escalation |
|
|
82
|
+
| Exact enrolled simple `shell/bash` command | Reviewer, within the operator envelope only |
|
|
83
|
+
| Exact separately enrolled native `write/edit` or eligible `bash` escalation | Review at native `approval/request`; a fresh allow returns `allowed-once` |
|
|
84
|
+
| Unenrolled, unsupported or ambiguous operation | Native human approval; unattended rejects |
|
|
85
|
+
| Explicit model denial | Rejected, no semantic retry |
|
|
86
|
+
| Cancellation, binding mismatch or changed authority | No automatic grant |
|
|
87
|
+
|
|
88
|
+
`escalationCandidates` defaults to `[]`. Ordinary `shellCandidates` do not authorize widening. Register exact rules separately:
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
escalationCandidates:
|
|
92
|
+
- tool: write
|
|
93
|
+
cwd: /absolute/canonical/project
|
|
94
|
+
mode: danger-full-access
|
|
95
|
+
filePath: /absolute/other-project/notes.txt
|
|
96
|
+
- tool: edit
|
|
97
|
+
cwd: /absolute/canonical/project
|
|
98
|
+
mode: danger-full-access
|
|
99
|
+
filePath: /absolute/other-project/notes.txt
|
|
100
|
+
- tool: bash
|
|
101
|
+
cwd: /absolute/canonical/project
|
|
102
|
+
mode: danger-full-access
|
|
103
|
+
command: git status --short
|
|
104
|
+
escalationApprovalTtlMs: 30000
|
|
105
|
+
escalationMaxTimeoutMs: 30000
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Rules require exactly tool/cwd/mode and filePath or command. No prefixes, globs or directory grants. Native DSH requests use `sandbox_permissions: danger-full-access` with `justification`, not Codex's `require_escalated`. Only widening from Workspace Write is supported. File targets must be absolute and canonical, with existing parents; links, hard-linked writes, protected/system targets and unknown arguments do not get automatic approval. The reviewer receives complete bounded file-write/edit arguments, including content, or refuses oversized/sensitive inputs without truncating them.
|
|
109
|
+
|
|
110
|
+
**Automatic Bash escalation requires no `jobs` service**, a visible sandbox-capable shell, false/omitted `run_in_background`, an explicit positive `timeoutMs` within the configured limit, and no workdir change. A jobs-enabled DSH profile can promote even a foreground call after timeout. Such requests stay manual or are rejected in unattended mode; the plugin never silently disables jobs. Neither a timeout nor one-shot approval proves that arbitrary descendants cannot outlive the call.
|
|
111
|
+
|
|
112
|
+
The native tool consumes the grant; the plugin does not execute the command itself. Async context and opaque execution tokens bind the exact agent, call, signal, arguments, cwd, policy, user intent and filesystem state. Concurrent identical call IDs cannot borrow a grant. One execution consumes one approval slot; no cross-call grant cache exists. `escalationApprovalTtlMs` limits decision freshness, not process lifetime, revocation or rollback. Expiry asks a human or denies; side effects are not undone. See [ADR-0002](docs/ADR-0002.md) for the complete execution contract.
|
|
113
|
+
|
|
114
|
+
## Modes and composition
|
|
115
|
+
|
|
116
|
+
`off` installs nothing. `shadow` observes ordinary candidates without protecting execution; **enrolled ordinary commands can now use the conversation model even without an explicit endpoint**, and consume reviewer tokens. Shadow escalation remains native/manual without automatic review. Empty candidate lists make no model requests on installation. `smart` enforces policy and sends uncertainty to native human approval. `unattended` can grant eligible reviewed one-shot escalations but rejects uncertainty and other approval requests.
|
|
117
|
+
|
|
118
|
+
Native `approval: never` rejects before any answerer. For automatic escalation, keep native approval at `ask` even with plugin `unattended`; `ask` dispatches to an answerer, not necessarily a human. Missing native approval services fail closed. Ordinary passes call `next()`, preserving other policy denials and prompts. Never stack this with official experimental Auto, Autogate or another auto-answerer, because human fallback assumes the downstream native human channel.
|
|
119
|
+
|
|
120
|
+
## Budgets, privacy and limitations
|
|
121
|
+
|
|
122
|
+
| Setting | Default | Meaning |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| `timeoutMs`, `maxInputBytes` | `8000`, `8192` | Per-stage deadline and combined UTF-8 prompt/input cap |
|
|
125
|
+
| `fastOutputTokens`, `deepOutputTokens` | `64`, `256` | Explicit native `maxTokens` or HTTP output caps |
|
|
126
|
+
| `tokenField` | `max_tokens` | HTTP only; alternatively `max_completion_tokens` |
|
|
127
|
+
| `fastCallsPerTask`, `deepCallsPerTask` | `20`, `3` | Atomic logical-review reservations per direct user task |
|
|
128
|
+
| `sessionBudgetUnits` | `100000` | Prompt bytes + output cap + 1024 reserved per stage |
|
|
129
|
+
| `consecutiveDenials`, `totalDenials` | `3`, `20` | Non-allowing review/error fuse per task/session |
|
|
130
|
+
|
|
131
|
+
Fast JSON is `allow/review/deny`; deep JSON is `allow/ask/deny`. Extra fields, invalid JSON, tool calls, missing finish or output truncation cannot grant. Both transports have bounded responses, including native reasoning text, and an outer cancellation/timeout deadline. No plugin-level transport or semantic retry is added. Native adapters/middleware remain host-controlled and may implement their own transport policy; reservations count logical reviews, not every downstream wire attempt.
|
|
132
|
+
|
|
133
|
+
Only the action and latest direct human text are supplied. No main transcript, assistant reasoning or tool results are copied. Descriptions and justifications do not authorize actions. Secret detection is heuristic; following a model or configuring a remote reviewer sends bounded review inputs to that provider, including file contents for escalation. Fixed routes never silently change data destinations on failure.
|
|
134
|
+
|
|
135
|
+
Preflight and escalation share a session ledger, reserved before I/O with no error refunds. Usage counts include DSH's disjoint cache counters when reported. Reservations are not exact billable tokens, currency limits or a cap on the main agent. Providers must honor limits. New direct user messages reset task counters but not session totals; model changes reset neither; plugin reload/restart resets in-memory counters. The fuse stops more reviews, not the whole agent loop.
|
|
136
|
+
|
|
137
|
+
Tests, builds, installation and Git hooks can run repository-controlled code and are not inherently safe. Enrollment does not pin executable contents. `grep/glob`, PTC, MCP, PowerShell, remote execution, complex shell and subagent model grants remain outside the automatic envelope. Trusted same-process plugins and execution providers remain trusted; unloading a policy removes its guards. Path rechecks cannot eliminate OS TOCTOU. Use external isolation with restricted credentials and egress.
|
|
138
|
+
|
|
139
|
+
## Verification
|
|
140
|
+
|
|
141
|
+
Run `npm test`, `npm run build` (JavaScript syntax checks, not TypeScript checking), and `npm pack --dry-run` in this package, plus repository `pnpm check`. Tests cover routing, concurrent sessions, native streams, caps, stale decisions, model service removal, preflight/escalation binding, real loopback HTTP, and real Cordis/ToolRuntime/ApprovalService/LlmRuntime contracts. CI requires installed DSH dependencies; offline source-only tests may explicitly skip native contracts.
|
|
142
|
+
|
|
143
|
+
The native test uses a controlled adapter and fixture session/tool/policy. It proves runtime integration, not live model accuracy, OS isolation or authenticated Web/Headless acceptance. These remain in [ADR-0002's checklist](docs/ADR-0002.md). Logs separate assessment, escalation outcome and final result without raw commands/prompts/keys; host retention is operator-managed. No durable budget/audit database, cross-call grant cache, PI probe or graphical model picker is bundled. No absolute safety or savings percentage is claimed.
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
Original implementation under [SATA License 2.1](LICENSE). Community designs informed the work; their code was not vendored.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# DSH Safe Auto(候选版)
|
|
2
|
+
|
|
3
|
+
[English / 完整配置表](README.md) · [单次提权](docs/ADR-0002.md) · [审批模型路由](docs/ADR-0003.md)
|
|
4
|
+
|
|
5
|
+
复用 DSH 原生工具循环、模型适配器和审批服务。**审批模型默认跟随当前对话,也能单独指定 provider/model,或继续使用独立 HTTP 端点。** 在明确登记的范围内,可以自动审核并批准单次沙箱提权,但不会把整个会话切成 Full Access。模型只能缩小规则给定的权限范围。
|
|
6
|
+
|
|
7
|
+
仍为本地 POSIX/native 工具候选版,未经独立安全审计。保留原生沙箱、人工审批、备份和外层隔离,不宣称兼容所有工具或平台。
|
|
8
|
+
|
|
9
|
+
## 安装与默认配置
|
|
10
|
+
|
|
11
|
+
在仓库检查通过后,向一次性测试 profile 安装本地包:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm install --frozen-lockfile
|
|
15
|
+
pnpm --filter @klarkxy/dsh-safe-auto test
|
|
16
|
+
dsh plugin --profile web add ./plugins/dsh-safe-auto
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
PR 不等于 npm 已发布。在 bundle 插入的 `dsh-safe-auto` 条目中配置 `config`:
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
mode: shadow
|
|
23
|
+
workspaceRoots:
|
|
24
|
+
- /absolute/canonical/project
|
|
25
|
+
shellCandidates:
|
|
26
|
+
- git status --short
|
|
27
|
+
# 模型字段不填,默认跟随本对话。
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
另行选择原生 Workspace Write;工作区必须是规范绝对路径,与会话 cwd 和沙箱 root 完全相同。配置严格校验,修改后重载插件。**目前通过 profile 配置,未提供图形化模型选择器或自动设置表单。**
|
|
31
|
+
|
|
32
|
+
## 审批模型可以单独设置
|
|
33
|
+
|
|
34
|
+
### 默认:跟随当前对话
|
|
35
|
+
|
|
36
|
+
`endpoint`、`fastProvider`、`fastModel` 均留空。每次需要模型审核时,读取请求会话已经接受的 `requestHeader().config` 的 provider/model;尚无请求 header 才使用同一 Agent 的 options。存在但不完整的 header 不会偷偷回退。对话下一轮接受了新模型,后续审核随之更新;不同会话不会共用一个全局模型选择。
|
|
37
|
+
|
|
38
|
+
直接复用 DSH 对应 provider 的适配器和凭据,**不用再填一遍 API Key**。只继承模型路由,不继承聊天历史、主 Agent 提示词、工具权限、回放状态、思考档位或输出预算。审批仍是独立、无工具的模型请求,使用自身的提示和成本上限。需要大量推理 token 的模型可能要提高审批输出上限;无法支持的参数应失败关闭,不能丢掉限制后执行。
|
|
39
|
+
|
|
40
|
+
### 独立指定 DSH 模型
|
|
41
|
+
|
|
42
|
+
在同一插件配置中成对设置:
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
fastProvider: your-review-provider
|
|
46
|
+
fastModel: your-small-review-model
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
填写 DSH 已配置的实际 provider/model ID。可以和对话完全不同;对话切换模型不会影响这个固定审批模型。清空两项恢复跟随。provider 不存在、模型不可用、凭据错误等会回人工或拒绝,**不静默改用对话模型或其他服务**。
|
|
50
|
+
|
|
51
|
+
可选深审也可单独指定,包括主审批模型继续跟随对话的情况:
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
deepProvider: your-deep-review-provider
|
|
55
|
+
deepModel: your-deep-review-model
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
只有快速审核返回 `review` 才调用深审。两项均为空表示不开启深审,不会默认再调用一次对话模型。不确定时 Smart 回人工,无人值守拒绝。原生路由的 provider/model 必须成对填写。
|
|
59
|
+
|
|
60
|
+
### 保留独立 HTTP 端点
|
|
61
|
+
|
|
62
|
+
RC2 的配置继续有效:
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
endpoint: https://your-trusted-gateway.example/v1/chat/completions
|
|
66
|
+
fastModel: your-http-review-model
|
|
67
|
+
# deepModel: your-http-deep-model
|
|
68
|
+
apiKeyEnv: DSH_SAFE_AUTO_API_KEY
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
这是完整 OpenAI-compatible Chat Completions URL,可接兼容 lapp 网关。此模式不要填写 `fastProvider/deepProvider`;与原生路由混填会报错。只有 HTTP 模式读取上述 API Key 环境变量。仅允许 HTTPS 或 loopback HTTP,拒绝重定向、URL 凭据、查询参数和 fragment。恢复跟随时要同时清空 endpoint 和 HTTP fastModel,不能只删 endpoint。
|
|
72
|
+
|
|
73
|
+
普通预检和单次提权都使用这套选择。一次审核固定两阶段路由;审核期间模型变化会作废自动放行结果,最终 guard 再次核对。模型服务失效不会导致安全 guard 一并卸载。切换模型不重置预算;已经明确拒绝的操作也不会因为换模型被改成可再次争取放行。
|
|
74
|
+
|
|
75
|
+
## 默认行为与单次提权
|
|
76
|
+
|
|
77
|
+
`off` 不安装策略。`shadow` 只观察,不保护执行;**登记了普通候选命令后,即使没有独立 endpoint,也可能使用对话模型产生审核费用**。提权在 Shadow 中仍交原生审批,不自动审查或批准。安装时候选列表默认全空,不自动产生审核请求。
|
|
78
|
+
|
|
79
|
+
`smart` 对普通工作区 `read/read_image/write/edit` 做零模型检查,保留其他原生策略;受保护凭据、安全配置和部分危险命令硬拒绝。未知情况交人工。`unattended` 可以批准符合条件的单次提权,但不确定和其他审批需求均拒绝。
|
|
80
|
+
|
|
81
|
+
`escalationCandidates` 与 `shellCandidates` 独立,普通命令候选不授予沙箱外权限。例如:
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
escalationCandidates:
|
|
85
|
+
- tool: write
|
|
86
|
+
cwd: /absolute/canonical/project
|
|
87
|
+
mode: danger-full-access
|
|
88
|
+
filePath: /absolute/other-project/notes.txt
|
|
89
|
+
- tool: edit
|
|
90
|
+
cwd: /absolute/canonical/project
|
|
91
|
+
mode: danger-full-access
|
|
92
|
+
filePath: /absolute/other-project/notes.txt
|
|
93
|
+
- tool: bash
|
|
94
|
+
cwd: /absolute/canonical/project
|
|
95
|
+
mode: danger-full-access
|
|
96
|
+
command: git status --short
|
|
97
|
+
escalationApprovalTtlMs: 30000
|
|
98
|
+
escalationMaxTimeoutMs: 30000
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
每条规则只有 tool/cwd/mode 和 filePath 或 command 四项,不允许前缀、通配符或整个目录授权。原生 DSH 使用 `sandbox_permissions: danger-full-access` 加 justification,不是 Codex 的 require_escalated。当前只支持 Workspace Write 到单次 Full Access。文件目标要是规范绝对路径且父目录已存在;链接、硬链接写入、受保护/系统目标及未知参数不自动放行。write/edit 分别登记;完整有界内容交给 Reviewer,超长或命中敏感检测时不截断后批准。
|
|
102
|
+
|
|
103
|
+
**Bash 自动提权要求没有 jobs 服务**、可确认的沙箱 shell、不请求后台、显式正数 timeoutMs 且不超过配置上限,workdir 不变。加载 jobs 的 DSH 可能把超时前台任务转后台;这种组合仍走人工或无人值守拒绝,不会偷偷关闭 jobs。超时和一次性批准不能证明所有派生进程都已经退出。
|
|
104
|
+
|
|
105
|
+
只在匹配的原生 approval/request 内返回 allowed-once,由原生工具消费,不自行执行或修改会话权限。批准绑定 opaque token、异步上下文、Agent、callId、signal、完整参数、cwd、权限模式、直接用户意图和文件状态。并发相同 callId 不能互借;每次执行只消费一次,无跨调用批准缓存。审批 TTL 是决定新鲜度,不是进程 TTL、撤销或回滚;操作副作用不会自动撤销。详见 [ADR-0002](docs/ADR-0002.md)。
|
|
106
|
+
|
|
107
|
+
**原生 approval: never 优先拒绝所有审批。** 启用自动提权时,原生服务仍需设为 ask,即使插件模式是 unattended。ask 表示分发给审批者,不代表一定有人在线。缺少原生审批服务时不提权。不要叠加官方 Auto、Autogate 或其他自动 answerer,人工 fallback 假定下游是原生人工通道。
|
|
108
|
+
|
|
109
|
+
## 成本、隐私与安全边界
|
|
110
|
+
|
|
111
|
+
默认快速/深审输出上限 64/256 tokens,分别写入原生 maxTokens 或 HTTP 参数;每个直接用户任务最多 20/3 次;每阶段超时 8000ms,输入上限 8192 字节;连续 3 次或每会话累计 20 次非允许审核触发熔断。快速返回 allow/review/deny,深审返回 allow/ask/deny。错误 JSON、额外字段、工具调用、缺失 finish 和截断不能批准;原生 reasoning 内容也受响应大小限制。
|
|
112
|
+
|
|
113
|
+
普通预检和提权共享网络前原子预算,失败不退额。sessionBudgetUnits 默认 100000,按提示 UTF-8 字节数 + 输出上限 + 1024 预留,不是精确账单或主 Agent 总成本。原生缓存计数按 DSH 的互斥计量合并。插件不主动进行传输/语义重试;宿主适配器或中间件可能有自身传输策略,预留计数是逻辑审核请求,不是所有底层 HTTP 尝试。新用户消息只重置任务计数,换模型不重置;重载/重启仍会重置内存计数。
|
|
114
|
+
|
|
115
|
+
仅发送动作和最近直接人类文本,不发送完整历史、主 Agent 推理或工具结果。justification 不是授权。敏感检测是启发式;选定或跟随远端 provider 会发送这些有界输入,文件提权包含内容。固定配置失效时不偷偷更换数据目的地。
|
|
116
|
+
|
|
117
|
+
DSH workspace-write 只约束文件效果,不是网络隔离或全面敏感读取防护。单次 Full Access 确实移除该次 DSH 文件沙箱;精确规则不等于 OS 仅放开一个文件。路径重查不能消除 TOCTOU。测试、构建、安装和 Git hooks 可运行任意代码,命令登记不固定其未来内容。无人值守需额外隔离凭据与网络。可信同进程插件、工具和执行提供方仍属于信任基础。
|
|
118
|
+
|
|
119
|
+
## 验证与剩余范围
|
|
120
|
+
|
|
121
|
+
运行 npm test、npm run build(JavaScript 语法检查,不是 TypeScript typecheck)、npm pack --dry-run 和全仓 pnpm check。测试包括路由/并发/失效/取消,以及真实 DSH LlmRuntime、ToolRuntime、ApprovalService 和受控适配器集成。CI 必须运行真实 DSH tests;缺依赖的离线本地环境可以明确跳过。
|
|
122
|
+
|
|
123
|
+
受控模型和 fixture 的 session/tool/policy 不是在线模型准确率、OS 沙箱或 authenticated Web/Headless 验收。剩余清单在 ADR-0002。日志区分 assessment、escalation 和实际 result,不记录原始命令/提示/Key,留存由宿主管理。
|
|
124
|
+
|
|
125
|
+
PowerShell、PTC/MCP、远程、复杂 Shell、子代理模型提权和 read-only 到 workspace-write 仍未纳入自动放行。没有持久化预算/审计库、跨调用批准缓存、PI probe 或图形化模型选择器。不承诺绝对安全或固定节省比例。
|
|
126
|
+
|
|
127
|
+
## 许可证
|
|
128
|
+
|
|
129
|
+
原始实现使用 [SATA License 2.1](LICENSE),参考社区设计但未直接复制其代码。
|
package/cordis.patch.yml
ADDED
package/docs/ADR-0001.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# ADR-0001: bounded preflight, not an escalation auto-answerer
|
|
2
|
+
|
|
3
|
+
Status: historical RC1 decision, partially superseded by [ADR-0002](ADR-0002.md) in `0.1.0-rc.2`.
|
|
4
|
+
|
|
5
|
+
**The no-auto-escalation statements below describe RC1, not the current candidate.** RC2 adds separately enrolled, execution-bound native `allowed-once` approvals, including unattended mode, without changing standing sandbox policy. Use ADR-0002 and the current READMEs for configuration and deployment acceptance. The original decision is retained here as history.
|
|
6
|
+
|
|
7
|
+
## Evidence baseline
|
|
8
|
+
|
|
9
|
+
DSH contracts were inspected at `477b4f420553e8a52c2fbccc464d7561b239c443` (`0.1.7-rc.2`):
|
|
10
|
+
|
|
11
|
+
- [Tool pipeline and opaque execution identity](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/core/tools/src/index.ts). `prepareExecution` runs pre-execute, resolves any ask, then applies guards before dispatch. `tools/result` receives the final immutable result. Guards are not an async approval API.
|
|
12
|
+
- [Approval service](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/interaction/user-approval/src/index.ts). `never` rejects before answerers; only `allowed-once` grants; `effectivePolicy` is private despite older prose implying otherwise.
|
|
13
|
+
- [Sandbox semantics](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/docs/subsystems/sandbox.md). File effects only; full access bypasses confinement; providers may report partial enforcement. Approval can widen a single call without changing the session's displayed standing mode.
|
|
14
|
+
- [Public synchronous policy resolver](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/sandbox/sandbox-policy/src/index.ts).
|
|
15
|
+
- [Cordis plugin registry](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/docs/cordis-api/registry.md) accepts a real Standard Schema v1 validator. The package supplies one rather than an unvalidated Config object. No volatile settings form is promised.
|
|
16
|
+
|
|
17
|
+
Design references: [Autogate](https://github.com/wangxing-git/dsh-autogate) (public hooks, path checks, independent reviewer) and [Approve for me](https://github.com/timeance/dsh-approve-for-me) (rules bound maximum grant, tool-free review, fail-closed). These are not independent security endorsements. Approve for me's README itself marks full authenticated profile smoke outstanding. No community source was vendored, so there is no MIT-derived code to silently relicense.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
Implement a small original module instead of importing a broad permissive approval engine. Reuse DSH's maintained extension contracts, not its internals. Runtime has no additional npm dependencies. The smaller capability set is deliberate: do not ship a regex-based universal shell parser or assume model review replaces a sandbox.
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
pre-execute
|
|
25
|
+
deterministic deny -> deny
|
|
26
|
+
ordinary local file -> next native policy
|
|
27
|
+
exact enrolled command -> fast -> optional deep -> next / ask / deny
|
|
28
|
+
everything else -> native ask (unattended: deny)
|
|
29
|
+
existing downstream asks/denials remain authoritative
|
|
30
|
+
native ask resolution
|
|
31
|
+
monotonic guard rechecks state and proves preflight ran for this execution token
|
|
32
|
+
tool dispatch -> final result audit
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The plugin NEVER answers `allowed-once`. It rejects approval requests in unattended mode and otherwise delegates them. An LLM's `allow` is only permission to continue through preflight under existing constraints; it cannot approve an unrelated tool ask or escalation.
|
|
36
|
+
|
|
37
|
+
All current shared `workspaceRoots` and `shellCandidates` are explicit host-operator grants. Candidate commands match exactly, including all arguments. File names/contents and tool descriptions are not user authority. Only the latest direct `user/message` supplies reviewer authority. Missing history, attachments, unresolved short replies, delegated instructions or oversized input cannot enlarge capabilities. Short human replies can lack sufficient context and cause manual fallback by design.
|
|
38
|
+
|
|
39
|
+
The session authority reader uses existing `snapshotEvents()` in one isolated adapter function. DSH deprecates synchronous historical reads; do not spread this dependency. Replace it with a host projection/read contract when that can preserve direct-user provenance without trusting model summaries.
|
|
40
|
+
|
|
41
|
+
## What the prototype does not guarantee
|
|
42
|
+
|
|
43
|
+
Model false negatives, secret-detection recall, network confinement, protection from malicious same-process plugins, and TOCTOU-free filesystem identity are NOT guaranteed. Rechecks reduce stale decisions but do not replace enforcement by filesystem/process providers. Operator-enrolled scripts/executables and their dependencies can change after enrollment. Do not treat a cached or exact command name as immutable code identity.
|
|
44
|
+
|
|
45
|
+
The plugin checks the resolved policy, not the existence or completeness of every enforcing backend. Use a correctly composed DSH profile. An artificial provider reporting Workspace Write is not a sandbox. Local POSIX path inspection is not valid evidence for SSH/Windows filesystem identity, so those paths/workflows remain outside the auto-grant contract.
|
|
46
|
+
|
|
47
|
+
Reviewer reservations use bytes and caps, not a universal tokenizer. They bound this instance's attempts and payloads, not a provider's billing implementation or the main agent. No long-lived approval grants, cross-call allow cache, budget persistence, native DSH provider adapter, PI probe, full PTC or arbitrary MCP compatibility is included. These are explicit deferred work, not implemented features.
|
|
48
|
+
|
|
49
|
+
Audit phases distinguish proposed assessment from actual outcome. Host logs are not a durable tamper-proof ledger. Counters reset on reload/restart. Unattended is denial-on-uncertainty, not a promise the overall agent task will finish.
|
|
50
|
+
|
|
51
|
+
## Validation and release gate
|
|
52
|
+
|
|
53
|
+
Automated tests: deterministic policies and protected paths; symlink/hardlink/race checks; full-access rejection; exact command envelope; strict completion parsing; oversized stream limits; signal-ignoring timeout; cancellation and unload; concurrent budget reservation; circuit behavior; direct-user provenance; downstream-policy preservation; opaque token isolation and bypass prevention; default shadow behavior.
|
|
54
|
+
|
|
55
|
+
The real Cordis/ToolRuntime contract test loads the same installed DSH dependencies as the existing workspace, verifies the Standard Schema plugin mount, real guard ordering, same-ID cleanup, downstream denials, preflight bypass rejection and disposal. It uses fixture tool bodies and a fixture sandbox-policy service, NOT a real operating-system sandbox. In CI missing host packages fail, rather than silently skip.
|
|
56
|
+
|
|
57
|
+
Before promoting beyond candidate:
|
|
58
|
+
|
|
59
|
+
1. `pnpm install --frozen-lockfile && pnpm check` must pass including the real-host contract test. Inspect npm pack contents and the prerelease `next` tag behavior; do not publish by merging without review.
|
|
60
|
+
2. Install the local package into a disposable authenticated Web profile with native tools and real filesystem/process sandbox providers. Start shadow, inspect logs, then enable smart. Confirm ordinary file work, protected targets, unknown tools, user rejection and unavailable reviewer behavior.
|
|
61
|
+
3. Repeat in Headless/unattended: approvals must not be auto-granted; verify cancellation, provider failure and limits. Test partial/missing sandbox backends and Full Access rejection explicitly.
|
|
62
|
+
4. Use a fake HTTP server for deterministic fast/deep outcomes and a separately consented real endpoint to validate its `tokenField`, usage, caps and structured output. Do not spend user API credit implicitly from tests.
|
|
63
|
+
5. Run a task corpus and malicious-input corpus before making accuracy, prompt-injection resistance or token-saving claims. Test supported OS/provider versions individually.
|
|
64
|
+
|
|
65
|
+
CI should remain frozen-lockfile and must not be loosened merely to make this package pass. A dependency-free new workspace importer may need to be recorded by pnpm; any resulting lockfile-only metadata change should contain no dependency version updates.
|
package/docs/ADR-0002.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# ADR-0002: native, execution-bound one-shot escalation
|
|
2
|
+
|
|
3
|
+
Status: implemented in `0.1.0-rc.2`; automated contracts verified; live-profile acceptance pending.
|
|
4
|
+
|
|
5
|
+
This partially supersedes [ADR-0001](ADR-0001.md): its prohibition on returning `allowed-once` and blanket unattended escalation rejection were an RC1 limitation, not the final design. Ordinary preflight, explicit envelopes, bounded review, no global permission writes and conservative failure handling remain in force.
|
|
6
|
+
|
|
7
|
+
## Verified source baseline
|
|
8
|
+
|
|
9
|
+
Contracts are pinned to DSH `0.1.7-rc.2`, commit `477b4f420553e8a52c2fbccc464d7561b239c443`:
|
|
10
|
+
|
|
11
|
+
- [Native escalation helper](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/sandbox/sandbox/src/escalation.ts): the `workspace-write` widening target is `danger-full-access`; `approveEscalation` asks before execution and returns a mode for that call, not a session mutation. Its exact audited reason includes target mode and justification.
|
|
12
|
+
- [ApprovalService](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/interaction/user-approval/src/index.ts): requires an open turn, appends asked/decided with paired IDs, fails closed, and enforces native `never` before answerers. Requests omit tool arguments and execution tokens.
|
|
13
|
+
- [ToolRuntime](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/core/tools/src/index.ts): preflight and monotonic guards precede the around-dispatch `tools/execute` seam; immutable argument snapshots and opaque execution tokens are registry-owned. Final results are observable separately.
|
|
14
|
+
- [Bash consumer](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/shell/tool-bash/src/index.ts): native approval is requested inside tool execution; with jobs mounted, a foreground timeout may promote to background. Without jobs, the foreground executor deadline applies.
|
|
15
|
+
- [Filesystem consumer](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/fs/tool-fs/src/sandbox.ts): consumes the same one-shot helper before file operations.
|
|
16
|
+
- [Filesystem world mapping](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/fs/fs/src/index.ts) and [local implementation](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/fs/fs-local/src/index.ts): `processPathFromHostPath` returns no mapping for an unavailable host world; local paths can be verified without assuming SSH and local paths share identity.
|
|
17
|
+
|
|
18
|
+
Compatibility is evidence for this pinned version, not a guarantee about future DSH releases. No private `effectivePolicy`, monkey-patching, global sandbox switch or custom executor is used.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
Keep two separate decisions: ordinary preflight allows trying an action under standing policy; an escalation verdict approves the exact native permission request. Add an independent, empty-by-default `escalationCandidates` envelope. Each rule matches a native tool, enrolled cwd, requested mode, and exact command or absolute file path. Only native `bash`, `write` and `edit` widening from Workspace Write are candidates. Full arguments still require bounded independent model review and direct human task authority.
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
pre-execute: hard checks + deterministic escalation assessment
|
|
26
|
+
-> preserve downstream ask/deny
|
|
27
|
+
-> save execution-token binding; no model call yet
|
|
28
|
+
monotonic guard: confirm binding and unchanged policy
|
|
29
|
+
-> admit the native tool to ask, not to execute unconfined
|
|
30
|
+
around tools/execute: AsyncLocalStorage with one live execution
|
|
31
|
+
-> native approveEscalation -> ApprovalService.request
|
|
32
|
+
-> approval/request: exact identity and reason match
|
|
33
|
+
-> claim one slot before awaiting
|
|
34
|
+
-> envelope + shared budget + fast/optional-deep review
|
|
35
|
+
-> recheck binding, lifetime, cancellation and freshness
|
|
36
|
+
-> allowed-once / rejected / cancelled / native human fallback
|
|
37
|
+
native tool consumes mode for this invocation only
|
|
38
|
+
-> tools/result clears execution decision; next call remains confined
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
An `AsyncLocalStorage` scope is necessary because ApprovalRequest has neither full arguments nor a registry token. A process-wide map keyed by model-visible callId can confuse agents or parallel calls. Match agent object, tool name, callId, current signal and complete helper reason against the active dispatch; the opaque token binds its preflight. Mark the single slot claimed before any await. Detached child async work retains an inactive store and cannot reuse a finished dispatch. Other same-process plugins are trusted, not adversaries this mechanism can isolate.
|
|
42
|
+
|
|
43
|
+
The SHA-256 binding includes complete arguments, effective policy/root, latest direct user message identity and text, execution-world/lifetime flags and file state where applicable. Both automatic and fallback outcomes are rechecked before return. It is a stale-decision detector, not an OS capability or promise of zero races. Model justifications are data and never authority.
|
|
44
|
+
|
|
45
|
+
## Process and file lifetime
|
|
46
|
+
|
|
47
|
+
Automatic Bash widening requires no jobs service, a sandbox-capable shell, no background request, bounded explicit timeoutMs and no alternate workdir. Jobs-enabled compositions fall back to a human or reject unattended; the plugin does not alter global jobs configuration. Arbitrary descendants, scripts, hooks, PATH and mutable executable inputs remain risks requiring isolation and narrow enrollment. Do not label a simple command string as a validated process tree.
|
|
48
|
+
|
|
49
|
+
File widening requires an exact canonical absolute target, existing canonical parent, regular/non-linked target (or a new file for write), and supported argument fields. Credential/configuration/system targets remain denied. The parent/target stat snapshot is compared around review. A full-access file call still depends on the trusted file provider; there is no finer kernel path grant in this plugin.
|
|
50
|
+
|
|
51
|
+
Automatic decision freshness defaults to 30 seconds, with a separate default 30-second maximum requested Bash timeout. The former is not a process kill timer, retrospective revocation or rollback. Fresh human approval may resolve an expired automatic review in smart mode, but changed bindings and cancelled requests do not grant.
|
|
52
|
+
|
|
53
|
+
## Failure, budget and composition
|
|
54
|
+
|
|
55
|
+
Only a fresh `allow` inside the envelope produces `allowed-once`. Explicit denial is final for that question; uncertainty/errors use native human fallback in smart and reject in unattended. Missing identity or an inactive/claimed context rejects. No semantic retries and no transferable allow cache.
|
|
56
|
+
|
|
57
|
+
Both phases share session budget reservations and fuses. Native escalation calls do not receive another model review in preflight. HTTP caps, bounded streams, cancellation and late-answer handling remain unchanged. Budgets are per-instance in memory, not exact billing or the main-agent budget.
|
|
58
|
+
|
|
59
|
+
Native `approval: ask` is required for the answerer to run, including plugin unattended mode. Native `never` wins before this listener. Preserve other preflight decisions; unrelated asks cannot acquire an automatic grant from this escalation envelope. Do not stack with other auto answerers. Audit decisions before returning permission, preserving DSH's own paired audit events; logger failures cannot create a grant.
|
|
60
|
+
|
|
61
|
+
## Validation
|
|
62
|
+
|
|
63
|
+
The PR adds 50 escalation unit/race/fault tests, a real loopback HTTP transport test, and a real Cordis/ToolRuntime/ApprovalService/approveEscalation contract test. Combined with RC1's tests, CI ran **119 tests, all passing, none skipped**, for implementation commit `37c4c987c84656f0490f09232f2a0612bc576630` ([CI #34](https://github.com/klarkxy/dsh-plugins/actions/runs/36293861872)). Frozen-lockfile installation and the entire existing `pnpm check` also passed without loosening CI.
|
|
64
|
+
|
|
65
|
+
The native contract verifies an outside-workspace disposable fixture write, the real asked/decided pair, native `never` precedence, downstream denial and absence of escalation inheritance on the next call. A loopback server exercises real HTTP, not a paid model. Session storage, filesystem policy and tool bodies are test fixtures. This verifies interface composition, not authenticated client routing, an actual OS sandbox or classifier accuracy. Local dependency-free testing ran the 50 new unit tests plus HTTP test; the new native contract ran in CI with the workspace's pinned DSH dependencies.
|
|
66
|
+
|
|
67
|
+
## Remaining deployment acceptance
|
|
68
|
+
|
|
69
|
+
- [ ] Install a local candidate into disposable authenticated Web and Headless profiles, preserving actual users' profiles. Verify required native services and plugin Config load/reload.
|
|
70
|
+
- [ ] Exercise actual native Bash and filesystem consumers with real OS providers. Verify full/partial/missing enforcement separately, one-shot mode consumption and the next command's unchanged standing sandbox.
|
|
71
|
+
- [ ] Check jobs-enabled Bash stays manual/rejected; no-jobs Bash respects timeout/cancellation. Exercise descendant/process cleanup rather than inferring it from a returned tool result.
|
|
72
|
+
- [ ] Check native human fallback, explicit rejection, cancellation, shutdown during review and no other auto answerer in the chain.
|
|
73
|
+
- [ ] With separately authorized model credentials, test provider output caps and structured output, task corpus, prompt injection and false-negative behavior. Loopback tests are not model quality measurements.
|
|
74
|
+
- [ ] Complete OS/provider-version-specific security review before stable release. Keep prerelease/Draft status until acceptance evidence exists.
|
|
75
|
+
|
|
76
|
+
No PR merge, npm release, user profile change or paid model invocation is part of this implementation change. The project catalog only accepts published latest packages, so this prerelease is deliberately not added there.
|
package/docs/ADR-0003.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# ADR-0003: conversation-following and independent reviewer routes
|
|
2
|
+
|
|
3
|
+
Status: implemented in RC3. This supersedes the HTTP-only routing statements in ADR-0001/0002, not their remaining execution restrictions or deployment acceptance gates.
|
|
4
|
+
|
|
5
|
+
## Requirement
|
|
6
|
+
|
|
7
|
+
The approval model must be independently configurable, and default to the current conversation. Apply this consistently to normal preflight and one-shot escalation. Do not duplicate provider credentials, share main-agent context, add ambient permissions, or silently switch data destinations on failure.
|
|
8
|
+
|
|
9
|
+
## Verified contracts
|
|
10
|
+
|
|
11
|
+
Inspected DSH `477b4f420553e8a52c2fbccc464d7561b239c443` (`0.1.7-rc.2`):
|
|
12
|
+
|
|
13
|
+
- `packages/llm/llm/src/types.ts`: `GenerateOptions` accepts provider/model, one-shot identity-free user inputs, a separate system prompt, tools, maxTokens and signal. `RequestUserInput` has no durable id/source. TokenUsage counters are disjoint; native finish must be stop for a valid review.
|
|
14
|
+
- `packages/llm/llm/src/index.ts`: `LlmRuntime.stream` uses registered adapters and normalizes adapter failures. Native `LlmAdapter` registration and controlled-stream integration are exercised in CI.
|
|
15
|
+
- Existing session request headers carry the accepted call config; the plugin reads only provider/model. Same-agent options are a fallback only when the header is absent.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
With endpoint/fastProvider/fastModel empty, resolve the requesting conversation's provider/model for every eligible review. Fixed native mode requires fastProvider + fastModel. The optional deep stage requires deepProvider + deepModel and may use another provider even when the primary follows the conversation. Leaving deep empty disables it, preventing an implicit second expensive call.
|
|
20
|
+
|
|
21
|
+
Explicit HTTP endpoint + fastModel retains RC2 behavior. Native provider fields with an HTTP endpoint are invalid. Partial native pairs fail load validation. Missing routes/adapters or failed calls ask/deny; they never switch to another provider, another session or an implicit HTTP route.
|
|
22
|
+
|
|
23
|
+
Only route identifiers are inherited. Reviewer calls use a new prompt and one identity-free input, no tools, main transcript, request replay state, parent sessionId, reasoning setting or output budget. Native adapters own credentials. The reviewer retains its own caps; adapter defaults can still affect reasoning. Incompatibility fails closed, and users can independently choose a suitable review model/cap.
|
|
24
|
+
|
|
25
|
+
Snapshot fast/deep routes once per action. Compare again after review and in the final preflight guard/approval completion. A changed model or removed native service cannot grant from stale review; explicit model denials remain final. The optional llm dependency is a child injection so its disappearance does not unload root safety guards. Model changes never reset task/session budgets.
|
|
26
|
+
|
|
27
|
+
The plugin adds no retry. Native host middleware/provider transport retries remain host policy, not additional grants or precisely metered wire attempts. Reservation units still bound logical review stages and payloads rather than guaranteeing billing. Existing `allowed-once`, authority, hard-risk, context isolation, cancellation and enrollment restrictions remain unchanged.
|
|
28
|
+
|
|
29
|
+
## Configuration surface and scope
|
|
30
|
+
|
|
31
|
+
RC3 exposes model selection through the existing validated profile Config. There is no graphical model picker or volatile settings form in this change. Config edits require reload. Following a newly accepted conversation model does not require plugin reload. The model-selection UI and live-profile/provider acceptance are not claimed complete.
|
|
32
|
+
|
|
33
|
+
Default enrollment remains empty and mode remains shadow. However, a profile that already enrolled ordinary commands without an endpoint can now review them with the conversation model, including in shadow mode. Document this behavior change and its potential model usage. Shadow escalation remains manual and does not call the reviewer.
|
|
34
|
+
|
|
35
|
+
## Tests
|
|
36
|
+
|
|
37
|
+
Route validation/defaults; accepted header priority; options fallback; malformed-header refusal; fixed/deep/HTTP isolation; concurrent sessions; stream protocol and byte caps; tools/invalid finish refusal; reasoning usage; timeout/cancellation; provider removal; stale decision and final-guard recheck; model-switch budget retention; native route in one-shot escalation; and semantic denial across a model change.
|
|
38
|
+
|
|
39
|
+
The native contract loads the workspace's actual Cordis, ToolRuntime and LlmRuntime, registers a controlled LlmAdapter, exercises two accepted conversation models and a distinct fixed reviewer, then removes the model service and proves the root gate remains. This is not a live-provider, OS sandbox or authenticated Web test. No user credentials or paid model calls are used by tests.
|
package/dsh.plugin.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "@klarkxy/dsh-safe-auto",
|
|
3
|
+
"version": "0.1.0-rc.3",
|
|
4
|
+
"main": "src/index.js",
|
|
5
|
+
"description": "Budgeted preflight and one-shot escalation with conversation-following or independent reviewers.",
|
|
6
|
+
"engines": { "dsh": ">=0.1.7-rc.2 <0.2.0" },
|
|
7
|
+
"contributes": { "tools": [], "skills": [] }
|
|
8
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@klarkxy/dsh-safe-auto",
|
|
3
|
+
"version": "0.1.0-rc.3",
|
|
4
|
+
"description": "Budgeted DSH preflight and one-shot escalation with conversation-following or independent reviewers.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.js",
|
|
9
|
+
"./package.json": "./package.json"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src/*.js",
|
|
13
|
+
"cordis.patch.yml",
|
|
14
|
+
"dsh.plugin.json",
|
|
15
|
+
"README*.md",
|
|
16
|
+
"docs/*.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "node --check src/config.js && node --check src/policy.js && node --check src/escalation.js && node --check src/model-route.js && node --check src/reviewer.js && node --check src/gate.js && node --check src/index.js",
|
|
21
|
+
"test": "node --test test/*.test.js"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": "^22.19.0 || >=24.0.0",
|
|
25
|
+
"dsh": ">=0.1.7-rc.2 <0.2.0"
|
|
26
|
+
},
|
|
27
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/klarkxy/dsh-plugins.git",
|
|
31
|
+
"directory": "plugins/dsh-safe-auto"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"dsh-plugin",
|
|
35
|
+
"deepseek-harness",
|
|
36
|
+
"approval",
|
|
37
|
+
"security"
|
|
38
|
+
],
|
|
39
|
+
"dsh": {
|
|
40
|
+
"bundle": {
|
|
41
|
+
"patch": "./cordis.patch.yml"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"registry": "https://registry.npmjs.org/"
|
|
47
|
+
},
|
|
48
|
+
"dshRelease": {
|
|
49
|
+
"contentHash": "72a33a51f676cd1ddadeca46571eb0ddf05e35e919cffa38cf785e3fef33d226"
|
|
50
|
+
}
|
|
51
|
+
}
|