@deepseek-ai/dsh-sandbox-policy 0.1.1-rc.2 → 0.1.2-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +109 -17
- package/README.zh.md +115 -23
- package/lib/index.js +27 -24
- package/lib/invariant.js +1 -1
- package/lib/types/index.d.ts +15 -2
- package/lib/types/session-mode.d.ts +4 -13
- package/package.json +17 -14
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/sandbox/sandbox-policy/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 2a6ef9be4b38e65ace8d0ae1586542f0872dfc69
|
|
6
|
+
README.zh.md: 87c6149b7590cb39e4265ca4b8964df6cf2e91ad
|
package/README.md
CHANGED
|
@@ -1,33 +1,110 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "The shared per-call sandbox policy resolver and current model context for users and maintainers composing, configuring, or debugging file-effect policy across enforcing capabilities."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-sandbox-policy
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`dsh-sandbox-policy` resolves the file-effect mode and workspace root for every confined capability call from one shared policy home, and tells the model the current policy before each request. A deployment sets a default mode and a fallback workspace root; a session can switch its own mode, and the switch survives restart because it lives in the session log. Every enforcing capability — bash, filesystem, terminal — reads the same resolved policy, so the mode a call runs under never depends on which family resolved it. The model sees one concise `sandbox:policy` contribution naming the mode and workspace, without a separate inventory of mounted capabilities.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
Mount this package in any composition where sandbox-enforcing capabilities run: it owns the deployment default and the per-session overrides those capabilities consume, and it contributes the current policy to the model's runtime-context snapshot.
|
|
29
|
+
|
|
30
|
+
### When to choose it
|
|
31
|
+
|
|
32
|
+
Choose it for every composition with confined capabilities (bash, filesystem, terminal) so one policy home keeps them from drifting into different modes or workspace roots. Skip it only when nothing enforces sandbox policy — with no consumers, the resolved policy has no effect.
|
|
33
|
+
|
|
34
|
+
### Minimal configuration
|
|
35
|
+
|
|
36
|
+
Load the package with a default mode; the fail-safe default is `read-only`, and a deployment that wants a workspace-writable agent opts into `workspace-write` explicitly.
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
- name: '@deepseek-ai/dsh-sandbox-policy'
|
|
40
|
+
config:
|
|
41
|
+
mode: workspace-write
|
|
42
|
+
workspaceRoot: /absolute/path/to/workspace
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
| Field | Default | Meaning |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `mode` | `read-only` | The deployment default mode a session starts from, validated at load |
|
|
48
|
+
| `workspaceRoot` | `process.cwd()` | The fallback root `workspace-write` may write under for agentless calls or sessions without a cwd; normal agent calls use the session's immutable cwd instead |
|
|
49
|
+
|
|
50
|
+
The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-sandbox-policy) is the exhaustive source for every accepted field and its JSDoc.
|
|
51
|
+
|
|
52
|
+
### Switching a session's mode
|
|
53
|
+
|
|
54
|
+
A session's mode can be switched at runtime through a UI policy control or an explicit switch; the switch is recorded in the session log and takes effect on the session's next confined call. The switch survives restart through replay, and each session keeps its own mode — two sessions never see each other's state. A switched session keeps its immutable workspace cwd as the writable boundary.
|
|
55
|
+
|
|
56
|
+
### Failures and recovery
|
|
6
57
|
|
|
7
|
-
|
|
58
|
+
An invalid configured mode is rejected when the plugin loads, so a typo fails loud instead of silently changing policy. A session without a cwd, and agentless calls, fall back to the configured workspace root; a call with an approved explicit mode uses that mode for exactly that call.
|
|
8
59
|
|
|
9
|
-
|
|
60
|
+
-----
|
|
10
61
|
|
|
11
|
-
|
|
62
|
+
<a id="understand-the-implementation"></a>
|
|
63
|
+
## Understand the implementation
|
|
12
64
|
|
|
13
|
-
|
|
14
|
-
|
|
65
|
+
<details>
|
|
66
|
+
<summary>Implementation internals — click to expand</summary>
|
|
15
67
|
|
|
16
|
-
|
|
68
|
+
This section explains policy resolution, the per-session store, and the model-visible contribution; the observable behavior is fully covered in [Use this package](#use-this-package).
|
|
17
69
|
|
|
18
|
-
|
|
19
|
-
- `ctx.sandboxPolicy.defaultMode` / `ctx.sandboxPolicy.workspaceRoot` — the deployment default and fallback root used by `resolve()`.
|
|
20
|
-
- `sandbox:policy` — a request-time cache-safe context contribution derived directly from `resolve({ session })`. It states the mode's capability-neutral file-effect contract and the canonical session workspace under `workspace-write`; tool owners retain operation-specific denial and escalation guidance.
|
|
21
|
-
- `effectiveSandboxMode(events)` — the pure fold of a session's `sandbox/mode` events (the last switch wins, or `undefined`), used inside `resolve()`.
|
|
22
|
-
- `setSandboxMode(session, mode)` — THE write path for a per-session override: appends exactly one `sandbox/mode` event. The switch IS its event; nothing mutates the mode out of band.
|
|
23
|
-
- `SANDBOX_MODES` — every mode, for option advertisement and runtime validation.
|
|
70
|
+
### Resolution precedence
|
|
24
71
|
|
|
25
|
-
|
|
72
|
+
`resolve({ session, mode })` returns one complete per-call policy: an approved explicit mode outranks the session's last `sandbox/mode` event, which outranks the deployment default. The session's immutable `cwd` is canonicalized with filesystem semantics before becoming the workspace root, so `symlink/..` agrees with process working-directory resolution; otherwise the configured fallback applies.
|
|
26
73
|
|
|
27
|
-
|
|
74
|
+
### The per-session store
|
|
28
75
|
|
|
29
|
-
A runtime switch is one log-only `sandbox/mode` event on the session it applies to. `effective = explicit grant ?? fold(events) ?? deployment default`, so an override survives restart by replay and two sessions never see each other's state. Workspace identity
|
|
76
|
+
A runtime switch is one log-only `sandbox/mode` event on the session it applies to — the switch IS its event, and nothing mutates mode state out of band. `effective = explicit grant ?? fold(events) ?? deployment default`, so an override survives restart by replay and two sessions never see each other's state. Workspace identity needs no event: the immutable `SessionHeader.cwd` recorded at creation is the root for every call in that session. The event stays log-only; before each request, the owner contributes the current fact to the full runtime-context snapshot, and the agent loop logs that snapshot as a sourced `user/message`.
|
|
30
77
|
|
|
78
|
+
### Model-visible text
|
|
79
|
+
|
|
80
|
+
The `sandbox:policy` contribution states the mode's capability-neutral file-effect contract and the canonical session workspace under `workspace-write`. It does not enumerate mounted capabilities; tool plugins retain operation-specific denial and escalation guidance, approval policy contributes separately to the same snapshot, and plan guidance remains `dsh-plan-mode`'s system section. The optional `./invariant` companion rejects a forged durable `sandbox/mode` event whose value falls outside the closed mode vocabulary.
|
|
81
|
+
|
|
82
|
+
### Source map
|
|
83
|
+
|
|
84
|
+
| File | Role |
|
|
85
|
+
|---|---|
|
|
86
|
+
| [`src/index.ts`](src/index.ts) | Plugin entry: `SandboxPolicyService`, `Config` schema, policy resolution and context contribution |
|
|
87
|
+
| [`src/session-mode.ts`](src/session-mode.ts) | The `sandbox/mode` event, its fold, and the write path |
|
|
88
|
+
| [`src/invariant.ts`](src/invariant.ts) | Invariant companion: rejects `sandbox/mode` values outside the closed vocabulary |
|
|
89
|
+
|
|
90
|
+
</details>
|
|
91
|
+
|
|
92
|
+
-----
|
|
93
|
+
|
|
94
|
+
<a id="further-exploration"></a>
|
|
95
|
+
## Further Exploration
|
|
96
|
+
|
|
97
|
+
Start with the subsystem reference for the shared vocabulary, then the seam contract, the cross-family decision, and the model-context decision.
|
|
98
|
+
|
|
99
|
+
- [Process sandbox subsystem](../../../docs/subsystems/sandbox.md) — modes, per-call policy, and enforcement semantics.
|
|
100
|
+
- [Sandbox seam package](../sandbox/README.md) — the confinement contract every enforcing capability implements.
|
|
101
|
+
- [Cross-family file sandbox decision](../../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.md) — why one shared policy home exists.
|
|
102
|
+
- [Current sandbox policy context decision](../../../.agents/notes/implemented/feature/2026-07-30-current-sandbox-policy-context.md) — how the policy reaches the model before each request.
|
|
103
|
+
- [Capability-neutral policy context decision](../../../.agents/notes/implemented/simplification/2026-07-31-capability-neutral-sandbox-policy-context.md) — why the contribution names no mounted capabilities.
|
|
104
|
+
|
|
105
|
+
-----
|
|
106
|
+
|
|
107
|
+
<a id="model-experience"></a>
|
|
31
108
|
## Model Experience
|
|
32
109
|
|
|
33
110
|
### Current file sandbox policy
|
|
@@ -64,6 +141,21 @@ The stable system prompt remains byte-identical across mode changes. A changed f
|
|
|
64
141
|
|
|
65
142
|
## Known Limitations and Deferred Work
|
|
66
143
|
|
|
144
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
These limits define the policy surface this package provides. They are current package constraints, not a general sandbox comparison or a task backlog.
|
|
148
|
+
|
|
67
149
|
- **One primary workspace root per session** — policy resolves `SessionHeader.cwd`; extra writable roots are not part of `SandboxExecutionPolicy`.
|
|
68
150
|
- **File-effect modes only** — `SandboxMode` governs file effects; network and process policy are outside its vocabulary, so no knob here restricts them.
|
|
69
151
|
- **Temporary areas are deliberately summarized** — enforcing backends grant different platform temporary areas, which are selected after policy resolution and therefore cannot be enumerated truthfully in the current context.
|
|
152
|
+
|
|
153
|
+
<a id="dev-note"></a>
|
|
154
|
+
### Dev Note
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
158
|
+
|
|
159
|
+
None.
|
|
160
|
+
|
|
161
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,40 +1,117 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
description: "面向组合、配置或排查跨执行能力文件效果策略的用户与维护者的共享逐调用沙箱策略解析器与当前模型上下文。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-sandbox-policy
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`dsh-sandbox-policy` 为每次受限能力调用从统一的策略归属位置解析文件效果模式与工作区根目录,并在每次请求前把当前策略告知模型。部署方设置默认模式与回退工作区根目录;会话可以切换自己的模式,切换因存在于会话日志中而跨重启保留。每个强制执行能力——bash、文件系统、终端——读取同一份解析出的策略,因此调用运行的模式绝不取决于由哪个家族解析。模型会看到一条简洁的 `sandbox:policy` 贡献,指明模式与工作区,而不会收到一份已挂载能力的清单。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
在任何运行沙箱强制执行能力的组合中挂载此包:它拥有这些能力消费的部署默认值与逐会话覆盖,并把当前策略贡献给模型的运行时上下文快照。
|
|
29
|
+
|
|
30
|
+
### 何时选择
|
|
31
|
+
|
|
32
|
+
为每个带受限能力(bash、文件系统、终端)的组合选择它,让单一策略归属位置防止它们漂移到不同的模式或工作区根目录。只有没有任何沙箱策略强制执行时才跳过它——没有消费方时,解析出的策略不起作用。
|
|
33
|
+
|
|
34
|
+
### 最小配置
|
|
35
|
+
|
|
36
|
+
用默认模式加载本包;故障安全默认值是 `read-only`,想要可写工作区 agent 的部署需要显式选择 `workspace-write`。
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
- name: '@deepseek-ai/dsh-sandbox-policy'
|
|
40
|
+
config:
|
|
41
|
+
mode: workspace-write
|
|
42
|
+
workspaceRoot: /absolute/path/to/workspace
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
| 字段 | 默认值 | 含义 |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| `mode` | `read-only` | 会话起始的部署默认模式,加载时验证 |
|
|
48
|
+
| `workspaceRoot` | `process.cwd()` | 无 agent(智能体)调用或没有 cwd 的会话在 `workspace-write` 下可写入的回退根目录;普通 agent 调用改用会话的不可变 cwd |
|
|
49
|
+
|
|
50
|
+
生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-sandbox-policy)是每个受支持字段及其 JSDoc 的穷尽式真源。
|
|
51
|
+
|
|
52
|
+
### 切换会话模式
|
|
53
|
+
|
|
54
|
+
会话的模式可以在运行时通过 UI 策略控件或显式切换来更改;切换记录在会话日志中,并在该会话的下一次受限调用时生效。切换通过回放跨重启保留,每个会话保持自己的模式——两个会话绝不会看到彼此状态。切换后的会话继续以不可变的工作区 cwd 作为写入边界。
|
|
55
|
+
|
|
56
|
+
### 失败与恢复
|
|
6
57
|
|
|
7
|
-
|
|
58
|
+
无效的配置模式会在插件加载时被拒绝,因此拼写错误会大声失败,而不是静默改变策略。没有 cwd 的会话与无 agent 调用回退到配置的工作区根目录;带已批准显式模式的调用只在该次调用中使用该模式。
|
|
8
59
|
|
|
9
|
-
|
|
60
|
+
-----
|
|
10
61
|
|
|
11
|
-
|
|
62
|
+
<a id="understand-the-implementation"></a>
|
|
63
|
+
## 理解实现
|
|
12
64
|
|
|
13
|
-
|
|
14
|
-
|
|
65
|
+
<details>
|
|
66
|
+
<summary>实现细节——点击展开</summary>
|
|
15
67
|
|
|
16
|
-
|
|
68
|
+
本节解释策略解析、逐会话存储与模型可见贡献;可观察行为已在[使用本包](#use-this-package)中完整说明。
|
|
17
69
|
|
|
18
|
-
|
|
19
|
-
- `ctx.sandboxPolicy.defaultMode`/`ctx.sandboxPolicy.workspaceRoot`:`resolve()` 使用的部署默认值与回退根目录。
|
|
20
|
-
- `sandbox:policy`:直接派生自 `resolve({ session })` 的请求时缓存安全上下文贡献。它说明该模式中与具体能力无关的文件操作约定,以及 `workspace-write` 下规范化的会话工作区;工具归属方仍负责特定于操作的拒绝与升权引导。
|
|
21
|
-
- `effectiveSandboxMode(events)`:会话 `sandbox/mode` 事件的纯 fold(最后一次切换胜出,没有则为 `undefined`),在 `resolve()` 内使用。
|
|
22
|
-
- `setSandboxMode(session, mode)`:逐会话覆盖的唯一写入路径:恰好追加一条 `sandbox/mode` 事件。切换本身就是事件;不会在带外修改模式。
|
|
23
|
-
- `SANDBOX_MODES`:所有模式,用于选项展示与运行时验证。
|
|
70
|
+
### 解析优先级
|
|
24
71
|
|
|
25
|
-
|
|
72
|
+
`resolve({ session, mode })` 返回一份完整的逐调用策略:已批准的显式模式优先于会话最后一条 `sandbox/mode` 事件,后者又优先于部署默认值。会话的不可变 `cwd` 先按文件系统语义规范化,再成为工作区根目录,因此 `symlink/..` 与进程工作目录解析一致;否则使用配置的回退值。
|
|
26
73
|
|
|
27
|
-
|
|
74
|
+
### 逐会话存储
|
|
28
75
|
|
|
29
|
-
|
|
76
|
+
运行时切换是在对应会话日志中追加的一条仅记录 `sandbox/mode` 事件——切换本身就是事件,任何机制都不会在带外修改模式状态。`effective = explicit grant ?? fold(events) ?? deployment default`,因此覆盖通过回放跨重启保留,两个会话也绝不会看到彼此状态。工作区标识无需事件:创建时记录的不可变 `SessionHeader.cwd` 是该会话每次调用使用的根。事件仍只进入日志;在每次请求前,归属方会把当前事实贡献给完整运行时上下文快照,agent loop(智能体循环)将该快照记录为一条带来源的 `user/message`。
|
|
30
77
|
|
|
78
|
+
### 模型可见文本
|
|
79
|
+
|
|
80
|
+
`sandbox:policy` 贡献说明模式的与具体能力无关的文件操作约定,以及 `workspace-write` 下规范化的会话工作区。它不枚举已挂载能力;工具插件保留特定于操作的拒绝与升权引导,批准策略单独贡献给同一份快照,计划引导仍由 `dsh-plan-mode` 的系统段落管理。可选的 `./invariant` 配套组件会拒绝值超出封闭模式词汇的伪造持久 `sandbox/mode` 事件。
|
|
81
|
+
|
|
82
|
+
### 源码地图
|
|
83
|
+
|
|
84
|
+
| 文件 | 职责 |
|
|
85
|
+
|---|---|
|
|
86
|
+
| [`src/index.ts`](src/index.ts) | 插件入口:`SandboxPolicyService`、`Config` schema、策略解析与上下文贡献 |
|
|
87
|
+
| [`src/session-mode.ts`](src/session-mode.ts) | `sandbox/mode` 事件、其 fold 与写入路径 |
|
|
88
|
+
| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件:拒绝超出封闭词汇的 `sandbox/mode` 值 |
|
|
89
|
+
|
|
90
|
+
</details>
|
|
91
|
+
|
|
92
|
+
-----
|
|
93
|
+
|
|
94
|
+
<a id="further-exploration"></a>
|
|
95
|
+
## 进一步探索
|
|
96
|
+
|
|
97
|
+
先从子系统参考文档了解共享词汇,再看 seam 约定、跨家族决策与模型上下文决策。
|
|
98
|
+
|
|
99
|
+
- [进程沙箱子系统](../../../docs/subsystems/sandbox.zh.md)——模式、逐调用策略与强制执行语义。
|
|
100
|
+
- [沙箱 seam 包](../sandbox/README.zh.md)——每个强制执行能力实现的隔离约定。
|
|
101
|
+
- [跨家族文件沙箱决策](../../../.agents/notes/implemented/feature/2026-07-14-cross-family-fs-sandbox.zh.md)——为何存在统一的共享策略归属位置。
|
|
102
|
+
- [当前沙箱策略上下文决策](../../../.agents/notes/implemented/feature/2026-07-30-current-sandbox-policy-context.zh.md)——策略如何在每次请求前到达模型。
|
|
103
|
+
- [与具体能力无关的策略上下文决策](../../../.agents/notes/implemented/simplification/2026-07-31-capability-neutral-sandbox-policy-context.zh.md)——为何贡献不点名已挂载能力。
|
|
104
|
+
|
|
105
|
+
-----
|
|
106
|
+
|
|
107
|
+
<a id="model-experience"></a>
|
|
31
108
|
## 模型体验
|
|
32
109
|
|
|
33
110
|
### 当前文件沙箱策略
|
|
34
111
|
|
|
35
|
-
####
|
|
112
|
+
#### 模型看到什么
|
|
36
113
|
|
|
37
|
-
每个 agent 会话的当前运行时上下文快照中都有一项 `sandbox:policy`
|
|
114
|
+
每个 agent 会话的当前运行时上下文快照中都有一项 `sandbox:policy` 贡献。它不枚举已挂载的能力。工具插件继续负责操作与升权引导,批准策略单独贡献给同一份快照,计划引导仍由 `dsh-plan-mode` 的系统段落管理。
|
|
38
115
|
|
|
39
116
|
##### 只读
|
|
40
117
|
|
|
@@ -62,8 +139,23 @@ Current DSH file policy: danger-full-access. The DSH file sandbox does not restr
|
|
|
62
139
|
|
|
63
140
|
模式切换时,稳定的系统提示词仍逐字节相同。变化后的完整上下文快照会追加到保留的历史之后,从而保留此前已缓存的前缀;后续未变化的请求会复用该保留快照。
|
|
64
141
|
|
|
65
|
-
##
|
|
142
|
+
## 已知限制与延期工作
|
|
143
|
+
|
|
144
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
这些限制定义了本包提供的策略表面。它们是当前包约束,不是通用沙箱对比或任务积压。
|
|
148
|
+
|
|
149
|
+
- **每个会话只有一个主要工作区根目录**——策略解析 `SessionHeader.cwd`;额外可写根目录不属于 `SandboxExecutionPolicy`。
|
|
150
|
+
- **仅限文件操作模式**——`SandboxMode` 管控文件操作;网络和进程策略不在其词汇中,因此这里没有限制它们的旋钮。
|
|
151
|
+
- **有意概述临时区域**——强制执行后端会授予不同的平台临时区域,这些区域在策略解析后才会选定,因此无法在当前上下文中如实枚举。
|
|
152
|
+
|
|
153
|
+
<a id="dev-note"></a>
|
|
154
|
+
### 开发备注
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
158
|
+
|
|
159
|
+
无。
|
|
66
160
|
|
|
67
|
-
|
|
68
|
-
- **仅限文件操作模式**:`SandboxMode` 管控文件操作;网络和进程策略不在其词汇中,因此这里没有限制它们的旋钮。
|
|
69
|
-
- **有意概述临时区域**:强制执行后端会授予不同的平台临时区域,这些区域在策略解析后才会选定,因此无法在当前上下文中如实枚举。
|
|
161
|
+
</details>
|
package/lib/index.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { Service } from "@deepseek-ai/cordis";
|
|
3
|
-
import z from "
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import z$1 from "@deepseek-ai/schemastery";
|
|
4
5
|
import { canonicalPath } from "@deepseek-ai/dsh-sandbox";
|
|
5
6
|
//#region lib/types/session-mode.js
|
|
6
7
|
/**
|
|
7
8
|
* Per-session sandbox-mode override: the session log as the store. A runtime
|
|
8
9
|
* switch (a UI policy control or test scenario) is recorded as one
|
|
9
10
|
* `sandbox/mode` event on the session it applies to;
|
|
10
|
-
* `effective =
|
|
11
|
+
* `effective = projection state ?? the deployment default`, so an override
|
|
11
12
|
* survives restart by replay, two sessions can never see each other's state,
|
|
12
13
|
* and there is no external config store. The event is log-only (the
|
|
13
14
|
* `approval/*` precedent): the policy owner projects the fold into each model
|
|
@@ -29,24 +30,10 @@ const SANDBOX_MODES = [
|
|
|
29
30
|
"danger-full-access"
|
|
30
31
|
];
|
|
31
32
|
/**
|
|
32
|
-
* The session's sandbox-mode override: the last `sandbox/mode` event in the
|
|
33
|
-
* log, or undefined when the session never switched (callers apply the
|
|
34
|
-
* deployment default). The pure fold — resume needs no catch-up machinery
|
|
35
|
-
* because replaying the log IS the state.
|
|
36
|
-
* @param events - session events in log order (other event types are skipped).
|
|
37
|
-
* @returns the mode of the last switch event, or undefined without one.
|
|
38
|
-
*/
|
|
39
|
-
function effectiveSandboxMode(events) {
|
|
40
|
-
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
41
|
-
const event = events[index];
|
|
42
|
-
if (event.type === "sandbox/mode") return event.data.mode;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
33
|
* THE write path for a session's sandbox-mode override: appends exactly one
|
|
47
34
|
* `sandbox/mode` event — the switch IS its event; nothing mutates mode state
|
|
48
35
|
* out of band. Takes effect on the session's next confined call (bash or fs)
|
|
49
|
-
* —
|
|
36
|
+
* — consumers read the shared projection state.
|
|
50
37
|
* @param session - the session the override belongs to.
|
|
51
38
|
* @param mode - the mode every subsequent confined call in this session runs
|
|
52
39
|
* under (until the next switch).
|
|
@@ -60,7 +47,9 @@ function setSandboxMode(session, mode) {
|
|
|
60
47
|
* The sandbox POLICY home (`ctx.sandboxPolicy`): the single owner of the
|
|
61
48
|
* deployment's sandbox fallbacks plus per-session resolution: the file-effect
|
|
62
49
|
* {@link SandboxMode}, the `workspace-write` root, and the override kit (the
|
|
63
|
-
* `sandbox/mode` event, its fold, and its write path
|
|
50
|
+
* `sandbox/mode` event, its fold, and its write path; the fold is the
|
|
51
|
+
* `sandboxMode` session-projection unit registered here, while the event and
|
|
52
|
+
* write path come from `./session-mode.ts`).
|
|
64
53
|
* Before each agent request, the owner also contributes the resolved policy to
|
|
65
54
|
* the cache-safe runtime-context snapshot. The agent loop logs that snapshot as
|
|
66
55
|
* model history, so replay reconstructs the same mode and root the enforcing
|
|
@@ -92,6 +81,12 @@ function renderPolicyContext(policy) {
|
|
|
92
81
|
}
|
|
93
82
|
}
|
|
94
83
|
}
|
|
84
|
+
/** The sandbox-mode projection's state schema (state equals the public shape). */
|
|
85
|
+
const sandboxModeStateSchema = z.union([
|
|
86
|
+
z.literal("read-only"),
|
|
87
|
+
z.literal("workspace-write"),
|
|
88
|
+
z.literal("danger-full-access")
|
|
89
|
+
]).nullable();
|
|
95
90
|
/**
|
|
96
91
|
* The sandbox-policy service (`ctx.sandboxPolicy`). Owns the deployment
|
|
97
92
|
* default mode, fallback workspace root, and current request-time policy
|
|
@@ -99,14 +94,15 @@ function renderPolicyContext(policy) {
|
|
|
99
94
|
* mode log and immutable cwd travel together to every enforcing capability.
|
|
100
95
|
*/
|
|
101
96
|
var SandboxPolicyService = class extends Service {
|
|
102
|
-
static Config = z.object({
|
|
103
|
-
mode: z.union([
|
|
97
|
+
static Config = z$1.object({
|
|
98
|
+
mode: z$1.union([
|
|
104
99
|
"read-only",
|
|
105
100
|
"workspace-write",
|
|
106
101
|
"danger-full-access"
|
|
107
102
|
]).default("read-only"),
|
|
108
|
-
workspaceRoot: z.string()
|
|
103
|
+
workspaceRoot: z$1.string()
|
|
109
104
|
});
|
|
105
|
+
static inject = ["sessionProjections"];
|
|
110
106
|
/** The deployment default mode — the fallback beneath a session override. */
|
|
111
107
|
defaultMode;
|
|
112
108
|
/** The absolute `workspace-write` fallback root for calls without a session cwd. */
|
|
@@ -115,10 +111,17 @@ var SandboxPolicyService = class extends Service {
|
|
|
115
111
|
super(ctx, "sandboxPolicy");
|
|
116
112
|
this.defaultMode = config.mode;
|
|
117
113
|
this.workspaceRoot = resolveWorkspaceRoot(config.workspaceRoot ?? process.cwd());
|
|
114
|
+
ctx.sessionProjections.register({
|
|
115
|
+
key: "sandboxMode",
|
|
116
|
+
stateVersion: 1,
|
|
117
|
+
stateSchema: sandboxModeStateSchema,
|
|
118
|
+
init: () => null,
|
|
119
|
+
apply: (state, event) => event.type === "sandbox/mode" ? event.data.mode : state
|
|
120
|
+
});
|
|
118
121
|
ctx.inject(["systemPrompt"], (scope) => {
|
|
119
122
|
scope.systemPrompt.context({
|
|
120
123
|
name: "sandbox:policy",
|
|
121
|
-
order:
|
|
124
|
+
order: scope.systemPrompt.getContextOrder("SANDBOX_POLICY"),
|
|
122
125
|
text: (context) => {
|
|
123
126
|
const session = context.agent?.session;
|
|
124
127
|
return session === void 0 ? "" : renderPolicyContext(this.resolve({ session }));
|
|
@@ -149,8 +152,8 @@ var SandboxPolicyService = class extends Service {
|
|
|
149
152
|
* @returns the last logged mode, or `undefined` without one.
|
|
150
153
|
*/
|
|
151
154
|
overrideOf(session) {
|
|
152
|
-
return
|
|
155
|
+
return this.ctx.sessionProjections.stateOf(session, "sandboxMode") ?? void 0;
|
|
153
156
|
}
|
|
154
157
|
};
|
|
155
158
|
//#endregion
|
|
156
|
-
export { SANDBOX_MODES, SandboxPolicyService, SandboxPolicyService as default,
|
|
159
|
+
export { SANDBOX_MODES, SandboxPolicyService, SandboxPolicyService as default, setSandboxMode };
|
package/lib/invariant.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Per-session sandbox-mode override: the session log as the store. A runtime
|
|
4
4
|
* switch (a UI policy control or test scenario) is recorded as one
|
|
5
5
|
* `sandbox/mode` event on the session it applies to;
|
|
6
|
-
* `effective =
|
|
6
|
+
* `effective = projection state ?? the deployment default`, so an override
|
|
7
7
|
* survives restart by replay, two sessions can never see each other's state,
|
|
8
8
|
* and there is no external config store. The event is log-only (the
|
|
9
9
|
* `approval/*` precedent): the policy owner projects the fold into each model
|
package/lib/types/index.d.ts
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* The sandbox POLICY home (`ctx.sandboxPolicy`): the single owner of the
|
|
3
3
|
* deployment's sandbox fallbacks plus per-session resolution: the file-effect
|
|
4
4
|
* {@link SandboxMode}, the `workspace-write` root, and the override kit (the
|
|
5
|
-
* `sandbox/mode` event, its fold, and its write path
|
|
5
|
+
* `sandbox/mode` event, its fold, and its write path; the fold is the
|
|
6
|
+
* `sandboxMode` session-projection unit registered here, while the event and
|
|
7
|
+
* write path come from `./session-mode.ts`).
|
|
6
8
|
* Before each agent request, the owner also contributes the resolved policy to
|
|
7
9
|
* the cache-safe runtime-context snapshot. The agent loop logs that snapshot as
|
|
8
10
|
* model history, so replay reconstructs the same mode and root the enforcing
|
|
@@ -18,10 +20,11 @@
|
|
|
18
20
|
* @module @deepseek-ai/dsh-sandbox-policy
|
|
19
21
|
*/
|
|
20
22
|
import { Context, Service } from '@deepseek-ai/cordis';
|
|
23
|
+
import { z as zod } from 'zod';
|
|
21
24
|
import z from '@deepseek-ai/schemastery';
|
|
22
25
|
import { type SandboxExecutionPolicy, type SandboxMode } from '@deepseek-ai/dsh-sandbox';
|
|
23
26
|
import type { Session } from '@deepseek-ai/dsh-session';
|
|
24
|
-
export { SANDBOX_MODES,
|
|
27
|
+
export { SANDBOX_MODES, setSandboxMode } from './session-mode.ts';
|
|
25
28
|
declare module '@deepseek-ai/cordis' {
|
|
26
29
|
interface Context {
|
|
27
30
|
sandboxPolicy: SandboxPolicyService;
|
|
@@ -50,6 +53,15 @@ export interface SandboxPolicyRequest {
|
|
|
50
53
|
/** Explicit approved mode override, which outranks session policy. */
|
|
51
54
|
mode?: SandboxMode;
|
|
52
55
|
}
|
|
56
|
+
/** The sandbox-mode projection's state schema (state equals the public shape). */
|
|
57
|
+
declare const sandboxModeStateSchema: zod.ZodNullable<zod.ZodUnion<readonly [zod.ZodLiteral<"read-only">, zod.ZodLiteral<"workspace-write">, zod.ZodLiteral<"danger-full-access">]>>;
|
|
58
|
+
type SandboxModeState = zod.infer<typeof sandboxModeStateSchema>;
|
|
59
|
+
declare module '@deepseek-ai/dsh-session-projection/types' {
|
|
60
|
+
interface SessionProjectionStateMap {
|
|
61
|
+
/** Last logged sandbox-mode override, or null before one (deployment default applies at resolve time). */
|
|
62
|
+
sandboxMode: SandboxModeState;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
53
65
|
/**
|
|
54
66
|
* The sandbox-policy service (`ctx.sandboxPolicy`). Owns the deployment
|
|
55
67
|
* default mode, fallback workspace root, and current request-time policy
|
|
@@ -58,6 +70,7 @@ export interface SandboxPolicyRequest {
|
|
|
58
70
|
*/
|
|
59
71
|
export declare class SandboxPolicyService extends Service {
|
|
60
72
|
static Config: z<Config>;
|
|
73
|
+
static inject: string[];
|
|
61
74
|
/** The deployment default mode — the fallback beneath a session override. */
|
|
62
75
|
readonly defaultMode: SandboxMode;
|
|
63
76
|
/** The absolute `workspace-write` fallback root for calls without a session cwd. */
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Per-session sandbox-mode override: the session log as the store. A runtime
|
|
3
3
|
* switch (a UI policy control or test scenario) is recorded as one
|
|
4
4
|
* `sandbox/mode` event on the session it applies to;
|
|
5
|
-
* `effective =
|
|
5
|
+
* `effective = projection state ?? the deployment default`, so an override
|
|
6
6
|
* survives restart by replay, two sessions can never see each other's state,
|
|
7
7
|
* and there is no external config store. The event is log-only (the
|
|
8
8
|
* `approval/*` precedent): the policy owner projects the fold into each model
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*
|
|
18
18
|
* @module dsh-sandbox-policy/session-mode
|
|
19
19
|
*/
|
|
20
|
-
import type { Session
|
|
20
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
21
21
|
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox';
|
|
22
22
|
declare module '@deepseek-ai/dsh-session/types' {
|
|
23
23
|
interface SessionEventMap {
|
|
@@ -25,7 +25,7 @@ declare module '@deepseek-ai/dsh-session/types' {
|
|
|
25
25
|
* The session's sandbox mode was switched — log-only (like `approval/*`;
|
|
26
26
|
* NOT a surface event, carries no `surfaceOp`): durable and replayable,
|
|
27
27
|
* never in the model transcript. The LAST such event is the session's
|
|
28
|
-
* override (
|
|
28
|
+
* override (folded by the sandboxMode projection unit). `source: 'delegation'` marks
|
|
29
29
|
* an override seeded into a child; an absent source is a runtime switch.
|
|
30
30
|
*/
|
|
31
31
|
'sandbox/mode': {
|
|
@@ -37,20 +37,11 @@ declare module '@deepseek-ai/dsh-session/types' {
|
|
|
37
37
|
}
|
|
38
38
|
/** Every {@link SandboxMode}, for option advertisement and runtime validation of untrusted mode strings. */
|
|
39
39
|
export declare const SANDBOX_MODES: readonly SandboxMode[];
|
|
40
|
-
/**
|
|
41
|
-
* The session's sandbox-mode override: the last `sandbox/mode` event in the
|
|
42
|
-
* log, or undefined when the session never switched (callers apply the
|
|
43
|
-
* deployment default). The pure fold — resume needs no catch-up machinery
|
|
44
|
-
* because replaying the log IS the state.
|
|
45
|
-
* @param events - session events in log order (other event types are skipped).
|
|
46
|
-
* @returns the mode of the last switch event, or undefined without one.
|
|
47
|
-
*/
|
|
48
|
-
export declare function effectiveSandboxMode(events: readonly SessionEvent[]): SandboxMode | undefined;
|
|
49
40
|
/**
|
|
50
41
|
* THE write path for a session's sandbox-mode override: appends exactly one
|
|
51
42
|
* `sandbox/mode` event — the switch IS its event; nothing mutates mode state
|
|
52
43
|
* out of band. Takes effect on the session's next confined call (bash or fs)
|
|
53
|
-
* —
|
|
44
|
+
* — consumers read the shared projection state.
|
|
54
45
|
* @param session - the session the override belongs to.
|
|
55
46
|
* @param mode - the mode every subsequent confined call in this session runs
|
|
56
47
|
* under (until the next switch).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-sandbox-policy",
|
|
3
3
|
"description": "Per-call sandbox policy resolver and current model context: deployment fallbacks plus each session's mode and workspace root, shared by every enforcing capability family",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -32,22 +32,25 @@
|
|
|
32
32
|
],
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
36
|
-
"@deepseek-ai/dsh-
|
|
37
|
-
"@deepseek-ai/dsh-
|
|
38
|
-
"@deepseek-ai/dsh-
|
|
39
|
-
"@deepseek-ai/
|
|
40
|
-
"@deepseek-ai/
|
|
35
|
+
"@deepseek-ai/dsh-agent": "^0.1.2-alpha.2",
|
|
36
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
|
|
37
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.2",
|
|
38
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
39
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.2",
|
|
40
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
41
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.2"
|
|
41
42
|
},
|
|
42
43
|
"dependencies": {
|
|
43
|
-
"
|
|
44
|
+
"zod": "^4.4.3",
|
|
45
|
+
"@deepseek-ai/schemastery": "^3.18.2"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
46
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
47
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
48
|
-
"@deepseek-ai/dsh-sandbox": "^0.1.
|
|
49
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
50
|
-
"@deepseek-ai/dsh-system-prompt": "^0.1.
|
|
51
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
48
|
+
"@deepseek-ai/dsh-agent": "^0.1.2-alpha.2",
|
|
49
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.2-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.2",
|
|
53
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
54
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.2"
|
|
52
55
|
}
|
|
53
56
|
}
|