Merge pull request #6756 from deepseek-harness/fix/persistent-bash-input-mangling
fix(tools): disable history expansion in the persistent bash shell
This commit is contained in:
commit
cd9ee0ee4c
7 files changed
+23
-11
No files matched your search
@@ -3841,7 +3841,7 @@ export interface Config {
|
||||
## `@deepseek-ai/dsh-tool-bash-persistent`
|
||||
|
||||
- `inject`: `tools` · `terminals` · `workingDirectory`
|
||||
- `source`: [`packages/shell/tool-bash-persistent/src/index.ts:445`](../packages/shell/tool-bash-persistent/src/index.ts)
|
||||
- `source`: [`packages/shell/tool-bash-persistent/src/index.ts:447`](../packages/shell/tool-bash-persistent/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Configuration for the persistent Bash tool. */
|
||||
|
||||
@@ -3843,7 +3843,7 @@ export interface Config {
|
||||
## `@deepseek-ai/dsh-tool-bash-persistent`
|
||||
|
||||
- `inject`: `tools` · `terminals` · `workingDirectory`
|
||||
- `source`: [`packages/shell/tool-bash-persistent/src/index.ts:445`](../packages/shell/tool-bash-persistent/src/index.ts)
|
||||
- `source`: [`packages/shell/tool-bash-persistent/src/index.ts:447`](../packages/shell/tool-bash-persistent/src/index.ts)
|
||||
|
||||
```ts config-catalog
|
||||
/** Configuration for the persistent Bash tool. */
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
en: 857e036f41c913ac
|
||||
zh: e9f383f7ee384c59
|
||||
/deepseek-ai-dsh-tool-bash-persistent/understand-the-implementation/command-flow:
|
||||
en: 3b7bf7c20b266c31
|
||||
zh: 85a325ef65fa7ac7
|
||||
en: c511f75009ba3743
|
||||
zh: b3b722e00b2910b1
|
||||
/deepseek-ai-dsh-tool-bash-persistent/further-exploration:
|
||||
en: 42c387ccaab7e64c
|
||||
zh: e58ec4ea60cf7711
|
||||
@@ -72,8 +72,8 @@
|
||||
en: ba6a48888c3361e5
|
||||
zh: 3a8f648053555dc9
|
||||
/deepseek-ai-dsh-tool-bash-persistent/known-limitations-and-deferred-work:
|
||||
en: dc5a908f2b2da040
|
||||
zh: 3afceaabcf324771
|
||||
en: b12111850d752f0b
|
||||
zh: 92b855387ec187ff
|
||||
/deepseek-ai-dsh-tool-bash-persistent/known-limitations-and-deferred-work/dev-note:
|
||||
en: e67f633dc6fe7773
|
||||
zh: 3bb0c20c93f16f98
|
||||
@@ -85,7 +85,7 @@ This section explains the design decisions behind the tool and points at the cod
|
||||
|
||||
### Command flow
|
||||
|
||||
A first command spawns the shell through `ctx.terminals.spawn`, disables input echo (`stty -echo`), and waits for readiness. Each command is then wrapped into one physical line — a printf of the start marker, the command body escaped with `$'…'`, and a printf of the end marker plus `$?` — so embedded newlines cannot leak terminal prompts into the result. The tool polls the scrollback in 1,000-line pages until the end marker appears, extracts the span, and renders it with any status marker. A timeout aborts the deadline, captures the partial output, and resets the shell.
|
||||
A first command spawns the shell through `ctx.terminals.spawn`, disables input echo and history expansion (`stty -echo; set +H`), and waits for readiness. Each command is then wrapped into one physical line — a printf of the start marker, the command body escaped with `$'…'`, and a printf of the end marker plus `$?` — so embedded newlines cannot leak terminal prompts into the result. With history expansion off, `!` sequences reach bash unchanged instead of being rewritten or rejecting the whole line. The tool polls the scrollback in 1,000-line pages until the end marker appears, extracts the span, and renders it with any status marker. A timeout aborts the deadline, captures the partial output, and resets the shell.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -148,6 +148,7 @@ These limits define when the tool is a poor fit or needs special care. They are
|
||||
- **An interactive foreground child returns early with partial output only where the subprocess provider proves its stdin wait** — elsewhere the call runs to `timeoutMs`.
|
||||
- **Explicit `exit` and timeout discard shell state** — cancellation also resets and discards the result, even when a complete status marker is already observable; the next call starts a fresh shell.
|
||||
- **Environment facts such as network access and package mirrors belong in the configured `description`** — not this package's default.
|
||||
- **Raw control characters other than newline and carriage return reach readline as typed keys** — a literal TAB, ESC, or DEL in the command is consumed as an editing key, so content that needs them, such as a Makefile heredoc, should be written with a file tool or produced by `printf` escapes.
|
||||
|
||||
<a id="dev-note"></a>
|
||||
### Dev Note
|
||||
|
||||
@@ -85,7 +85,7 @@ kind: "package-reference"
|
||||
|
||||
### 命令流程
|
||||
|
||||
首条命令通过 `ctx.terminals.spawn` 生成 shell,禁用输入回显(`stty -echo`),并等待就绪。随后每条命令都包装成一行物理文本——printf 起始标记、用 `$'…'` 转义的命令体、printf 结束标记加 `$?`——因此内嵌换行无法把终端提示符泄漏进结果。工具以 1,000 行一页轮询 scrollback,直到出现结束标记,提取区间并连同任何状态标记一起渲染。超时会中止截止时间、捕获部分输出并重置 shell。
|
||||
首条命令通过 `ctx.terminals.spawn` 生成 shell,禁用输入回显和历史展开(`stty -echo; set +H`),并等待就绪。随后每条命令都包装成一行物理文本——printf 起始标记、用 `$'…'` 转义的命令体、printf 结束标记加 `$?`——因此内嵌换行无法把终端提示符泄漏进结果。关闭历史展开后,`!` 序列原样到达 bash,不会被改写,也不会使整行被拒绝。工具以 1,000 行一页轮询 scrollback,直到出现结束标记,提取区间并连同任何状态标记一起渲染。超时会中止截止时间、捕获部分输出并重置 shell。
|
||||
|
||||
</details>
|
||||
|
||||
@@ -148,6 +148,7 @@ kind: "package-reference"
|
||||
- **交互式前台子进程只在子进程提供方证明其 stdin 等待时才提前返回部分输出**——否则调用一直运行到 `timeoutMs`。
|
||||
- **显式 `exit` 与超时会丢弃 shell 状态**——取消同样重置并丢弃结果,即使完整状态标记已经可观察;下一次调用启动全新 shell。
|
||||
- **网络访问与包镜像等环境事实属于配置的 `description`**——而不是本包的默认描述。
|
||||
- **除换行与回车外,原始控制字符会作为按键到达 readline**——命令中的字面 TAB、ESC 或 DEL 会被当作编辑按键消耗,因此需要这些字符的内容(例如 Makefile heredoc)应通过文件工具写入,或用 `printf` 转义生成。
|
||||
|
||||
<a id="dev-note"></a>
|
||||
### 开发备注
|
||||
|
||||
@@ -267,10 +267,12 @@ function persistentShells(ctx: Context, config: ResolvedConfig): PersistentShell
|
||||
live.delete(owner)
|
||||
}, 'tool-bash-persistent owner cache cleanup')
|
||||
}
|
||||
// Echo suppression only: the prompt stays the backend's own, so the
|
||||
// backend's prompt-based readiness detection keeps working.
|
||||
// Suppress input echo and history expansion, which would otherwise
|
||||
// rewrite `!` sequences or discard the whole wrapper line before bash
|
||||
// parses it. The prompt stays the backend's own, so the backend's
|
||||
// prompt-based readiness detection keeps working.
|
||||
const setup = ctx.terminals.startSend(owner, spawned.sessionId, {
|
||||
text: 'stty -echo',
|
||||
text: 'stty -echo; set +H',
|
||||
submit: true,
|
||||
signal: combinedSignal,
|
||||
})
|
||||
|
||||
@@ -165,6 +165,14 @@ suite('persistent Bash through a real cordis.yml Loader composition', () => {
|
||||
))
|
||||
expect(heredoc).toBe('alpha\nbeta\n[Command finished with exit code 0]')
|
||||
|
||||
// Interactive bash applies history expansion to typed input before parsing;
|
||||
// it must neither rewrite `!` sequences nor drop the whole wrapper line.
|
||||
const history = text(await execute(
|
||||
'history-expansion',
|
||||
"echo '<!doctype html>' > page.html; cat page.html; echo 'it'; echo hi!!",
|
||||
))
|
||||
expect(history).toBe('<!doctype html>\nit\nhi!!\n[Command finished with exit code 0]')
|
||||
|
||||
const pipeline = text(await execute(
|
||||
'pipeline',
|
||||
'{ sleep 0.1; printf "delayed\\n"; } | cat',
|
||||
|
||||
Reference in new issue
Block a user