mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
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:
@@ -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 .agents/notes/implemented/feature/2026-08-02-pwsh-tool-bash-parity.md
|
||||
2026-08-02-pwsh-tool-bash-parity.md: 286acbce3289331a107924120da91464b2303bab
|
||||
2026-08-02-pwsh-tool-bash-parity.zh.md: 4de34c1416821436c156b1b19b7ae775779e8915
|
||||
2026-08-02-pwsh-tool-bash-parity.md: bf40c440b9f7f330412d8949f59c54d541152d45
|
||||
2026-08-02-pwsh-tool-bash-parity.zh.md: bc67dac29900eacf9e615e76fe947e3e642a3979
|
||||
|
||||
@@ -22,6 +22,8 @@ The first Windows-native foundation shipped `dsh-tool-pwsh` as a deliberately mi
|
||||
|
||||
**Keep the minimal profile and fix only the claims.** Rejected: the review's core finding was that text contracts copied from bash drift without the corresponding implementation; a minimal tool plus accurate claims still leaves pwsh calls without background execution, without contributor parity, and with a divergent marker story that must be re-justified forever.
|
||||
|
||||
**Reject a mismatched executor dialect at load.** Attempted and reverted before merge: a `ShellDialect` marker (`bash` | `powershell`) on `BashExecutor`, with both shell tools throwing when the mounted executor speaks another shell. It forced every executor implementation — including each test and example fake — to declare a dialect, adding noise to every shell-tool test for a guard with no in-repo or plausible deployment to catch (shipped compositions always pair tool-pwsh with `dsh-pwsh-local` and tool-bash with `dsh-bash-local`). The pairing contract stays documented in each tool's README instead.
|
||||
|
||||
**Extract a fully shared tool implementation base (abstract shell dialect, two thin leaves).** Considered and deferred: the bash-env extraction and the structural mirror (`render.ts`/`background.ts` twins) are the foundation it would rest on; a full base waits until a third dialect or the persistent-PTY twin makes the abstraction's shape observable.
|
||||
|
||||
## Consequences
|
||||
@@ -29,7 +31,6 @@ The first Windows-native foundation shipped `dsh-tool-pwsh` as a deliberately mi
|
||||
- The bash and pwsh tools are now behaviorally interchangeable for foreground and background shell work (minus sandbox), and the pwsh prompt/description sentences are each backed by the renderer — the reviewer's grep-against-code check passes.
|
||||
- Parity ran BOTH ways once: the pwsh tool's structured foreground abort (`HarnessError('tool call aborted', TOOL_ABORTED)` with name `AbortError`) was backported to the bash tool, replacing its uncoded `Error('command aborted')` — a model-visible/logged change pinned by exact-shape tests on both sides and by the cancel-tool-calls fixture.
|
||||
- `@deepseek-ai/dsh-bash-env` is a new shipped package; `dsh-tool-bash`'s `dshHome` config moved there, so compositions mounting the shell tools must also mount `bash-env` (the spine bundles do).
|
||||
- The seam now carries a `dialect` (`ShellDialect`: `bash` | `powershell` — concrete shells, not families; a POSIX-ish sibling would be its own value), and both shell tools reject a mismatched executor at load, so pairing tool-pwsh with bash-local (or the reverse) fails loud instead of surfacing commands handed to the wrong parser as ordinary nonzero exits.
|
||||
- Windows-only semantics (CRLF normalization, forced-termination exit-1/signal-null, POSIX-only self-signal) remain pinned by tests as before.
|
||||
- The pwsh tool's per-file coverage gate rides on the scriptable fake-executor suite (`tests/tools.spec.ts`); the real-pwsh integration and Loader-composition suites self-skip where `pwsh` is absent, mirroring the bash suites' division of labor.
|
||||
- The roadmap proposal's parity stage is delivered; its remaining stages are the Windows default composition and pwsh TUI/GUI rendering.
|
||||
|
||||
@@ -22,6 +22,8 @@ Status: implemented
|
||||
|
||||
**保留最小画像,只修声明。** 否决:review 的核心发现是"从 bash 复制的文本契约在缺少对应实现时会漂移";最小工具加准确声明仍让 pwsh 调用没有后台执行、没有 contributor 对等、并留下一个必须永远重新辩护的偏离 marker 故事。
|
||||
|
||||
**在加载时拒绝不匹配的执行器方言。** 合并前尝试过并撤回:在 `BashExecutor` 上加 `ShellDialect` 标记(`bash` | `powershell`),两个 shell 工具在挂载的执行器说另一种方言时抛错。它迫使每个执行器实现——包括每个测试与示例的 fake——都要声明 dialect,为一道仓内及合理部署中都没有目标可拦的护栏(交付组合总是把 tool-pwsh 配 `dsh-pwsh-local`、tool-bash 配 `dsh-bash-local`)给每个 shell 工具测试添噪。配对契约改由各工具 README 记录。
|
||||
|
||||
**提取完全共享的工具实现基座(抽象 shell 方言,两个薄叶子)。** 考虑后推迟:bash-env 提取与结构镜像(`render.ts`/`background.ts` 孪生)是它要立足的基础;在出现第三种方言或持久 PTY 孪生、让抽象的形态可观察之前,不做完整基座。
|
||||
|
||||
## 后果
|
||||
@@ -29,7 +31,6 @@ Status: implemented
|
||||
- bash 与 pwsh 工具在前台与后台 shell 工作(减 sandbox)上行为可互换,pwsh 的 prompt/描述句每句都有渲染器背书——reviewer 的“拿代码 grep 对证”检查通过。
|
||||
- 对齐也反向发生过一次:pwsh 工具的结构化前台中止(`HarnessError('tool call aborted', TOOL_ABORTED)`,name 为 `AbortError`)被回移到 bash 工具,取代其无码的 `Error('command aborted')`——这是模型可见/入日志的变更,由两侧的精确形状测试与 cancel-tool-calls fixture 钉住。
|
||||
- `@deepseek-ai/dsh-bash-env` 成为新的交付包;`dsh-tool-bash` 的 `dshHome` 配置迁往那里,因此挂载 shell 工具的组合也必须挂载 `bash-env`(spine bundle 已如此)。
|
||||
- seam 现在携带 `dialect`(`ShellDialect`:`bash` | `powershell`——具体 shell 而非家族;类 POSIX 的同胞将是自己的值),两个 shell 工具在加载时拒绝不匹配的执行器:把 tool-pwsh 与 bash-local 误配(或反之)会响亮失败,而不是让交错 parser 的命令表现为普通非零退出。
|
||||
- Windows 专属语义(CRLF 归一化、强制终止 exit-1/signal-null、仅 POSIX 的自信号)一如既往由测试钉住。
|
||||
- pwsh 工具的 per-file 覆盖门禁由可脚本化的 fake-executor 套件(`tests/tools.spec.ts`)承担;真实 pwsh 的集成与 Loader 组合套件在无 `pwsh` 的宿主自跳过,与 bash 套件的分工一致。
|
||||
- 路线图提案的 parity 阶段已交付;其余阶段是 Windows 默认组合与 pwsh TUI/GUI 渲染。
|
||||
|
||||
@@ -293,7 +293,7 @@ abstract start(spec: BashExecSpec): BashProcess
|
||||
|
||||
Types: [BashExecRequest](../core-data-structures/bash.md) · [BashExecSpec](../core-data-structures/bash.md) · [BashProcess](../core-data-structures/bash.md) · [BashRunResult](../core-data-structures/bash.md)
|
||||
|
||||
Source: [`packages/bash/bash/src/index.ts:59`](../../packages/bash/bash/src/index.ts)
|
||||
Source: [`packages/bash/bash/src/index.ts:51`](../../packages/bash/bash/src/index.ts)
|
||||
|
||||
## `ctx.bashEnv` — `BashEnvRegistry`
|
||||
|
||||
|
||||
@@ -80,8 +80,6 @@ function assertPositiveFinite(name: string, value: number): void {
|
||||
export class LocalBashExecutor extends BashExecutor {
|
||||
static inject = ['subprocess']
|
||||
|
||||
readonly dialect = 'bash' as const
|
||||
|
||||
static Config: z<Config> = z.object({
|
||||
cwd: z.string(),
|
||||
timeoutMs: z.number().default(120_000),
|
||||
|
||||
@@ -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/bash/README.md
|
||||
README.md: b4dbfb6cd9af92d65e30be8d0237b17611b6cbe8
|
||||
README.zh.md: 8cf73948970bf0ba465d04b5bedc7e4146fdd217
|
||||
README.md: d7bf746969f52000fe298b65b995b7c631d8001c
|
||||
README.zh.md: a7c0cac0bce2154362c822c213a44f3c507d541c
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
The **bash executor seam**: an abstract `BashExecutor` service (`ctx.bash`) defining WHAT a bash backend does — run foreground commands and start background processes — without saying HOW. Every implementation declares its `dialect` (`ShellDialect`: the concrete shell that parses the command string, `bash` or `powershell`), and the model-facing shell tools reject a mismatched executor at load. Task ids, ownership, collection, cancellation, and notices belong to the generic `ctx.tasks` runtime.
|
||||
The **bash executor seam**: an abstract `BashExecutor` service (`ctx.bash`) defining WHAT a bash backend does — run foreground commands and start background processes — without saying HOW. Task ids, ownership, collection, cancellation, and notices belong to the generic `ctx.tasks` runtime.
|
||||
|
||||
This package is the interface quarter of the bash capability, split so each concern can evolve (and be swapped) independently:
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
**bash 执行器 seam**:抽象 `BashExecutor` 服务(`ctx.bash`)定义 bash 后端做什么,即运行前台命令与启动后台进程,但不规定如何实现。每个实现声明自己的 `dialect`(`ShellDialect`:解析命令字符串的具体 shell,`bash` 或 `powershell`),模型侧 shell 工具在加载时拒绝不匹配的执行器。task id、所有权、收集、取消与通知属于通用 `ctx.tasks` 运行时。
|
||||
**bash 执行器 seam**:抽象 `BashExecutor` 服务(`ctx.bash`)定义 bash 后端做什么,即运行前台命令与启动后台进程,但不规定如何实现。task id、所有权、收集、取消与通知属于通用 `ctx.tasks` 运行时。
|
||||
|
||||
本包(package)是 bash 能力中负责接口的四分之一,各项职责因此可以独立演进(和替换):
|
||||
|
||||
|
||||
@@ -29,14 +29,6 @@ declare module 'cordis' {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The shell language a command string is written in. Values name concrete
|
||||
* shells, not families — the executor hands the string verbatim to that
|
||||
* shell's parser (`bash -c`, `pwsh -Command`), so a POSIX-ish sibling such
|
||||
* as zsh or fish would be its own dialect, never `bash`.
|
||||
*/
|
||||
export type ShellDialect = 'bash' | 'powershell'
|
||||
|
||||
/**
|
||||
* Abstract bash execution service. Subclass, implement the abstract methods,
|
||||
* and load the subclass as a plugin — it registers as `ctx.bash` (one
|
||||
@@ -61,14 +53,6 @@ export abstract class BashExecutor extends Service {
|
||||
super(ctx, 'bash')
|
||||
}
|
||||
|
||||
/**
|
||||
* The shell dialect this executor's `run`/`start` parse commands with.
|
||||
* Model-facing shell tools reject a mismatched executor at load
|
||||
* (misconfiguration fails loud): a PowerShell command handed to `bash -c`
|
||||
* would otherwise surface as an ordinary nonzero exit.
|
||||
*/
|
||||
abstract readonly dialect: ShellDialect
|
||||
|
||||
/**
|
||||
* The sandbox mode this executor applies by default, or `undefined` when it
|
||||
* does not sandbox commands.
|
||||
|
||||
@@ -10,8 +10,6 @@ import type { BashExecRequest, BashExecSpec, BashProcess, BashProcessRead, BashR
|
||||
* owes the abstract class.
|
||||
*/
|
||||
class StubExecutor extends BashExecutor {
|
||||
readonly dialect = 'bash' as const
|
||||
|
||||
resolve(request: BashExecRequest): BashExecSpec {
|
||||
return {
|
||||
command: request.command,
|
||||
|
||||
@@ -104,8 +104,6 @@ function assertPositiveFinite(name: string, value: number): void {
|
||||
export class PwshLocalExecutor extends BashExecutor {
|
||||
static inject = ['subprocess']
|
||||
|
||||
readonly dialect = 'powershell' as const
|
||||
|
||||
static Config: z<Config> = z.object({
|
||||
cwd: z.string(),
|
||||
timeoutMs: z.number().default(120_000),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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`);结果渲染和后台进程适配仍是实现细节,由同包测试覆盖。
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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-pwsh/README.md
|
||||
README.md: 7bc1c0998a67ee772ec54eb46bebed52e5164588
|
||||
README.zh.md: 4f74b7cb4b737fd4c4f788586568676423f59cd4
|
||||
README.md: dfe26a63684d61dcdd6f969c2c2261dac79325c7
|
||||
README.zh.md: 2344f8477e5b15f2c4d366dd82b46358eacbc1b7
|
||||
|
||||
@@ -4,7 +4,7 @@ English | [中文](README.zh.md)
|
||||
|
||||
The model-facing `pwsh` tool registered over the `ctx.bash` executor seam. Intended for Windows compositions where a PowerShell executor (e.g. `@deepseek-ai/dsh-pwsh-local`) backs `ctx.bash`; the tool contract is PowerShell-dialect: native `C:\...` paths and `$env:NAME` variables. Behavior mirrors `dsh-tool-bash` call-for-call minus the sandbox surface — foreground and `run_in_background` execution through the generic task runtime, the managed `DSH_*` environment through the shared `bash-env` registry, and the bash marker/truncation rendering story (a clean exit produces no marker).
|
||||
|
||||
Requires a loaded executor implementation and the `bash-env` plugin; the tool stays pending until both exist (`inject: ['tools', 'bash', 'systemPrompt', 'bashEnv']`), and rejects an executor whose `dialect` is not `powershell` at load — a PowerShell command handed to `bash -c` would surface as an ordinary nonzero exit.
|
||||
Requires a loaded executor implementation and the `bash-env` plugin; the tool stays pending until both exist (`inject: ['tools', 'bash', 'systemPrompt', 'bashEnv']`).
|
||||
|
||||
The package root exposes only the Cordis plugin contract (`name`, `inject`, `Config`, `apply`); result rendering (`src/render.ts`) and background-task adaptation (`src/background.ts`) mirror the bash tool's structure and stay reachable through the package's `./src/*` export.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
注册在 `ctx.bash` 执行器 seam 之上的模型可见 `pwsh` 工具。面向由 PowerShell 执行器(如 `@deepseek-ai/dsh-pwsh-local`)支撑 `ctx.bash` 的 Windows 组合;工具契约是 PowerShell 方言:原生 `C:\...` 路径与 `$env:NAME` 变量。行为与 `dsh-tool-bash` 逐调用对齐、减去 sandbox 面——通过通用任务运行时执行前台与 `run_in_background`、通过共享 `bash-env` 注册表管理 `DSH_*` 环境、以及 bash 的 marker/截断渲染故事(干净退出不产生 marker)。
|
||||
|
||||
需要已加载的执行器实现与 `bash-env` 插件;两者都存在前工具保持 pending(`inject: ['tools', 'bash', 'systemPrompt', 'bashEnv']`),并在加载时拒绝 `dialect` 不为 `powershell` 的执行器——PowerShell 命令被交给 `bash -c` 只会表现为普通的非零退出。
|
||||
需要已加载的执行器实现与 `bash-env` 插件;两者都存在前工具保持 pending(`inject: ['tools', 'bash', 'systemPrompt', 'bashEnv']`)。
|
||||
|
||||
包根只导出 Cordis 插件契约(`name`、`inject`、`Config`、`apply`);结果渲染(`src/render.ts`)与后台任务适配(`src/background.ts`)镜像 bash 工具的结构,并可通过包的 `./src/*` 导出访问。
|
||||
|
||||
|
||||
@@ -138,11 +138,6 @@ const BACKGROUND_OUTPUT_PROPERTIES = {
|
||||
/* jscpd:ignore-end */
|
||||
|
||||
export function apply(ctx: Context, config: Config = {}): void {
|
||||
// Model commands are written in PowerShell; a mismatched executor would
|
||||
// hand them to bash and surface as ordinary nonzero exits.
|
||||
if (ctx.bash.dialect !== 'powershell') {
|
||||
throw new Error(`tool-pwsh: the mounted executor speaks '${ctx.bash.dialect}', not powershell — mount dsh-pwsh-local or the matching shell tool`)
|
||||
}
|
||||
const backgroundEnabled = config.enableRunInBackground ?? true
|
||||
|
||||
ctx.systemPrompt.section({
|
||||
|
||||
@@ -23,7 +23,7 @@ import AgentRegistry from '@deepseek-ai/dsh-agent'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import { BashExecutor } from '@deepseek-ai/dsh-bash'
|
||||
import type { BashExecRequest, BashExecSpec, BashProcess, BashRunResult, ShellDialect } from '@deepseek-ai/dsh-bash'
|
||||
import type { BashExecRequest, BashExecSpec, BashProcess, BashRunResult } from '@deepseek-ai/dsh-bash'
|
||||
import * as ToolPwsh from '@deepseek-ai/dsh-tool-pwsh'
|
||||
import * as BashEnvPlugin from '@deepseek-ai/dsh-bash-env'
|
||||
import type { BashProcessRead } from '@deepseek-ai/dsh-bash'
|
||||
@@ -38,8 +38,6 @@ const testToolSignal = new AbortController().signal
|
||||
* handle.
|
||||
*/
|
||||
class FakeBash extends BashExecutor {
|
||||
readonly dialect: ShellDialect = 'powershell'
|
||||
|
||||
requests: BashExecRequest[] = []
|
||||
specs: BashExecSpec[] = []
|
||||
startCalls = 0
|
||||
@@ -201,19 +199,6 @@ async function callUntilText(
|
||||
}
|
||||
|
||||
describe('registration', () => {
|
||||
it('rejects an executor speaking another shell dialect at load', async () => {
|
||||
class BashDialectExecutor extends FakeBash {
|
||||
override readonly dialect = 'bash' as const
|
||||
}
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SystemPrompt)
|
||||
await ctx.plugin(ToolRegistry)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
await ctx.plugin(BashEnvPlugin)
|
||||
await ctx.plugin(BashDialectExecutor)
|
||||
await expect(ctx.plugin(ToolPwsh)).rejects.toThrow("the mounted executor speaks 'bash', not powershell")
|
||||
})
|
||||
|
||||
it('registers the pwsh tool with its prompt section and schema', async () => {
|
||||
const { ctx } = await setup()
|
||||
const schema = ctx.tools.schemas().find(s => s.name === 'pwsh')
|
||||
|
||||
@@ -48,8 +48,6 @@ function runResult(stdout: string, overrides: Partial<BashRunResult> = {}): Bash
|
||||
|
||||
/** A scriptable fake `ctx.bash` recording the command it was asked to run. */
|
||||
class FakeBash extends BashExecutor {
|
||||
readonly dialect = 'bash' as const
|
||||
|
||||
commands: string[] = []
|
||||
result: BashRunResult = runResult(`${tmuxLine()}\n`)
|
||||
runError?: Error
|
||||
|
||||
Reference in New Issue
Block a user