dsh-gitbash-shell 0.39.3 → 0.39.4
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 +32 -0
- package/README.md +6 -2
- package/README_EN.md +10 -5
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +2 -1
- package/src/index.js +72 -6
- package/src/inline-tools.js +8 -7
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,38 @@
|
|
|
3
3
|
> 倒序排列,新版本条目在最上面。条目格式:`## vX.Y.Z — YYYY-MM-DD` + 类型(feat / fix / docs / chore)+ 要点 + 相关链接。
|
|
4
4
|
> 纪律见 AGENTS.md「变更记录纪律」:发版前先更新本文件并随版本提交;事故复盘、复现与真机验证记录也记在这里。
|
|
5
5
|
|
|
6
|
+
## v0.39.4 — 2026-10-14
|
|
7
|
+
|
|
8
|
+
**类型**:feat(dsh-ops 0.3.0 互通:gb_* 退役 + 统一方言桥) / fix(docs)。
|
|
9
|
+
|
|
10
|
+
### dsh-ops 0.3.0 互通:gb_* 退役 + 双工具家族统一 `/c/` 方言
|
|
11
|
+
|
|
12
|
+
dsh-ops(T-Auto)0.3.0 起以原生 Rust `ops_*` 文件工具(`ops_inspect_local_file`/`ops_grep`/`ops_glob`/`ops_replace`,
|
|
13
|
+
走 DSH 原生 policy/sandbox/subprocess 通道)取代 FastCtx 上游,并新增 PTC 批量能力(`ops_batch`/`ops_result`,
|
|
14
|
+
opt-in)与跨平台(win/mac/linux)支持。本版把并存形态收敛为「各司其职 + 方言由本插件统一」:
|
|
15
|
+
|
|
16
|
+
- **`fastctxTools` 默认关(弃用声明)**:上游 FastCtx 已被亲爹弃用,`ops_*` 同职能且更强(原生沙箱 mediation、
|
|
17
|
+
统一 `path` 契约、CAS 原子替换、跨平台)。默认值 true→false(schema、stub reader、live reader、client 卡
|
|
18
|
+
四处同步);显式开启可恢复,已装 dsh-ops 时无论开关如何都自动让位(既有 `opsPresent` 检测,0.3.0 工具名
|
|
19
|
+
未变,零改动兼容)。
|
|
20
|
+
- **bash 不让位(产品裁决,2026-10-14)**:dsh-ops 的 `ops_bash` 仅 full-access 会话发布且走裸 `ctx.subprocess`
|
|
21
|
+
(无沙箱 confine、无 unconfined 如实标注);受限会话只有本插件的 bash 可用,且两工具不同名并存无害——
|
|
22
|
+
本插件继续是「全模式真 Git Bash 沙箱通道」,`ops_bash` 是 full-access 下的第二选择。
|
|
23
|
+
- **入参翻译扩展**:字段白名单加 `root`(ops_batch/ops_result 的路径参数,官方工具无此键,零碰撞);
|
|
24
|
+
新增 `operations[]` 对象数组的 `path`/`to` 与 `protect[]` 字符串数组的嵌套翻译(同 present `files[].path`
|
|
25
|
+
的先例)。模型说 `/c/...`,Rust worker 收到原生绝对路径。
|
|
26
|
+
- **出参成功面回流**:ops_* 六件加入 `rewriteResultPaths`——文件四件的 `items[].path`/`matches[].path`
|
|
27
|
+
(Rust 端所有路径字段统一叫 `path`)与 PTC 两件的 `root` 回显 `/c/`;`detailsRef` 等引用 ID 不透明不改写。
|
|
28
|
+
- **失败面回流**:`ERROR_CONTENT_TOOLS` 加 ops_* 六件——失败 content 与 `error.message` 里的 Windows 路径
|
|
29
|
+
统一回答 `/c/`(`error.message` 走无条件 `msysEcho`,自动覆盖)。
|
|
30
|
+
- **提示词无需改动**:dsh-ops 的提示走 `systemPrompt.section`(与官方同挂点),本插件的 `windowsToMsys`
|
|
31
|
+
源头替换覆盖全部 sections;其文案无 `C:/` 字面量(「正斜杠分隔符」与 `/c/` 方言天然兼容)。
|
|
32
|
+
- **已知事项**:未安装 dsh-better-sidebar 的部署,启动时宿主对探测式 patch edit 打一条
|
|
33
|
+
`entry "better-sidebar" not found` warn——宿主 patch 引擎对缺失行的固有行为,插件侧无法在保留该 edit
|
|
34
|
+
功能(boot 层侧栏终端 shell 默认)的前提下去除,README 已说明。
|
|
35
|
+
- **测试**:新增 dsh-ops 互通矩阵(入参 root/operations/protect、出参 items/matches/root、失败 content/message、
|
|
36
|
+
wrapper 门控源码断言、fastctxTools 默认关断言);全量 **172/172**(+1 win32-only skip)。
|
|
37
|
+
|
|
6
38
|
## v0.39.3 — 2026-10-14
|
|
7
39
|
|
|
8
40
|
**类型**:fix(自有 bash 工具的宿主服务兼容 #27、MSYS 参数/编码提示、源码路径边界与 unconfined 可观测性) / test / docs。
|
package/README.md
CHANGED
|
@@ -36,10 +36,14 @@ POSIX 宿主上完整保留;
|
|
|
36
36
|
会话隐藏,主机保持 bash 优先;开启后它直接可见可用——无需安装任何额外组件。
|
|
37
37
|
旧 PowerShell 7 旁路工具(v0.37–v0.38.5 的独立工具)已随 v0.39.0 新架构退役;
|
|
38
38
|
命令仍然首选 bash 工具。
|
|
39
|
-
- **`gb_*` FastCtx 文件工具(
|
|
39
|
+
- **`gb_*` FastCtx 文件工具(默认关,v0.39.4 起弃用)**:`gb_inspect_local_file`/`gb_grep`/
|
|
40
40
|
`gb_glob`/`gb_replace`(Rust 实现:批量范围读取带 token 预算、grep 截断
|
|
41
41
|
标注、编码探测)。它们是官方 read/grep/glob/edit 的**补充**——官方工具接
|
|
42
|
-
|
|
42
|
+
文件沙箱,仍是默认推荐。**dsh-ops 0.3.0 起原生 `ops_*` 文件工具取代了
|
|
43
|
+
FastCtx 上游,本组默认关闭**(`fastctxTools` 显式开启可恢复);已安装
|
|
44
|
+
dsh-ops 时无论开关如何都会自动让位。与 dsh-ops 并存时本插件继续负责
|
|
45
|
+
`/c/` 方言统一:`ops_*` 的入参(含 `root`/`operations[].path`/`protect`)
|
|
46
|
+
自动转换、返回与报错路径自动回流 `/c/`。
|
|
43
47
|
|
|
44
48
|
四个「· Git Bash」**变体 preset 仍保留,默认关闭**(`variantPresets` 开关,
|
|
45
49
|
设置卡可开):开启后注册 `standard-gitbash`/`minimal-gitbash`/`code-gitbash`/
|
package/README_EN.md
CHANGED
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
> on a PRIVATE Git Bash executor instance, gives the four OFFICIAL presets
|
|
11
11
|
> Git Bash through a per-agent inline tool patch (v0.35.0), keeps the
|
|
12
12
|
> official `pwsh` tool (REAL PowerShell) hidden behind a switch (`pwshTool`,
|
|
13
|
-
> off by default),
|
|
14
|
-
>
|
|
15
|
-
>
|
|
13
|
+
> off by default), keeps the `gb_*` FastCtx file tools behind a switch
|
|
14
|
+
> (`fastctxTools`, off by default since v0.39.4 — dsh-ops 0.3.0's native
|
|
15
|
+
> `ops_*` tools superseded FastCtx), and keeps the four `· Git Bash` variant
|
|
16
|
+
> presets behind a switch (`variantPresets`, off by default).
|
|
16
17
|
|
|
17
18
|
### Host requirement & preset alignment
|
|
18
19
|
|
|
@@ -60,8 +61,12 @@ The old "replace the executor" three-step patch became runtime work:
|
|
|
60
61
|
tool face says "pwsh yes / bash no"; the official `pwsh` tool (REAL
|
|
61
62
|
PowerShell) stays hidden behind the `pwshTool` switch (off by default);
|
|
62
63
|
the `gb_*`
|
|
63
|
-
FastCtx tools
|
|
64
|
-
|
|
64
|
+
FastCtx tools (`src/fastctx-bridge.js`) publish alongside — **retired to
|
|
65
|
+
an explicit opt-in from v0.39.4** (dsh-ops 0.3.0 replaced FastCtx with
|
|
66
|
+
native `ops_*` file tools; when dsh-ops is present the bridge stands down
|
|
67
|
+
regardless, and this plugin keeps the `/c/` dialect unified across both
|
|
68
|
+
tool families by translating `ops_*` arguments and echoing their results
|
|
69
|
+
and errors back in MSYS form). With the switch on, it registers the four
|
|
65
70
|
preset variants **declaratively** (`ctx.agentPresets.register`,
|
|
66
71
|
v0.32.0+) — the row sets are committed, diffable JS data
|
|
67
72
|
(`src/compositions.js`) and **no directory is ever materialized**;
|
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-gitbash-shell",
|
|
3
|
-
"version": "0.39.
|
|
3
|
+
"version": "0.39.4",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"description": "Windows 全模式 Git Bash:自带 bash 工具(私有 Git Bash 执行器实例,官方 PowerShell 执行器原样保留),官方四个模式经行内工具补位直接获得 Git Bash(v0.35.0 起;v0.39.0 起不再替换宿主执行器),四个 *·Git Bash 变体 preset 可开关;官方 pwsh 工具默认隐藏、开关可见(真 PowerShell);附带 gb_* FastCtx 文件工具,检测到 dsh-ops 自动让位。联动:dsh-ptc-cordis-preset(PTC 创造模式带 Git Bash 行且可去重)+ dsh-better-sidebar(终端标签自动切 Git Bash)。",
|
|
6
6
|
"engines": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-gitbash-shell",
|
|
3
|
-
"version": "0.39.
|
|
3
|
+
"version": "0.39.4",
|
|
4
4
|
"description": "Windows 上给 agent 一个真正的 Git Bash 命令工具:私有 Git Bash 执行器实例(官方 PowerShell 执行器原样保留),统一 MSYS 盘根路径方言,官方 pwsh 工具默认隐藏可开关,物化四个内置预设的 Git Bash 变体,侧栏终端接管可开关。· Give agents a real Git Bash tool on Windows: a private Git Bash executor instance (the official PowerShell executor stays untouched), a unified MSYS drive-root path dialect, an opt-in official pwsh tool, and four Git Bash preset variants.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/src/client.js
CHANGED
|
@@ -2299,7 +2299,8 @@ window.__ModuleLoader__.load({
|
|
|
2299
2299
|
writeField exactly like every other row on this card. */
|
|
2300
2300
|
var variantOn = snap.value.variantPresets === true;
|
|
2301
2301
|
var pwshOn = snap.value.pwshTool !== false;
|
|
2302
|
-
|
|
2302
|
+
// Default OFF (v0.39.4): must match the host's `=== true` reader.
|
|
2303
|
+
var fctxOn = snap.value.fastctxTools === true;
|
|
2303
2304
|
|
|
2304
2305
|
var modeSection = fold("sec.mode",
|
|
2305
2306
|
E("div", { className: "gb-row" },
|
package/src/index.js
CHANGED
|
@@ -517,7 +517,7 @@ export const Config = Schema === null ? undefined : Schema.object({
|
|
|
517
517
|
* Default ON; Windows-only payload, stands down entirely when dsh-ops is
|
|
518
518
|
* present (its ops_* tools already cover the same ground).
|
|
519
519
|
*/
|
|
520
|
-
fastctxTools: live(Schema.boolean().default(
|
|
520
|
+
fastctxTools: live(Schema.boolean().default(false)),
|
|
521
521
|
})
|
|
522
522
|
|
|
523
523
|
/** Read one Config value across eras: Volatile ref (>= 0.1.7) or plain value. */
|
|
@@ -582,8 +582,8 @@ function makeLiveReader(ctx, config) {
|
|
|
582
582
|
variantPresets: valueOf(config.variantPresets) === true,
|
|
583
583
|
// Default ON (v0.35.0): the pwsh replacement fills the denied host tool.
|
|
584
584
|
pwshTool: valueOf(config.pwshTool) !== false,
|
|
585
|
-
// Default
|
|
586
|
-
fastctxTools: valueOf(config.fastctxTools)
|
|
585
|
+
// Default OFF (v0.39.4, dsh-ops 0.3.0): FastCtx is dead upstream and dsh-ops ships stronger native ops_* file tools; an explicit true re-enables the bridge. It still stands down when dsh-ops is present.
|
|
586
|
+
fastctxTools: valueOf(config.fastctxTools) === true,
|
|
587
587
|
}
|
|
588
588
|
}
|
|
589
589
|
return {
|
|
@@ -593,7 +593,7 @@ function makeLiveReader(ctx, config) {
|
|
|
593
593
|
autoTerminalShell: () => dialect().autoTerminalShell !== false,
|
|
594
594
|
variantPresets: () => (hasConfig ? valueOf(config.variantPresets) === true : false),
|
|
595
595
|
pwshTool: () => (hasConfig ? valueOf(config.pwshTool) !== false : true),
|
|
596
|
-
fastctxTools: () => (hasConfig ? valueOf(config.fastctxTools)
|
|
596
|
+
fastctxTools: () => (hasConfig ? valueOf(config.fastctxTools) === true : false),
|
|
597
597
|
// Default OFF: only an explicit true asks for deduplication.
|
|
598
598
|
suppressPeerCordis: () => (hasConfig ? valueOf(config.suppressPeerCordis) === true : false),
|
|
599
599
|
}
|
|
@@ -773,7 +773,9 @@ function pathEcho(value, env) {
|
|
|
773
773
|
* content VERBATIM: output is data, never rewritten (the same red line as
|
|
774
774
|
* file content in successful results).
|
|
775
775
|
*/
|
|
776
|
-
|
|
776
|
+
// ops_* six (v0.39.4, dsh-ops 0.3.0): their failures quote Windows paths too —
|
|
777
|
+
// the error content and error.message must answer in the same /c/ dialect.
|
|
778
|
+
const ERROR_CONTENT_TOOLS = new Set(['read', 'read_image', 'write', 'edit', 'glob', 'grep', 'present', 'ops_inspect_local_file', 'ops_glob', 'ops_grep', 'ops_replace', 'ops_batch', 'ops_result'])
|
|
777
779
|
|
|
778
780
|
export function rewriteErrorContent(content, options) {
|
|
779
781
|
if (!Array.isArray(content)) return content
|
|
@@ -868,6 +870,37 @@ export function rewriteResultPaths(name, value, env) {
|
|
|
868
870
|
})
|
|
869
871
|
return changed ? { ...value, files } : value
|
|
870
872
|
}
|
|
873
|
+
// v0.39.4 (dsh-ops 0.3.0): ops_* tools answer with the same paths the
|
|
874
|
+
// model must keep speaking /c/ about. Their Rust worker names every path
|
|
875
|
+
// field `path` (items[] / matches[]) and ops_batch/ops_result echo the
|
|
876
|
+
// operation `root`; detailsRef ids are opaque, never rewritten.
|
|
877
|
+
if (name === 'ops_grep' && Array.isArray(value.matches)) {
|
|
878
|
+
let changed = false
|
|
879
|
+
const matches = value.matches.map((match) => {
|
|
880
|
+
if (!match || typeof match !== 'object' || typeof match.path !== 'string' || match.path === '') return match
|
|
881
|
+
const out = toMsys(match.path)
|
|
882
|
+
if (out === match.path) return match
|
|
883
|
+
changed = true
|
|
884
|
+
return { ...match, path: out }
|
|
885
|
+
})
|
|
886
|
+
return changed ? { ...value, matches } : value
|
|
887
|
+
}
|
|
888
|
+
if ((name === 'ops_inspect_local_file' || name === 'ops_glob' || name === 'ops_replace') && Array.isArray(value.items)) {
|
|
889
|
+
let changed = false
|
|
890
|
+
const items = value.items.map((item) => {
|
|
891
|
+
if (!item || typeof item !== 'object' || typeof item.path !== 'string' || item.path === '') return item
|
|
892
|
+
const out = toMsys(item.path)
|
|
893
|
+
if (out === item.path) return item
|
|
894
|
+
changed = true
|
|
895
|
+
return { ...item, path: out }
|
|
896
|
+
})
|
|
897
|
+
return changed ? { ...value, items } : value
|
|
898
|
+
}
|
|
899
|
+
if ((name === 'ops_batch' || name === 'ops_result') && typeof value.root === 'string' && value.root !== '') {
|
|
900
|
+
const out = toMsys(value.root)
|
|
901
|
+
if (out !== value.root) return { ...value, root: out }
|
|
902
|
+
return value
|
|
903
|
+
}
|
|
871
904
|
if (name === 'glob' && Array.isArray(value.paths)) {
|
|
872
905
|
let changed = false
|
|
873
906
|
const paths = value.paths.map((p) => {
|
|
@@ -909,7 +942,9 @@ export function rewriteResultPaths(name, value, env) {
|
|
|
909
942
|
|
|
910
943
|
const MSYS_DRIVE_ROOT = /^\/([a-z])\/(.*)$/i
|
|
911
944
|
const MSYS_BARE_DRIVE = /^\/([a-z])$/i
|
|
912
|
-
|
|
945
|
+
// 'root' (v0.39.4): dsh-ops ops_batch/ops_result name their path argument `root`;
|
|
946
|
+
// no official tool uses that key, so the extra alias is collision-free.
|
|
947
|
+
const TRANSLATABLE_PATH_FIELDS = new Set(['file_path', 'path', 'workdir', 'root'])
|
|
913
948
|
|
|
914
949
|
// ── virtual mounts + env (v0.17.0) ─────────────────────────────────────
|
|
915
950
|
//
|
|
@@ -1143,6 +1178,37 @@ export function translatePathArguments(args, env) {
|
|
|
1143
1178
|
changed = true
|
|
1144
1179
|
}
|
|
1145
1180
|
}
|
|
1181
|
+
// v0.39.4 (dsh-ops 0.3.0): ops_batch carries its paths one level down —
|
|
1182
|
+
// operations[] objects with path/to, and protect[] as a plain string
|
|
1183
|
+
// array. Same deal as present's files[].path: the field-name whitelist
|
|
1184
|
+
// only sees top-level keys. The model speaks /c/ everywhere; these
|
|
1185
|
+
// arrive at the Rust worker as native absolute paths.
|
|
1186
|
+
if (Array.isArray(next.operations)) {
|
|
1187
|
+
const operations = next.operations.map((operation) => {
|
|
1188
|
+
if (!operation || typeof operation !== 'object') return operation
|
|
1189
|
+
let out = operation
|
|
1190
|
+
for (const field of ['path', 'to']) {
|
|
1191
|
+
if (typeof out[field] !== 'string') continue
|
|
1192
|
+
const translated = translateMsysPath(out[field], env)
|
|
1193
|
+
if (translated !== out[field]) out = { ...out, [field]: translated }
|
|
1194
|
+
}
|
|
1195
|
+
return out
|
|
1196
|
+
})
|
|
1197
|
+
if (operations.some((operation, index) => operation !== next.operations[index])) {
|
|
1198
|
+
next.operations = operations
|
|
1199
|
+
changed = true
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (Array.isArray(next.protect)) {
|
|
1203
|
+
const protect = next.protect.map((entry) => {
|
|
1204
|
+
if (typeof entry !== 'string') return entry
|
|
1205
|
+
return translateMsysPath(entry, env)
|
|
1206
|
+
})
|
|
1207
|
+
if (protect.some((entry, index) => entry !== next.protect[index])) {
|
|
1208
|
+
next.protect = protect
|
|
1209
|
+
changed = true
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1146
1212
|
return changed ? next : args
|
|
1147
1213
|
}
|
|
1148
1214
|
|
package/src/inline-tools.js
CHANGED
|
@@ -285,19 +285,20 @@ export function mountInlineTools(ctx, deps) {
|
|
|
285
285
|
const fiber = scope.ctx.plugin(module)
|
|
286
286
|
await untilDisposed(fiber, state.controller.signal)
|
|
287
287
|
if (!isLive(agent, state)) return
|
|
288
|
-
retained = true
|
|
289
288
|
// An INACTIVE fiber resolves its await() without ever running apply()
|
|
290
289
|
// (issue #27): awaiting the plugin proves nothing, so read the registry
|
|
291
|
-
// back before claiming success. A miss
|
|
292
|
-
//
|
|
290
|
+
// back before claiming success. A miss THROWS — the catch below logs the
|
|
291
|
+
// explicit activation failure and the finally disposes this half-mounted
|
|
292
|
+
// scope instead of leaking it for the agent's lifetime (shape credit:
|
|
293
|
+
// KouzakiUmi's PR #28).
|
|
293
294
|
const registeredNames = scope.ctx.get !== undefined
|
|
294
295
|
? ((scope.ctx.get('tools')?.schemas?.(agent) ?? []).map((schema) => schema && typeof schema.name === 'string' ? schema.name : ''))
|
|
295
296
|
: []
|
|
296
|
-
if (registeredNames.includes('bash')) {
|
|
297
|
-
|
|
298
|
-
} else {
|
|
299
|
-
log('own bash tool did NOT register for this agent: the module fiber likely went INACTIVE (this host lacks an injected service the module requires) or the tools read-back is unavailable — bash is NOT available in this session; please report this with your dsh version (issue #27 pattern)')
|
|
297
|
+
if (registeredNames.includes('bash') === false) {
|
|
298
|
+
throw new Error('bash tool did not register for this agent (module fiber INACTIVE — this host lacks an injected service the module requires — or the tools read-back is unavailable); bash is NOT available in this session, please report this with your dsh version (issue #27)')
|
|
300
299
|
}
|
|
300
|
+
retained = true
|
|
301
|
+
log('own bash tool activated for this agent (Git Bash execution; official pwsh tool real but hidden by default)')
|
|
301
302
|
} catch (error) {
|
|
302
303
|
if (isLive(agent, state)) log(`own bash tool activation failed for this agent: ${error?.message ?? error}`)
|
|
303
304
|
} finally {
|