dsh-gitbash-shell 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +61 -39
- package/README_EN.md +59 -35
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/compositions.js +17 -3
- package/src/index.js +57 -5
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
> 倒序排列,新版本条目在最上面。条目格式:`## vX.Y.Z — YYYY-MM-DD` + 类型(feat / fix / docs / chore)+ 要点 + 相关链接。
|
|
4
4
|
> 纪律见 AGENTS.md「变更记录纪律」:发版前先更新本文件并随版本提交;事故复盘、复现与真机验证记录也记在这里。
|
|
5
5
|
|
|
6
|
+
## v0.34.0 — 2026-10-04
|
|
7
|
+
|
|
8
|
+
**类型**:feat(跟随 dsh 0.2.1:宿主可用时三个全工具变体自动纳入官方时间上下文与提醒工具)
|
|
9
|
+
|
|
10
|
+
- **背景**:dsh 0.2.1 给每个全工具官方 preset(standard/ptc/cordis)新增两行——`time-context`(@deepseek-ai/dsh-time-context,请求时钟上下文)与 `tool-schedule`(@deepseek-ai/dsh-tool-schedule,提醒工具 schedule_create/delete/list/update),并给 subagent / subagent_fork 的 config 加 `toolFilter.deny` 四个 schedule_* 工具(官方「提醒工具按模式提供:极简模式和子代理不可用」);官方 minimal 无变化(0.1.7-rc.2 与 0.2.1 的 minimal.patch.yml sha256 逐字节一致)。
|
|
11
|
+
- **同步方式(探测式,不抬宿主下限)**:preset 行的包缺失会拒绝**整棵挂载**(AGENTS 不变量 2),所以新行不能无条件加——`src/index.js` 新增 `probeHostExtras(ctx)`:从 **`ctx.baseUrl`**(行挂载的同一解析基准,`prepareProfileEntries`)逐包 `require.resolve` 探测,可用才加行 + 加 toolFilter;探测一次每 boot,peer 翻转触发的 reconcile 重注册复用同一结果;**任何解析异常都降级为不可用,绝不 reject boot**。`minimalPluginsFor` 不动(minimal 无新行)。dsh ≤ 0.2.0 宿主行为逐字节不变(0.1.7 镜像),0.2.1+ 宿主全对齐(0.2.1 镜像)——`engines.dsh` 保持 `>=0.1.7-rc`。
|
|
12
|
+
- **组合与测试**:`pluginsFor` 增加 `hostExtras` 参数(`{ timeContext, toolSchedule }`,缺省全 false;toolFilter 跟随 toolSchedule 单独走)。冒烟 99 → 101 项(+hostExtras 专项 + probeHostExtras 单测,含抛异常降级与半探测边界);**`tests/align-official.mjs` 的 pins 升级到 dsh-v0.2.1-alpha.1**(standard/cordis/ptc 换新 sha;minimal 不变),对齐目标切为「全探测行集 × 0.2.1 文本」——本机实测 **alignment OK: all four variants mirror the official row sequence**(34/35/35/7 行);旧宿主形态由 smoke 的 0.1.7 镜像断言继续锁死。两处既有源码形状断言随签名同步(`pluginsFor` 形参、`registerVariant` 调用)。
|
|
13
|
+
- **上游预警(AGENTS 第 8 条追记)**:dsh 仓库有 **proposed** 状态的「让 shell 环境声明只负责键所有权」提案(2026-09-19)——落地后将删 `list()`、`BashEnvVariable`、描述对象与仅描述性校验,贡献方声明改为**显式只读键集合**。本插件 `shellEnv.register` 的 `variables: { DSH_PATH_DIALECT: { description } }` 正是描述对象形状,**提案实施的下一版 dsh 升级必须同步改贡献形状**(register/collect/所有权冲突/未声明拒绝语义不变)。
|
|
14
|
+
- 相关:[dsh v0.2.1-alpha.1 release notes](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.2.1-alpha.1)、[shell-env 提案(中文)](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/proposed/simplification/2026-09-19-shell-env-key-declarations.zh.md)。
|
|
15
|
+
|
|
16
|
+
## v0.33.1 — 2026-10-02
|
|
17
|
+
|
|
18
|
+
**类型**:docs(issue #14:README「保留官方沙箱语义」与 Windows 受限模式的实际行为不一致)
|
|
19
|
+
|
|
20
|
+
> 报告者 **SenkjM** 的五条观察(enforcement 元数据、完整性级别 Low→Medium、shell 越界写成功而文件工具被拒、一次性提示、README 关键词计数)**全部与设计一致**;他指出的是文档缺口——行为在代码层有如实标注(§1 红线「绝不静默假成功」守住的是结果元数据与运行期提示),但 README 层面除了「保留官方沙箱语义」这句误导性概括外零披露。本版补齐,并顺带修正 README 里残留的物化时代描述。
|
|
21
|
+
|
|
22
|
+
- **README.md / README_EN.md 新增「安全语义:Windows 上的受限模式」章节**(置于「它做了什么」之后):POSIX 完整保留 / Windows 受限模式 shell 走 unconfined 的根因(MSYS2 vs restricted-token,OS 层冲突无插件内解)与取舍理由、如何标注(结果元数据 `enforcement: "unconfined"` + 每进程一次性日志)、**文件工具不受影响**(fs 沙箱另一层,边界不一致如实写明)、可观察对照(完整性级别不再压低 Low→Medium、TEMP 不再隔离)、以及「需要 shell 也被硬拦 ⇒ 本插件在 Windows 上不适合你,卸载即回 pwsh-sandbox,没有中间态」的明确选择提示;「它解决什么」的「保留官方沙箱语义」改为指向该章节。
|
|
23
|
+
- **顺带清理 README 的物化时代残留描述**(行为未变,纯文档对齐 v0.32.0+ 现状):「它做了什么」第 3 条改为声明式注册(`ctx.agentPresets.register`,行集为已提交 JS 数据,不再物化目录,旧物化树按 marker 单向清理);删除「dsh 版本双适配」「dsh 0.1.6 适配」两个过时段落(era 机制已随 v0.32.0 删除),替换为「宿主要求与 preset 对齐」(下限 >=0.1.7-rc + smoke 锁对齐 + preset id 不变);「配置」/「ptc 联动」/「去重开关」段的「物化」措辞与旧宿主(≤0.1.6)双时代描述同步更新。
|
|
24
|
+
- docs-only:src/ 零改动,冒烟 99 项不变(README 内容无 smoke 断言)。发 patch 版本而非仅推文档,是因为 README 随 npm 包分发——安全披露应让 `npm view` / 插件管理页看到的 README 同步更新。
|
|
25
|
+
|
|
26
|
+
- 相关:issue #14(<https://github.com/KannaKuron/dsh-gitbash-shell/issues/14>)、CHANGELOG v0.13.2(取舍的完整分析)。
|
|
27
|
+
|
|
6
28
|
## v0.33.0 — 2026-10-01
|
|
7
29
|
|
|
8
30
|
**类型**:fix(issue #13:Scoop 安装 Git 时 `bashPath` 配置正确但每次启动仍报 could NOT be resolved 并弹引导框)+ feat(解析链新增 `git --exec-path` 与 GitForWindows 注册表两级发现)
|
package/README.md
CHANGED
|
@@ -6,14 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
> 让 DeepSeek Harness (dsh) 在 Windows 上**全部使用 Git Bash** 的官方风格插件
|
|
8
8
|
> —— 以 Git for Windows 的 `bash.exe` 替换 PowerShell 执行器,并为所有模式
|
|
9
|
-
>
|
|
9
|
+
> 注册对应的 Git Bash 版 agent preset。
|
|
10
10
|
|
|
11
11
|
## 它解决什么
|
|
12
12
|
|
|
13
13
|
官方 Windows 组合默认把 `dsh-pwsh-sandbox` 作为 `ctx.shell`(PowerShell 执行器),
|
|
14
14
|
且各 preset 的 `tool-bash` 行在 win32 上被禁用——因为在 Windows 上裸 `bash`
|
|
15
15
|
会解析到 `C:\Windows\System32\bash.exe`(WSL 占位),本插件直接指定
|
|
16
|
-
`C:/Program Files/Git/bin/bash.exe
|
|
16
|
+
`C:/Program Files/Git/bin/bash.exe`。沙箱语义在 POSIX 宿主上完整保留;
|
|
17
|
+
**Windows 宿主上受限模式有一处重要例外——见下方
|
|
18
|
+
[「安全语义:Windows 上的受限模式」](#安全语义windows-上的受限模式)**。
|
|
17
19
|
|
|
18
20
|
安装本插件后:
|
|
19
21
|
|
|
@@ -28,29 +30,20 @@
|
|
|
28
30
|
Git Bash host 下会拿到"暗示 PowerShell 语法的工具",请改用上面的变体;
|
|
29
31
|
已装 dsh-ptc-cordis-preset 的话,`PTC 创造模式` 用户 preset 不受影响。
|
|
30
32
|
|
|
31
|
-
###
|
|
33
|
+
### 宿主要求与 preset 对齐
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
过的目录照旧不碰。
|
|
35
|
+
自 v0.32.0 起宿主下限为 **dsh >= 0.1.7-rc**(经 peer `@deepseek-ai/dsh` 声明,
|
|
36
|
+
prerelease 含入求值;旧宿主不再支持)。四个变体的行集是 `src/compositions.js`
|
|
37
|
+
里已提交的 JS 数据,镜像官方 0.1.7 的 standard/minimal/ptc/cordis 行切分并叠加
|
|
38
|
+
Git Bash 增量;跟随 dsh 升级的对齐由冒烟测试
|
|
39
|
+
`compositions: full variants mirror the official row split` 锁住。
|
|
40
|
+
**preset id 保持 `code-gitbash` 不变**——会话钉在 id 上,dsh 0.1.2 把内置 `code`
|
|
41
|
+
改名 `ptc` 时本插件的变体 id 就没有跟随(改名会让已固定的会话报 preset not found)。
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
所以把旧名钉死在资产里的 preset 在新版上会直接不可用。本插件两个拼法都不钉:物化时
|
|
47
|
-
**从宿主自己的内置 preset 现场抄**那一行的 id、包名与 `disabled` 状态
|
|
48
|
-
(`rowFormsOf` / `alignEngineRow`),并让 `tool-ralph` 跟随新版默认的 `disabled: true`。
|
|
49
|
-
改写是纯字符串手术(不解析 YAML,`!!js` 安全)且幂等;探测失败(旧宿主、无 roster)时
|
|
50
|
-
资产保持逐字节原样——**一份资产通吃两个 era,升级顺序无关**。
|
|
51
|
-
|
|
52
|
-
同一版还把 `LocalBashExecutor` 的受保护钩子改成了异步:插件的 `runArgv` 结果解包、
|
|
53
|
-
`start` 的返回形态与 `confine` 的取消信号都按**加载期探测**自适应,新旧宿主行为一致。
|
|
43
|
+
**dsh 0.2.1 增量自动随行(v0.34.0)**:官方 0.2.1 给全工具 preset 加了时间上下文
|
|
44
|
+
(`time-context`)与提醒工具(`schedule_*`,子代理不可用)——本插件在 0.2.1+ 宿主上
|
|
45
|
+
探测到对应包即自动把这两行与 subagent 的 schedule_* deny 纳入三个全工具变体
|
|
46
|
+
(极简变体不加,与官方一致);旧宿主行为逐字节不变,**无需为此升级 dsh**。
|
|
54
47
|
|
|
55
48
|
## 安装(公开 npm 插件,推荐)
|
|
56
49
|
|
|
@@ -74,24 +67,59 @@ bundle patch(`cordis.patch.yml`)应用三个改动:
|
|
|
74
67
|
2. 插入 `gitbash-executor`(`dsh-gitbash-shell/shell`):继承官方
|
|
75
68
|
`@deepseek-ai/dsh-bash-sandbox`,仅把内层 argv 换成
|
|
76
69
|
`<git-bash.exe> -c <command>`。沙箱策略/拒绝分类/后台任务/超时/设置节
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
70
|
+
沿用官方实现;full-access 分支单独接 Git Bash(父类那里硬编码裸 `bash`);
|
|
71
|
+
**Windows 上受限分支有一处例外,见下一节**;
|
|
72
|
+
3. 插入 `gitbash-shell`(`dsh-gitbash-shell`):以**声明式注册**
|
|
73
|
+
(`ctx.agentPresets.register`,v0.32.0 起)提供上表 4 个 preset 变体——
|
|
74
|
+
行集是仓库里已提交、可 diff 的 JS 数据(`src/compositions.js`),**不再物化
|
|
75
|
+
任何目录**;旧版本遗留在磁盘上的物化树,启动时仅当 marker 判定为
|
|
76
|
+
「未被用户改过」才单向清理,用户改过的绝不碰。
|
|
82
77
|
|
|
83
78
|
**环境变量**:`bash.exe` 是 host 进程的直接子进程(不经 git-bash 登录壳),完整继承
|
|
84
79
|
系统环境变量与 `DSH_*` 快照,和原来 pwsh 拿到的完全一致。
|
|
85
80
|
|
|
81
|
+
## 安全语义:Windows 上的受限模式
|
|
82
|
+
|
|
83
|
+
**POSIX 宿主**:官方沙箱语义**完整保留**——受限令牌、工作区围栏、拒绝分类,
|
|
84
|
+
与 `@deepseek-ai/dsh-bash-sandbox` 完全一致。本节只讲 Windows。
|
|
85
|
+
|
|
86
|
+
**Windows 宿主上的事实**:`read-only` / `workspace-write` 权限预设下的 **shell 命令**,
|
|
87
|
+
实际以**不受限(unconfined)方式执行**:
|
|
88
|
+
|
|
89
|
+
- **为什么**:MSYS2 运行时无法在 restricted-token 沙箱下启动——`msys-2.0.dll`
|
|
90
|
+
初始化时创建的 cygheap 映射与信号管道,DACL 只含用户 SID;而 WRITE_RESTRICTED
|
|
91
|
+
令牌的二次写检查要求 restricting-SID ACE → 初始化即死于 Win32 error 5 /
|
|
92
|
+
0xC0000142,**任何版本、任何参数都无解**(cmd/pwsh 走匿名管道不受影响)。
|
|
93
|
+
这是 OS 层冲突,插件内没有真正的修法,只有两个选择:**如实标注的绕过**(本插件,
|
|
94
|
+
v0.13.0 起)或**直接拒绝执行**。选了前者,是因为装这个插件的目的就是用 Git Bash——
|
|
95
|
+
静默换 pwsh 顶上、或让每条命令都失败,都不如把真相摆在结果里。
|
|
96
|
+
- **如何标注(绝不静默)**:每条此类结果的元数据携带
|
|
97
|
+
`sandbox: { mode: "<请求的模式>", denied: false, enforcement: "unconfined" }`,
|
|
98
|
+
并在每个进程首次受限调用时打印一行说明日志(`File sandbox enforcement is
|
|
99
|
+
partial on this host.`)。
|
|
100
|
+
- **文件工具不受影响**:read / write / edit / glob / grep 的沙箱是**另一层**
|
|
101
|
+
(fs-tool policy),照常拦截工作区外访问——也就是说 `workspace-write` 下
|
|
102
|
+
shell 能写出工作区、文件工具不能,**两者的边界并不一致**。
|
|
103
|
+
- **可观察的对照**(相对 pwsh-sandbox 时代):会话进程的完整性级别不再被压低
|
|
104
|
+
(Low → Medium);`$env:TEMP` 不再被重定向到 per-session 隔离目录。
|
|
105
|
+
|
|
106
|
+
**如果你需要 shell 也被硬性拦住**(例如不希望模型经 shell 写工作区外的任何路径),
|
|
107
|
+
这个插件在 Windows 上不适合你——卸载即回到 `pwsh-sandbox` 的受限语义,没有中间态。
|
|
108
|
+
`danger-full-access` 预设本来就不受限,装不装本插件行为一致。
|
|
109
|
+
|
|
110
|
+
> 这一取舍的完整分析(含 0.6.0~0.13.1 的故障史与社区两派对比)见 CHANGELOG
|
|
111
|
+
> v0.13.2 与 `src/shell.js` 头注释。
|
|
112
|
+
|
|
86
113
|
## 配置
|
|
87
114
|
|
|
88
115
|
`gitbash-shell` 行(行 id 自 v0.24.0 起与设置命名空间同串;≤ v0.23.0 为 `gitbash-presets`)
|
|
89
|
-
支持 `presets`
|
|
116
|
+
支持 `presets` 数组,只注册你常用的模式(未列出的变体不再进入名录;改动经
|
|
117
|
+
volatile-update 触发重注册):
|
|
90
118
|
|
|
91
119
|
```yaml
|
|
92
120
|
- id: gitbash-shell
|
|
93
121
|
config:
|
|
94
|
-
presets: [standard-gitbash, minimal-gitbash] #
|
|
122
|
+
presets: [standard-gitbash, minimal-gitbash] # 默认注册全部 4 个
|
|
95
123
|
suppressPeerCordis: false # 与 dsh-ptc-cordis-preset 去重,默认关
|
|
96
124
|
```
|
|
97
125
|
|
|
@@ -219,14 +247,11 @@ preset 会被插件自动清理;宿主 shell 回退为 PowerShell。
|
|
|
219
247
|
## 与 dsh-ptc-cordis-preset 联动
|
|
220
248
|
|
|
221
249
|
本插件在 host 上发布 `gitBash` 能力服务(`{ active, bashPath }`,仅 Windows 为 active)。
|
|
222
|
-
[dsh-ptc-cordis-preset](https://github.com/KannaKuron/dsh-ptc-cordis-preset) v0.5.0+
|
|
223
|
-
`PTC 创造模式` 时会检测该信号:两个插件都安装时,**PTC
|
|
250
|
+
[dsh-ptc-cordis-preset](https://github.com/KannaKuron/dsh-ptc-cordis-preset) v0.5.0+ 在注册
|
|
251
|
+
`PTC 创造模式` 时会检测该信号:两个插件都安装时,**PTC 创造模式自动注册为 Git Bash 版**
|
|
224
252
|
(`tool-bash` 启用、`tool-pwsh` 禁用),无需新增模式、无需手工修改 preset;
|
|
225
253
|
只装本插件时 PTC 创造模式保持原样(由它自己的插件管理)。
|
|
226
254
|
|
|
227
|
-
> 切换生效后若 `ptc-cordis` 目录已存在且被旧版本物化,删除
|
|
228
|
-
> `~/.dsh/.agent-presets/ptc-cordis` 并重启,即由新逻辑重新物化。
|
|
229
|
-
|
|
230
255
|
### 去重开关:`suppressPeerCordis`(默认关,v0.25.0)
|
|
231
256
|
|
|
232
257
|
联动生效后,`创造模式 · Git Bash`(本插件的 `cordis-gitbash`)与对方已经 Git Bash 化的
|
|
@@ -245,14 +270,11 @@ preset 会被插件自动清理;宿主 shell 回退为 PowerShell。
|
|
|
245
270
|
|
|
246
271
|
**生效时机**:
|
|
247
272
|
|
|
248
|
-
-
|
|
273
|
+
- **开关是实时的**:走行 Config 的 volatile 通道,对方的能力则经
|
|
249
274
|
`ctx.inject(['ptcCordisPreset'])` 观察(**与插件行激活顺序无关**),两边任一变化都当场
|
|
250
275
|
reconcile——摘掉打日志 `preset 'cordis-gitbash' retired (dsh-ptc-cordis-preset covers Creation
|
|
251
276
|
mode on Git Bash)`,恢复打 `preset 'cordis-gitbash' registered declaratively`。已经挂载的会话
|
|
252
277
|
钉在自己的组合快照上,不受影响;名录变化从新会话开始可见。
|
|
253
|
-
- **旧宿主(dsh ≤ 0.1.6)是启动时判定一次**:旧宿主没有可观察的注册表,能力探测是有界的
|
|
254
|
-
(默认 1s,读不到即视为"对方不覆盖"),所以打开开关后,上一轮物化出来的 `cordis-gitbash`
|
|
255
|
-
目录会被清理;把开关关掉后,该变体要到**下一次启动**才重新物化。
|
|
256
278
|
|
|
257
279
|
**两侧设置卡上是同一份状态**:权威值只有本插件这一行 Config 一份;对方的设置卡通过
|
|
258
280
|
`ctx.configForms.get('gitbash-shell')` **绑定同一行**、写同一个字段(DSH 官方支持编辑另一个
|
package/README_EN.md
CHANGED
|
@@ -5,25 +5,20 @@
|
|
|
5
5
|
[简体中文](README.md) | English
|
|
6
6
|
|
|
7
7
|
> Run **every agent shell command through Git Bash** on Windows with DeepSeek
|
|
8
|
-
> Harness (dsh) — replaces the PowerShell executor and
|
|
8
|
+
> Harness (dsh) — replaces the PowerShell executor and registers Git Bash
|
|
9
9
|
> variants of all four agent presets.
|
|
10
10
|
|
|
11
|
-
###
|
|
11
|
+
### Host requirement & preset alignment
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
`
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
both eras, in either upgrade order**.
|
|
23
|
-
|
|
24
|
-
The same release made `LocalBashExecutor`'s protected hooks asynchronous: the
|
|
25
|
-
`runArgv` result unwrapping, the `start` return shape and the `confine` cancellation
|
|
26
|
-
signal are all probed at load time, so both host generations behave identically.
|
|
13
|
+
Since v0.32.0 the host floor is **dsh >= 0.1.7-rc** (declared through the
|
|
14
|
+
`@deepseek-ai/dsh` peer, prereleases included; older hosts are no longer
|
|
15
|
+
supported). The four variants are committed JS row sets in
|
|
16
|
+
`src/compositions.js`, mirroring the official 0.1.7 standard/minimal/ptc/cordis
|
|
17
|
+
split plus the Git Bash delta; upgrade alignment is locked by the smoke test
|
|
18
|
+
`compositions: full variants mirror the official row split`. **Preset ids
|
|
19
|
+
never change** (`code-gitbash` kept its historical id when dsh 0.1.2 renamed
|
|
20
|
+
the built-in `code` to `ptc` — sessions are pinned to ids, and a rename would
|
|
21
|
+
orphan them).
|
|
27
22
|
|
|
28
23
|
## Install (public npm package)
|
|
29
24
|
|
|
@@ -35,19 +30,6 @@ dsh plugin --profile web add dsh-gitbash-shell
|
|
|
35
30
|
`dsh.bundle` declaration, appends the package to `dsh.profile.bundles`.
|
|
36
31
|
**Restart the profile's host to activate.**
|
|
37
32
|
|
|
38
|
-
## Works with both dsh 0.1.1 and 0.1.2+
|
|
39
|
-
|
|
40
|
-
dsh 0.1.2 renamed the built-in `code` preset to `ptc` (`mode: code` → `mode: ptc`,
|
|
41
|
-
no compatibility aliases) and added new built-in rows (`command-goal`, …). For
|
|
42
|
-
the affected variants (standard/code/cordis) this plugin **ships both committed
|
|
43
|
-
era texts**, probes the built-in roster at every boot, records the choice in
|
|
44
|
-
`.plugin-managed.json` (`base`), and re-materializes automatically when the
|
|
45
|
-
detection flips. `minimal-gitbash`'s built-in base did not change across the
|
|
46
|
-
rename, so one text serves both eras. **Preset ids never change**
|
|
47
|
-
(`code-gitbash` keeps its historical id — sessions are pinned to ids, and a
|
|
48
|
-
rename would orphan them). Either upgrade order converges automatically; directories
|
|
49
|
-
you modified are still never touched.
|
|
50
|
-
|
|
51
33
|
## What it does
|
|
52
34
|
|
|
53
35
|
The bundle patch (`cordis.patch.yml`):
|
|
@@ -56,17 +38,59 @@ The bundle patch (`cordis.patch.yml`):
|
|
|
56
38
|
2. mounts `dsh-gitbash-shell/shell` — a subclass of the shipped
|
|
57
39
|
`@deepseek-ai/dsh-bash-sandbox` whose inner argv is
|
|
58
40
|
`<git-bash.exe> -c <command>` (sandbox policy, denial classification,
|
|
59
|
-
background jobs, and settings behavior all inherited
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
`
|
|
63
|
-
|
|
64
|
-
|
|
41
|
+
background jobs, and settings behavior all inherited; **one Windows
|
|
42
|
+
exception for confined modes — see the next section**);
|
|
43
|
+
3. mounts `dsh-gitbash-shell`, which registers the four preset variants
|
|
44
|
+
**declaratively** (`ctx.agentPresets.register`, v0.32.0+) — the row sets
|
|
45
|
+
are committed, diffable JS data (`src/compositions.js`) and **no directory
|
|
46
|
+
is ever materialized**; materialized trees left by older versions are
|
|
47
|
+
cleaned one-way at boot only when the marker proves the user never edited
|
|
48
|
+
them.
|
|
65
49
|
|
|
66
50
|
Environment: `bash.exe` is spawned as a direct child of the host, so it
|
|
67
51
|
inherits the full system environment plus the `DSH_*` snapshot, exactly like
|
|
68
52
|
the pwsh executor did.
|
|
69
53
|
|
|
54
|
+
## Security semantics: confined modes on Windows
|
|
55
|
+
|
|
56
|
+
**POSIX hosts**: the official sandbox semantics are **fully preserved** —
|
|
57
|
+
restricted tokens, workspace fencing, denial classification, identical to
|
|
58
|
+
`@deepseek-ai/dsh-bash-sandbox`. This section is about Windows only.
|
|
59
|
+
|
|
60
|
+
**On Windows**, shell commands under the `read-only` / `workspace-write`
|
|
61
|
+
permission presets actually run **UNCONFINED**:
|
|
62
|
+
|
|
63
|
+
- **Why**: the MSYS2 runtime cannot start under the restricted-token sandbox —
|
|
64
|
+
`msys-2.0.dll` creates its cygheap mapping and signal pipe with DACLs naming
|
|
65
|
+
only the user SID, while a WRITE_RESTRICTED token's pass-2 write check
|
|
66
|
+
demands a restricting-SID ACE, so init dies with Win32 error 5 / 0xC0000142
|
|
67
|
+
before argv ever runs — **every version, no workaround** (cmd/pwsh use
|
|
68
|
+
anonymous pipes and are unaffected). This is an OS-level conflict with no
|
|
69
|
+
in-plugin cure; the two options are **a labelled bypass** (this plugin,
|
|
70
|
+
since v0.13.0) or **refusing to run at all**. The former was chosen because
|
|
71
|
+
installing this plugin means wanting Git Bash — silently falling back to
|
|
72
|
+
pwsh, or failing every command, is worse than putting the truth in the result.
|
|
73
|
+
- **How it is labelled (never silent)**: every such result carries
|
|
74
|
+
`sandbox: { mode: "<requested mode>", denied: false, enforcement: "unconfined" }`,
|
|
75
|
+
plus a one-time notice per process (`File sandbox enforcement is partial on
|
|
76
|
+
this host.`).
|
|
77
|
+
- **File tools are NOT affected**: the read / write / edit / glob / grep
|
|
78
|
+
sandbox is a **separate layer** (the fs-tool policy) and still fences
|
|
79
|
+
out-of-workspace access. Under `workspace-write` the shell can write outside
|
|
80
|
+
the workspace while file tools cannot — **the two boundaries differ**.
|
|
81
|
+
- **Observable differences** vs the pwsh-sandbox era: the session process's
|
|
82
|
+
integrity level is no longer lowered (Low → Medium), and `$env:TEMP` is no
|
|
83
|
+
longer redirected to a per-session directory.
|
|
84
|
+
|
|
85
|
+
**If you need the shell itself hard-fenced too** (e.g. the model must not
|
|
86
|
+
write anywhere outside the workspace through the shell), this plugin is not
|
|
87
|
+
for you on Windows — uninstalling restores `pwsh-sandbox`'s confined
|
|
88
|
+
semantics; there is no middle ground. The `danger-full-access` preset is
|
|
89
|
+
unconfined by design either way.
|
|
90
|
+
|
|
91
|
+
> The full analysis (the 0.6.0–0.13.1 failure history and the community's two
|
|
92
|
+
> camps) is in the CHANGELOG under v0.13.2 and in the `src/shell.js` header.
|
|
93
|
+
|
|
70
94
|
## Config
|
|
71
95
|
|
|
72
96
|
The `gitbash-shell` row (its id has matched the settings namespace since v0.24.0; it was
|
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-gitbash-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"description": "Windows 全模式 Git Bash:用 Git for Windows bash 替换 PowerShell 执行器,声明式注册 standard/minimal/code/cordis 的 Git Bash 变体 preset(preset id 永不变)。联动:dsh-ptc-cordis-preset(两者同装时 PTC 创造模式带 Git Bash 行且可去重)+ dsh-better-sidebar(终端 UI 标签与模型 terminal_* 工具自动切到 Git Bash)。",
|
|
6
6
|
"engines": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-gitbash-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"description": "Windows 上让 agent 的每条 shell 命令改经 Git for Windows bash 执行;物化四个内置预设的 Git Bash 变体,统一 MSYS 盘根路径方言,侧栏终端接管可开关。· Run every agent shell command through Git for Windows bash on Windows; materialize Git Bash variants of the built-in presets, unify the MSYS drive-root path dialect, and adopt the sidebar terminal behind a switch.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/src/compositions.js
CHANGED
|
@@ -63,11 +63,23 @@ When ready, call exit_plan_mode with the complete plan markdown, starting with a
|
|
|
63
63
|
* workflow setting is untouched and returns once it is not.
|
|
64
64
|
* @returns {object[]} the declarative plugins list.
|
|
65
65
|
*/
|
|
66
|
-
export function pluginsFor({ kind, gitBash, skillsDir, pythonActive = false }) {
|
|
66
|
+
export function pluginsFor({ kind, gitBash, skillsDir, pythonActive = false, hostExtras = {} }) {
|
|
67
67
|
const win = typeof process !== 'undefined' && process.platform === 'win32'
|
|
68
68
|
const bashDisabled = gitBash ? false : win
|
|
69
69
|
const pwshDisabled = gitBash ? true : !win
|
|
70
70
|
const workflowOn = kind !== 'ptc' && pythonActive !== true
|
|
71
|
+
// dsh 0.2.1 added two rows to every full-tool official preset — time-context
|
|
72
|
+
// (durable clock readings) and tool-schedule (the reminder tools) — plus a
|
|
73
|
+
// toolFilter deny of the four schedule_* tools on the subagent/subagent_fork
|
|
74
|
+
// configs (reminders never reach a subagent). A preset row whose package is
|
|
75
|
+
// absent rejects the WHOLE mount, so these ride a host probe (index.js
|
|
76
|
+
// probeHostExtras, resolved from ctx.baseUrl — the base rows mount from);
|
|
77
|
+
// absent/older hosts keep the byte-identical 0.1.7 row set. The deny list
|
|
78
|
+
// follows toolSchedule alone: denying tools that do not exist is noise.
|
|
79
|
+
const { timeContext = false, toolSchedule = false } = hostExtras
|
|
80
|
+
const subagentDeny = toolSchedule
|
|
81
|
+
? { toolFilter: { deny: ['schedule_create', 'schedule_delete', 'schedule_list', 'schedule_update'] } }
|
|
82
|
+
: {}
|
|
71
83
|
const rows = [
|
|
72
84
|
{
|
|
73
85
|
id: 'persona',
|
|
@@ -82,6 +94,7 @@ export function pluginsFor({ kind, gitBash, skillsDir, pythonActive = false }) {
|
|
|
82
94
|
name: '@deepseek-ai/dsh-agent-instructions',
|
|
83
95
|
config: { maxBytes: 65536 },
|
|
84
96
|
},
|
|
97
|
+
...(timeContext ? [{ id: 'time-context', name: '@deepseek-ai/dsh-time-context' }] : []),
|
|
85
98
|
{ id: 'tool-bash', name: '@deepseek-ai/dsh-tool-bash', ...off(bashDisabled) },
|
|
86
99
|
{ id: 'tool-pwsh', name: '@deepseek-ai/dsh-tool-pwsh', ...off(pwshDisabled) },
|
|
87
100
|
{ id: 'tool-fs', name: '@deepseek-ai/dsh-tool-fs' },
|
|
@@ -91,6 +104,7 @@ export function pluginsFor({ kind, gitBash, skillsDir, pythonActive = false }) {
|
|
|
91
104
|
config: { sampleOverCapGlobResults: false },
|
|
92
105
|
},
|
|
93
106
|
{ id: 'tool-jobs', name: '@deepseek-ai/dsh-tool-jobs' },
|
|
107
|
+
...(toolSchedule ? [{ id: 'tool-schedule', name: '@deepseek-ai/dsh-tool-schedule' }] : []),
|
|
94
108
|
...((kind === 'standard' || kind === 'ptc') ? [
|
|
95
109
|
{ id: 'skill-filesystem', name: '@deepseek-ai/dsh-skill-filesystem' },
|
|
96
110
|
{ id: 'tool-skill', name: '@deepseek-ai/dsh-tool-skill' },
|
|
@@ -136,12 +150,12 @@ export function pluginsFor({ kind, gitBash, skillsDir, pythonActive = false }) {
|
|
|
136
150
|
{
|
|
137
151
|
id: 'tool-subagent',
|
|
138
152
|
name: '@deepseek-ai/dsh-tool-subagent',
|
|
139
|
-
config: { provider: 'spawn', toolName: 'subagent', modelSelectionSettings: true, backgroundMode: 'continuable' },
|
|
153
|
+
config: { provider: 'spawn', toolName: 'subagent', modelSelectionSettings: true, backgroundMode: 'continuable', ...subagentDeny },
|
|
140
154
|
},
|
|
141
155
|
{
|
|
142
156
|
id: 'tool-subagent-fork',
|
|
143
157
|
name: '@deepseek-ai/dsh-tool-subagent',
|
|
144
|
-
config: { provider: 'fork', toolName: 'subagent_fork', backgroundMode: 'continuable' },
|
|
158
|
+
config: { provider: 'fork', toolName: 'subagent_fork', backgroundMode: 'continuable', ...subagentDeny },
|
|
145
159
|
},
|
|
146
160
|
{
|
|
147
161
|
id: 'tool-subagent-codex',
|
package/src/index.js
CHANGED
|
@@ -1628,17 +1628,63 @@ function cleanupLegacyTrees(presetIds) {
|
|
|
1628
1628
|
}
|
|
1629
1629
|
}
|
|
1630
1630
|
|
|
1631
|
+
/**
|
|
1632
|
+
* Probe the dsh 0.2.1 host additions for the variant compositions: every
|
|
1633
|
+
* full-tool official preset gained a `time-context` row (durable clock
|
|
1634
|
+
* readings) and a `tool-schedule` row (the reminder tools). A preset row whose
|
|
1635
|
+
* package is absent rejects the WHOLE mount, so the rows ride this probe: each
|
|
1636
|
+
* is added only when its package resolves from the HOST's module base —
|
|
1637
|
+
* `ctx.baseUrl`, the exact base `prepareProfileEntries` mounts preset rows
|
|
1638
|
+
* from, so the probe can never disagree with the mounter. On dsh <= 0.2.0 both
|
|
1639
|
+
* stay false and the row set stays byte-identical to the 0.1.7 mirror. Probed
|
|
1640
|
+
* once per boot; the profile's package set does not change under a running
|
|
1641
|
+
* host. minimal never gains the rows (the official minimal preset has none).
|
|
1642
|
+
* @param {object} ctx - cordis context (any fiber of the running host).
|
|
1643
|
+
* @param {Function} [resolve] - predicate override for tests; receives a
|
|
1644
|
+
* package specifier, resolves like `require.resolve`, returns boolean.
|
|
1645
|
+
* @returns {Promise<{timeContext: boolean, toolSchedule: boolean}>}
|
|
1646
|
+
*/
|
|
1647
|
+
export async function probeHostExtras(ctx, resolve) {
|
|
1648
|
+
let ok = resolve
|
|
1649
|
+
if (ok === undefined) {
|
|
1650
|
+
try {
|
|
1651
|
+
const { createRequire } = await import('node:module')
|
|
1652
|
+
const req = createRequire(ctx?.baseUrl ?? import.meta.url)
|
|
1653
|
+
ok = (specifier) => {
|
|
1654
|
+
try { req.resolve(specifier); return true } catch { return false }
|
|
1655
|
+
}
|
|
1656
|
+
} catch {
|
|
1657
|
+
ok = () => false
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
const [timeContext, toolSchedule] = await Promise.all([
|
|
1661
|
+
Promise.resolve(safeProbe(ok, '@deepseek-ai/dsh-time-context')).then(Boolean),
|
|
1662
|
+
Promise.resolve(safeProbe(ok, '@deepseek-ai/dsh-tool-schedule')).then(Boolean),
|
|
1663
|
+
])
|
|
1664
|
+
return { timeContext, toolSchedule }
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
/** Run one probe predicate; ANY failure reads as "absent" (a throwing
|
|
1668
|
+
* resolver must degrade the row away, never reject the boot). */
|
|
1669
|
+
function safeProbe(ok, specifier) {
|
|
1670
|
+
try {
|
|
1671
|
+
return ok(specifier)
|
|
1672
|
+
} catch {
|
|
1673
|
+
return false
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1631
1677
|
/**
|
|
1632
1678
|
* Register one variant definition; returns the unregister function. Mount
|
|
1633
1679
|
* failures stay visible through the roster's broken diagnostic instead of
|
|
1634
1680
|
* failing the boot.
|
|
1635
1681
|
*/
|
|
1636
|
-
async function registerVariant(ctx, presetId, { gitBashActive, skillsDir, pythonActive }) {
|
|
1682
|
+
async function registerVariant(ctx, presetId, { gitBashActive, skillsDir, pythonActive, hostExtras = {} }) {
|
|
1637
1683
|
const meta = PRESET_META[presetId]
|
|
1638
1684
|
if (!meta) return undefined
|
|
1639
1685
|
const plugins = meta.kind === 'minimal'
|
|
1640
1686
|
? minimalPluginsFor()
|
|
1641
|
-
: pluginsFor({ kind: meta.kind, gitBash: gitBashActive, skillsDir, pythonActive })
|
|
1687
|
+
: pluginsFor({ kind: meta.kind, gitBash: gitBashActive, skillsDir, pythonActive, hostExtras })
|
|
1642
1688
|
return ctx.agentPresets.register({
|
|
1643
1689
|
id: presetId,
|
|
1644
1690
|
name: meta.name,
|
|
@@ -1671,6 +1717,10 @@ async function runDeclarativeEra(ctx, presetIds, readSuppress) {
|
|
|
1671
1717
|
cleanupLegacyTrees(presetIds)
|
|
1672
1718
|
const gitBashActive = process.platform === 'win32'
|
|
1673
1719
|
const skillsDir = await resolveSkillsDir()
|
|
1720
|
+
// The dsh 0.2.1 additions (time-context / tool-schedule rows) probe once per
|
|
1721
|
+
// boot and ride every (re-)registration below — the profile's package set
|
|
1722
|
+
// cannot change under a running host.
|
|
1723
|
+
const hostExtras = await probeHostExtras(ctx)
|
|
1674
1724
|
/** presetId -> unregister, i.e. exactly what is live right now. */
|
|
1675
1725
|
const live = new Map()
|
|
1676
1726
|
let peerGitBash = false
|
|
@@ -1715,7 +1765,7 @@ async function runDeclarativeEra(ctx, presetIds, readSuppress) {
|
|
|
1715
1765
|
for (const presetId of want) {
|
|
1716
1766
|
if (live.has(presetId)) continue
|
|
1717
1767
|
try {
|
|
1718
|
-
const unregister = await registerVariant(ctx, presetId, { gitBashActive, skillsDir, pythonActive: peerPython })
|
|
1768
|
+
const unregister = await registerVariant(ctx, presetId, { gitBashActive, skillsDir, pythonActive: peerPython, hostExtras })
|
|
1719
1769
|
if (unregister) {
|
|
1720
1770
|
live.set(presetId, unregister)
|
|
1721
1771
|
console.log(TAG + " preset '" + presetId + "' registered declaratively")
|
|
@@ -1732,7 +1782,9 @@ async function runDeclarativeEra(ctx, presetIds, readSuppress) {
|
|
|
1732
1782
|
}
|
|
1733
1783
|
|
|
1734
1784
|
await reconcile()
|
|
1735
|
-
console.log(TAG + ' registered ' + live.size + ' preset(s) declaratively (' + [...live.keys()].join(', ') + ')'
|
|
1785
|
+
console.log(TAG + ' registered ' + live.size + ' preset(s) declaratively (' + [...live.keys()].join(', ') + ')' + (hostExtras.timeContext || hostExtras.toolSchedule
|
|
1786
|
+
? ' [dsh 0.2.1 additions: ' + (hostExtras.timeContext ? 'time-context' : '') + (hostExtras.timeContext && hostExtras.toolSchedule ? ' + ' : '') + (hostExtras.toolSchedule ? 'tool-schedule (subagents denied)' : '') + ']'
|
|
1787
|
+
: ''))
|
|
1736
1788
|
|
|
1737
1789
|
// The peer's capability: `ctx.inject` makes this independent of row order,
|
|
1738
1790
|
// and the child scope's disposer runs when the peer unmounts (or its row is
|
|
@@ -2443,4 +2495,4 @@ export async function apply(ctx, config = {}) {
|
|
|
2443
2495
|
|
|
2444
2496
|
}
|
|
2445
2497
|
// Test surface: pure helpers, no Cordis context required.
|
|
2446
|
-
export const _internal = { PRESET_IDS, PEER_COVERED_PRESET_ID, PEER_CAPABILITY, isDelegatedAgent, dialectApplies, PEER_PYTHON_FIELD, PEER_PYTHON_BACKEND_FIELD, peerFact, peerBackend, pythonBackendActive, effectivePresetIds, translateDispatch, translateMsysPath, translatePathArguments, rewriteCodePaths, scanCodeLiterals, programPrelude, translateGlobArguments, buildTranslateEnv, rewriteErrorContent, rewriteErrorMessage, rewriteFailureMessage, msysEcho, driveToMsys, pathEcho, ERROR_CONTENT_TOOLS, windowsToMsys, rewriteResultPaths, adoptSidebarShell, MARKER_FILE, classify, hashTree, installRegisterShim }
|
|
2498
|
+
export const _internal = { PRESET_IDS, PEER_COVERED_PRESET_ID, PEER_CAPABILITY, isDelegatedAgent, dialectApplies, PEER_PYTHON_FIELD, PEER_PYTHON_BACKEND_FIELD, peerFact, peerBackend, pythonBackendActive, effectivePresetIds, translateDispatch, translateMsysPath, translatePathArguments, rewriteCodePaths, scanCodeLiterals, programPrelude, translateGlobArguments, buildTranslateEnv, rewriteErrorContent, rewriteErrorMessage, rewriteFailureMessage, msysEcho, driveToMsys, pathEcho, ERROR_CONTENT_TOOLS, windowsToMsys, rewriteResultPaths, adoptSidebarShell, MARKER_FILE, classify, hashTree, installRegisterShim, probeHostExtras }
|