dsh-gitbash-shell 0.39.1 → 0.39.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/CHANGELOG.md +100 -0
- package/README.md +38 -14
- package/README_EN.md +54 -18
- package/dsh.plugin.json +1 -1
- package/package.json +3 -3
- package/src/bash-render.js +21 -10
- package/src/bash-tool.js +79 -26
- package/src/client.js +36 -25
- package/src/compositions.js +11 -7
- package/src/fastctx-bridge.js +78 -31
- package/src/index.js +185 -152
- package/src/inline-tools.js +124 -50
- package/src/shell.js +14 -14
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,106 @@
|
|
|
3
3
|
> 倒序排列,新版本条目在最上面。条目格式:`## vX.Y.Z — YYYY-MM-DD` + 类型(feat / fix / docs / chore)+ 要点 + 相关链接。
|
|
4
4
|
> 纪律见 AGENTS.md「变更记录纪律」:发版前先更新本文件并随版本提交;事故复盘、复现与真机验证记录也记在这里。
|
|
5
5
|
|
|
6
|
+
## v0.39.3 — 2026-10-14
|
|
7
|
+
|
|
8
|
+
**类型**:fix(自有 bash 工具的宿主服务兼容 #27、MSYS 参数/编码提示、源码路径边界与 unconfined 可观测性) / test / docs。
|
|
9
|
+
|
|
10
|
+
### issue #27:自有 bash 工具在无 `workingDirectory` 服务的宿主上永不注册
|
|
11
|
+
|
|
12
|
+
- **根因(route B 镜像的契约盲区)**:自有 bash 工具镜像的是新版宿主(`0.2.1-alpha.x`)官方 tool-bash
|
|
13
|
+
的 `inject` 列表(含 `workingDirectory`),而兼容矩阵声明的 `0.2.0-rc.2` 上**没有该服务**——cordis 对
|
|
14
|
+
无法满足的 inject 让模块 fiber **永久 INACTIVE**:`apply()` 从不执行、工具永不注册、零错误日志,且
|
|
15
|
+
inline-tools 的激活日志照打(`await` 一个 INACTIVE fiber 会正常 resolve)——**假阳性成功**。
|
|
16
|
+
与 v0.24.0 settingsScope、v0.24.3 静态 peer import 同一失败类(静默失效、宿主日志全绿)。
|
|
17
|
+
- **修复**:① `inject` 收敛为 `['tools', 'systemPrompt', 'shellEnv']`(服务最小面,`workingDirectory`
|
|
18
|
+
改为使用点 `ctx.get()` 可选读取);② cwd 接缝双向兼容——有 `workingDirectory` 的宿主走
|
|
19
|
+
`ensure()`(与新版官方一致),没有的宿主退回 rc.2 时代官方 `resolveWorkdir` 的同一条接缝
|
|
20
|
+
(`policy.workspaceRoot ?? session.header.cwd`),**不再落到宿主进程目录**;③ 激活日志必须经
|
|
21
|
+
`tools.schemas(agent)` 回读确认 `bash` 真正在册,不在册打显式失败行(点名 INACTIVE 可能性 +
|
|
22
|
+
请携带 dsh 版本报告)——**消除假阳性**。
|
|
23
|
+
- **教训(§4l 追记⑧)**:镜像宿主模块时,`inject` 面是**契约面**——必须取兼容矩阵内**最老宿主**的
|
|
24
|
+
形状,或一律可选读取;「宿主新版本有」不等于「矩阵内都有」。
|
|
25
|
+
- 报告者 largoalladonzella 提供了完整根因链(asar 字节级取证 + cordis `_refresh`/`_setEpoch` 源码 +
|
|
26
|
+
官方两版 inject 对照)与本机验证补丁;本仓库按其建议实现并补真断言。
|
|
27
|
+
- 新增回归:无 `workingDirectory` 的最小 ctx 上 `apply()` 照常注册 + cwd 回退正确;有服务的宿主
|
|
28
|
+
`ensure()` 接缝不变;源码断言锁死硬 inject 列表与「回读门控激活日志」。本地全量 **168/168**(+1
|
|
29
|
+
win32-only skip)。
|
|
30
|
+
|
|
31
|
+
### PR #24:超时转后台的独立 sandbox 警示
|
|
32
|
+
|
|
33
|
+
- promoted 返回值显式携带可选 sandbox facts,输出 schema 保持闭合;defineTool 与 JSON-schema 回退共用此契约。
|
|
34
|
+
- `renderPromoted` 自行追加 sandbox notes,不再依赖 output 预先含警示;promotion 输出保留丢失/spill 提示,
|
|
35
|
+
避免重复追加 sandbox 警示。无 sandbox/full-access 的原反馈保持不变,denied/runnerFailed 分支继续保留。
|
|
36
|
+
- 纯回归改用不含警示的原始文本,覆盖空输出、full-access、拒绝与 runner failure。
|
|
37
|
+
新增实际 ToolRuntime→私有执行器→内存 provider 的 promotion 回归(Jobs wait/read 为受控桩),
|
|
38
|
+
验证 RO/WW 警示各出现一次、full 无警示、DTO 与最终模型文本分离、丢失/spill 提示及 root 服务不变。
|
|
39
|
+
- 验证:普通完整回归 **166/166**、实际安装Core完整preview **18/18**,均exit0,无skip/cancel;
|
|
40
|
+
语法与diff检查通过。不等同于生产GUI或真实Jobs时钟验收。
|
|
41
|
+
|
|
42
|
+
### MSYS 兼容边界尝试修复
|
|
43
|
+
|
|
44
|
+
- **范围**:从 [`6b26ded`](https://github.com/KouzakiUmi/dsh-gitbash-shell/commit/6b26ded6cc3a2a6113579149286047629b433c48)
|
|
45
|
+
开出 `fix/msys-compatibility-boundaries`;不改 root PowerShell、market patch、生产 Profile、
|
|
46
|
+
安全默认或版本号。原生参数转换/混合编码/Windows shell confinement 不能在这层安全地普遍修好,
|
|
47
|
+
因此修工具提示与可观测性,不全局 EXCL=*、不猜码、不将警示冒充沙箱修复。
|
|
48
|
+
- **源码路径**:run_code 的完整 Unicode 字母/组合字符/数字和含 `#` 文件名字面量走共享挂载表;
|
|
49
|
+
合法 EOF 行注释不再让整份重写弃权;tagged templates(含 String.raw、调用/索引 tag)保持原样,
|
|
50
|
+
避免 raw 设备路径被错误转义;of/await/yield 等上下文词可作 tag 时保守不改。
|
|
51
|
+
未闭合/不确定正则与除法仍整份 no-op,未扩展插值或动态表达式猜测。
|
|
52
|
+
词法层仍无法识别非路径数据的业务用途,提示明确建议直接 Node fs/path 用 native/cwd-relative 路径。
|
|
53
|
+
- **提示门控**:真实 prompt listener 读取 live codePaths;OFF 不再承诺程序字符串会被翻译,
|
|
54
|
+
ON 改为 best-effort 并指出模板、动态/非路径数据限制。关闭 codePaths 不改变工具路径字段翻译。
|
|
55
|
+
- **原生互操作**:登记的 bash 描述无条件说明 `/all`/`/c` 转换、双斜杠/单命令 EXCL 前缀及其
|
|
56
|
+
对 `/c/...` 的影响;检查预期效果而非只看 exit0。说明 UTF-8 捕获与 CP936/GBK 不匹配,
|
|
57
|
+
显式转码须在字节捕获前且保持退出状态;locale 与全流猜码不是普遍修复。
|
|
58
|
+
- **unconfined 可见性**:执行句柄在等待 result() 前带 sandbox facts;前台正文、后台 outcome/
|
|
59
|
+
增量嵌入与超时转后台显示警示。后台登记输出联合新增可选 sandbox facts,指出计划执行边界,
|
|
60
|
+
不把未启动的 job 宣称为已经执行。保留原请求 mode,不把 unconfined 当 denial/escalation,
|
|
61
|
+
并明确不得通过 shell 绕过文件工具策略拒绝。双语说明区分文件读能力与 write/edit 围栏,
|
|
62
|
+
删除“所有未来版本都无解”的过度结论。
|
|
63
|
+
- **验证**:新增9项兼容回归和1项后台登记回归,普通全量 **166/166**;新增3项实际 Core
|
|
64
|
+
ToolRuntime/执行句柄/run_code transport 回归,完整无头 preview **17/17**。旧护栏保留;打包 dry-run、语法和 diff
|
|
65
|
+
检查通过。新增 preview 使用实际宿主 API 与内存 subprocess,已有 preview 保留真实 Git Bash/
|
|
66
|
+
PowerShell 进程验收;不等同于生产 GUI、真实 PTC 引擎或新 confinement 方案验证。
|
|
67
|
+
|
|
68
|
+
## v0.39.2 — 2026-10-13
|
|
69
|
+
|
|
70
|
+
**类型**:fix(route B 工具/预设生命周期、动态配置与客户端回归) / test。
|
|
71
|
+
|
|
72
|
+
- **复现与裁定**:两名 Sol 分区审查、Grok 交叉反证与主代理隔离复验确认了工具面、生命周期、
|
|
73
|
+
组合、代码扫描和客户端状态转换的问题;普通子 scope 的原始冲突仍由 v0.39.1 的 `shell` 隔离修复。
|
|
74
|
+
FastCtx 全文路径回显是既定策略,不误当文件工具替换问题;minimal 的 persistent PTY 不与全工具
|
|
75
|
+
变体的执行器来源混为一谈。只修既有功能,不新增功能、改 root shell 或市场 patch。
|
|
76
|
+
- **变体接线**:Windows 全工具 Git Bash 变体不再启用依赖 root PowerShell 的官方 `tool-bash`;
|
|
77
|
+
保留官方 pwsh 面,经同一行内补位通道接入私有 Git Bash 工具。minimal 的 persistent 通道保持。
|
|
78
|
+
- **工具生命周期**:存活 agent 的 `pwshTool` 双向翻转同步解除/重建自身的 restrict;异步导入/
|
|
79
|
+
挂载在插件或 agent 退役后不再迟到注册;jobs 服务卸载按真实 Cordis 活跃状态恢复前台 bash。
|
|
80
|
+
- **降级分支**:plain definition 的 required 标志递归转换为合法 JSON Schema;升级助手不可用
|
|
81
|
+
时仍解析并传递每会话 standing policy,不错误回退部署默认。两项为模块解析降级的受控复现,
|
|
82
|
+
不宣称本机生产已发生导入失败,也不改变 Windows 受限模式明示 unconfined 的既定取舍。
|
|
83
|
+
- **预设与默认值**:串行 reconcile 退役时关闭新任务、清理晚到 register 并等待 unregister;
|
|
84
|
+
动态关闭变体在退役前迁回部署默认,避免后续新会话引用不存在的 preset。既有钉住会话不迁移。
|
|
85
|
+
- **路径与终端**:run_code 扫描辨认关键字后的正则并在不确定时整份 no-op,不改正则数据;
|
|
86
|
+
FastCtx 入/出参获得 execution 上下文并服从总方言、委托与虚拟挂载开关;better-sidebar 的
|
|
87
|
+
adoptSidebar 动态翻转恢复接管前值并保护接管后的用户手选,不改变官方 autoTerminalShell
|
|
88
|
+
关后只停止未来写入的独立语义。
|
|
89
|
+
- **FastCtx 错误**:子进程/写管道 error 进入初始化 Promise,失败清理 pending 计时器与连接;
|
|
90
|
+
MCP `isError:true` 按官方桥约定抛错,不再把失败包装成宿主成功 value。
|
|
91
|
+
发现与连接启动采用 single-flight,拥有在途连接并在卸载时取消/等待,不让并发 agent 的发现流程
|
|
92
|
+
覆盖已在使用的 lazy client。该丢失所有权链已用受控 MCP 传输复现,并新增关闭/晚注册回归。
|
|
93
|
+
- **客户端**:设置卡所有 Hooks 在 readiness early-return 前调用;工具链进度轮询在网络/HTTP
|
|
94
|
+
暂态失败后重试,卸载或失效时取消,不因单次请求失败永久卡在忙碌状态。
|
|
95
|
+
- **回归入口**:新增可移植客户端/桥/运行时/宿主回归与实际 Core preview 集成用例,接入现有
|
|
96
|
+
`npm test`/`npm run test:preview`;不新增依赖、不跳过旧断言或 Node 22 矩阵。对齐上游 v0.39.1
|
|
97
|
+
(`521be01`)后完整测试 **156/156**、实际 Core 无头 preview **14/14**、打包 dry-run 和 diff 检查
|
|
98
|
+
通过。新增执行级回归保留 #21 的 real-array glob 契约与 stringified-array 响亮拒绝;不改已发布
|
|
99
|
+
版本号、历史记录或 npm 发布流程。Grok 对修复 diff 的只读反证未发现确定遗漏。
|
|
100
|
+
首轮全量检查发现三条旧源码正则仍锁旧实现,改为对应新保护条件并补同值/手选行为测试,没有删掉
|
|
101
|
+
保护断言。原 [Issue #19](https://github.com/KannaKuron/dsh-gitbash-shell/issues/19) 和
|
|
102
|
+
[PR #20](https://github.com/KannaKuron/dsh-gitbash-shell/pull/20) 已完成;这些后续修复由新 PR 承载,
|
|
103
|
+
Windows/Linux × Node 22/24 的远端结果见其检查。preview 不等同于完整 Electron GUI 或真 MCP
|
|
104
|
+
exe 并发验收,模块解析降级使用受控夹具复现。
|
|
105
|
+
|
|
6
106
|
## v0.39.1 — 2026-10-13
|
|
7
107
|
|
|
8
108
|
**类型**:fix(route B 私有执行器的真实服务隔离与读取授权) / test / ci。
|
package/README.md
CHANGED
|
@@ -118,20 +118,19 @@ bundle patch(`cordis.patch.yml`)是**纯新增**(v0.39.0 起):只插入本插件
|
|
|
118
118
|
**Windows 宿主上的事实**:`read-only` / `workspace-write` 权限预设下的 **shell 命令**,
|
|
119
119
|
实际以**不受限(unconfined)方式执行**:
|
|
120
120
|
|
|
121
|
-
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
shell 能写出工作区、文件工具不能,**两者的边界并不一致**。
|
|
121
|
+
- **为什么**:已验证的 Git Bash/MSYS 组合在现有 restricted-token 后端下初始化失败,
|
|
122
|
+
历史复现为 `msys-2.0.dll` 的 cygheap/signal-pipe 对象权限与 restricting-SID
|
|
123
|
+
二次写检查不兼容(Win32 error 5 / 0xC0000142)。当前没有提供可用的受限 Git Bash
|
|
124
|
+
实现,不声称所有未来运行时或隔离方案都无解。本插件保留**如实标注的绕过**;
|
|
125
|
+
没有静默回退 pwsh/cmd/WSL,本次兼容修复也没有恢复 shell 写入围栏。
|
|
126
|
+
- **如何标注**:结果元数据保留
|
|
127
|
+
`sandbox: { mode: "<请求的模式>", denied: false, enforcement: "unconfined" }`;
|
|
128
|
+
工具描述、前台正文、后台登记及结果摘要、超时转后台反馈都明确警示 shell 写限制未生效。
|
|
129
|
+
后台登记标明的是计划采用的执行边界,不代表命令已经运行;实例首次调用另有日志。
|
|
130
|
+
- **文件工具是另一层**:官方 fs-tool policy 对 write/edit 保留策略检查:
|
|
131
|
+
`read-only` 拒绝修改,`workspace-write` 只允许声明的工作区和平台临时可写根。
|
|
132
|
+
read/glob/grep 等读取并非一律限制在工作区内。shell 进程不受这些工具检查约束,
|
|
133
|
+
**两者的边界并不一致**;绝不能用 shell 绕过文件工具已返回的策略拒绝。
|
|
135
134
|
- **可观察的对照**(相对 pwsh-sandbox 时代):会话进程的完整性级别不再被压低
|
|
136
135
|
(Low → Medium);`$env:TEMP` 不再被重定向到 per-session 隔离目录。
|
|
137
136
|
|
|
@@ -396,6 +395,31 @@ PTC/创造及用户自建模式一律覆盖。
|
|
|
396
395
|
都不再改写,文件工具收 Windows 路径。**bash 始终是 Git Bash,不受此开关影响**——它只决定
|
|
397
396
|
跨工具的路径方言。
|
|
398
397
|
|
|
398
|
+
## 原生 Windows 程序与 run_code 的兼容边界
|
|
399
|
+
|
|
400
|
+
工具描述始终警示下面的参数、编码和安全限制;这些限制不随路径方言关闭而消失。
|
|
401
|
+
插件**不会自动改写 command、全局关闭 MSYS 参数转换或猜测输出编码**。
|
|
402
|
+
|
|
403
|
+
- **斜杠开关**:MSYS 在启动原生 Windows 程序时可能把 `/all`、`/c` 当路径。
|
|
404
|
+
本机已验证 `ipconfig //all`、`cmd //c` 或单命令
|
|
405
|
+
`MSYS2_ARG_CONV_EXCL='/all' ipconfig /all` 可保留开关。
|
|
406
|
+
排除值是前缀:排除 `/c` 也会影响真正的 `/c/...` 路径,该次原生调用应明确用
|
|
407
|
+
Windows drive-letter 路径。不要全局设置 `MSYS2_ARG_CONV_EXCL='*'`。
|
|
408
|
+
`cmd /c` 被转换后可能 exit0 却没有执行所需命令,应检查预期效果而非只看退出码。
|
|
409
|
+
- **输出编码**:宿主按 UTF-8 解码;原生 Windows 程序可能输出 CP936/GBK 或其他代码页。
|
|
410
|
+
先使用程序自己的 UTF-8 输出选项,或在捕获前按已知编码显式转换字节,保持原退出状态。
|
|
411
|
+
`LANG/LC_ALL` 不保证改变原生程序的代码页。不要全流默认 GBK 或依替换字符数自动猜码:
|
|
412
|
+
同一 Bash 调用可能混合 UTF-8/GBK,已有乱码字符串也不能可靠恢复原字节。
|
|
413
|
+
- **run_code**:`codePaths` 默认开,与 `posixPaths`/委托 gate 同时生效时,完整、可判定的
|
|
414
|
+
普通路径字面量按共享挂载表尽力转换,包括中文/组合字符/空格/`#` 文件名。
|
|
415
|
+
EOF 行注释不再阻止转换;tagged template(包括 String.raw)保持原样,避免破坏 raw/tag 语义。
|
|
416
|
+
插值模板、转义路径、不确定的正则/除法语法仍可能不改;不确定时整份 no-op。
|
|
417
|
+
直接调用 Node fs/path 时优先明确的 Windows drive-letter 或 cwd-relative 路径;
|
|
418
|
+
工具 path/file_path 字段仍走工具参数翻译。词法扫描不能识别路由、搜索数据或 shell
|
|
419
|
+
command 的用途;此类字符串不要依赖自动重写,必要时关闭 `codePaths`(提示同步标为 OFF)。
|
|
420
|
+
- **沙箱**:参见上方安全章节。警示可见性得到修复,但 Windows shell 的 unconfined
|
|
421
|
+
取舍未改变,文件工具的策略拒绝绝不授权 shell 重试。
|
|
422
|
+
|
|
399
423
|
## 与 dsh-better-sidebar 联动
|
|
400
424
|
|
|
401
425
|
装了 [dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar)(v0.15.2+) 时,Windows 上本插件会通过**它的官方设置补丁口**(运行时 `terminalShell` 设置,对方文档明示"settings-page overrides win for terminals opened afterwards")把它打开的终端 shell 指向 Git Bash——**UI 终端标签与模型侧 `terminal_*` 工具统一生效**,不改对方一行代码、新开的终端即生效。此外,**本插件的 bundle patch 还会给对方的行补上 `config.shell`**(启动期解析,连 **tab 标题也会显示 bash**;对方未安装时该行无害跳过)。规则:
|
package/README_EN.md
CHANGED
|
@@ -93,24 +93,26 @@ restricted tokens, workspace fencing, denial classification, identical to
|
|
|
93
93
|
**On Windows**, shell commands under the `read-only` / `workspace-write`
|
|
94
94
|
permission presets actually run **UNCONFINED**:
|
|
95
95
|
|
|
96
|
-
- **Why**:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
- **
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
the workspace
|
|
96
|
+
- **Why**: tested Git Bash/MSYS combinations failed to initialize under the
|
|
97
|
+
existing restricted-token backend. Historical reproduction found cygheap/
|
|
98
|
+
signal-pipe object permissions incompatible with the restricting-SID write
|
|
99
|
+
check (Win32 error 5 / 0xC0000142). No working confined Git Bash implementation
|
|
100
|
+
is provided here; this is not a claim that every future runtime or isolation
|
|
101
|
+
design is impossible. The plugin retains its **labelled bypass**, with no
|
|
102
|
+
silent fallback to pwsh/cmd/WSL. Compatibility fixes do not restore confinement.
|
|
103
|
+
- **How it is labelled**: results retain
|
|
104
|
+
`sandbox: { mode: "<requested mode>", denied: false, enforcement: "unconfined" }`.
|
|
105
|
+
The tool description, foreground text, initial background registration,
|
|
106
|
+
background outcome and timeout-promotion feedback warn that shell write
|
|
107
|
+
restrictions are not enforced. Initial registration describes the planned
|
|
108
|
+
boundary, not a claim that the command has already run; there is also a
|
|
109
|
+
one-time notice per executor instance.
|
|
110
|
+
- **Official file tools have a separate policy**: write/edit still reject
|
|
111
|
+
mutations under `read-only`; `workspace-write` checks the declared workspace
|
|
112
|
+
and platform writable temp roots. Reads such as read/glob/grep are not all
|
|
113
|
+
limited to the workspace. Shell processes are not governed by these tool
|
|
114
|
+
checks — **the two boundaries differ**. Never bypass a file-tool policy
|
|
115
|
+
denial through shell.
|
|
114
116
|
- **Observable differences** vs the pwsh-sandbox era: the session process's
|
|
115
117
|
integrity level is no longer lowered (Low → Medium), and `$env:TEMP` is no
|
|
116
118
|
longer redirected to a per-session directory.
|
|
@@ -125,6 +127,40 @@ official PowerShell executor was never touched by this plugin. The
|
|
|
125
127
|
> The full analysis (the 0.6.0–0.13.1 failure history and the community's two
|
|
126
128
|
> camps) is in the CHANGELOG under v0.13.2 and in the `src/shell.js` header.
|
|
127
129
|
|
|
130
|
+
## Native Windows and run_code compatibility boundaries
|
|
131
|
+
|
|
132
|
+
The tool description always warns about these argument, encoding and security
|
|
133
|
+
boundaries, independently of the path-dialect switch. The plugin does not
|
|
134
|
+
silently rewrite command source, globally disable argument conversion or guess
|
|
135
|
+
output encodings.
|
|
136
|
+
|
|
137
|
+
- **Slash options**: MSYS can treat native Windows `/all` and `/c` as paths.
|
|
138
|
+
Tested alternatives are `ipconfig //all`, `cmd //c`, or a per-command
|
|
139
|
+
`MSYS2_ARG_CONV_EXCL='/all' ipconfig /all`. Exclusions match prefixes: excluding
|
|
140
|
+
`/c` also excludes `/c/...` file paths, so explicitly use native drive-letter
|
|
141
|
+
paths for those arguments. Do not set `MSYS2_ARG_CONV_EXCL='*'` globally.
|
|
142
|
+
A converted `cmd /c` may exit0 without performing the intended action; verify
|
|
143
|
+
the expected effect, not only the exit code.
|
|
144
|
+
- **Output encodings**: the host decodes UTF-8. Native programs may emit CP936/GBK
|
|
145
|
+
or another code page. Prefer a program's UTF-8 option, or explicitly convert
|
|
146
|
+
known-encoding bytes before capture while preserving the original exit state.
|
|
147
|
+
Locale variables do not guarantee native UTF-8. Do not decode all Bash output
|
|
148
|
+
as GBK or infer encoding from replacement-character counts: one call can mix
|
|
149
|
+
UTF-8 and GBK, and an already-corrupted string cannot recover the original bytes.
|
|
150
|
+
- **run_code**: `codePaths` defaults ON and follows the total/delegate dialect
|
|
151
|
+
gates. Complete compatible plain path literals are rewritten best-effort with
|
|
152
|
+
the shared mount table, including Unicode letters/marks, spaces and `#` names.
|
|
153
|
+
Legal EOF line comments no longer prevent rewriting; tagged templates (such
|
|
154
|
+
as String.raw) retain their data and raw semantics. Interpolation, escapes,
|
|
155
|
+
uncertain regex/division syntax may remain unchanged; uncertainty abandons the
|
|
156
|
+
entire rewrite. Prefer native drive-letter or cwd-relative paths for direct
|
|
157
|
+
Node fs/path APIs; typed tool path arguments remain translated separately.
|
|
158
|
+
Lexical scanning cannot distinguish routes/search data/shell command strings
|
|
159
|
+
from paths. Do not rely on rewriting those values; disable `codePaths` when
|
|
160
|
+
needed (the model hint now accurately states OFF).
|
|
161
|
+
- **Security**: warnings are now visible, but Windows unconfined behavior is
|
|
162
|
+
unchanged. A file-tool policy denial never authorizes a shell retry.
|
|
163
|
+
|
|
128
164
|
## Config
|
|
129
165
|
|
|
130
166
|
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.39.
|
|
3
|
+
"version": "0.39.3",
|
|
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.3",
|
|
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": {
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"icon.svg"
|
|
53
53
|
],
|
|
54
54
|
"scripts": {
|
|
55
|
-
"test": "node --test tests/smoke.mjs tests/private-executor.mjs",
|
|
56
|
-
"test:preview": "node --import ./tests/host-loader.mjs --test tests/preview.mjs",
|
|
55
|
+
"test": "node --test tests/smoke.mjs tests/private-executor.mjs tests/client-regressions.mjs tests/bridge-regressions.mjs tests/runtime-regressions.mjs tests/host-regressions.mjs tests/compatibility-regressions.mjs",
|
|
56
|
+
"test:preview": "node --import ./tests/host-loader.mjs --test tests/preview.mjs tests/bridge-preview.mjs tests/runtime-preview.mjs tests/host-preview.mjs",
|
|
57
57
|
"version": "node -e \"const fs=require('fs');const m=JSON.parse(fs.readFileSync('dsh.plugin.json','utf8'));const v=JSON.parse(fs.readFileSync('package.json','utf8')).version;m.version=v;fs.writeFileSync('dsh.plugin.json',JSON.stringify(m,null,2)+'\\n')\" && git add dsh.plugin.json"
|
|
58
58
|
},
|
|
59
59
|
"keywords": [
|
package/src/bash-render.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* here). Instead the plugin ships its OWN bash tool whose executor is a private
|
|
8
8
|
* `GitBashSandboxExecutor` instance (never registered as a service), and this
|
|
9
9
|
* file mirrors the official tool's rendering contract call-for-call so the
|
|
10
|
-
* model sees
|
|
10
|
+
* model sees the official output shapes, with an explicit unconfined warning:
|
|
11
11
|
*
|
|
12
12
|
* - `render.ts` of `@deepseek-ai/dsh-tool-bash` (result / promoted / job-read
|
|
13
13
|
* text shaping), and
|
|
@@ -28,6 +28,17 @@ export function sandboxDenialMarker(mode) {
|
|
|
28
28
|
return `[sandbox: file access denied under ${mode} mode]`
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/** A capability warning, not a denial or permission to bypass another denial. */
|
|
32
|
+
export function sandboxUnconfinedMarker(mode) {
|
|
33
|
+
return `[sandbox: Git Bash is unconfined on Windows — shell file-write restrictions are NOT enforced (requested mode: ${mode}); official file-tool write/edit policy still applies; never bypass its denial through shell]`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Include the planned execution boundary even before the job has spawned. */
|
|
37
|
+
export function renderBackground(jobId, sandbox) {
|
|
38
|
+
const notes = sandboxNotes(sandbox, [])
|
|
39
|
+
return `started background job ${jobId}${notes.length ? '\n' + notes.join('\n') : ''}`
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
/** The official same-turn escalation hint (escalation.ts:84). */
|
|
32
43
|
export function escalationHintMarker(subject) {
|
|
33
44
|
return `[sandbox: escalation available — retry this exact ${subject} once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]`
|
|
@@ -84,6 +95,7 @@ export function renderResult(result, escalationModes = []) {
|
|
|
84
95
|
markers.push(sandboxDenialMarker(result.sandbox.mode))
|
|
85
96
|
if (escalationModes.length > 0) markers.push(escalationHintMarker('command'))
|
|
86
97
|
}
|
|
98
|
+
if (result.sandbox?.enforcement === 'unconfined') markers.push(sandboxUnconfinedMarker(result.sandbox.mode))
|
|
87
99
|
// A command may trap SIGTERM and exit 0 after timeout; still report interruption.
|
|
88
100
|
if (result.timedOut) markers.push(`[timed out after ${result.timeoutMs}ms]`)
|
|
89
101
|
// A kill from outside the call (the human stopping its job) is not a
|
|
@@ -103,16 +115,19 @@ export function renderResult(result, escalationModes = []) {
|
|
|
103
115
|
/**
|
|
104
116
|
* Shape a foreground call that stopped waiting into the text the model sees.
|
|
105
117
|
* Mirrors the official `renderPromoted` (tool-bash render.ts:77).
|
|
106
|
-
* @param {{ jobId: string, timeoutMs: number, output: string }} promoted
|
|
118
|
+
* @param {{ jobId: string, timeoutMs: number, output: string, sandbox?: object }} promoted
|
|
119
|
+
* @param {string[]} [escalationModes]
|
|
107
120
|
* @returns {string}
|
|
108
121
|
*/
|
|
109
|
-
export function renderPromoted(promoted) {
|
|
122
|
+
export function renderPromoted(promoted, escalationModes = []) {
|
|
110
123
|
const body = promoted.output.length > 0
|
|
111
124
|
? (promoted.output.endsWith('\n') ? promoted.output : `${promoted.output}\n`)
|
|
112
125
|
: ''
|
|
113
|
-
|
|
126
|
+
const text = `${body}[still running after ${promoted.timeoutMs}ms; moved to background job ${promoted.jobId}]\n`
|
|
114
127
|
+ 'The command keeps running in the background. You will be notified when it finishes; '
|
|
115
128
|
+ 'read newer output with job_output, stop it with job_kill.'
|
|
129
|
+
const notes = sandboxNotes(promoted.sandbox, escalationModes)
|
|
130
|
+
return notes.length > 0 ? `${text}\n${notes.join('\n')}` : text
|
|
116
131
|
}
|
|
117
132
|
|
|
118
133
|
/**
|
|
@@ -129,6 +144,7 @@ function sandboxNotes(sandbox, escalationModes) {
|
|
|
129
144
|
if (escalationModes.length > 0) notes.push(escalationHintMarker('command'))
|
|
130
145
|
return notes
|
|
131
146
|
}
|
|
147
|
+
if (sandbox?.enforcement === 'unconfined') return [sandboxUnconfinedMarker(sandbox.mode)]
|
|
132
148
|
return []
|
|
133
149
|
}
|
|
134
150
|
|
|
@@ -192,12 +208,7 @@ export function renderJobRead(delta, lossy, spillPaths, sandbox, escalationModes
|
|
|
192
208
|
const paths = Array.isArray(spillPaths) ? spillPaths : []
|
|
193
209
|
notices.push(`[some output was dropped from memory; full output: ${paths.length > 0 ? paths.join(', ') : '(unavailable)'}]`)
|
|
194
210
|
}
|
|
195
|
-
|
|
196
|
-
notices.push(`[sandbox: the sandbox runner itself failed under ${sandbox.mode} mode — the command did not run; this is a sandbox problem, not a command failure]`)
|
|
197
|
-
} else if (sandbox && sandbox.denied) {
|
|
198
|
-
notices.push(sandboxDenialMarker(sandbox.mode))
|
|
199
|
-
if (escalationModes.length > 0) notices.push(escalationHintMarker('command'))
|
|
200
|
-
}
|
|
211
|
+
notices.push(...sandboxNotes(sandbox, escalationModes))
|
|
201
212
|
if (notices.length === 0) return delta
|
|
202
213
|
return `${delta}${delta.length > 0 && !delta.endsWith('\n') ? '\n' : ''}${notices.join('\n')}`
|
|
203
214
|
}
|
package/src/bash-tool.js
CHANGED
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
import {
|
|
44
44
|
ESCALATION_TARGETS,
|
|
45
45
|
parseExitStatus,
|
|
46
|
+
renderBackground,
|
|
46
47
|
renderJobRead,
|
|
47
48
|
renderPromoted,
|
|
48
49
|
renderResult,
|
|
@@ -86,7 +87,7 @@ export async function resolveHostModule(ctx, name) {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
/**
|
|
89
|
-
* The official bash description
|
|
90
|
+
* The official bash description plus unavoidable Git for Windows boundaries.
|
|
90
91
|
* @returns {string}
|
|
91
92
|
*/
|
|
92
93
|
export function bashDescription() {
|
|
@@ -97,7 +98,11 @@ export function bashDescription() {
|
|
|
97
98
|
+ 'Provide `description` before `command` in the arguments. '
|
|
98
99
|
+ 'Before any delete or move, verify that the resolved absolute target path is the intended one; never run it against a computed path you have not checked. '
|
|
99
100
|
+ 'An unset variable expands to an empty string, so guard variables in such paths with `${VAR:?}`. '
|
|
100
|
-
+ 'Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]`, a policy denial: do not retry another way.'
|
|
101
|
+
+ 'Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]`, a policy denial: do not retry another way. '
|
|
102
|
+
+ 'Git for Windows (MSYS) converts path-looking arguments when invoking native Windows programs: ipconfig /all and cmd /c may break; use ipconfig //all or cmd //c, or a per-command MSYS2_ARG_CONV_EXCL prefix. '
|
|
103
|
+
+ 'Exclusions are prefixes: excluding /c also excludes /c/... paths, so use native drive-letter paths for those arguments; do not disable conversion globally. Verify the expected effect as well as the exit code. '
|
|
104
|
+
+ 'Output is decoded as UTF-8; native Windows programs may emit OEM/ANSI bytes (for example CP936/GBK) and display garbled text. Use a program UTF-8 option or explicit known-encoding conversion before capture; locale variables alone do not fix native output, and mixed streams cannot safely be guessed or decoded globally as GBK. '
|
|
105
|
+
+ 'On Windows, this Git Bash route does NOT enforce shell file-write restrictions in read-only/workspace-write mode (unconfined). Official file-tool write/edit policy is separate; never use shell to bypass its denial.'
|
|
101
106
|
}
|
|
102
107
|
|
|
103
108
|
/** The official foreground/present call view (index.ts:108). */
|
|
@@ -187,9 +192,17 @@ const BACKGROUND_OUTPUT_PROPERTIES = {
|
|
|
187
192
|
kind: { type: 'string', required: true, const: 'background' },
|
|
188
193
|
jobId: { type: 'string', required: true },
|
|
189
194
|
cwd: { type: 'string', required: true },
|
|
195
|
+
sandbox: {
|
|
196
|
+
type: 'object', additionalProperties: false,
|
|
197
|
+
properties: {
|
|
198
|
+
mode: { type: 'string', required: true },
|
|
199
|
+
denied: { type: 'boolean', required: true },
|
|
200
|
+
enforcement: { type: 'string' },
|
|
201
|
+
},
|
|
202
|
+
},
|
|
190
203
|
}
|
|
191
204
|
|
|
192
|
-
/** The official output union
|
|
205
|
+
/** The official output union, with optional background sandbox facts. */
|
|
193
206
|
function outputSchemaSpec() {
|
|
194
207
|
return {
|
|
195
208
|
oneOf: [
|
|
@@ -203,6 +216,10 @@ function outputSchemaSpec() {
|
|
|
203
216
|
jobId: { type: 'string', required: true },
|
|
204
217
|
timeoutMs: { type: 'number', required: true },
|
|
205
218
|
output: { type: 'string', required: true },
|
|
219
|
+
sandbox: {
|
|
220
|
+
...BACKGROUND_OUTPUT_PROPERTIES.sandbox,
|
|
221
|
+
properties: { ...BACKGROUND_OUTPUT_PROPERTIES.sandbox.properties, runnerFailed: { type: 'boolean' } },
|
|
222
|
+
},
|
|
206
223
|
},
|
|
207
224
|
},
|
|
208
225
|
{
|
|
@@ -248,20 +265,29 @@ function outputSchemaSpec() {
|
|
|
248
265
|
}
|
|
249
266
|
}
|
|
250
267
|
|
|
251
|
-
/**
|
|
252
|
-
function
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (
|
|
260
|
-
out.properties[key] =
|
|
268
|
+
/** Convert key-level spec flags at every depth, including union branches. */
|
|
269
|
+
function schemaSpecToJson(spec) {
|
|
270
|
+
const { required: marker, ...out } = spec
|
|
271
|
+
if (Array.isArray(marker)) out.required = [...marker]
|
|
272
|
+
if (spec.properties !== undefined) {
|
|
273
|
+
const required = Array.isArray(marker) ? [...marker] : []
|
|
274
|
+
out.properties = {}
|
|
275
|
+
for (const [key, value] of Object.entries(spec.properties)) {
|
|
276
|
+
if (value.required === true && !required.includes(key)) required.push(key)
|
|
277
|
+
out.properties[key] = schemaSpecToJson(value)
|
|
261
278
|
}
|
|
262
|
-
|
|
279
|
+
if (required.length > 0) out.required = required
|
|
280
|
+
}
|
|
281
|
+
for (const key of ['oneOf', 'anyOf', 'allOf']) {
|
|
282
|
+
if (Array.isArray(spec[key])) out[key] = spec[key].map(schemaSpecToJson)
|
|
263
283
|
}
|
|
264
|
-
|
|
284
|
+
if (spec.items !== undefined) out.items = schemaSpecToJson(spec.items)
|
|
285
|
+
return out
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** Plain JSON-schema variant of the same union (fallback when defineTool is absent). */
|
|
289
|
+
function outputSchemaJson() {
|
|
290
|
+
return schemaSpecToJson(outputSchemaSpec())
|
|
265
291
|
}
|
|
266
292
|
|
|
267
293
|
/** The official args validation (index.ts:73). */
|
|
@@ -310,7 +336,12 @@ export function createBashToolModule({ executor, toolsModule, sandboxModule, llm
|
|
|
310
336
|
: (subject) => `The narrowest wider sandbox mode for a one-shot retry of the exact ${subject} the sandbox just denied; the retry asks the user for approval.`
|
|
311
337
|
|
|
312
338
|
return {
|
|
313
|
-
|
|
339
|
+
// workingDirectory is NOT a service on every host this plugin claims
|
|
340
|
+
// compatibility with (0.2.0-rc.2 has none — issue #27). A missing hard
|
|
341
|
+
// inject leaves the whole module fiber permanently INACTIVE: apply() never
|
|
342
|
+
// runs, the tool never registers, and cordis logs nothing. Read it
|
|
343
|
+
// optionally at the use site (below) instead of injecting it.
|
|
344
|
+
inject: ['tools', 'systemPrompt', 'shellEnv'],
|
|
314
345
|
apply(ctx, config = {}) {
|
|
315
346
|
const backgroundEnabled = config?.enableRunInBackground ?? true
|
|
316
347
|
const promoteOnTimeout = (config?.promoteOnTimeout ?? true) && backgroundEnabled
|
|
@@ -320,8 +351,9 @@ export function createBashToolModule({ executor, toolsModule, sandboxModule, llm
|
|
|
320
351
|
const defaultMode = executor.sandboxMode
|
|
321
352
|
const escalationReady = defaultMode !== undefined && typeof approveEscalation === 'function'
|
|
322
353
|
const escalationModes = escalationReady ? ESCALATION_TARGETS : []
|
|
354
|
+
// Missing approval helpers narrow escalation, not the session's standing policy.
|
|
323
355
|
const resolveSandboxPolicy = (exec) => {
|
|
324
|
-
if (
|
|
356
|
+
if (defaultMode === undefined) return undefined
|
|
325
357
|
const policy = ctx.get('sandboxPolicy')
|
|
326
358
|
return policy ? policy.resolve(exec?.agent === undefined ? {} : { session: exec.agent.session }) : undefined
|
|
327
359
|
}
|
|
@@ -414,14 +446,17 @@ export function createBashToolModule({ executor, toolsModule, sandboxModule, llm
|
|
|
414
446
|
}
|
|
415
447
|
if (view.status === 'running' || view.status === 'stopping') {
|
|
416
448
|
const read = registry.read(attached.id, owner)
|
|
449
|
+
const sandbox = attached.process()?.sandbox
|
|
417
450
|
return {
|
|
418
451
|
kind: 'promoted',
|
|
419
452
|
cwd: spec.workdir,
|
|
420
453
|
jobId: attached.id,
|
|
421
454
|
timeoutMs,
|
|
455
|
+
// Preserve ring/spill notices; sandbox notes belong to the final renderer.
|
|
422
456
|
output: renderJobRead(
|
|
423
|
-
ringDelta(read.chunks), read.lossy, read.job.output.spillPaths ?? [],
|
|
457
|
+
ringDelta(read.chunks), read.lossy, read.job.output.spillPaths ?? [], undefined, escalationModes,
|
|
424
458
|
),
|
|
459
|
+
...(sandbox === undefined ? {} : { sandbox }),
|
|
425
460
|
}
|
|
426
461
|
}
|
|
427
462
|
registry.remove(attached.id, owner)
|
|
@@ -476,7 +511,16 @@ export function createBashToolModule({ executor, toolsModule, sandboxModule, llm
|
|
|
476
511
|
const policy = approvedMode === undefined
|
|
477
512
|
? standingPolicy
|
|
478
513
|
: { ...standingPolicy, mode: approvedMode }
|
|
479
|
-
|
|
514
|
+
// Optional read (issue #27): on hosts that provide workingDirectory
|
|
515
|
+
// this is the same ensure() seam the modern official tool-bash uses;
|
|
516
|
+
// on hosts without it, fall back to the session cwd exactly the way
|
|
517
|
+
// the rc.2-era official resolveWorkdir did (policy root ?? header).
|
|
518
|
+
const workingDirectory = ctx.get !== undefined ? ctx.get('workingDirectory') : undefined
|
|
519
|
+
const cwd = exec.agent === undefined
|
|
520
|
+
? undefined
|
|
521
|
+
: workingDirectory !== undefined
|
|
522
|
+
? await workingDirectory.ensure(exec.agent, exec.signal)
|
|
523
|
+
: (standingPolicy?.workspaceRoot ?? exec.agent?.session?.header?.cwd)
|
|
480
524
|
const workdir = resolveWorkdir(args.workdir, cwd)
|
|
481
525
|
const dshEnv = ctx.shellEnv.collect(exec)
|
|
482
526
|
const request = {
|
|
@@ -495,7 +539,15 @@ export function createBashToolModule({ executor, toolsModule, sandboxModule, llm
|
|
|
495
539
|
}
|
|
496
540
|
if (exec.signal.aborted) throw toolAborted(llmModule?.HarnessError)
|
|
497
541
|
const spec = executor.resolve({ ...request, onExpiry: 'none' })
|
|
498
|
-
|
|
542
|
+
// The job may still be preparing: expose the planned route, not a
|
|
543
|
+
// claim that the command has already run or been confined.
|
|
544
|
+
const route = typeof executor.gitBashRoute === 'function' ? executor.gitBashRoute(spec) : undefined
|
|
545
|
+
const sandbox = route === undefined ? undefined : {
|
|
546
|
+
mode: route.mode, denied: false,
|
|
547
|
+
...(route.unconfined ? { enforcement: 'unconfined' } : {}),
|
|
548
|
+
}
|
|
549
|
+
return { kind: 'background', jobId: startJob(jobs, args, exec, spec).id, cwd: spec.workdir,
|
|
550
|
+
...(sandbox === undefined ? {} : { sandbox }) }
|
|
499
551
|
}
|
|
500
552
|
if (jobs !== undefined && promote) {
|
|
501
553
|
const spec = executor.resolve({ ...request, onExpiry: 'none' })
|
|
@@ -524,9 +576,9 @@ export function createBashToolModule({ executor, toolsModule, sandboxModule, llm
|
|
|
524
576
|
render: (_args, value) => [{
|
|
525
577
|
type: 'text',
|
|
526
578
|
text: value.kind === 'background'
|
|
527
|
-
?
|
|
579
|
+
? renderBackground(value.jobId, value.sandbox)
|
|
528
580
|
: value.kind === 'promoted'
|
|
529
|
-
? renderPromoted(value)
|
|
581
|
+
? renderPromoted(value, escalationModes)
|
|
530
582
|
: renderResult(value, escalationModes),
|
|
531
583
|
}],
|
|
532
584
|
presentationMeta: (_args, value) => ({ cwd: value.cwd }),
|
|
@@ -548,15 +600,15 @@ export function createBashToolModule({ executor, toolsModule, sandboxModule, llm
|
|
|
548
600
|
// keys reach execute too, where enableRunInBackground enforces them
|
|
549
601
|
// ("Undeclared keys are allowed, so schema omission also needs
|
|
550
602
|
// enforcement", index.ts:498).
|
|
551
|
-
parameters: { type: 'object', properties: parametersSpec()
|
|
603
|
+
parameters: schemaSpecToJson({ type: 'object', properties: parametersSpec() }),
|
|
552
604
|
output: {
|
|
553
605
|
schema: outputSchemaJson(),
|
|
554
606
|
render: (_args, value) => [{
|
|
555
607
|
type: 'text',
|
|
556
608
|
text: value.kind === 'background'
|
|
557
|
-
?
|
|
609
|
+
? renderBackground(value.jobId, value.sandbox)
|
|
558
610
|
: value.kind === 'promoted'
|
|
559
|
-
? renderPromoted(value)
|
|
611
|
+
? renderPromoted(value, escalationModes)
|
|
560
612
|
: renderResult(value, escalationModes),
|
|
561
613
|
}],
|
|
562
614
|
presentationMeta: (_args, value) => ({ cwd: value.cwd }),
|
|
@@ -580,7 +632,8 @@ export function createBashToolModule({ executor, toolsModule, sandboxModule, llm
|
|
|
580
632
|
jobCtx.effect(() => () => {
|
|
581
633
|
unregister()
|
|
582
634
|
try {
|
|
583
|
-
|
|
635
|
+
// Cordis FiberState.ACTIVE is the numeric enum member 2.
|
|
636
|
+
if (ctx.fiber?.state === 2) foregroundOnly = ctx.tools.register(bashTool(undefined))
|
|
584
637
|
} catch { /* the scope is going away */ }
|
|
585
638
|
})
|
|
586
639
|
})
|