dsh-gitbash-shell 0.43.2 → 0.44.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 +45 -0
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/bash-render.js +6 -4
- package/src/compositions.js +35 -18
- package/src/index.js +46 -16
- package/src/inline-tools.js +28 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,51 @@
|
|
|
3
3
|
> 倒序排列,新版本条目在最上面。条目格式:`## vX.Y.Z — YYYY-MM-DD` + 类型(feat / fix / docs / chore)+ 要点 + 相关链接。
|
|
4
4
|
> 纪律见 AGENTS.md「变更记录纪律」:发版前先更新本文件并随版本提交;事故复盘、复现与真机验证记录也记在这里。
|
|
5
5
|
|
|
6
|
+
## [0.44.0] — 2026-10-28
|
|
7
|
+
|
|
8
|
+
### feat:跟随 dsh 0.2.2-alpha.2 的工具执行器架构(executor 行、subagent 模型键、assemble 事件收窄、promoted 文案精简)
|
|
9
|
+
|
|
10
|
+
- **背景**:官方 0.2.2-alpha.2 重做了工具执行架构(`docs/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/`):`dsh-tools` 的 `mode`/`maxParallelSubCalls` 配置被 `z.never()` 拒绝,执行模式改由显式 `tool-executor-native` / `tool-executor-ptc` 行承载(base 已挂 native,PTC 侧需 preset 级覆盖);`dsh-agent-tool-presentation` 仍支持但已弃用。同期 preset 还有三处形状变化,官方 preset 逐行对齐面因此多了一个代际。
|
|
11
|
+
- **组合跟随(探测门控)**:`probeHostExtras` 增加 `executorPlugin`(`@deepseek-ai/dsh-tool-executor-ptc` 可解析)与 `profileContext`(live 服务读取)。新宿主上:PTC 变体改挂 `tool-executor-ptc`,不再挂 `tool-presentation(mode: ptc)`;`tool-subagent-list-agents` 行删除(list_agents 已并入 `tool-subagent-control` 主行);subagent 配置键 `modelSelectionSettings` → `modelSelection` + `modelDiscovery`;plugin-manager / plugin-market 两行改由 `profileContext` 门控(官方 `!!js "!ctx.get('profileContext')"` 语义,三个变体一致)。旧宿主全部保持原行集不变——组合对齐的向后兼容纪律照旧。
|
|
12
|
+
- **assemble 事件收窄**:0.2.2-alpha.2 的 `system-prompt/assemble` 只带 prompt presentation,不再带 `tools`(`PromptAssembly` 拆分,工具声明改由 provider 持有)。`applyToolRouting` 与 `runCodeHintFor` 因此改为:宿主给了工具列表就用宿主的,否则从 `ctx.get('tools').schemas(agent)` 读回——新老宿主都拿到真实工具面,方言路由与 run_code 提示句不受影响。
|
|
13
|
+
- **promoted 文案精简**:测试钩子 `renderPromoted` 跟随官方简化——只留 `[still running after Nms; moved to background job id]`,跟随指引(读输出/停止)改由官方 `tool:jobs` 段落统一承载,不再重复。
|
|
14
|
+
- **测试(337 项:336 pass + 1 skip)**:`probeHostExtras` 断言扩到新字段(含 live 服务读取与抛错降级);新增 alpha.2 组合形态测试(executor 行/无 list-agents 行/新 subagent 键/profile 门控/旧宿主不变);源码断言与新工具列表回退路径。
|
|
15
|
+
|
|
16
|
+
## [0.43.3] — 2026-10-27
|
|
17
|
+
|
|
18
|
+
### fix:工具面晚于 `agent/created` 落定时,bash 补位永久错过(真机复现;PR #36,致谢 @qiantao448-ux 首次贡献)
|
|
19
|
+
|
|
20
|
+
- **症状**:冷启动后某个会话既没有 `bash`、又留着一个被拦截的 `pwsh`。插件日志里**没有**
|
|
21
|
+
`own bash tool activated`,但 `tools/pre-execute` 的拒绝句照旧生效——两层里只有调用层活着,
|
|
22
|
+
「注册 bash + 遮蔽 pwsh」那一次 `reconcile` 从没跑成。
|
|
23
|
+
- **根因**:`reconcile` 只由 `agent/created`、挂载时的存量 sweep 和设置开关变化触发;面为空时
|
|
24
|
+
250ms×5 有界重试,**过期即终局**。桌面端启动要先挂 profile 级插件再挂预设行,这个窗口很容易
|
|
25
|
+
被跨过去,而宿主其实有 `tools/change` 事件——本插件没监听,也没有第二个落点。
|
|
26
|
+
- **修法**:在 `tools/pre-execute` 里做一次**惰性再对账**——该 agent 还没有 bash scope 时重跑
|
|
27
|
+
决策。第一次工具调用是"面一定已落定"的最早时刻;`state.scope` 一旦存在即永久跳过,幂等,
|
|
28
|
+
且不监听 `tools/change`(不会自己触发自己)。
|
|
29
|
+
- **真机验证**:Windows 桌面端(bash 5.3,`pwshTool` 关闭),重启后日志出现
|
|
30
|
+
`own bash tool ready (private Git Bash executor at C:/Program Files/Git/bin/bash.exe)` 与
|
|
31
|
+
`own bash tool activated for this agent`,`bash` 工具可用、`uname -s` 返回 `MINGW64_NT-*`。
|
|
32
|
+
|
|
33
|
+
### fix:面里没有 pwsh 时撤掉自己注册的 bash(遮蔽型 preset 不再漏 shell)
|
|
34
|
+
|
|
35
|
+
- 自有 bash 注册在 **agent 自有 scope**;宿主契约写明掩码只裁全局工具、**scope 注册保持可见**,
|
|
36
|
+
所以一旦注册成功,任何 preset 的 `restrict({ allow })` 都拿不回来。若在面还没落定时抢注,
|
|
37
|
+
纯聊天类 preset(只放行两个工具的白名单)会凭空多出一条命令通道。
|
|
38
|
+
- 修法:`reconcile` 观测到的 `names` 是**解开自身 pwsh 掩码之后**的面,权威;只要它非空且不含
|
|
39
|
+
`pwsh`,就 dispose 自己那个 scope 并撤掉 ops shell 遮蔽,下一轮再按真实面决策。
|
|
40
|
+
`names.length > 0` 兜住"注册表读不到/读到空"的情形,避免把读取失败误判成"被掩码"。
|
|
41
|
+
- 与上一条互为正反面:一条负责"该有时一定要有",一条负责"不该有时一定没有"。
|
|
42
|
+
|
|
43
|
+
### 补充(维护者):回归测试锁定两处新行为
|
|
44
|
+
|
|
45
|
+
- `tests/host-regressions.mjs` 新增两测试:①`inlineNeeds` 对 pwsh-less/空面的负例(撤回永不
|
|
46
|
+
重建的直接依据:`needsBash` 要求 `pwsh` 在场);②源码断言锁 lazy 再对账的守卫形状
|
|
47
|
+
(scope/pending 幂等 + try/catch 不破坏工具调用)与 stand-down 分支的位置与顺序
|
|
48
|
+
(条件 `names.length > 0 && !names.includes('pwsh') && state.scope`,scope 置空先于
|
|
49
|
+
`needsBash` 判断,dispose 真实发生并留日志)。全量 336 项 0 fail。
|
|
50
|
+
|
|
6
51
|
## [0.43.2] — 2026-10-23
|
|
7
52
|
|
|
8
53
|
### fix:被我们杀掉的 run 统一归 `code=-1`(Windows `taskkill /F` 退出码语义)
|
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.44.0",
|
|
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.
|
|
3
|
+
"version": "0.44.0",
|
|
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/bash-render.js
CHANGED
|
@@ -119,7 +119,11 @@ export function renderResult(result, escalationModes = []) {
|
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
121
|
* Shape a foreground call that stopped waiting into the text the model sees.
|
|
122
|
-
* Mirrors the official `renderPromoted` (tool-bash render.ts:77)
|
|
122
|
+
* Mirrors the official `renderPromoted` (tool-bash render.ts:77) as of dsh
|
|
123
|
+
* 0.2.2-alpha.2: the marker alone — the follow-up guidance now lives in the
|
|
124
|
+
* shared `tool:jobs` system-prompt section, so the older "read newer output
|
|
125
|
+
* with job_output, stop it with job_kill" sentence would duplicate it
|
|
126
|
+
* (upstream simplified this in 0.2.2-alpha.2).
|
|
123
127
|
* @param {{ jobId: string, timeoutMs: number, output: string, sandbox?: object }} promoted
|
|
124
128
|
* @param {string[]} [escalationModes]
|
|
125
129
|
* @returns {string}
|
|
@@ -128,9 +132,7 @@ export function renderPromoted(promoted, escalationModes = []) {
|
|
|
128
132
|
const body = promoted.output.length > 0
|
|
129
133
|
? (promoted.output.endsWith('\n') ? promoted.output : `${promoted.output}\n`)
|
|
130
134
|
: ''
|
|
131
|
-
const text = `${body}[still running after ${promoted.timeoutMs}ms; moved to background job ${promoted.jobId}]
|
|
132
|
-
+ 'The command keeps running in the background. You will be notified when it finishes; '
|
|
133
|
-
+ 'read newer output with job_output, stop it with job_kill.'
|
|
135
|
+
const text = `${body}[still running after ${promoted.timeoutMs}ms; moved to background job ${promoted.jobId}]`
|
|
134
136
|
const notes = sandboxNotes(promoted.sandbox, escalationModes)
|
|
135
137
|
return notes.length > 0 ? `${text}\n${notes.join('\n')}` : text
|
|
136
138
|
}
|
package/src/compositions.js
CHANGED
|
@@ -90,12 +90,19 @@ export function pluginsFor({ kind, gitBash, skillsDir, pythonActive = false, hos
|
|
|
90
90
|
// toolFilter deny from the subagent/subagent_fork configs (official
|
|
91
91
|
// reminders reach subagents again — the deny was never load-bearing,
|
|
92
92
|
// nothing replaces it); alpha.4 added a tool-plugin-market row to every
|
|
93
|
-
// full preset.
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
|
|
93
|
+
// full preset. 0.2.2-alpha.2 then reworked the tool execution architecture
|
|
94
|
+
// (`executorPlugin`): the official executors moved into explicit
|
|
95
|
+
// tool-executor-native/-ptc rows, the subagent config renamed
|
|
96
|
+
// `modelSelectionSettings` to `modelSelection` + opt-in `modelDiscovery`,
|
|
97
|
+
// list-agents folded into the tool-subagent-control row itself (its
|
|
98
|
+
// separate row is gone), and the manager/market rows enable under a
|
|
99
|
+
// profile context instead of staying off outside cordis. A preset row whose
|
|
100
|
+
// package is absent rejects the WHOLE mount, so these ride a host probe
|
|
101
|
+
// (index.js probeHostExtras, resolved from ctx.baseUrl — the base rows
|
|
102
|
+
// mount from); absent/older hosts keep the byte-identical earlier row set.
|
|
103
|
+
// The minimal variant carries its own 0.2.1-alpha.3 delta (tool-fs +
|
|
104
|
+
// compaction) behind the compaction flag.
|
|
105
|
+
const { timeContext = false, toolSchedule = false, pluginMarket = false, compaction = false, executorPlugin = false, profileContext = false } = hostExtras
|
|
99
106
|
const rows = [
|
|
100
107
|
{
|
|
101
108
|
id: 'persona',
|
|
@@ -168,11 +175,15 @@ export function pluginsFor({ kind, gitBash, skillsDir, pythonActive = false, hos
|
|
|
168
175
|
isolate: { workflowEngine: true },
|
|
169
176
|
config: [
|
|
170
177
|
{ id: 'tool-subagent-control', name: '@deepseek-ai/dsh-tool-subagent-control' },
|
|
171
|
-
|
|
178
|
+
// 0.2.2-alpha.2 folded list_agents into the control row itself; the
|
|
179
|
+
// separate child row is gone, so alpha.2+ hosts must not mount it.
|
|
180
|
+
...(executorPlugin ? [] : [{ id: 'tool-subagent-list-agents', name: '@deepseek-ai/dsh-tool-subagent-control/list-agents' }]),
|
|
172
181
|
{
|
|
173
182
|
id: 'tool-subagent',
|
|
174
183
|
name: '@deepseek-ai/dsh-tool-subagent',
|
|
175
|
-
config:
|
|
184
|
+
config: executorPlugin
|
|
185
|
+
? { provider: 'spawn', toolName: 'subagent', modelSelection: true, modelDiscovery: true, ...(timeContext ? {} : { backgroundMode: 'continuable' }) }
|
|
186
|
+
: { provider: 'spawn', toolName: 'subagent', modelSelectionSettings: true, ...(timeContext ? {} : { backgroundMode: 'continuable' }) },
|
|
176
187
|
},
|
|
177
188
|
{
|
|
178
189
|
id: 'tool-subagent-fork',
|
|
@@ -207,11 +218,13 @@ export function pluginsFor({ kind, gitBash, skillsDir, pythonActive = false, hos
|
|
|
207
218
|
},
|
|
208
219
|
]
|
|
209
220
|
if (kind === 'ptc') {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
221
|
+
// 0.2.2-alpha.2 replaced the deprecated presentation selector with the
|
|
222
|
+
// explicit PTC executor row (the base deployment keeps
|
|
223
|
+
// tool-executor-native; a scoped executor row overrides it for this
|
|
224
|
+
// agent). Older hosts keep the presentation row they ship.
|
|
225
|
+
rows.push(executorPlugin
|
|
226
|
+
? { id: 'tool-executor-ptc', name: '@deepseek-ai/dsh-tool-executor-ptc' }
|
|
227
|
+
: { id: 'tool-presentation', name: '@deepseek-ai/dsh-agent-tool-presentation', config: { mode: 'ptc' } })
|
|
215
228
|
}
|
|
216
229
|
if (kind === 'cordis') {
|
|
217
230
|
rows.push({ id: 'tool-cordis', name: '@deepseek-ai/dsh-tool-cordis' })
|
|
@@ -224,16 +237,20 @@ export function pluginsFor({ kind, gitBash, skillsDir, pythonActive = false, hos
|
|
|
224
237
|
rows.push({
|
|
225
238
|
id: 'tool-plugin-manager',
|
|
226
239
|
name: '@deepseek-ai/dsh-plugin-manager/tools',
|
|
227
|
-
// The official 0.
|
|
228
|
-
//
|
|
229
|
-
|
|
240
|
+
// The official rows: before 0.2.2-alpha.2 ptc/standard ship it disabled and
|
|
241
|
+
// cordis keeps it on (every web deployment has a profile context). From
|
|
242
|
+
// 0.2.2-alpha.2 on every preset enables it exactly when a profile context
|
|
243
|
+
// exists, so the probe decides for all three variants.
|
|
244
|
+
...(executorPlugin ? off(!profileContext) : off(kind !== 'cordis')),
|
|
230
245
|
})
|
|
231
246
|
if (pluginMarket) {
|
|
232
247
|
rows.push({
|
|
233
|
-
// The official 0.2.1-alpha.4 rows
|
|
248
|
+
// The official 0.2.1-alpha.4 rows, following the manager's split on
|
|
249
|
+
// both architectures (alpha.4: disabled on standard/ptc, profile-gated
|
|
250
|
+
// on cordis; alpha.2 of 0.2.2: profile-gated everywhere).
|
|
234
251
|
id: 'tool-plugin-market',
|
|
235
252
|
name: '@deepseek-ai/dsh-plugin-market/tools',
|
|
236
|
-
...off(kind !== 'cordis'),
|
|
253
|
+
...(executorPlugin ? off(!profileContext) : off(kind !== 'cordis')),
|
|
237
254
|
})
|
|
238
255
|
}
|
|
239
256
|
return rows
|
package/src/index.js
CHANGED
|
@@ -1791,22 +1791,32 @@ function cleanupLegacyTrees(presetIds) {
|
|
|
1791
1791
|
}
|
|
1792
1792
|
|
|
1793
1793
|
/**
|
|
1794
|
-
* Probe the dsh
|
|
1795
|
-
*
|
|
1796
|
-
*
|
|
1797
|
-
*
|
|
1798
|
-
*
|
|
1799
|
-
*
|
|
1800
|
-
*
|
|
1801
|
-
*
|
|
1802
|
-
*
|
|
1803
|
-
*
|
|
1804
|
-
*
|
|
1794
|
+
* Probe the dsh host additions for the variant compositions.
|
|
1795
|
+
*
|
|
1796
|
+
* `timeContext` / `toolSchedule` / `pluginMarket` / `compaction` are the
|
|
1797
|
+
* 0.2.1 rows (durable clock readings, reminder tools, the plugin market row,
|
|
1798
|
+
* the minimal tool-fs + compaction delta). `executorPlugin` marks the
|
|
1799
|
+
* 0.2.2-alpha.2 tool-execution architecture: the packages tool-executor-native
|
|
1800
|
+
* and tool-executor-ptc exist, the official presets mount the latter for PTC,
|
|
1801
|
+
* the subagent config renamed `modelSelectionSettings` to
|
|
1802
|
+
* `modelSelection` + `modelDiscovery`, and list-agents folded into the control
|
|
1803
|
+
* row. `profileContext` is the host fact the manager/market rows gate on since
|
|
1804
|
+
* alpha.2 (`!!js "!ctx.get('profileContext')"`) — computed from the live
|
|
1805
|
+
* service, not the package set.
|
|
1806
|
+
*
|
|
1807
|
+
* A preset row whose package is absent rejects the WHOLE mount, so every row
|
|
1808
|
+
* rides this probe: each is added only when its package resolves from the
|
|
1809
|
+
* HOST's module base — `ctx.baseUrl`, the exact base `prepareProfileEntries`
|
|
1810
|
+
* mounts preset rows from, so the probe can never disagree with the mounter.
|
|
1811
|
+
* On older hosts all stay false and the row set stays byte-identical to the
|
|
1812
|
+
* earlier mirror. Probed once per boot; the profile's package set does not
|
|
1813
|
+
* change under a running host.
|
|
1805
1814
|
* @param {object} ctx - cordis context (any fiber of the running host).
|
|
1806
1815
|
* @param {Function} [resolve] - predicate override for tests; receives a
|
|
1807
1816
|
* package specifier, resolves like `require.resolve`, returns boolean.
|
|
1808
1817
|
* @returns {Promise<{timeContext: boolean, toolSchedule: boolean,
|
|
1809
|
-
* pluginMarket: boolean, compaction: boolean
|
|
1818
|
+
* pluginMarket: boolean, compaction: boolean, executorPlugin: boolean,
|
|
1819
|
+
* profileContext: boolean}>}
|
|
1810
1820
|
*/
|
|
1811
1821
|
export async function probeHostExtras(ctx, resolve) {
|
|
1812
1822
|
let ok = resolve
|
|
@@ -1821,13 +1831,20 @@ export async function probeHostExtras(ctx, resolve) {
|
|
|
1821
1831
|
ok = () => false
|
|
1822
1832
|
}
|
|
1823
1833
|
}
|
|
1824
|
-
const [timeContext, toolSchedule, pluginMarket, compaction] = await Promise.all([
|
|
1834
|
+
const [timeContext, toolSchedule, pluginMarket, compaction, executorPlugin] = await Promise.all([
|
|
1825
1835
|
Promise.resolve(safeProbe(ok, '@deepseek-ai/dsh-time-context')).then(Boolean),
|
|
1826
1836
|
Promise.resolve(safeProbe(ok, '@deepseek-ai/dsh-tool-schedule')).then(Boolean),
|
|
1827
1837
|
Promise.resolve(safeProbe(ok, '@deepseek-ai/dsh-plugin-market/tools')).then(Boolean),
|
|
1828
1838
|
Promise.resolve(safeProbe(ok, '@deepseek-ai/dsh-compaction-basic')).then(Boolean),
|
|
1839
|
+
Promise.resolve(safeProbe(ok, '@deepseek-ai/dsh-tool-executor-ptc')).then(Boolean),
|
|
1829
1840
|
])
|
|
1830
|
-
|
|
1841
|
+
let profileContext = false
|
|
1842
|
+
try {
|
|
1843
|
+
profileContext = ctx?.get?.('profileContext') !== undefined
|
|
1844
|
+
} catch {
|
|
1845
|
+
profileContext = false
|
|
1846
|
+
}
|
|
1847
|
+
return { timeContext, toolSchedule, pluginMarket, compaction, executorPlugin, profileContext }
|
|
1831
1848
|
}
|
|
1832
1849
|
|
|
1833
1850
|
/** Run one probe predicate; ANY failure reads as "absent" (a throwing
|
|
@@ -2274,12 +2291,25 @@ export async function apply(ctx, config = {}) {
|
|
|
2274
2291
|
no run_code hint — so they meet the shell they were composed for. */
|
|
2275
2292
|
const dialect = liveSettings.dialect()
|
|
2276
2293
|
const applies = dialectApplies(dialect, assembleContext && assembleContext.agent)
|
|
2294
|
+
// dsh 0.2.2-alpha.2 narrowed this event to prompt PRESENTATION: it no
|
|
2295
|
+
// longer carries `tools` ("tool declarations remain provider-owned"),
|
|
2296
|
+
// so read the same list back from the tools service; older hosts keep
|
|
2297
|
+
// handing it over on the assembly.
|
|
2298
|
+
const offeredTools = Array.isArray(assembly && assembly.tools)
|
|
2299
|
+
? assembly.tools
|
|
2300
|
+
: (() => {
|
|
2301
|
+
try {
|
|
2302
|
+
return ctx.get('tools')?.schemas?.(assembleContext && assembleContext.agent) ?? []
|
|
2303
|
+
} catch {
|
|
2304
|
+
return []
|
|
2305
|
+
}
|
|
2306
|
+
})()
|
|
2277
2307
|
// Routing is dialect-independent and follows actual offered tools.
|
|
2278
2308
|
// PTC may expose only run_code here: its generic SDK decision rule
|
|
2279
2309
|
// relies on the registered bindings' own descriptions, not guessed
|
|
2280
2310
|
// hidden names. Replace our block on reassembly/visibility changes.
|
|
2281
2311
|
if (assembly && typeof assembly === 'object') {
|
|
2282
|
-
applyToolRouting(
|
|
2312
|
+
applyToolRouting(offeredTools, {
|
|
2283
2313
|
pwshVisible: liveSettings.pwshTool() === true,
|
|
2284
2314
|
msys: applies && dialect.posixPaths === true,
|
|
2285
2315
|
terminalGitBash: terminalDialect.gitBash === true,
|
|
@@ -2302,7 +2332,7 @@ export async function apply(ctx, config = {}) {
|
|
|
2302
2332
|
// v0.20.1: the run_code sentence rides the SAME context entry, but
|
|
2303
2333
|
// only when this assembly actually offers the tool (see
|
|
2304
2334
|
// runCodeHintFor) — every other mode stays quiet.
|
|
2305
|
-
const hint = runCodeHintFor(
|
|
2335
|
+
const hint = runCodeHintFor(offeredTools, dialect.codePaths === true)
|
|
2306
2336
|
if (hint !== '') {
|
|
2307
2337
|
for (const assembled of Array.isArray(assembly.contexts) ? assembly.contexts : []) {
|
|
2308
2338
|
if (assembled && assembled.name === 'gitbash-shell:posix-paths' && typeof assembled.text === 'string') {
|
package/src/inline-tools.js
CHANGED
|
@@ -502,6 +502,21 @@ export function mountInlineTools(ctx, deps) {
|
|
|
502
502
|
}
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
|
+
// A face WITHOUT pwsh is not the official win32 composition: something
|
|
506
|
+
// masked the host tool away, and the only such mask in the wild is a
|
|
507
|
+
// preset gate (`tools.restrict({ allow })`, e.g. the pure-chat preset).
|
|
508
|
+
// Our bash lives in an AGENT-OWNED scope, and scoped registrations stay
|
|
509
|
+
// visible through masks by contract — so a registration made while the
|
|
510
|
+
// face was still settling would leak a shell into that preset for the
|
|
511
|
+
// rest of the agent's life. `names` above is the PRE-MASK observation
|
|
512
|
+
// (our own deny was lifted first), so it is authoritative: stand down.
|
|
513
|
+
if (names.length > 0 && !names.includes('pwsh') && state.scope) {
|
|
514
|
+
const withdrawn = state.scope
|
|
515
|
+
state.scope = undefined
|
|
516
|
+
if (state.denyOpsShell) { state.denyOpsShell(); state.denyOpsShell = undefined }
|
|
517
|
+
void Promise.resolve(withdrawn.dispose()).catch((error) => log(`own bash withdrawal failed: ${error?.message ?? error}`))
|
|
518
|
+
log('own bash tool withdrawn for this agent (the tool face carries no pwsh: a preset mask owns the tool set)')
|
|
519
|
+
}
|
|
505
520
|
if (needs.needsBash && !state.scope) {
|
|
506
521
|
startBash(agent, state)
|
|
507
522
|
}
|
|
@@ -576,6 +591,19 @@ export function mountInlineTools(ctx, deps) {
|
|
|
576
591
|
// ON the REAL tool answers for itself.
|
|
577
592
|
ctx.on('tools/pre-execute', (exec, next) => {
|
|
578
593
|
try {
|
|
594
|
+
// THE FACE CAN SETTLE AFTER `agent/created` (Unreleased). Preset rows
|
|
595
|
+
// and plugin tools keep registering while the Loader walks the tree,
|
|
596
|
+
// so on a slow boot the bounded retry above can burn through its five
|
|
597
|
+
// 250ms attempts on an empty face and be FINAL: the agent then never
|
|
598
|
+
// gets its bash and keeps a visible (but denied) pwsh. The first tool
|
|
599
|
+
// call is the first moment the face is guaranteed to be settled, so
|
|
600
|
+
// re-run the decision there while this agent still owns no bash scope.
|
|
601
|
+
// Idempotent: once the scope exists this branch never fires again.
|
|
602
|
+
const lazyAgent = exec?.agent
|
|
603
|
+
const lazyState = lazyAgent ? tracked.get(lazyAgent) : undefined
|
|
604
|
+
if (lazyState && !lazyState.scope && !lazyState.pending) {
|
|
605
|
+
try { reconcile(lazyAgent) } catch (error) { log(`lazy re-reconcile failed: ${error?.message ?? error}`) }
|
|
606
|
+
}
|
|
579
607
|
if (exec?.name === 'pwsh' && !opsSeen && deps.pwshEnabled?.() !== true) {
|
|
580
608
|
return { kind: 'deny', reason: hiddenPwshDenyReason() }
|
|
581
609
|
}
|