revert(bash): drop the executor dialect guard

The ShellDialect marker on BashExecutor and the load-time rejection in
tool-bash/tool-pwsh force every test and example fake executor to declare
a dialect for a guard with no in-repo or plausible deployment to catch
(shipped compositions always pair the tool with its matching executor),
and they break the example composition suites that stub ctx.bash without
a dialect (agent-spine-demo/cli-demo/acp-demo lost the bash tool).

Keep the non-dialect doc fixes that landed with the attempt: the tool-bash
README bashEnv contract trim with the stale example import removed, the
acp-snapshot suite.ts 'usable pwsh' JSDoc, and the pwsh.cordis.yml comment
indent. The parity note records the attempt and revert under Alternatives.
This commit is contained in:
Huanqi Cao
2026-08-03 23:16:53 +08:00
parent b43358e215
commit f14f826648
22 changed files with 21 additions and 87 deletions
+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/bash/tool-bash/README.md
README.md: be89acf06dbc6b4c420dd7fd34eb6ccb842c1fbd
README.zh.md: 7886addd4c941d81a90546518a5967f64edcdf61
README.md: f21b6b4344fcfb94d88f1f7e1c4444ee89e0ead8
README.zh.md: c9dd3f2250631f3300edaf2c246e6925f3e12005
+1 -1
View File
@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
The model-facing `bash` tool registered over the `ctx.bash` executor seam. Foreground execution stays behind that seam; a background process handle is registered with the generic `ctx.tasks` runtime and controlled through `task_output`, `task_list`, and `task_kill` from `@deepseek-ai/dsh-tool-tasks`.
Requires a loaded executor implementation (e.g. `@deepseek-ai/dsh-bash-local`) and the [`@deepseek-ai/dsh-bash-env`](../bash-env/README.md) registry; the plugin stays pending until every injected service exists (`inject: ['tools', 'bash', 'systemPrompt', 'bashEnv']`), and rejects an executor whose `dialect` is not `bash` at load — a bash command handed to another shell's parser would surface as an ordinary nonzero exit.
Requires a loaded executor implementation (e.g. `@deepseek-ai/dsh-bash-local`) and the [`@deepseek-ai/dsh-bash-env`](../bash-env/README.md) registry; the plugin stays pending until every injected service exists (`inject: ['tools', 'bash', 'systemPrompt', 'bashEnv']`).
The package root exposes only the Cordis plugin contract (`name`, `inject`, `Config`, `apply`); result rendering and background-process adaptation remain implementation details covered by same-package tests.
+1 -1
View File
@@ -4,7 +4,7 @@
模型侧 `bash` 工具,注册在 `ctx.bash` 执行器 seam 上。前台执行始终位于该 seam 之后;后台进程句柄会注册到通用 `ctx.tasks` 运行时,并通过 `task_output``task_list``task_kill` 控制;这些工具由 `@deepseek-ai/dsh-tool-tasks` 提供。
需要加载执行器实现(例如 `@deepseek-ai/dsh-bash-local`)与 [`@deepseek-ai/dsh-bash-env`](../bash-env/README.md) 注册表;在每个注入服务就绪之前,插件会保持等待状态(`inject: ['tools', 'bash', 'systemPrompt', 'bashEnv']`,并在加载时拒绝 `dialect` 不为 `bash` 的执行器——bash 命令被交给其他 shell 解析只会表现为普通的非零退出
需要加载执行器实现(例如 `@deepseek-ai/dsh-bash-local`)与 [`@deepseek-ai/dsh-bash-env`](../bash-env/README.md) 注册表;在每个注入服务就绪之前,插件会保持等待状态(`inject: ['tools', 'bash', 'systemPrompt', 'bashEnv']`)。
包(package)根只公开 Cordis 插件契约(`name``inject``Config``apply`);结果渲染和后台进程适配仍是实现细节,由同包测试覆盖。
-5
View File
@@ -188,11 +188,6 @@ const BACKGROUND_OUTPUT_PROPERTIES = {
} as const
export function apply(ctx: Context, config: Config = {}): void {
// Model commands are written in bash; a mismatched executor would hand
// them to another shell's parser and surface as ordinary nonzero exits.
if (ctx.bash.dialect !== 'bash') {
throw new Error(`tool-bash: the mounted executor speaks '${ctx.bash.dialect}', not bash — mount a bash executor (e.g. dsh-bash-local) or the matching shell tool`)
}
const backgroundEnabled = config.enableRunInBackground ?? true
const defaultMode = ctx.bash.sandboxMode
const escalationModes: readonly SandboxMode[] = defaultMode === undefined ? [] : ESCALATION_TARGETS
+1 -20
View File
@@ -5,7 +5,7 @@ import { describe, expect, it, vi } from 'vitest'
import { Context } from 'cordis'
import { CallId } from '@deepseek-ai/dsh-llm'
import { BashExecutor } from '@deepseek-ai/dsh-bash'
import type { BashExecRequest, BashExecSpec, BashProcess, BashProcessRead, BashRunResult, ShellDialect } from '@deepseek-ai/dsh-bash'
import type { BashExecRequest, BashExecSpec, BashProcess, BashProcessRead, BashRunResult } from '@deepseek-ai/dsh-bash'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry, { TOOL_ABORTED, TOOL_ABORTED_BEFORE_DISPATCH } from '@deepseek-ai/dsh-tools'
import AgentRegistry from '@deepseek-ai/dsh-agent'
@@ -101,8 +101,6 @@ async function callUntilText(
}
class RecordingSandboxExecutor extends BashExecutor {
readonly dialect = 'bash' as const
readonly modes: Array<string | undefined> = []
override get sandboxMode() {
@@ -156,8 +154,6 @@ class RecordingSandboxExecutor extends BashExecutor {
/** Test executor that records whether the background start boundary was crossed. */
class CountingStartExecutor extends BashExecutor {
readonly dialect: ShellDialect = 'bash'
starts = 0
resolve(request: BashExecRequest): BashExecSpec {
@@ -365,19 +361,6 @@ describe('bash tool', () => {
expect(text(result)).toContain('tool execution arguments must be losslessly JSON-serializable')
})
it('rejects an executor speaking another shell dialect at load', async () => {
class PowershellDialectExecutor extends CountingStartExecutor {
override readonly dialect: ShellDialect = 'powershell'
}
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
await ctx.plugin(AgentRegistry)
await ctx.plugin(BashEnvPlugin)
await ctx.plugin(PowershellDialectExecutor)
await expect(ctx.plugin(ToolBash)).rejects.toThrow("the mounted executor speaks 'powershell', not bash")
})
it('registers the bash schema with run_in_background exposed by default', async () => {
const ctx = await setup()
const schemas = ctx.tools.schemas()
@@ -1084,8 +1067,6 @@ describe('the model-facing bash tool builds its request from named args only (no
* hands back an already-settled fake handle so the task registration completes.
*/
class RecordingBashExecutor extends BashExecutor {
readonly dialect = 'bash' as const
readonly requests: BashExecRequest[] = []
resolve(request: BashExecRequest): BashExecSpec {
this.requests.push(request)