# sbclaw **Repository Path**: Ky1eYang/sbclaw ## Basic Information - **Project Name**: sbclaw - **Description**: sbclaw = As Backend Claw - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: v0.3 - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-15 - **Last Updated**: 2026-10-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # sbclaw 工作区版本见 `Cargo.toml`。Agent Component 契约见 `crates/shared/wit/agent-world.wit`(WIT 包 `sbclaw:harness@0.17.0`)。 OpenCode 协议兼容版本钉在 `crates/server/src/protocol.rs` (`OPENCODE_COMPAT_VERSION`,当前 `1.18.27`)。 sbclaw 是本地 Agent Runtime:对 Session 做调度、工具、WASM agent team 与持久化, 并通过 OpenCode 兼容的 HTTP/JSON/SSE 出口让客户端 attach。统一 CLI 二进制名是 `sbclaw`。 | Crate | 职责 | | --- | --- | | `sbclaw-core`(`crates/core`) | 单 Session 运行时:Session/SessionManager、Context、Host、LLM、Harness/WASM、工具、持久化 | | `sbclaw-shared`(`crates/shared`) | WIT `agent-world`、host-hook 传输、`HarnessDocument` 格式、workspace/host-RPC 类型、工具协议;不含工具实例 | | `sbclaw-storage`(`crates/storage`) | SQLite/PostgreSQL 驱动、事务与类型化参数;业务表及迁移仍由 Core/Server 各自拥有 | | `sbclaw-host-local`(`crates/host-local`) | 本机 FS/PTY 执行后端;ServerLocal 与 ClientLocal(TUI)共用 | | `sbclaw-agents-coding`(`crates/agents/coding`) | Coding agent team WASM guest:profile、prompt、LLM 工具目录(guest FS + host-bound workspace 工具) | | `sbclaw-server-backend`(`crates/server`) | Axum HTTP/JSON/SSE 出口与 OpenCode 协议投影;二进制名 `sbclaw-server` | | `frontend/` | 独立 React + TypeScript + Vite Web 客户端,消费 HTTP/SSE API | | `sbclaw-tui`(`crates/tui/main`) | 原生 Ratatui 客户端,走 current `/api/*` HTTP/SSE;状态在 `/.sbclaw/tui` | | `sbclaw-entry`(`crates/entry`) | 统一 CLI:`serve` / `tui` / `attach` / `doctor`。TUI 只 attach,不拉起 server | | `sbclaw-xtask`(`crates/xtask`) | 仓库任务:`build-wasm` | TUI 与 web 客户端都只连接已经运行的 server。客户端状态写在 `/.sbclaw/tui/`, 不是 ServerWorkspace。 `ServerWorkspace` 是 server 本机的 `.sbclaw/` 根:配置、WASM、模型缓存、日志、会话持久化。 `SessionWorkspace` 是 Session 面向的项目位置。配置里的 `[host.default]`(仍接受旧表 `[host]`) 只给 `/api/v1` 与未版本化 `/api` session create 提供缺省 Host: - `kind = "server"`(默认):**ServerLocal**,server 进程本机目录,只配 `path` - `kind = "server-remote"`:Server SSH。serve 进程按 OpenSSH config 的 Host 别名连接(缺省 `~/.ssh/config`,或 `[host].ssh_config` / `config`)。工作区来自该 Host 的 `RemoteCommand cd `;进程、PTY、搜索、patch、mutation/undo 已实现。TUI 的 Local SSH(`kind = "client-remote"`)由 TUI 机器发起 SSH,经隧道把字节流交给同一套 RemoteHost - `kind = "container"`:预留,创建 Session 时返回未实现错误 `kind = "client"`(**ClientLocal**)不出现在 `[host.default]`。TUI 或 Web 经 `/api/v2` 带 Client 路径创建。Host 执行属于声明了 `x-sbclaw-client` 且 kind 为 `tui` 或 `web` 的那个客户端。 TUI 和 `sbclaw acp` 发送 `X-API-Key`,Web 可使用 API Key 或 SSO Cookie;SSO 关闭时 Web 使用 local-user 身份。密钥来自 `SBCLAW_API_KEY`、TUI 的 `.sbclaw/tui/auth.json`,或浏览器的 `sbclaw.client.apiKey`。`sbclaw acp` 只读环境变量 `SBCLAW_API_KEY`。每个身份只能看到自己的会话。会话归属记在 Server 数据库中。 命令 `cwd` 是 SessionWorkspace 内的相对目录,默认 `.`。guest 文件工具不使用 WASI 项目目录 preopen;Host 工具经当前 Session Host 绑定执行。 后端统一位于 `/api/v1`、`/api/v2`、`/api/auth`、`/api/admin/v1`;未版本化 `/api` 业务别名继续可用。未知 API 返回 JSON 404,其余路径归前端 SPA。旧根路由及 command/integration/reference 空桩已移除。 ### 首次配置与数据库 首次自动生成配置后,打开 Web 输入启动日志中的 Setup Token,选择 SQLite 或 PostgreSQL, 再选择开放模式或 SSO。启用 SSO 时完成一次管理员认证后才解除业务门禁;配置和运行时原地发布。 已有完整配置不因缺少 initialized 标记而强制初始化;显式 `[server.setup] initialized=false` 可以在重启后重新进入向导。OIDC 正常回调地址是 `/api/auth/callback`,向导回调为 `/api/setup/callback`。 ```toml [server.database] type = "sqlite" path = ".sbclaw/sbclaw.db" # 或:type = "postgres" # url = "postgresql://user:password@127.0.0.1:5432/sbclaw?sslmode=require" ``` SQLite 路径指 Core 数据库,Server 使用同目录 `backend.db`。当前 SQLite v2 可导入空 PostgreSQL 目标,源文件保留;未知版本或非空目标合并会被拒绝。审计文件、undo 对象和认证密封密钥仍保留在 ServerWorkspace。 ### 模型网关与 WS OpenAI 客户端使用 `baseURL=http://127.0.0.1:4096/api/v1` 和 Bearer API Key。 `models` 提供可用模型,重名为 `model(provider)`;`chat/completions` 支持普通 JSON 和 SSE, 复用已配置 provider。未支持的请求字段明确报错,不静默忽略。 `/api/v2/ws` 提供鉴权、心跳和插件声明协议;LLM 经 MCP 触发客户端执行属于后续协议接入。 ### 旧 TUI 项目归组 新版 TUI 持久保存工作区身份,重启后的租约连接仍使用同一身份。旧会话没有这项信息时, 可从项目菜单明确确认“同一路径的旧会话属于同一工作区”进行归组,刷新后保留,并归并旧空书签。 这只改变浏览器分组,不授予 Host 执行权限;更新 TUI 在原工作区重新认领会话后会补齐服务端身份。 已绑定身份的会话要求 claim 提交匹配身份,不允许通过省略身份切换到另一执行端。 ### 传输重试与 Responses 回放 `provider..options.max_retries` 缺省为 3(初次请求外最多重试三次), `retry_backoff_ms` 缺省为 1000;模型自己的 `options` 可覆盖 provider,设置 `max_retries=0` 禁用。 会话流式重试会撤回当前尝试的片段,保留此前已完成轮次,完整响应后才执行工具。 OpenAI 网关非流式请求可原子重试;SSE 已发出可见内容后遇到中断会报错,不重新拼接第二份回答。 Responses 使用稳定会话 cache key、顶层 instructions,并持久化原生输出项供后续调用及重启回放。 `responses_prompt_cache` 为可选布尔能力覆盖:缺省自动识别支持模型,true 显式发送 implicit 缓存选项, false 省略。缓存命中取决于上游,客户端不承诺固定命中比例。 ## 环境要求 - Rust stable,edition 2024。 - OpenCode `1.18.x`(可选,官方 TUI attach)。 - 构建 Agent Wasm 时需要 `wasm32-wasip2`;`wasm-tools` 仅在 `--validate` 时需要。 - Web 客户端需要 Node.js 20.19+ 或 22.12+,使用 npm 管理依赖。 ```powershell rustc --version cargo --version opencode --version # 可选 dx --version # 仅 web 客户端 ``` ## 配置 单一文档。`[server]` 是 server 侧;其余顶层字段是 session 设置。嵌入脚手架是仓库根的 `config_template.toml`。无配置文件时 server 会把该模板写入最终 ServerWorkspace 的 `.sbclaw/config.toml`。JSON / JSONC 仍可读取与写回(写回保持源格式)。 发现顺序(首个命中生效): 1. `--config`,否则环境变量 `SBCLAW_CONFIG` 2. `/.sbclaw/config.toml`,然后 `config.json` / `config.jsonc` (`--workspace` > `SBCLAW_WORKSPACE` > 启动 cwd 作为 discovery root) 3. 用户配置目录(`directories::ProjectDirs`:Linux/macOS 为 `~/.config/sbclaw`, Windows 为 `%APPDATA%\sbclaw`)下的 `config.toml` / `config.json` / `config.jsonc` `SBCLAW_CACHE_DIR` 已退役。相对 `[host.default].path` 以 ServerWorkspace 为基准。Server SSH 的 `config` 是 server 上的 OpenSSH 文件。`[host.default]` 只服务 `/api` 与 `/api/v1`,不描述 ClientLocal 或 Local SSH。 最小可用 TOML(字段与 `config_template.toml` 对齐;按环境改模型与密钥): ```toml model = "openai/gpt-5.2" small_model = "openai/gpt-5.1-mini" [server] hostname = "127.0.0.1" port = 4096 workspace = "." # web 客户端跨源时打开,例如: # cors = ["http://127.0.0.1:8080"] [server.session] agent = "plan" agent_team = "sbclaw_agents_coding" [host.default] kind = "server" path = "." [provider.openai] name = "OpenAI Compatible" protocol = "openai-compatible" [provider.openai.options] baseURL = "https://api.openai.com/v1" apiKey = "sk-your-key" setCacheKey = true [provider.openai.models."gpt-5.2"] name = "GPT-5.2" ``` 只写默认模型引用、没有写 `models` 时,server 也会把主模型和 small model 注入 OpenCode 模型目录,但建议显式声明模型限制。脚手架里的默认 session agent 是 `plan`; 缺失 `[server.session]` 时代码默认是 `build`。历史 `[agent]` 段会被忽略——agent 目录由 已加载的 WASM `agents-list` 定义。 环境覆盖:`SBCLAW_HOST`、`SBCLAW_PORT`、`SBCLAW_WORKSPACE`、`SBCLAW_LOG_DIR`、 `SBCLAW_CONFIG`、`RUST_LOG`。TUI 另认 `SBCLAW_TUI_URL`。 ## 启动 Server 在工作区根目录: ```powershell cargo build --release .\target\release\sbclaw.exe serve ``` 无子命令时 `sbclaw` 也等价于 `serve`。也可以: ```powershell cargo run -p sbclaw-entry -- serve cargo run -p sbclaw-server-backend ``` 首次链接 Wasmtime 可能需要几十秒。该进程是前台常驻服务。`Ctrl+C` 优雅关闭。 自定义绑定: ```powershell $env:SBCLAW_HOST = "127.0.0.1" $env:SBCLAW_PORT = "4108" $env:SBCLAW_WORKSPACE = "E:\work\project" $env:SBCLAW_LOG_DIR = "E:\logs\sbclaw" $env:RUST_LOG = "info,sbclaw_core=debug,sbclaw_server=debug" .\target\release\sbclaw.exe serve ``` `sbclaw doctor` 打印 bind、ServerWorkspace、配置路径,并探测 `/api/v1/health`。 ## 连接客户端 原生 TUI、Web 与 ACP 使用 `/api/*` HTTP/SSE。依赖旧根路由的 OpenCode 官方 TUI 不再直接兼容;请使用下述原生客户端。 ### sbclaw TUI 先启动 server,再在另一个终端: ```powershell # 默认 http://127.0.0.1:4096 .\target\release\sbclaw.exe tui .\target\release\sbclaw.exe tui --url http://127.0.0.1:4096 .\target\release\sbclaw.exe tui --host 127.0.0.1 --port 4108 .\target\release\sbclaw.exe tui --session ses_xxxxxxxx .\target\release\sbclaw.exe tui --new # attach 与 tui 同一实现 .\target\release\sbclaw.exe attach --url http://127.0.0.1:4096 cargo run -p sbclaw-tui -- --url http://127.0.0.1:4096 ``` URL 优先级:`--url` > `SBCLAW_TUI_URL` > `--host`/`--port`(默认 `127.0.0.1:4096`)。 `--session` 优先于 `--new`。 客户端状态: ```text /.sbclaw/tui/state.toml /.sbclaw/tui/history.jsonl /.sbclaw/tui/mcp.jsonc ``` MCP 由 TUI 读取 `mcp.jsonc` 后上传到当前 session;仓库根 `mcp-template.jsonc` 是模板。 OAuth 未实现。LLM 经静态 native 工具 `mcp_call` 调用 MCP。 对话正文由 `crates/tui/main/src/components/view` 原生绘制(markdown / think / tool / diff / file tree / json),不加载 TUI render `.wasm`。Session 列表只显示顶层会话。 常用操作(帮助弹层以 `?` 为准): | 按键 | 操作 | | --- | --- | | `Enter` | 提交 prompt | | `Shift+Enter` / `Ctrl+J` | 插入换行 | | `Esc` 两次 | 中止 busy session / 折叠 / 关闭 popup | | `Ctrl+\` / `Ctrl+L` / `/session` | dashboard | | `Ctrl+N` / `/new` | 新 session | | `Shift+Tab` | 循环 agent(mode) | | `/` | slash 命令 | | `Ctrl+P` | 命令面板 | | `Ctrl+G` / `/theme` | 主题(`dark` / `light` / `grok` / `indigo`) | | `y` / `Ctrl+Y` | 复制 focused block 原文 | | `/compact [hint]` | 后台 compact | | `/undo` / `/redo` | 撤销/重做最近 turn | | `?` | 帮助 | | `Ctrl+C` 两次 | 退出 TUI,不停止 Server | ### React Web 客户端 始终从仓库根启动: ```powershell npm --prefix frontend ci npm --prefix frontend run dev ``` 默认 `http://127.0.0.1:8080`,Vite 将 API 和登录路径代理到 `http://127.0.0.1:4096`。 后端地址可通过 `SBCLAW_API_TARGET` 修改。API 地址、路由和登录说明见 [frontend/README.md](frontend/README.md)。 运行 `npm --prefix frontend run build` 生成 `frontend/dist`,从仓库根启动服务端即可同源托管。 其他部署位置通过 `SBCLAW_WEB_ROOT` 指定静态产物目录,也可放置在启动目录的 `web/public`。 ## Agent Wasm ```powershell rustup target add wasm32-wasip2 # 仅 --validate 需要: cargo install wasm-tools cargo run -p sbclaw-xtask -- build-wasm cargo run -p sbclaw-xtask -- build-wasm --only sbclaw-agents-coding --release --validate ``` 产物: ```text target/wasm32-wasip2/debug/sbclaw_agents_coding.wasm target/wasm32-wasip2/release/sbclaw_agents_coding.wasm ``` Server 扫描 `/.sbclaw/agents` 下全部 `*.wasm`。每个能实例化 `agent-world` 且 `agents-list` 非空的文件是一个 agent team(agents、tools、prompt、 权限、框架元数据)。失败文件告警跳过。没有 wasm team 时仍注册 native 工具,并回退 core 的 chat agent。 ```powershell New-Item -ItemType Directory -Force .sbclaw\agents | Out-Null Copy-Item target\wasm32-wasip2\release\sbclaw_agents_coding.wasm .sbclaw\agents\ .\target\release\sbclaw.exe serve ``` coding team 的 primary agent:`plan` / `build`。subagent(只能经 `task` 委托): `explore` / `execute`。guest 工具:`read` / `write` / `edit` / `diff`,以及 host-bound 的 `list` / `glob` / `grep` / `patch` / `shell` / `pty`。core native:`signal` / `sleep` / `ref_expand` / `task` / `mcp_call`。Question / Todo 是 server ClientTool。 plan 模式:只自动允许 `git status` / `git diff` / `git commit` 族;其余 shell/pty 询问。 Conditional 写仅允许本 session 创建的 Markdown 计划文件。explore 只读,禁止 shell 与写工具。 ## 验证服务 ```powershell curl.exe --noproxy "*" http://127.0.0.1:4096/api/v1/health curl.exe --noproxy "*" http://127.0.0.1:4096/api/v1/event curl.exe --noproxy "*" http://127.0.0.1:4096/config/providers curl.exe --noproxy "*" -N -m 3 http://127.0.0.1:4096/global/event ``` `/api/v1/health` 返回 JSON(含 `opencode_compatibility`)。legacy SSE `/global/event` 首条事件为 `server.connected`。未知 API 路由也应是 JSON 错误。 Provider/内核闭环: ```powershell $session = Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{}' ` -Uri 'http://127.0.0.1:4096/api/v1/session' $id = $session.data.id $body = @{ prompt = 'Reply with exactly OK.'; delivery = 'steer' } | ConvertTo-Json Invoke-RestMethod -Method Post -ContentType 'application/json' -Body $body ` -Uri "http://127.0.0.1:4096/api/v1/session/$id/prompt" Invoke-RestMethod -Method Post -Uri "http://127.0.0.1:4096/api/v1/session/$id/wait" Invoke-RestMethod -Uri "http://127.0.0.1:4096/api/v1/session/$id/context" ``` 成功时 context 中包含 user message 和 assistant message。 ## 完整 TUI 多轮会话 下面是一条 attach-compatible 验收路径。OpenCode TUI 会打 legacy 请求和 `/global/event`,不要只测 current `/api/*`。仓库没有 `python/` pytest 套件。 1. 选择 `plan` agent,输入: ```text Call the shell tool exactly once with the exact command pwd. After it completes, reply with only the absolute workspace path. ``` 预期:出现 `permission.asked`(plan 对 `pwd` 询问,不是自动 Allow)。Allow once 后 shell 完成,输出为当前 SessionWorkspace 的规范化绝对路径。 2. 输入: ```text Call the shell tool exactly once with this exact command: Remove-Item -Path permission_probe.txt. Do not use another command. After it completes, reply briefly. ``` 预期:再次 `permission.asked`(`remove-item` 为 mutate)。Allow once 后 tool `completed`,测试 workspace 中的 `permission_probe.txt` 被删除。 3. 输入: ```text Call the task tool exactly once with agent=explore. The child must read README.md and report the exact validation marker SBCLAW_README_MARKER_7F3A91. After it returns, reply with the child's result. ``` 预期:plan 创建 `agent=explore` 的 child(`parentID` 为当前 session)。child 读取 `README.md` 并返回 `SBCLAW_README_MARKER_7F3A91`;父 task part metadata 含 child session ID。 4. 输入: ```text State the path returned by pwd and the exact README validation marker from the explore child. ``` 预期:父 agent 同时返回 workspace 路径和 README marker。 `/global/event` 首条必须是 `server.connected`。事件流应包含 busy/idle、message/tool 更新、permission ask/reply、child session 更新。durable history sequence 严格递增; 工具不得停在 `pending` 或 `running`。 SBCLAW_README_MARKER_7F3A91 ## 日志 JSONL,10 MiB 轮转,保留五个归档: | 路径 | 内容 | | --- | --- | | `/.sbclaw/logs/server/log.jsonl` | HTTP 与 server 进程 tracing | | `/.sbclaw/logs/core/log.jsonl` | Session、工具和运行时;server 把 core 层组合进同一进程 subscriber | | `/.sbclaw/logs/tui/log.jsonl` | TUI 只写文件,不写 stderr,忽略 `SBCLAW_LOG_DIR` | | `.sbclaw/sessions//request/` | Provider 审计:send 为增量 unified diff + `.baseline`,fetch 为完整 received JSON | `SBCLAW_LOG_DIR` 只改 server 日志目录(且必须在 ServerWorkspace 内)。`RUST_LOG` 控制 tracing 级别。审计文件可能含 prompt、工具参数和响应,仅本地调试,不要上传。 Anthropic fetch 文档始终带聚合 `usage`(`input` / `output` / `reasoning` / `cache_read` / `cache_write`)。这与 `promptCache.persistRawSse` 无关;后者默认关闭, 只控制是否持久化原始 SSE 帧。 ## 持久化 ```text .sbclaw/ sbclaw.db # session / history / mutation backend.db # 密钥、归属、OpenCode projection agents/ template/ # 仅 [server.session] template_dynamic = true sessions// patches//meta.json objects/index.json objects/ab/ request/ .baseline /_request.patch /_response.jsonl /turn_.json subsessions// ... ``` `sbclaw.db` 的 `session` 行是会话身份:kind、agent、模型、host、workspace、活动 turn。 稳定字段是列,`extras` 只放小模型、provider headers 这类嵌套内容。`history` 记录 回合,turn 正文在该行的 `extras`。`backend.db` 的 `projection_message` / `projection_part` 是折叠后的 OpenCode 消息;`projection_event` 是折叠前的 `message.updated` / `message.part.updated` / `message.removed`。core 对话真相是 `history` 加上 session 行里的 `pre_turn` 链。 `template_dynamic = true` 时从 `.sbclaw/template/` 读 `SYSTEM.xml` / `STATE.xml` / `HARNESS.xml` / `FUNCTIONAL.xml` / `SUBAGENT.xml`(缺失则从 `crates/core/template` 种子)。默认关闭,会话使用嵌入模板。Tera 变量仍来自 `context/variables.rs`。 workspace undo 按 turn:`objects/` 是 SHA-256 内容寻址 blob;发生文件变更的已完成 turn 写 `patches//meta.json`。隐藏目录(任意 `.xxx` 路径分量)和 `target/` 被忽略。重启从 `sbclaw.db` 的 `history` 和 session 行里的 `pre_turn` 链恢复对话,假定 live workspace 已与活动 turn 一致。 进程和 WASM 实例不恢复。 写入必须增量:append-only JSONL 或有界原子替换。手动 compact 会原子写入 durable synthetic continuation;synthetic 标记与当时的 agent/model/provider/variant 进入 turn history,投影里对应用户消息与 text part 标为 synthetic。 模式/agent 切换保持稳定 system prompt 字节级不变。活动 agent 指令走 runtime state 里的 ``。system prompt 只在 Agent Team(WASM catalog)变化或 compact 重建 context 时重建。 ## 开发验证 ```powershell cargo fmt --all -- --check cargo check --workspace --all-targets cargo run -p sbclaw-xtask -- build-wasm --validate ``` 工作区交付门是 `cargo fmt` + `cargo check --workspace --all-targets`。不要把 `cargo test --workspace` 当作交付门。WASM guest 变更仍需上面的 component 构建。 ## 常见问题 ### TUI 显示 `Request is not supported` 或 `text/html` 确认连的是刚启动的 sbclaw: ```powershell curl.exe --noproxy "*" -i http://127.0.0.1:4096/api/v1/health curl.exe --noproxy "*" -i http://127.0.0.1:4096/config/providers curl.exe --noproxy "*" -i http://127.0.0.1:4096/project/current ``` 响应必须是 `200 application/json`。非 2xx 看 `/.sbclaw/logs/server/log.jsonl`。 ### TUI 显示 `No provider selected` 检查 `/config/providers` 里默认 provider 的 `models` 是否包含 `model` 指定的模型 ID, 并确认有 API key。 ### web 客户端调不通 API 确认 server 已监听、CORS 含 `http://127.0.0.1:8080`,并用 `?api=http://127.0.0.1:4096` 或 loopback origin。非 loopback API host 会被前端拒绝。 ### release 构建长时间无输出 首次链接 Wasmtime 较慢。完成后 `target\release\sbclaw.exe serve` 会打印 listen 和 attach 地址。 ### Windows 无法覆盖 `target/release/sbclaw.exe` 先停止正在运行的 Server,再 build。 ## License MIT