kaos:执行环境抽象
统一的文件与进程 API、本地与 SSH 两种实现、withCwd 的不可变设计,以及 v2 引擎用 os/ 与 Runtime 接过这层抽象之后 kaos 的位置。
packages/kaos(约 3187 行,12 个源文件)是最小但最基础的包之一。名字是 Kimi Agent Operating System 的缩写,包描述是「Execution environment abstraction for AI agent applications」。
接口
src/kaos.ts 的 Kaos 接口,路径操作一段(第 25 行起):
/** Return the path style used by this environment. */
pathClass(): 'posix' | 'win32';
/** Normalize the given path string (resolve `.` / `..` segments). */
normpath(path: string): string;
/** Return the home directory of the current user. */
gethome(): string;
/** Return the current working directory. */
getcwd(): string;之后是 chdir / withCwd / withEnv、stat / iterdir / glob、readBytes / readText / readLines / writeBytes / writeText / mkdir,以及 exec / execWithEnv。
设计要点:
路径操作同步、IO 操作异步。 路径计算是纯字符串运算(即使目标是远程机器),不需要往返;文件与进程操作必然要 IO。
pathClass() 返回目标环境的路径风格。 本地在 Windows 上跑、通过 SSH 连 Linux 时,路径风格是远端的 posix 而非本地的 win32。
withCwd() 返回新的 Kaos 而非修改自身。 把「在某个目录下操作」表达成新的环境句柄,避免 chdir 的全局状态问题。同样思路的还有 withEnv(env):返回一个给所有子进程叠加环境变量的新 Kaos,而且记录在 spawn 时才读取,调用方改一个稳定的 record 就能影响之后的执行。
current.ts 用 AsyncLocalStorage 绑定「当前 Kaos」:runWithKaos() 让并发调用各自在自己的异步上下文里拿到不同实例,互不污染。
两种实现
local.ts LocalKaos —— 本地(node:child_process)
ssh.ts SSHKaos —— 远程(ssh2 + SFTP)ssh.ts 从 ssh2 引入 Client、ClientChannel、ConnectConfig、SFTPWrapper 等类型——SFTP 传文件、开 channel 跑命令。接口注释写着「local, SSH, containers, etc.」,容器是预留的方向。
Environment 探测
osEnv 由具体实现填充:LocalKaos 用 detectEnvironmentFromNode() 直接读,SSHKaos 则要连上去主动探测。
login-shell-path.ts 解决的问题是:从 GUI 启动器或非 login 的父 shell 拉起时,process.env.PATH 缺了 /opt/homebrew/bin 这类条目,Bash 工具找不到用户在交互 shell 里能用的命令。做法是跑一次用户的 login shell($SHELL -l -c /usr/bin/env),把缺的 PATH 条目补在末尾;Windows 跳过。
v0.29.1 之后新增的 shell-path-bridge.ts 处理 Windows 上的 Git Bash / MSYS2:原生路径与 shell 看到的 POSIX 路径互相翻译(/c/Users/x 即 C:\Users\x,/tmp/x 其实是 %TEMP%\x),词法翻不了的交给 bash 旁边的 cygpath -w,非 win32 bash 环境下恒等。
v1 退场之后,kaos 在哪
这是本篇变化最大的一节。v0.29.1 时,v1 引擎、acp-adapter 与 node-sdk 都依赖 kaos,v2 则另有 src/os/。现在 v1 与 acp-adapter 都已删除,引擎不再使用 kaos:agent-core-v2 的 package.json 没有声明它,src/ 里也没有任何 kaos 的 import。全仓声明依赖 kaos 的只剩 node-sdk(放在 devDependencies),源码里也只是 import type { Kaos }——KimiHarness 的会话选项仍有 kaos / persistenceKaos 两个字段。node-sdk 的 v2 客户端注释说得很坦白:
agent-core-v2 has no kaos injection point (its fs/process abstraction is the engine-internal hostFs domain, resolved at bootstrap)
所以在 v2 引擎下,这两个选项会被忽略,退化为普通的本地创建 / 恢复。
接过这层抽象的是 v2 自己的几块:
src/os/interface/ IHostFileSystem、IHostProcessService、IHostEnvironment、终端
src/os/backends/node-local/ 本地实现
src/runtime/ Runtime:按能力(fs / process / terminal)组合的执行环境
src/_base/execEnv/ 环境探测、login shell PATH、路径桥、文本解码等底层件_base/execEnv/ 里能看到 kaos 的影子:BufferedReadable、decodeTextWithErrors、globPatternToRegex、applyLoginShellPathFromNode、createShellPathBridge,名字与 kaos 里的一一对应。Runtime 则比 Kaos 多了能力声明与状态(connecting / ready / degraded / disconnected / draining / disposed),工具按需租用,acquire() 返回 RuntimeLease。
约束依然成立
v1 的 Bash 工具文件头写着「Execution goes through Kaos, never directly via node:child_process」。v2 是零注释区,这句话没了,做法没变:agent/tools/os/bash/bashTool.ts 第 187 行先 this.runtime.acquire(['process']) 租一个带进程能力的运行时,再从 lease.runtime.process 起进程。整个 v2 src/ 里直接 import node:child_process 的只有 5 个文件:本地后端 hostProcessService.ts、_base/execEnv/ 的环境探测与路径桥,以及 externalHooks 的 hook 运行器和 tower 的 git 协议。
「换个实例就能换执行环境」的承诺,在 v2 里由 acp-server 兑现:它在 Session 作用域注册 ACP 版 IHostFileSystem,文本读写走编辑器;再用 AcpRuntimeProviderFactory 让 Bash 在编辑器终端里执行(见 klient、SDK 与 ACP)。文件工具与 Bash 工具一行不改。
反过来,SSH 目前没有进入 v2:os/backends/ 下只有 node-local,引擎也不依赖 ssh2。SSHKaos 仍在 kaos 包里,却已没有通往引擎的路径。
为什么值得单独一层
三个收益在 v2 里都还成立:
- 可测试。
runtime/fakeRuntime.ts这类替身让工具测试不碰真实磁盘与进程。 - 可替换。Runtime 按能力声明,本地、编辑器(ACP)都只是一个 provider。
- 可扩展。将来接容器或远程执行,同样是加 backend / provider,工具层零改动。
代价也还是那句:每个 IO 调用多一层间接,而且必须全员遵守——这类抽象的价值与破例次数成反比。kaos 这个包本身的去向也说明了一点:抽象的思路会留下来,具体的包却可能被新架构内化。
下一篇:收尾复盘。