@klarkxy/dsh-safe-auto 0.1.0-rc.5 → 0.2.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/README.md CHANGED
@@ -1,146 +1,107 @@
1
1
  # Safe Auto (release candidate)
2
2
 
3
- [中文](README.zh-CN.md) · [Preflight](docs/ADR-0001.md) · [One-shot escalation](docs/ADR-0002.md) · [Reviewer routing](docs/ADR-0003.md)
3
+ Automatic review of native sandbox escalation requests. When a real `write`/`edit`/`bash`/`pwsh` call asks to widen its sandbox beyond Workspace Write, an independent reviewer model vets that one call — except a `bash` call the built-in static proof can show is read-only, which is granted without a model. A pass continues automatically; other review results offer an exact-call confirmation in the current session, with 60 seconds to approve once or reject. No response means rejection. The plugin never switches the standing session to Full Access.
4
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.
5
+ [简体中文](README.zh-CN.md) · [One-shot escalation](docs/ADR-0002.md) · [Reviewer routing](docs/ADR-0003.md) · [Authorization and evidence](docs/ADR-0004.md) · [Timed human confirmation](docs/ADR-0005.md) · [Static shell proof](docs/ADR-0006.md)
6
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.
7
+ > **0.2.0 is a breaking simplification.** The 0.1.x exact-rule preflight, workspace/shell/escalation candidate lists, `shadow`/`smart`/`unattended` modes, the deep review stage and the HTTP endpoint were removed. See [Migrating from 0.1.x](#migrating-from-01x).
8
8
 
9
- ## Install and configure
9
+ ## Install and enable
10
10
 
11
- Use a disposable profile from a checked repository checkout:
11
+ ```sh
12
+ dsh plugin --profile <profile> add @klarkxy/dsh-safe-auto
13
+ ```
14
+
15
+ From a checked source checkout instead:
12
16
 
13
17
  ```sh
14
18
  pnpm install --frozen-lockfile
15
19
  pnpm --filter @klarkxy/dsh-safe-auto test
16
- dsh plugin --profile web add ./plugins/dsh-safe-auto
20
+ dsh plugin --profile <profile> add ./plugins/dsh-safe-auto
17
21
  ```
18
22
 
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
- ```
23
+ The bundle inserts the `dsh-safe-auto` and `dsh-safe-auto-ui` profile rows. Safe Auto then appears as a fourth option in the composer's permission menu, next to the native presets — pick **Safe Auto** there to enable it for the current session, and pick any native preset to disable it again. Nothing is automatic until you do: no session inherits activation, and restart, a permission change or uninstall revokes it. The plugin page only holds reviewer configuration, safety notes and the read-only budget limits, grouped into tabs.
32
24
 
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.
25
+ > The composer permission control is a replacement slot: while Safe Auto is installed, the plugin renders that control itself (native presets included) so the option can appear in the menu. If the host redesigns this control, the plugin's copy may lag behind until updated.
34
26
 
35
- ## Choose the approval model
27
+ ## How it works
36
28
 
37
- ### Default: follow the conversation
29
+ 1. A native tool call asks for `sandbox_permissions` widening (`workspace-write` or `danger-full-access`).
30
+ 2. The plugin binds the live execution — agent, callId, signal, exact arguments, cwd, sandbox policy and the ordered direct-human instructions, including later restrictions and revocations.
31
+ 3. A fully literal `bash` command is granted without evidence or a model only when every invocation matches an exact positive grammar: case-sensitive POSIX program names, supported flags and values, input-only operand counts, safe redirects and bounded environment prefixes. Unknown options, abbreviations, clusters, output operands and assignment-only statements continue to review. Ordinary reads such as `ls -la`, `cat input | jq .name`, `grep -rn pattern src`, `find . -name "*.md"` and `ps aux` retain this fast path; `pwsh` does not.
32
+ 4. The exact request is reviewed once. For shell calls, bounded workspace package definitions and referenced local scripts provide untrusted evidence; their canonical paths and content are rechecked before granting.
33
+ 5. A reviewed pass returns `allowed-once`. For an otherwise eligible action, model denial, uncertainty, failure or budget exhaustion opens a timed confirmation. The dialog shows the exact operation, requested permission, reviewer opinion and countdown. Explicit approval grants this call after another binding/evidence check; rejection or expiry returns `rejected`. Closing the dialog rejects. No downstream approval answerer is consulted.
38
34
 
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.
35
+ Native prohibitions, structural refusals, changed evidence, cancellation and invalidated requests cannot be overridden. Timed confirmation requires the UI row and an authenticated browser client; without them, or with the timeout set to zero, the existing non-grant path remains. Headless sessions do not gain an automatic fallback.
40
36
 
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.
37
+ The reviewer sees the bounded action, chronological direct-human instructions and local evidence, with no main-agent prompt, private reasoning or tools. Authorization is judged by substance: necessary local installation, build and test steps may implement a requested development task without the user naming every command. Earlier authority remains visible after a progress question or “continue”; later restrictions still apply. Automatic allowance requires low/medium risk, medium/high authorization and bounded side effects.
42
38
 
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
- ```
39
+ Human text is retained without lossy selection, up to 12 KiB. Sensitive text, nontext constraints or overflow prevent automatic review rather than silently dropping restrictions; an otherwise valid action can receive fresh explicit confirmation through the timed UI without sending that history to the model. Evidence is capped at eight files, 6 KiB per file and 12 KiB overall. Protected, escaped, sensitive or unreadable files are withheld with explicit omission markers. Evidence coverage is partial: dependencies, imported code and workspace-wide scripts are not exhaustively inspected. Material remaining uncertainty prevents automatic permission.
51
40
 
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.
41
+ ## Configuration
53
42
 
54
- The optional deep stage is independently configurable, including while the primary reviewer follows the conversation:
43
+ Everything is optional; with no configuration the reviewer follows the current conversation's model. Edit the profile row's `config` and reload:
55
44
 
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.
45
+ | Key | Default | Meaning |
46
+ | --- | --- | --- |
47
+ | `enabled` | `true` | Master switch. `false` mounts nothing. |
48
+ | `provider` / `model` | *(follow conversation)* | Fixed reviewer route, configured as a pair using your DSH model IDs. |
49
+ | `reasoningEffort` | *(model default)* | Advertised reasoning effort of the fixed route. |
50
+ | `reviewerPrompt` | *(none)* | Extra review constraints (≤4096 chars); may only restrict, never widen. |
51
+ | `timeoutMs` | `30000` | Per-review deadline. |
52
+ | `humanApprovalTimeoutMs` | `60000` | Human confirmation window, separate from model review; `0` disables it. Maximum 300000 ms; profile-owned. |
53
+ | `maxInputBytes` | `32768` | Combined UTF-8 prompt/input cap; existing explicit values are retained. |
54
+ | `outputTokens` | `1024` | Reviewer output cap, including reasoning where the model counts it. Explicit profile values are retained; incomplete or invalid verdicts still require human confirmation. |
55
+ | `maxReviewsPerTask` | `20` | Review calls per direct user task. |
56
+ | `consecutiveDenials` | `3` | Consecutive explicit denials before the breaker opens. Uncertainty and failures reset the streak but still consume review reservations. |
57
+ | `staticReadonly` | `true` | Grant provably read-only `bash` escalations without a model call. `false` sends everything to the reviewer. |
62
58
 
63
- ### Independent HTTP endpoint (RC2-compatible)
59
+ Unknown keys fail validation rather than being ignored. Reviewer model, effort and prompt can also be set per deployment on the plugin page; those saved values override the profile route but never the budgets.
64
60
 
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
- ```
61
+ ## The reviewer model
71
62
 
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.
63
+ **Default: follow the conversation.** Leave `provider`/`model` empty. Each review resolves the provider/model from the requesting session's accepted request header, reusing DSH's configured credentials — no extra API key. Only the model route is inherited, not history, prompt, tools or budgets.
73
64
 
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.
65
+ **Fixed model.** Set `provider` and `model` together to pin a reviewer that stays put when the conversation switches models. A missing or invalid route rejects instead of falling back silently.
75
66
 
76
- ## Policy and one-shot escalation
67
+ ## Policy
77
68
 
78
- | Action | Smart behavior |
69
+ | Request | Outcome |
79
70
  | --- | --- |
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.
71
+ | Bound single escalation of `write`/`edit`/`bash`/`pwsh`, reviewer passes | `allowed-once`, this call only |
72
+ | Bound `bash` escalation proven read-only by the strict static parse | `allowed-once`, no model call, no budget consumed |
73
+ | Protected credentials/security files, secrets, dangerous programs (anywhere in a fully parsed command) | Hard denial before review |
74
+ | Model denial (including risk opinions), uncertainty or review failure for an eligible action | Timed human confirmation; no grant without explicit approval |
75
+ | Unknown tools, subagents, nested or unbound requests | No automatic grant |
76
+ | Human rejection or confirmation expiry | Rejected; fresh execution needs fresh review |
77
+ | Changed evidence, invalidated grants, audit failure or cancellation | No grant; terminal feedback |
78
+
79
+ Native `approval: never` still rejects before any answerer. Never stack this with another automatic approval answerer.
80
+
81
+ ## Migrating from 0.1.x
82
+
83
+ - Profile keys `mode`, `workspaceRoots`, `shellCandidates`, `escalationCandidates`, `endpoint`, `apiKeyEnv`, `tokenField`, `fast*`/`deep*` and the budget fuses `sessionBudgetUnits`/`totalDenials` were removed; a profile still carrying them fails validation. The equivalent of 0.1.x `approvalReview: true` plus a UI-enabled session is now the only behavior.
84
+ - `fastProvider`/`fastModel`/`fastReasoningEffort` became `provider`/`model`/`reasoningEffort`; `fastOutputTokens` became `outputTokens`; `fastCallsPerTask` became `maxReviewsPerTask`.
85
+ - Settings saved by the 0.1.x panel keep loading (retired keys are stripped), but the reviewer route falls back to following the conversation — re-pick a model on the plugin page if you had a fixed one.
86
+
87
+ ## Boundaries and limits
88
+
89
+ - **Not an audited boundary.** The reviewer is a language model and can be wrong, especially in adversarial contexts. Keep the native sandbox, isolation and backups you already rely on.
90
+ - **Static proof assumes operator-trusted PATH and runtimes.** It checks the original command text, not installed binary identity, shell functions or ambient runtime integrity. Explicit executable paths, `.exe`/`.cmd` suffixes, case variants, executable pager/config environment prefixes and persistent assignments do not qualify. Parser or grammar defects can produce incorrect proofs; this is not a runtime attestation or an absolute safety guarantee. An unproven command returns to reviewer/manual confirmation, not a new hard refusal.
91
+ - **Quoting does not bypass hard refusals.** Literal argv, environment and redirect values are decoded before the protected-path/secret checks, including adjacent quote fragments. These checks also apply outside the static path and cannot be overridden by reviewer or manual confirmation. Unquoted wildcard fragments still invalidate proof even next to quoted text. Bash `/dev/tcp` and `/dev/udp` file redirects return to review, including quoted and numbered-fd input redirects; ordinary file input, fd merges and `/dev/null` output retain their existing rules.
92
+ - **Git is not read-only by subcommand name.** Bare `git status`, worktree diffs, `git ls-files --modified` and other content-checking worktree operations remain under review because refresh/conversion can execute configured clean/process filters. Supported object/index reads require the original `--no-pager --no-optional-locks --no-lazy-fetch -c core.fsmonitor=false`; cached/staged diff also requires `--no-ext-diff --no-textconv --ignore-submodules=all`. History reads additionally need `--no-show-signature` and a supported fixed format such as `--oneline`. The plugin never prepends controls or rewrites argv. Exact `git version` and `git --no-pager config --get user.name` are narrow independent reads; transport helpers, editors, filters and mixed config mutations do not qualify. Ordinary `sort` can spill to temporary files, so only its check mode qualifies.
93
+ - **Dedicated confirmation channel.** Pinned DSH 0.2.0-rc.2 approval and question waterfalls accept automatic answerers. Manual confirmation instead uses the plugin's authenticated browser HTTP endpoints and unpredictable single-use request IDs. Fetch metadata and host origin/authentication checks protect the browser channel against cross-site requests; they do not prove physical human action. Authenticated clients, their transport and same-process plugins remain trusted. No model tool is registered for approval.
94
+ - **Server-owned expiry.** The window starts when a pending request is published. The server uses a monotonic deadline and atomically accepts the first valid answer; duplicate, late or cross-session answers fail. Disabling, changing settings, unloading or cancelling invalidates pending requests. Requests are in memory only, so restarts do not restore them. Both direct browser requests and the authenticated DSH desktop carrier are supported; incompatible request metadata is rejected.
95
+ - **Foreign native approvals remain native.** Blueprint order and Classmates model approvals continue to downstream answerers when no Safe Auto-owned execution context exists. Requests in an owned context, including mismatches, stale/claimed requests, and unbound impersonation of `bash`/`pwsh`/`write`/`edit`, remain fail-closed.
96
+ - **Coverage stops at native single-call escalation.** Only `write`/`edit`/`bash`/`pwsh` widening from `workspace-write` is reviewed. Other tools, MCP, remote execution, subagent calls and standing permission changes stay outside the automatic envelope.
97
+ - **Evidence is checked, execution is not atomic.** Evidence paths, versions and SHA-256 digests are revalidated before granting, but files can still change between approval and execution. Other same-process plugins remain trusted; unloading removes the guards. Keep external isolation with restricted credentials and egress.
98
+ - **No guarantee is claimed.** There is no fail-open option, no durable audit database and no claim of absolute safety or savings.
138
99
 
139
100
  ## Verification
140
101
 
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.
102
+ Run `npm test`, `npm run build` 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, execution binding, and real Cordis/ToolRuntime/ApprovalService/LlmRuntime contracts. CI requires installed DSH dependencies; offline source-only runs may skip the native contracts. If the Windows sandbox blocks the test runner's subprocess pipes, run each test file separately with `node --test --test-isolation=none test/<file>.test.js`.
142
103
 
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.
104
+ The native contract tests use controlled adapters and fixture sessions/tools. They prove runtime integration, not live model accuracy, OS isolation or authenticated Web/Headless acceptance; those remain in [ADR-0002's checklist](docs/ADR-0002.md). Logs separate assessment, outcome and final result without raw commands, prompts or keys; host retention is operator-managed.
144
105
 
145
106
  ## License
146
107
 
package/README.zh-CN.md CHANGED
@@ -1,129 +1,108 @@
1
- # Safe Auto(候选版)
1
+ # 安全自动(候选版)
2
2
 
3
- [English / 完整配置表](README.md) · [单次提权](docs/ADR-0002.md) · [审批模型路由](docs/ADR-0003.md)
3
+ 自动审核原生沙箱提权请求。当真实的 `write`/`edit`/`bash`/`pwsh` 调用要求把沙箱放宽到 Workspace Write 之外时,由独立审核模型把关这一次调用——内置静态证明能判定为只读的 `bash` 命令除外,它们不调模型直接放行。通过就自动继续;其他审核结果会在当前会话显示完整操作,给你 60 秒选择“仅批准这一次”或拒绝。无人响应则到期拒绝。插件不会把整个会话切成 Full Access。
4
4
 
5
- 复用 DSH 原生工具循环、模型适配器和审批服务。**审批模型默认跟随当前对话,也能单独指定 provider/model,或继续使用独立 HTTP 端点。** 在明确登记的范围内,可以自动审核并批准单次沙箱提权,但不会把整个会话切成 Full Access。模型只能缩小规则给定的权限范围。
5
+ [English](README.md) · [单次提权](docs/ADR-0002.md) · [审核模型路由](docs/ADR-0003.md) · [授权与证据](docs/ADR-0004.md) · [限时人工确认](docs/ADR-0005.md) · [静态 Shell 证明](docs/ADR-0006.md)
6
6
 
7
- 仍为本地 POSIX/native 工具候选版,未经独立安全审计。保留原生沙箱、人工审批、备份和外层隔离,不宣称兼容所有工具或平台。
7
+ > **0.2.0 是破坏性简化。** 0.1.x 的精确规则预检、workspace/shell/escalation 候选名单、`shadow`/`smart`/`unattended` 模式、深度审核阶段和 HTTP 端点均已删除。见[从 0.1.x 迁移](#从-01x-迁移)。
8
8
 
9
- ## 安装与默认配置
10
-
11
- 在仓库检查通过后,向一次性测试 profile 安装本地包:
9
+ ## 安装与启用
12
10
 
13
11
  ```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
- # 模型字段不填,默认跟随本对话。
12
+ dsh plugin --profile <profile> add @klarkxy/dsh-safe-auto
28
13
  ```
29
14
 
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 模型
15
+ 使用源码检出安装:
41
16
 
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
17
+ ```sh
18
+ pnpm install --frozen-lockfile
19
+ pnpm --filter @klarkxy/dsh-safe-auto test
20
+ dsh plugin --profile <profile> add ./plugins/dsh-safe-auto
56
21
  ```
57
22
 
58
- 只有快速审核返回 `review` 才调用深审。两项均为空表示不开启深审,不会默认再调用一次对话模型。不确定时 Smart 回人工,无人值守拒绝。原生路由的 provider/model 必须成对填写。
23
+ bundle 会插入 `dsh-safe-auto` 与 `dsh-safe-auto-ui` 两个 profile 条目。之后输入框的权限菜单里会在原生档位旁出现第四个选项——选择 **安全自动** 即为本会话启用,选回任意原生档位即停用。在此之前没有任何自动行为:会话不继承启用状态,重启、切换权限或卸载都会撤销它。插件页只保留审核配置、安全说明和只读的预算边界,按页签分区。
59
24
 
60
- ### 保留独立 HTTP 端点
25
+ > 输入框的权限控件是替换型插槽:安装 Safe Auto 期间由插件自己渲染这个控件(包含原生档位),选项才能出现在菜单里。如果宿主以后重新设计这个控件,插件的副本在新版本跟进前可能落后。
61
26
 
62
- RC2 的配置继续有效:
27
+ ## 工作原理
63
28
 
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
- ```
29
+ 1. 原生工具调用请求 `sandbox_permissions` 提权(`workspace-write` 或 `danger-full-access`)。
30
+ 2. 插件绑定当前活跃执行——agent、callId、signal、完整参数、cwd、沙箱策略,以及按时间排序的真人指令,包含后续限制和撤销。
31
+ 3. 完全按字面解析的 `bash` 命令,只有每次调用都符合精确正向语法才会不收集证据、不调模型直接放行:区分大小写的 POSIX 程序名、支持的选项和值、只含输入的操作数数量、安全重定向和受限环境前缀。未知选项、缩写、未列出的短选项组合、输出文件操作数及仅赋值的语句继续审核。`ls -la`、`cat input | jq .name`、`grep -rn pattern src`、`find . -name "*.md"`、`ps aux` 等普通读取保留快速通道;`pwsh` 不走此通道。
32
+ 4. 审核模型审查一次完整请求。Shell 调用会补充有界的工作区包配置和所引用本地脚本作为非可信证据;放行前重新核对路径与内容。
33
+ 5. 审核通过返回 `allowed-once`。请求本身符合范围时,模型拒绝、不确定、故障或预算耗尽会进入限时人工确认。窗口显示完整操作、请求权限、审核意见和倒计时;明确批准后再次核对绑定及证据,只放行这一调用。拒绝、关闭窗口或超时返回 `rejected`,不交给下游审批器。
70
34
 
71
- 这是完整 OpenAI-compatible Chat Completions URL,可接兼容 lapp 网关。此模式不要填写 `fastProvider/deepProvider`;与原生路由混填会报错。只有 HTTP 模式读取上述 API Key 环境变量。仅允许 HTTPS 或 loopback HTTP,拒绝重定向、URL 凭据、查询参数和 fragment。恢复跟随时要同时清空 endpoint 和 HTTP fastModel,不能只删 endpoint。
35
+ 宿主禁止、结构性拒绝、证据变化、取消或请求失效不能被覆盖。人工确认需要 UI 条目和已认证的浏览器客户端;没有界面或把人工超时设为零时,保持原来的不放行处理。Headless 会话不会获得自动回退权限。
72
36
 
73
- 普通预检和单次提权都使用这套选择。一次审核固定两阶段路由;审核期间模型变化会作废自动放行结果,最终 guard 再次核对。模型服务失效不会导致安全 guard 一并卸载。切换模型不重置预算;已经明确拒绝的操作也不会因为换模型被改成可再次争取放行。
37
+ 审核模型看到有界动作、按时间排序的真人指令和本地证据,不继承主代理提示词、隐藏推理或工具。授权按实际含义判断:完成开发任务所需的本地安装、构建和测试可以属于已授权工作,无须用户逐条点名命令。询问进度或说“继续”不会丢掉先前授权,后续限制仍然有效。只有低/中风险、至少中等授权且副作用有界才自动放行。
74
38
 
75
- ## 默认行为与单次提权
39
+ 真人文本完整保留,合计最多 12 KiB。敏感文本、非文本约束或超限会阻止自动审查,不会静默丢掉限制;请求本身有效时,仍可在限时窗口中明确批准当前完整操作,不把这段历史发给模型。证据最多八个文件、每个 6 KiB、合计 12 KiB;受保护、越界、敏感或不可读内容会被标记并排除。证据并非完整安全证明:不会穷举依赖、导入代码或整个 workspace 的脚本,仍有实质不确定性时不得自动放行。
76
40
 
77
- `off` 不安装策略。`shadow` 只观察,不保护执行;**登记了普通候选命令后,即使没有独立 endpoint,也可能使用对话模型产生审核费用**。提权在 Shadow 中仍交原生审批,不自动审查或批准。安装时候选列表默认全空,不自动产生审核请求。
41
+ ## 配置
78
42
 
79
- `smart` 对普通工作区 `read/read_image/write/edit` 做零模型检查,保留其他原生策略;受保护凭据、安全配置和部分危险命令硬拒绝。未知情况交人工。`unattended` 可以批准符合条件的单次提权,但不确定和其他审批需求均拒绝。
43
+ 全部可选;不配置时审核模型跟随当前对话。编辑 profile 条目的 `config` 并重载:
80
44
 
81
- `escalationCandidates` 与 `shellCandidates` 独立,普通命令候选不授予沙箱外权限。例如:
45
+ | 键 | 默认 | 含义 |
46
+ | --- | --- | --- |
47
+ | `enabled` | `true` | 总开关。`false` 不挂载任何守卫。 |
48
+ | `provider` / `model` | *(跟随对话)* | 固定审核路由,用你的 DSH 模型 ID 成对配置。 |
49
+ | `reasoningEffort` | *(模型默认)* | 固定路由 advertised 的思考强度。 |
50
+ | `reviewerPrompt` | *(无)* | 额外审核约束(≤4096 字符);只能收紧,不能放宽。 |
51
+ | `timeoutMs` | `30000` | 单次审查超时。 |
52
+ | `humanApprovalTimeoutMs` | `60000` | 独立于模型审查的人工确认窗口;`0` 关闭,最多 300000 ms,只在 profile 配置。 |
53
+ | `maxInputBytes` | `32768` | 提示词与输入合计的 UTF-8 字节上限;已有显式配置保持原值。 |
54
+ | `outputTokens` | `1024` | 审核输出上限,含模型计入的思考预算。已有 profile 显式值保持不变;不完整或无效结论仍需人工确认。 |
55
+ | `maxReviewsPerTask` | `20` | 每个真人任务的审查次数上限。 |
56
+ | `consecutiveDenials` | `3` | 连续明确拒绝达到该值后熔断。不确定和故障重置拒绝计数,但仍消耗审查预算。 |
57
+ | `staticReadonly` | `true` | 可证明只读的 `bash` 提权不调模型直接放行;`false` 则一律送审核模型。 |
82
58
 
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
- ```
59
+ 未知键会直接校验失败,而不是被忽略。审核模型、思考强度和提示词也可以在插件页配置;保存的值覆盖 profile 路由,但永远不覆盖预算。
100
60
 
101
- 每条规则只有 tool/cwd/mode 和 filePath 或 command 四项,不允许前缀、通配符或整个目录授权。原生 DSH 使用 `sandbox_permissions: danger-full-access` 加 justification,不是 Codex 的 require_escalated。当前只支持 Workspace Write 到单次 Full Access。文件目标要是规范绝对路径且父目录已存在;链接、硬链接写入、受保护/系统目标及未知参数不自动放行。write/edit 分别登记;完整有界内容交给 Reviewer,超长或命中敏感检测时不截断后批准。
61
+ ## 审核模型
102
62
 
103
- **Bash 自动提权要求没有 jobs 服务**、可确认的沙箱 shell、不请求后台、显式正数 timeoutMs 且不超过配置上限,workdir 不变。加载 jobs 的 DSH 可能把超时前台任务转后台;这种组合仍走人工或无人值守拒绝,不会偷偷关闭 jobs。超时和一次性批准不能证明所有派生进程都已经退出。
63
+ **默认:跟随对话。** `provider`/`model` 留空。每次审查从请求会话已接受的请求头解析 provider/model,复用 DSH 已配置的凭据——不需要额外 API key。继承的只有模型路由,不包括历史、提示词、工具或预算。
104
64
 
105
- 只在匹配的原生 approval/request 内返回 allowed-once,由原生工具消费,不自行执行或修改会话权限。批准绑定 opaque token、异步上下文、Agent、callId、signal、完整参数、cwd、权限模式、直接用户意图和文件状态。并发相同 callId 不能互借;每次执行只消费一次,无跨调用批准缓存。审批 TTL 是决定新鲜度,不是进程 TTL、撤销或回滚;操作副作用不会自动撤销。详见 [ADR-0002](docs/ADR-0002.md)。
65
+ **固定模型。** 成对设置 `provider` 和 `model`,对话切换模型时审核路由保持不变。路由缺失或非法时直接拒绝,不做静默回退。
106
66
 
107
- **原生 approval: never 优先拒绝所有审批。** 启用自动提权时,原生服务仍需设为 ask,即使插件模式是 unattended。ask 表示分发给审批者,不代表一定有人在线。缺少原生审批服务时不提权。不要叠加官方 Auto、Autogate 或其他自动 answerer,人工 fallback 假定下游是原生人工通道。
67
+ ## 策略
108
68
 
109
- ## 成本、隐私与安全边界
69
+ | 请求 | 结果 |
70
+ | --- | --- |
71
+ | 绑定的 `write`/`edit`/`bash`/`pwsh` 单次提权,审查通过 | `allowed-once`,仅此一次 |
72
+ | 严格静态解析可证明只读的 `bash` 提权 | `allowed-once`,不调模型、不耗预算 |
73
+ | 受保护的凭据/安全文件、密钥、危险程序(完整解析的命令中任何位置) | 审查前硬拒绝 |
74
+ | 符合请求范围的模型拒绝(含风险意见)、不确定或审查故障 | 限时人工确认;未明确批准就不放行 |
75
+ | 未知工具、子代理、嵌套或未绑定请求 | 不自动放行 |
76
+ | 人工拒绝或确认超时 | 拒绝;再次执行须重新审查 |
77
+ | 证据变化、请求失效、审计故障或取消 | 不放行并给出终止原因 |
110
78
 
111
- 默认快速/深审输出上限 64/256 tokens,分别写入原生 maxTokens 或 HTTP 参数;每个直接用户任务最多 20/3 次;每阶段超时 8000ms,输入上限 8192 字节;连续 3 次或每会话累计 20 次非允许审核触发熔断。快速返回 allow/review/deny,深审返回 allow/ask/deny。错误 JSON、额外字段、工具调用、缺失 finish 和截断不能批准;原生 reasoning 内容也受响应大小限制。
79
+ 原生 `approval: never` 仍在任何应答者之前拒绝。不要与其他自动审批应答者叠加使用。
112
80
 
113
- 普通预检和提权共享网络前原子预算,失败不退额。sessionBudgetUnits 默认 100000,按提示 UTF-8 字节数 + 输出上限 + 1024 预留,不是精确账单或主 Agent 总成本。原生缓存计数按 DSH 的互斥计量合并。插件不主动进行传输/语义重试;宿主适配器或中间件可能有自身传输策略,预留计数是逻辑审核请求,不是所有底层 HTTP 尝试。新用户消息只重置任务计数,换模型不重置;重载/重启仍会重置内存计数。
81
+ ## 从 0.1.x 迁移
114
82
 
115
- 仅发送动作和最近直接人类文本,不发送完整历史、主 Agent 推理或工具结果。justification 不是授权。敏感检测是启发式;选定或跟随远端 provider 会发送这些有界输入,文件提权包含内容。固定配置失效时不偷偷更换数据目的地。
83
+ - profile 键 `mode`、`workspaceRoots`、`shellCandidates`、`escalationCandidates`、`endpoint`、`apiKeyEnv`、`tokenField`、`fast*`/`deep*` 以及熔断 `sessionBudgetUnits`/`totalDenials` 已删除;仍携带这些键的 profile 会校验失败。0.1.x 的 `approvalReview: true` 加插件页启用,现在就是唯一行为。
84
+ - `fastProvider`/`fastModel`/`fastReasoningEffort` 更名为 `provider`/`model`/`reasoningEffort`;`fastOutputTokens` 更名为 `outputTokens`;`fastCallsPerTask` 更名为 `maxReviewsPerTask`。
85
+ - 0.1.x 面板保存的设置仍可加载(废弃键会被剥离),但审核路由回退为跟随对话——如果你用过固定模型,请在插件页重新选择。
116
86
 
117
- DSH workspace-write 只约束文件效果,不是网络隔离或全面敏感读取防护。单次 Full Access 确实移除该次 DSH 文件沙箱;精确规则不等于 OS 仅放开一个文件。路径重查不能消除 TOCTOU。测试、构建、安装和 Git hooks 可运行任意代码,命令登记不固定其未来内容。无人值守需额外隔离凭据与网络。可信同进程插件、工具和执行提供方仍属于信任基础。
87
+ ## 边界与限制
118
88
 
119
- ## 验证与剩余范围
89
+ - **不是经过审计的安全边界。** 审核方是语言模型,可能出错,尤其在对抗性上下文中。保留你原本依赖的原生沙箱、隔离和备份。
90
+ - **静态证明以操作者信任 PATH 和运行时为前提。** 它检查原始命令文本,不核验已安装二进制、shell 函数或环境运行时的完整性。显式程序路径、`.exe`/`.cmd` 后缀、大小写变体、可执行 pager/配置环境前缀和会持久化的赋值都不符合条件。解析器或语法缺陷仍可能造成错误证明;这不是运行时认证,也不保证绝对安全。无法证明的命令回到审核/人工确认,不新增硬拒绝。
91
+ - **引号不能绕过硬拒绝。** 保护路径和秘密检查先读取解码后的字面参数、环境及重定向值,包括相邻引号片段拼接;退出静态通道时仍执行这些检查,模型或人工确认不能覆盖。引号旁的未引用通配符仍使静态证明失败。Bash 的 `/dev/tcp`、`/dev/udp` 文件重定向回到审核,包括引用过的路径及编号 fd 输入重定向;普通文件输入、fd 合并和 `/dev/null` 输出保留原有规则。
92
+ - **Git 的子命令名不能证明只读。** 裸 `git status`、工作区 diff、`git ls-files --modified` 及其他工作区内容检查继续审核,因为刷新和内容转换可能执行配置中的 clean/process filter。支持的对象/索引读取必须在原始参数中带 `--no-pager --no-optional-locks --no-lazy-fetch -c core.fsmonitor=false`;暂存区 diff 还需 `--no-ext-diff --no-textconv --ignore-submodules=all`。历史读取另需 `--no-show-signature` 和受支持的固定格式,例如 `--oneline`。插件不会补参数或重写命令。精确的 `git version`、`git --no-pager config --get user.name` 是独立的窄范围读取;传输 helper、编辑器、filter 和混合配置修改都不能走快速通道。普通 `sort` 可能写临时文件,因此只保留其检查模式。
93
+ - **独立确认通道。** 当前 DSH 0.2.0-rc.2 的审批和问答链允许自动应答器。人工确认改用插件已认证的浏览器 HTTP 入口和不可预测、单次使用的请求 ID。Fetch Metadata 与宿主的来源、认证检查用于防跨站请求,并不证明物理真人点击;已认证客户端、其传输层和同进程插件仍然可信。不向模型注册审批工具。
94
+ - **服务端掌握时限。** 请求发布后开始计时,用单调时钟判定到期,只接受第一个有效答案。重复、迟到和跨会话回答均失败。停用、修改设置、卸载或取消会使待审批失效;请求仅存内存,重启不会恢复。兼容直接浏览器请求与已认证的 DSH 桌面传输;不符合要求的请求类型仍会被拒绝。
95
+ - **其他插件的原生审批保持原生流程。** 没有 Safe Auto 自有执行上下文时,Blueprint 顺序审批、Classmates 模型审批会继续交给后续处理器。已有自有上下文中的不匹配、过期、已领取请求,以及无上下文却冒用 `bash`/`pwsh`/`write`/`edit` 的请求仍然关闭放行。
96
+ - **覆盖范围止于原生单次提权。** 只审核从 `workspace-write` 放宽的 `write`/`edit`/`bash`/`pwsh`。其他工具、MCP、远程执行、子代理调用和常驻权限变更都在自动范围之外。
97
+ - **证据会复核,执行并非原子操作。** 放行前复核证据路径、版本和 SHA-256 摘要,但审批与执行之间仍可能发生文件变化。同进程插件仍然可信,卸载会移除守卫;实际隔离仍依赖外部手段及受限的凭据与出站。
98
+ - **不作任何保证。** 没有 fail-open 选项,没有持久审计数据库,也不承诺绝对安全或节省比例。
120
99
 
121
- 运行 npm test、npm run build(JavaScript 语法检查,不是 TypeScript typecheck)、npm pack --dry-run 和全仓 pnpm check。测试包括路由/并发/失效/取消,以及真实 DSH LlmRuntime、ToolRuntime、ApprovalService 和受控适配器集成。CI 必须运行真实 DSH tests;缺依赖的离线本地环境可以明确跳过。
100
+ ## 验证
122
101
 
123
- 受控模型和 fixture 的 session/tool/policy 不是在线模型准确率、OS 沙箱或 authenticated Web/Headless 验收。剩余清单在 ADR-0002。日志区分 assessment、escalation 和实际 result,不记录原始命令/提示/Key,留存由宿主管理。
102
+ 在本包运行 `npm test`、`npm run build` 和 `npm pack --dry-run`,以及仓库级 `pnpm check`。测试覆盖路由、并发会话、原生流、上限、过期决定、模型服务移除、执行绑定,以及真实 Cordis/ToolRuntime/ApprovalService/LlmRuntime 契约。CI 要求安装 DSH 依赖;离线纯源码运行可跳过原生契约。如果 Windows 沙箱拦截测试运行器的子进程管道,用 `node --test --test-isolation=none test/<file>.test.js` 逐文件运行。
124
103
 
125
- PowerShell、PTC/MCP、远程、复杂 Shell、子代理模型提权和 read-only 到 workspace-write 仍未纳入自动放行。没有持久化预算/审计库、跨调用批准缓存、PI probe 或图形化模型选择器。不承诺绝对安全或固定节省比例。
104
+ 原生契约测试使用受控适配器和夹具会话/工具,证明的是运行时集成,不是真实模型准确率、OS 隔离或已认证的 Web/Headless 验收;这些仍在 [ADR-0002 的清单](docs/ADR-0002.md)中。日志分离评估、结果和最终状态,不含原始命令、提示词或密钥;宿主留存由运维管理。
126
105
 
127
106
  ## 许可证
128
107
 
129
- 原始实现使用 [SATA License 2.1](LICENSE),参考社区设计但未直接复制其代码。
108
+ 原创实现基于 [SATA License 2.1](LICENSE)。社区设计启发了本工作;未打包其代码。
package/cordis.patch.yml CHANGED
@@ -1,3 +1,5 @@
1
1
  - insert:
2
2
  - id: dsh-safe-auto
3
3
  name: '@klarkxy/dsh-safe-auto'
4
+ - id: dsh-safe-auto-ui
5
+ name: '@klarkxy/dsh-safe-auto/ui-host'
package/docs/ADR-0001.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ADR-0001: bounded preflight, not an escalation auto-answerer
2
2
 
3
- Status: historical RC1 decision, partially superseded by [ADR-0002](ADR-0002.md) in `0.1.0-rc.2`.
3
+ Status: historical RC1 decision, partially superseded by [ADR-0002](ADR-0002.md) in `0.1.0-rc.2`. **The preflight mechanism itself (ordinary-command review, workspace/shell candidates, shadow/smart/unattended modes) was removed in `0.2.0-rc.0`**; only the execution-bound approval review of ADR-0002 remains.
4
4
 
5
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
6
 
@@ -14,7 +14,7 @@ DSH contracts were inspected at `477b4f420553e8a52c2fbccc464d7561b239c443` (`0.1
14
14
  - [Public synchronous policy resolver](https://github.com/deepseek-ai/deepseek-harness/blob/477b4f420553e8a52c2fbccc464d7561b239c443/packages/sandbox/sandbox-policy/src/index.ts).
15
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
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.
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 own README still marks the full authenticated profile smoke test as outstanding. No community source was vendored, so there is no MIT-derived code to silently relicense.
18
18
 
19
19
  ## Decision
20
20
 
@@ -34,9 +34,9 @@ tool dispatch -> final result audit
34
34
 
35
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
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.
37
+ The 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
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.
39
+ The session authority reader uses existing `snapshotEvents()` in one isolated adapter function. DSH deprecates synchronous historical reads; do not propagate this dependency. Replace it with a host projection/read contract when that can preserve direct-user provenance without trusting model summaries.
40
40
 
41
41
  ## What the prototype does not guarantee
42
42
 
@@ -54,12 +54,12 @@ Automated tests: deterministic policies and protected paths; symlink/hardlink/ra
54
54
 
55
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
56
 
57
- Before promoting beyond candidate:
57
+ Before promoting beyond candidate status:
58
58
 
59
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
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
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
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.
63
+ 5. Run task and malicious-input corpora before making accuracy, prompt-injection resistance or token-saving claims. Test supported OS/provider versions individually.
64
64
 
65
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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # ADR-0002: native, execution-bound one-shot escalation
2
2
 
3
- Status: implemented in `0.1.0-rc.2`; automated contracts verified; live-profile acceptance pending.
3
+ Status: implemented in `0.1.0-rc.2`; automated contracts verified; live-profile acceptance pending. Since `0.2.0-rc.0` this execution-bound approval review is the plugin's only mechanism; the exact-rule candidate enrollment described below was removed along with preflight.
4
4
 
5
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
6
 
@@ -38,7 +38,7 @@ native tool consumes mode for this invocation only
38
38
  -> tools/result clears execution decision; next call remains confined
39
39
  ```
40
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.
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 as 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
42
 
43
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
44
 
@@ -62,7 +62,7 @@ Native `approval: ask` is required for the answerer to run, including plugin una
62
62
 
63
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
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.
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 the HTTP test; the new native contract ran in CI with the workspace's pinned DSH dependencies.
66
66
 
67
67
  ## Remaining deployment acceptance
68
68
 
package/docs/ADR-0003.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ADR-0003: conversation-following and independent reviewer routes
2
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.
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. **`0.2.0-rc.0` narrowed the routing surface to a single reviewer route** (`provider`/`model`/`reasoningEffort`, defaulting to the conversation): the deep stage, the HTTP transport and the fast/deep split described below were removed.
4
4
 
5
5
  ## Requirement
6
6
 
@@ -20,7 +20,7 @@ With endpoint/fastProvider/fastModel empty, resolve the requesting conversation'
20
20
 
21
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
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.
23
+ Only route identifiers are inherited. Reviewer calls use a new prompt and one identity-free input, and carry 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
24
 
25
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
26