Volume 2 · Chapter 27

终端与 LSP:subprocess 之上的持久交互

终端(terminal)是「持久会话的交互式 shell」——16 页的 subprocess 有进程树管理,但没有「会话持续」的语义。终端 seam 在它之上加持久层。LSP 是语言服务器协议客户端——让模型读写代码符号而非文本。两者都是 16 页下层 seam 的消费者。

(卷一连接点) 16 页 subprocess(PTY/进程树)· 15 页 seam 模板 → 本域:terminal / terminal-bash / tool-terminal* / lsp / lsp-stdio → (回心脏) 终端的增量输出经 jobs(25 页)或直接进工具结果

示例本次示例:跨多次工具调用的终端会话

示例轨迹 27-1 · 终端 vs 普通 bash 的差异
# 普通 bash(17 页):每次调用 spawn 新进程
#   bash("pwd") → 新 bash → 退出;bash("cd x && pwd") → 又一个新 bash
# 终端(terminal-bash,inject=['terminals','sandboxPolicy','subprocess']):
#   terminal_exec("pwd")       → 会话 t1 的 PTY 进程执行
#   terminal_exec("cd src")    → 同一 PTY,目录状态保留
#   terminal_exec("pwd")       → /workspace/src(前一条 cd 生效了)
# 持久性在 TerminalSessionService(terminal/src/index.ts:105)的契约里
来源:27 页 §3 + 16 页 spawnTerminal
示例轨迹 27-2 · LSP 的结构化问答
# 模型:lsp("auth.ts 里 authenticate 的定义在哪?")
# lsp-stdio(inject=['fs','lsp','subprocess']):
#   subprocess.spawn 语言服务器进程(16 页下层)
#   JSON-RPC: textDocument/definition → 结果 {file:'src/auth.ts', line:42}
# 对比 grep 文本搜索:LSP 给的是符号级答案
# 换 subprocess provider(e2b 远程)→ LSP 与终端一起搬(15 页结论)
来源:27 页 §3 + 15 页共享执行世界

§1挂载条目

  • terminal-bash → dsh-terminal-bash(1116 行,可选挂载)——inject = ['terminals', 'sandboxPolicy', 'subprocess'](src/index.ts:26)
  • lsp-stdio → dsh-lsp-stdio(1664 行,可选挂载)——inject = ['fs', 'lsp', 'subprocess'](src/index.ts:47)
  • tool-terminal* / tool-lsp* 家族(模型可见工具,部分在 profile 层追加)

§2包文件地图

包规模角色
packages/terminal/terminal/—Service Definition:TerminalSessionService extends Service(src/index.ts:105)——持久终端会话契约
packages/terminal/terminal-bash/5 文件 / 1116 行Provider:bash 终端——依赖 terminals + sandboxPolicy + subprocess(16 页的下层)
packages/lsp/lsp/—Service Definition:Lsp extends Service implements LspService(src/index.ts:82)
packages/lsp/lsp-stdio/9 文件 / 1664 行Provider:stdio 传输的语言服务器——inject = ['fs','lsp','subprocess']

§3机制:持久会话与语言服务器

  1. terminal = subprocess + 持久:普通 bash 命令每次 spawn 新进程;终端会话保持一个 PTY 进程跨多次工具调用存活(增量输出、前台进程组)。16 页的 spawnTerminal 与进程树清理就是它的底座。
  2. lsp = 结构化代码访问:LSP 协议让模型做符号级操作(定义跳转、引用、补全),而不是 grep 文本。stdio provider 通过 subprocess spawn 语言服务器进程,走 JSON-RPC。
  3. 两者都站在 16 页的下层上:换 subprocess provider(如 e2b 远程)时,terminal 和 lsp 一起跟着搬——15 页「共享执行世界」结论的第二个例子。

§4关键代码

packages/terminal/terminal/src/index.ts终端会话服务105
105export class TerminalSessionService extends Service {
packages/lsp/lsp-stdio/src/index.tsstdio provider 的三依赖47
47export const inject = ['fs', 'lsp', 'subprocess']
105

terminal 的 Definition。持久会话的语义都在这里(会话生命周期、增量读取、前台控制)。

47

lsp-stdio 的依赖面说明了它的位置:fs(读文件)、lsp(自己注册进 seam)、subprocess(spawn 语言服务器进程)。三个 seam 叠三层。

§5易错点

终端会话的存活边界比命令长:17 页的「executor-only 重载后后台进程存活」在终端上更显著——终端会话绑定 subprocess provider,重载 terminal provider 不杀进程(与 16 页同一条边界)。

PTY 的前台进程组检测:16 页提过的 process-inspector(Linux /proc、macOS ps)在 0.1.3-alpha.2 新增了 windows-inspector(325 行)——三平台进程表探测齐全。这是 terminal 正确清理会话树的前提。