# @deepseek-ai/dsh-pty-local

[English](README.md) | 中文

这个本地 Linux／macOS `node-pty` 后端实现 `ctx.pty`；在其他平台加载时会以不支持为由失败。它在共享 `ctx.sandboxPolicy` 下启动交互式 shell，移除形似凭据的环境变量，保留有界的逐行输出，检测就绪状态，并清理以 `node-pty` 子进程为根的已捕获进程树。

## 插件（`pty-local`）

该插件注入 `pty`、`sandbox` 和 `sandboxPolicy`，然后注册所配置的后端类型（`shell`）。`danger-full-access` 会直接启动 shell；受限模式则通过 `ctx.sandbox` 包装确切的 shell argv。系统在 spawn 时解析会话的实际模式。当某个所有者存在开放的 PTY 或正在进行 spawn 时，如果配置变更会得到不同的实际模式，系统会在对应 `sandbox/mode` 事件提交前拒绝该变更。该限制绑定到确切所有者，因此即使本地提供方重新加载并保留现有会话，它仍然有效。更改模式前，请等待创建完成并关闭会话，避免以更宽权限打开的终端在权限降级后继续存在。

Linux 的就绪检测结合以下机制：由前台状态验证的私有 bash 提示符标记、前台进程组 syscall 检查、静默回退和绝对超时。macOS 没有 `/proc` syscall 接口，因此使用经过验证的提示符标记以及静默／超时。当可打印的提示符文本尚未到达时，即使 OSC 标记和 `PS1` 被拆到多个数据回调中，系统也不会把标记视为就绪。如果 bash 在内核发布其重新取得前台进程组的状态前打印标记，轮询会在普通静默上限之后再保留该候选状态 `handoffGraceMs`，使恰好同时发生的前台交接有机会胜出；该宽限至少要覆盖一个 `pollIntervalMs`，否则加载时即被拒绝。因此，继承 `PROMPT_COMMAND` 的交互式子进程无法一直抑制推断空闲就绪直至绝对超时。无法识别或读取的进程状态绝不会作为精确空闲的正向信号。同样，一次 send 之前就已存在的前台进程组 stdin 等待并不代表写入后就绪：必须先观察到同一进程组脱离该等待，之后再次进入等待才能使该次 send 完成；前台进程组发生变化则构成新的证据。尚未发布的启动过程中，回退路径要求已经观察到输出；零输出静默不能发布空会话，超时则拒绝 spawn。取消操作会关闭尚未发布的 shell，并以调用方提供的确切中止原因拒绝，即使当时还无法观察其前台进程组。如果关闭失败，`PtyBackendCleanupError` 会单独保留清理失败，供注册表 dispose（资源释放）时处理。未完成的终端控制序列受 `maxReadBytes` 限制；超过上限后，系统会丢弃内容直到其终止符。末尾的回车会跨回调保留，使拆分的 CRLF 合并为一个换行。

取消发送时，系统会解析当前前台进程组并发送真正的 `SIGINT`；它绝不会通过写入 `\x03` 模拟中断，因此，即使程序运行在 raw 模式下，也仍可取消。关闭操作先向后代发送 `SIGTERM` 并等待，再向已捕获的存活进程与新扫描到的后代之并集发送 `SIGKILL`，防止进程通过重新设定父进程而逃避清理。在停止 shell 前，系统会确认每个保留的进程身份都已消失，或者在 Linux 上已成为不再执行的僵尸进程；僵尸进程条目视为完全停稳，并会随 shell 退出而回收。如果仍有进程存活，失败结果不会缓存成永久拒绝的关闭操作；后续关闭仍会重试清理。

## 模型体验

### 间接消费方

#### 模型看到的内容

没有直接可见内容。模型通过 `@deepseek-ai/dsh-tool-pty` 可能收到有界的 MOTD、发送增量、scrollback 页、就绪原因和清理错误。

#### Token 影响

消费方返回有界的后端输出前没有影响。此包（package）不会把保留的 PTY scrollback 放入模型历史。

#### KV Cache 影响

不会直接使 KV Cache 失效；提示词、schema 与追加结果由消费方负责。

## 已知限制与暂缓事项

- 输出按行规范化；不支持全屏备用缓冲区交互。
- Linux 精确探针支持 x64 与 arm64 UAPI 表；其他架构使用提示符标记和静默／超时就绪机制。
- 如果后代进程在清理前守护化并重新设定父进程，它会脱离已捕获的进程树；清理绝不会扩大到启动器 PID 所属的整个 POSIX 会话，因为其中可能包含无关进程。
- harness 进程退出后，会话无法继续存在。
