# CST-Agent C11 OpenCode-like carrier that extends paper **CST** (confirmation frontier / irreversible emission) from Markdown→HTML to the **agent tool loop**. See [docs/theory_tool_cst.md](docs/theory_tool_cst.md). 评测/反哺论文素材:[docs/eval_tool_cst.md](docs/eval_tool_cst.md)。 ## 能不能用来工作? **可以**:有监督的本地改动 / 探索仓库(TUI + 13 个内置工具 + 带 diff 的权限确认 + `/undo`)。 **不宜**:无人值守长任务或当 CI 主力(网关偶发空答;headless 模式默认自动批准)。 ### 5 分钟试用 ```bash # Linux / WSL / macOS 一键:克隆到 ~/CST-agent、编译、装到 ~/.local/bin curl -fsSL https://git.whcself.xyz/WHC/CST-agent/raw/branch/main/scripts/bootstrap_linux.sh | bash # 已有仓库目录内(装到别处:CST_AGENT_PREFIX=/usr/local) ./scripts/bootstrap_linux.sh . # 以后升级:拉取最新代码、重新编译并安装(本地有未提交改动时跳过 pull) cst-agent-update cst-agent --version # 在项目目录里直接启动:首次会让你选 provider 并输入 API key(遮罩输入,按 provider 保存) cd ~/my-project && cst-agent ``` 一键脚本可以重复运行:已有仓库会 `git pull --ff-only`,代码有更新时干净重编,然后重新安装; 同时装一个 `cst-agent-update` 命令(等同重跑脚本)。NixOS 上会自动进 `nix-shell` 编译。 之后再启动会沿用上次的 provider、模型和推理强度(low / medium / high / max),不需要任何参数或环境变量。 界面里用 `/provider` 切换(没保存过 key 的会当场询问),`/model`、`/effort` 切换模型和推理强度,`/key` 更换 key。 完整环境变量说明:[docs/env_openai.md](docs/env_openai.md)。 | 平台 | 一键脚本 | |---|---| | Linux | [`scripts/bootstrap_linux.sh`](scripts/bootstrap_linux.sh) | | Termux | [`scripts/bootstrap_termux.sh`](scripts/bootstrap_termux.sh) | | Windows 交叉构建(在 Linux 上) | [`scripts/bootstrap_windows.sh`](scripts/bootstrap_windows.sh) → `dist/windows-x86_64/` | | Windows 运行 | 拷贝 dist 后 [`scripts/run_windows.bat`](scripts/run_windows.bat) | ### 迷你验收工程(论文案例) 故意带 bug 的小计算器:[`fixtures/mini_calc/`](fixtures/mini_calc/)(任务书 `TASK.md`)。 ```bash ./scripts/e2e_mini_calc.sh --baseline-only # 无 API:证明初始测试 RED ./scripts/e2e_mini_calc.sh --provider deepseek --protocol ctl # 有 key ``` 更多夹具(每个都有 `TASK.md` + `check.sh`):`makefile_tab`(写含 TAB 的 Makefile)、 `big_file`(写 5000 字节文件)、`test_fix_test`(先跑测试→修→再跑测试)、 `multi_file_refactor`(跨文件改名 + 修 bug,适合 `edit_file` / `grep`)、 `ask_or_todo`(必须用 `todo_write` 规划并全部完成;无人回答 `ask_user`)。 后两个需要 `--tools default`(脚本默认 `minimal`,即论文用的 4 个工具)。 ```bash scripts/e2e_fixture.sh makefile_tab --provider deepseek --protocol native-inc scripts/e2e_fixture.sh multi_file_refactor --provider deepseek --tools default # 批量实验:协议 x 模型 x 夹具 x N 次 → results.csv + summary.md scripts/run_matrix.sh -p "native-eos native-inc ctl ctl-heredoc xml" \ -m "deepseek:deepseek-flash deepseek:deepseek-v4-pro" -n 3 -r ``` 口径:`pass` / `fail` / `agent_error` / `timeout` — 见 [docs/eval_tool_cst.md](docs/eval_tool_cst.md)。 ## Build ### Make(Linux 默认) ```bash cd CST-agent make # 依赖 pkg-config 找 libcurl / ncursesw(找不到时退回 ncurses) make check # 单元测试 + 截断实验(含真实录制流) make asan # AddressSanitizer + UBSan 构建并跑全部测试 make install PREFIX=$HOME/.local # 或 sudo make install(默认 /usr/local) ``` NixOS:`nix-shell`(仓库自带 [`shell.nix`](shell.nix))后再 `make`。 macOS:`brew install curl ncurses pkg-config` 后直接 `make`(Makefile 会找 Homebrew 的 keg-only 库)。 各平台细节与验收清单见 [docs/compat.md](docs/compat.md)。 ### CMake(推荐跨编译器 / 交叉) ```bash # GCC cmake -B build-gcc -DCMAKE_C_COMPILER=gcc -DCST_BUILD_TUI=ON cmake --build build-gcc && ctest --test-dir build-gcc # Clang cmake -B build-clang -DCMAKE_C_COMPILER=clang -DCST_BUILD_TUI=ON cmake --build build-clang # Headless only(无 ncurses) cmake -B build-hd -DCST_BUILD_TUI=OFF && cmake --build build-hd # Windows MinGW 交叉(需安装 mingw-w64) cmake -B build-win -DCMAKE_TOOLCHAIN_FILE=cmake/mingw-w64-x86_64.cmake \ -DCST_BUILD_TUI=OFF cmake --build build-win # TUI:安装 PDCurses 后 -DCST_BUILD_TUI=ON -DPDCurses_ROOT=... ``` 依赖:`gcc` 或 `clang`、`libcurl`、Linux TUI 需 `ncursesw`;Windows TUI 需 **PDCurses**。 Sashidown(`vendor/sashidown`)与 cJSON(`vendor/cjson`,MIT)已随仓库提供。 API Key:TUI 里输入并按 provider 保存到配置目录的 `credentials`(权限 0600);`/key` 更换,`--ask-key` 在命令行输入。 `--api-key` 只用于本次进程、不落盘;`DEEPSEEK_API_KEY` 等环境变量和 `CST_API_KEY` 仅作为没有保存 key 时的兜底(方便 CI)。 ## 平台支持矩阵 | OS | Arch | 编译器 | Headless | TUI | Shell | |---|---|---|---|---|---| | Linux(glibc / musl) | x86_64 / aarch64 | GCC, Clang | yes | ncursesw | `/bin/sh` 或旁路 `busybox` | | WSL2 | x86_64 | 同 Linux | yes | ncursesw | `/bin/sh`(工作区建议放在 Linux 文件系统内) | | macOS | arm64 / x86_64 | Apple Clang | yes | ncurses(Homebrew) | `/bin/sh` | | Android Termux | aarch64 / … | Clang (Termux) | yes | ncurses(Termux 包) | `$PREFIX/bin/sh`;可旁路 busybox | | Windows | x86_64 / aarch64 | MinGW GCC/Clang | yes | PDCurses | **捆绑 busybox-w32** | ### Termux ```bash # 一键:装依赖、克隆到 ~/CST-agent、编译、安装到 $PREFIX/bin curl -fsSL https://git.whcself.xyz/WHC/CST-agent/raw/branch/main/scripts/bootstrap_termux.sh | bash # 以后升级(重跑上面那条也一样) cst-agent-update # 或手动 pkg install git clang make pkg-config binutils libcurl ncurses git clone https://git.whcself.xyz/WHC/CST-agent.git && cd CST-agent make && make install # 装到 Termux 的 $PREFIX/bin,之后任意目录运行 cst-agent # 可选:旁路 Unix busybox # ./scripts/build_third_party_mingw.sh busybox-unix ``` 环境:`PREFIX` 自动用于找 `sh`;`CST_EXE_DIR` / `argv[0]` 用于定位旁路 busybox(`/proc/self/exe` 受限时)。 ### Windows 发行包 见 [docs/README-windows.md](docs/README-windows.md)。交叉构建: ```bash ./scripts/build_third_party_mingw.sh all # curl+PDCurses+busybox-w32 # 源码:https://github.com/rmyorston/busybox-w32.git # https://github.com/mirror/busybox.git (Unix) cmake -B build-win-tui -DCMAKE_TOOLCHAIN_FILE=cmake/mingw-w64-x86_64.cmake \ -DCST_BUILD_TUI=ON -DCMAKE_PREFIX_PATH=$PWD/third_party/prefix-mingw ``` 产物示例:`dist/windows-x86_64/`(含 `cst-agent.exe` + `busybox.exe` + `libwinpthread-1.dll`)。 环境变量:`CST_SHELL`、`CST_EXE_DIR`、`CST_AUTO_APPROVE` 等。 ## Defaults (API) `--provider`(或 `CST_PROVIDER`)选择预设;显式的 `--api-base` / `--model` / 环境变量优先。 | provider | API Base | 默认模型 | Key 变量 | |---|---|---|---| | `minimax`(默认) | `https://api.minimax.cn/v1` | `MiniMax-M2.7` | `MINIMAX_API_KEY` | | `deepseek` | `https://api.deepseek.com/v1` | `deepseek-flash`(另有 `deepseek-v4-pro`) | `DEEPSEEK_API_KEY` | | `openai` | `https://api.openai.com/v1` | `gpt-4.1-mini` | `OPENAI_API_KEY` | | 用途 | 来源(优先级从高到低) | |---|---| | Provider | `--provider` → `CST_PROVIDER` → 上次使用 → `minimax` | | Key | `--api-key` → 已保存的 key(`credentials`)→ provider 的 Key 变量 → `CST_API_KEY` → TUI 询问 | | Base(含 `/v1`) | `--api-base` → `CST_API_BASE` → `OPENAI_BASE_URL` → `OPENAI_API_BASE` → 预设 | | Model | `--model` → `CST_MODEL` → `OPENAI_MODEL` → 该 provider 上次使用的模型 → 预设 | | 推理强度 | `--effort` → `CST_EFFORT` → 上次使用 → `medium` | “上次使用”记在配置目录的 `settings.json`(TUI 里切换时更新;headless 运行不改它)。 推理强度以 `reasoning_effort` 发送给 DeepSeek 和 OpenAI 推理模型(o 系列、gpt-5,`max` 映射为 `high`),其他模型不发送。 详见 [docs/env_openai.md](docs/env_openai.md)。 | 变量 | 作用(默认) | |---|---| | `CST_PROTOCOL` / `--protocol` | 工具调用协议(`native-eos`) | | `CST_TOOLS` / `--tools` | 工具集:`default`(全部)/ `minimal`(论文 4 件套) | | `CST_EFFORT` / `--effort` | 推理强度 `low` / `medium` / `high` / `max` | | `CST_CONFIRM` / `--confirm` | 哪些类别要确认:`shell,write,net`(默认)或 `none` | | `CST_MAX_STEPS` / `--max-steps` | 每个用户回合最多调用模型几次(30) | | `CST_TOOL_OUT_CAP` | 单个工具输出发给模型的字节上限(16384,保留头 60% + 尾 40%) | | `CST_CTX_WINDOW` | 发给模型的最近非 system 消息条数(80) | | `CST_CTX_TOKENS` / `CST_AUTO_COMPACT=0` | 覆盖模型上下文窗口大小 / 关闭自动压缩(用到 80% 时自动 `/compact`) | | `CST_NO_PROJECT_NOTES=1` | 不把工作区的 `AGENTS.md` 注入 system prompt | | `NO_COLOR` / `CST_ASCII=1` | 关闭颜色 / 强制 ASCII 图形(非 UTF-8 终端自动退化) | | `CST_SHELL_TIMEOUT` | `run_shell` 超时秒数(120),超时整组进程被杀,exit=124 | | `CST_METRICS=0` / `CST_METRICS_PATH` | 关闭 / 指定计时埋点 JSONL(默认 `.agent/metrics/.jsonl`) | | `CST_RECORD_SSE=DIR` / `CST_REPLAY_SSE=DIR` | 录制 / 回放原始 SSE 流(`NNNN.sse` + `NNNN.req.json`) | | `CST_APPROVAL=ask\|auto\|yolo` / `CST_SANDBOX=off` | 审批模式 / 关闭沙箱(回落到只读白名单规则) | | `CST_MODELS`、`CST_AUTO_APPROVE=1`、`CST_SHELL`、`CST_EXE_DIR` | 模型列表、自动批准(= yolo)、shell、旁路 busybox | ## Run ```bash # TUI(工作目录 = 当前目录;Assistant Markdown 经 Sashidown CST 渲染) cst-agent # Local CTL demo (no API) cst-agent --demo-ctl # 在命令行输入并保存当前 provider 的 key(不回显) cst-agent --ask-key --provider deepseek # Headless(默认 auto-approve shell/写文件) cst-agent --headless --prompt 'List files' ``` 会话相关命令行参数: | 参数 | 作用 | |---|---| | `-c` / `--continue` | 打开上次的会话(默认行为) | | `-N` / `--new` | 开新会话 | | `-R ID` / `--resume ID` | 打开指定会话(完整 id 或唯一前缀) | | `-L` / `--list-sessions` | 列出会话(时间、条数、标题)后退出 | | `-A MODE` / `--approval MODE` | 审批模式 `ask` / `auto`(默认)/ `yolo`,见下文 | | `-y` / `--yolo` | 等同 `--approval yolo`,只对本次运行有效 | | `-C LIST` / `--confirm LIST` | 需要确认的类别:`shell,write,net` 的任意组合或 `none` | | `-T PROFILE` / `--tools PROFILE` | `default` 或 `minimal` | | `-P NAME` / `-m NAME` / `-e LEVEL` | 本次使用的 provider / 模型 / 推理强度(不传则用上次的) | | `-a` / `--ask-key` | 在命令行输入并保存当前 provider 的 key | 工作目录默认就是当前目录(`-w DIR` 可指定别的目录)。 会话落盘:`/.agent/sessions/.jsonl` + `CURRENT`;`.agent/` 自带 `.gitignore`,不会被误提交。 ### 内置工具 | 工具 | 类别 | 说明 | |---|---|---| | `read_file` | 读 | 支持 `offset` / `limit` 分段读和行号 | | `list_dir` | 读 | 目录树(`depth`),遵守 `.gitignore`,跳过 `.git` / `node_modules` 等 | | `glob` | 读 | 按通配符找文件(支持 `**`) | | `grep` | 读 | 正则搜索内容,可限定路径 / glob、忽略大小写、带上下文 | | `write_file` | 写 | 整文件写入(自动建父目录) | | `edit_file` | 写 | 精确片段替换(必须唯一,或 `replace_all`) | | `apply_patch` | 写 | 应用 unified diff,多文件原子生效 | | `delete_file` / `move_file` | 写 | 删除 / 移动(重命名)文件 | | `run_shell` | shell | 非交互命令,超时整组进程被杀,Esc 可中断 | | `web_fetch` | 网络 | 抓取 URL,HTML 转纯文本 | | `todo_write` | 元 | 维护任务清单(TUI 右侧面板,Ctrl+T 开关) | | `ask_user` | 询问 | 向用户提问(可给选项);headless 下返回错误让模型自行决定 | `--tools minimal` 只保留 `read_file` / `run_shell` / `write_file` / `apply_patch`(论文实验口径)。 TUI 中 `/tools off NAME`、`/tools on NAME` 可临时开关单个工具。 ### 权限、撤销与项目说明 - **审批模式**(Shift+Tab 循环切换,`/approval`,`--approval`,`CST_APPROVAL`): | 模式 | 写文件 | run_shell | web_fetch | |---|---|---|---| | `ask` | 询问 | 询问 | 询问 | | `auto`(默认) | 直接执行 | 有沙箱 → 在沙箱里直接执行;没有沙箱 → 只读白名单命令直接执行,其余询问 | 询问 | | `yolo` | 直接执行 | 直接执行(不进沙箱) | 直接执行 | 已保存的规则(`a` / `p`)优先:匹配的命令不进沙箱直接运行。 `ask` / `auto` 的选择会记在 `settings.json`;`yolo` 只在本次运行有效。headless 行为不变(全部自动)。 - **沙箱**(C 实现,`src/sandbox_exec.c`): - Linux:Landlock(内核 5.13+,直接 syscall,无需 libseccomp)。子进程只能写工作区、临时目录、`/dev/null`、`/dev/tty`; 读和执行不受限。Landlock ABI ≥ 4(内核 6.7+)时同时禁止 TCP 连接和监听。 - macOS:`sandbox_init` profile,同样只放行工作区和临时目录写入,禁网。 - Android / Termux、Windows、老内核:没有沙箱,自动回落到规则判断(界面显示 `auto · rules`)。 - `CST_SANDBOX=off` 强制关闭沙箱。 - 沙箱里的命令如果像是被拦了(`Permission denied` / `权限不够` / `Read-only file system` 等),会弹窗问 “Rerun outside the sandbox?”:`y` 在沙箱外重跑一次 · `p` 保存命令前缀规则并重跑 · `n` 保留沙箱结果(模型会收到说明)。 - **只读白名单**(无沙箱时 `auto` 用):`ls cat head tail wc grep rg find pwd echo which file stat du tree` 和 `git status/diff/log/show/branch`;可以用 `|` `&&` `||` `;` 串联,但每段都必须在白名单里。 `find -exec/-delete`、`rg --pre`、`git branch NAME`、`>` 重定向(`2>&1`、`>/dev/null` 除外)、`$(…)`、反引号、 `<(…)`、`sudo`、`rm` 一律不算安全。 - **确认弹窗**会显示命令或写入的 diff: `y` 允许一次 · `a` 本工具总是允许 · `p` 总是允许这个命令前缀(如 `make`、`git status`)· `n` / Esc 拒绝。 `a` / `p` 写入 `/.agent/permissions.json`,`/permissions` 查看,`/permissions reset` 清空。 `sudo`、`rm`、解释器(`python3 -c …`)等危险前缀不会被建议成规则。 - **撤销**:每轮写文件前自动快照到 `.agent/undo/`,`/undo` 恢复上一轮改动的全部文件(新建的文件会被删除)。 - **AGENTS.md**:工作区根目录的 `AGENTS.md` 会注入 system prompt;`/init` 让 agent 自己生成一份。 - **自动压缩**:上一轮 prompt 用到模型上下文 80% 时,下一轮开始前自动总结历史(`CST_AUTO_COMPACT=0` 关闭)。 ### TUI 快捷键 | 按键 | 说明 | |---|---| | Enter | 发送;busy 时入队 | | Ctrl+N / Alt+Enter | 换行(粘贴的多行文本保持原样,不会被当成发送) | | ← → Home End Ctrl+A/E Ctrl+B/F Alt+B/F | 移动光标(按字 / 按词,支持中文宽字符) | | Ctrl+W / Ctrl+U / Ctrl+K / Alt+Backspace / Delete | 删词 / 删到行首 / 删到行尾 | | ↑ / ↓ | 提示历史(多行输入时先在行间移动) | | Tab | 补全 `/命令` 和 `@文件`;没有可补全时切换 Build / Plan(Plan 只开放只读工具) | | Shift+Tab | 循环切换审批模式 ask → auto → yolo | | Ctrl+P | 命令面板(输入过滤,↑↓ / 鼠标选择) | | Ctrl+O / Ctrl+T | 展开最近 tool 输出 / 开关任务面板 | | PgUp / PgDn / 鼠标滚轮 | 滚动对话 | | 鼠标点击 | 展开/折叠 tool 卡片和 Thought、在输入框里定位光标、点模式行切换 Build/Plan、在选择框里选中(`/mouse` 关闭后可用终端原生选择复制) | | Esc | 取消当前 turn(不会退出) | | Ctrl+C 两次 / 空输入 Ctrl+D | 退出 | ### 斜杠命令 输入 `/` 弹出补全;唯一前缀即可(如 `/st` → `/status`)。标 * 的命令在模型运行中也能用。 | 命令 | 说明 | |---|---| | `/help` * | 命令与按键 | | `/new`、`/session [id]`、`/rename <标题>` *、`/delete [id]` | 新建 / 选择切换 / 命名 / 删除会话 | | `/provider [name]`、`/model [name]`、`/effort [level]` | 切换 provider(缺 key 时当场输入并用 `/models` 校验)、模型(列表优先显示 API 实际返回的模型,标 `(api)`)、推理强度;都会记住 | | `/key` | 输入或更换当前 provider 的 API key(遮罩输入并保存) | | `/protocol [name]` | 切换工具调用协议 | | `/tools [on\|off NAME\|minimal\|default]` | 查看或开关工具 | | `/approval [ask\|auto\|yolo]` *、`/yolo` * | 审批模式;本次开关 yolo | | `/permissions [reset]` *、`/max-steps [N]` * | 权限规则、每轮步数 | | `/sidebar` *、`/mouse` * | 开关右侧边栏(≥120 列时显示)、开关鼠标 | | `/status` *、`/todo` * | 会话 / 模型 / 上下文占比 / 设置;任务清单 | | `/undo`、`/retry` | 撤销上一轮文件改动;丢弃上一条回复重问 | | `/diff [path]` * | `git status` + `git diff` | | `/compact` | 手动压缩上下文 | | `/export [file]` *、`/copy` * | 导出 Markdown 记录;复制上一条回复(OSC 52,需终端支持) | | `/init` | 让 agent 写 `AGENTS.md` | | `/details` *、`/quit` * | 展开全部 tool 输出;退出 | ### 协议 请求统一走 OpenAI 兼容 `POST …/chat/completions`(SSE)。工具调用的承载方式由 `--protocol` 决定: | 协议 | 承载 | 何时执行 | |---|---|---| | `native-eos`(默认) | 原生 `tools[]` / SSE `tool_calls` | 整个流结束后(基线) | | `native-inc` | 同上 | 某个 index 的参数完整(下一个 index 开始或 finish)即执行 | | `ctl` | 正文中的 CTL 帧,长度前缀参数 | `end` 行一被确认即执行,流仍在继续 | | `ctl-heredoc` | 正文中的 CTL 帧,`<` 文本 | 流结束后 | 文本协议(ctl / ctl-heredoc / xml)不发送 `tools[]`,工具结果以 `result exit= ` … `end` 帧放在下一条 user 消息里。 同一会话可以随时切换协议:历史中的调用会按当前协议重新渲染。 ## CTL tool language ```text call arg arg < TAG arg short inline value end ``` Execution happens only on confirmed `end` (`CTL_EV_CALL_READY`); no prefix of a call is ever executable. See [docs/theory_tool_cst.md](docs/theory_tool_cst.md). ## Starvation experiment ```bash make check ./scripts/replay_starvation.sh ./tests/test_replay_real # 真实 DeepSeek 录制流上的逐字节截断(实验 C) ``` CTL vs JSON-buffer 截断数字写入 [docs/eval_tool_cst.md](docs/eval_tool_cst.md) 实验 A / C;与 mini_calc e2e 一起作为 Tool-loop CST 附录素材(**不**替代 SD 主论文 Markdown→HTML 实验)。