| 1 | # Reasonix 工程规格 |
| 2 | |
| 3 | <a href="./SPEC.md">English</a> |
| 4 | |
| 5 | > Reasonix 是一个 coding agent:由极薄的 harness 驱动多个模型,所有能力都由配置和插件提供。本文是工程契约,代码应遵循它;需要改变行为时,应先更新契约,再修改代码。 |
| 6 | |
| 7 | 英文原文是规范性版本;本文按相同章节提供中文说明,代码标识符、配置键和协议名保持原样。 |
| 8 | |
| 9 | ## 1. 设计原则 |
| 10 | |
| 11 | 1. **配置与插件驱动。** 核心只依赖接口;具体模型和工具通过 registry 按名称解析、在配置中声明,或由插件注入,不硬编码 `switch model`。 |
| 12 | 2. **单一静态二进制。** 使用 `CGO_ENABLED=0`,一条命令完成跨平台编译,CLI 开箱即用。 |
| 13 | 3. **精简依赖。** 默认使用标准库。第三方依赖必须是纯 Go、足够轻量,且不能破坏单二进制、跨平台和分发体验;TOML parser 是当前唯一接受的基础依赖。 |
| 14 | 4. **两级扩展。** 编译期 built-in 通过 `init()` 自注册;运行时外部插件以 stdio JSON-RPC 子进程或 MCP 兼容传输接入。 |
| 15 | 5. **接口优先、registry 驱动。** `Provider` 与 `Tool` 都是接口。 |
| 16 | 6. **持续演进,不过度设计。** |
| 17 | |
| 18 | 所有代码、注释、面向用户的字符串、工具描述、system prompt 和英文规范以英语为主;README 同时维护英文版 `README.md` 与中文版 `README.zh-CN.md`。 |
| 19 | |
| 20 | ## 2. 目录与依赖方向 |
| 21 | |
| 22 | ```text |
| 23 | reasonix/ |
| 24 | ├── go.mod / go.sum |
| 25 | ├── Makefile |
| 26 | ├── README.md / README.zh-CN.md |
| 27 | ├── reasonix.example.toml |
| 28 | ├── docs/SPEC.md / docs/SPEC.zh-CN.md |
| 29 | ├── cmd/reasonix/main.go |
| 30 | ├── cmd/reasonix-plugin-example/ |
| 31 | └── internal/ |
| 32 | ├── cli/ |
| 33 | ├── config/ |
| 34 | ├── provider/ |
| 35 | │ └── openai/ |
| 36 | ├── tool/ |
| 37 | │ └── builtin/ |
| 38 | ├── permission/ |
| 39 | ├── command/ |
| 40 | ├── plugin/ |
| 41 | ├── remote/ |
| 42 | │ ├── forward/ |
| 43 | │ ├── sftpfs/ |
| 44 | │ └── bootstrap/ |
| 45 | └── agent/ |
| 46 | ``` |
| 47 | |
| 48 | 核心依赖方向保持无环: |
| 49 | |
| 50 | ```text |
| 51 | cli → {agent, plugin, config} → {tool, provider} |
| 52 | ``` |
| 53 | |
| 54 | `provider/openai`、`tool/builtin` 等 built-in 子包导入父包完成自注册,父包不反向导入子包。Remote-SSH 采用 `cli → remote/bootstrap → remote` 的分层,`remote` 及其子包不依赖 `cli`、`agent` 或 `serve`;host key 和 secret prompt 等交互都通过 callback 暴露,供桌面端复用。 |
| 55 | |
| 56 | ## 3. 核心抽象 |
| 57 | |
| 58 | ### 3.1 Provider 与 registry(`internal/provider`) |
| 59 | |
| 60 | ```go |
| 61 | type Provider interface { |
| 62 | Name() string |
| 63 | Stream(ctx context.Context, req Request) (<-chan Chunk, error) |
| 64 | } |
| 65 | |
| 66 | type Factory func(cfg Config) (Provider, error) |
| 67 | |
| 68 | func Register(kind string, f Factory) |
| 69 | func New(kind string, cfg Config) (Provider, error) |
| 70 | ``` |
| 71 | |
| 72 | - `openai` kind 实现 OpenAI-compatible `/chat/completions`。 |
| 73 | - OpenAI-compatible vendor 只是 `kind = "openai"` 的不同配置实例,通过 `base_url`、`model`、`api_key_env` 区分;新增兼容模型通常只需改配置。 |
| 74 | - 一个 provider 表示一个 vendor endpoint,可通过 `models` 暴露多个模型,并以 `default` 指定默认项。`default_model`、`--model` 和桌面端模型选择器都经 `Config.ResolveModel` 解析,可接受 provider 名、裸模型名或 `provider/model`。 |
| 75 | - `context_window` 是 provider 级默认值;`model_overrides.<model>.context_window` 可覆盖单个模型。 |
| 76 | - `max_output_tokens` 是独立的总输出预算,不由客户端 reasoning 字节上限换算。0 表示使用 provider 安全默认值,正数表示显式上限,负数表示在协议允许时省略;混合网关可用 `model_overrides.<model>.max_output_tokens` 覆盖单个模型。Anthropic 因协议要求仍会提供 `max_tokens` 默认值。 |
| 77 | - streaming tool-call delta 在 provider 内按 index 聚合,只向上层发出完整 `ToolCall`。 |
| 78 | |
| 79 | ### 3.2 Tool 与 registry(`internal/tool`) |
| 80 | |
| 81 | ```go |
| 82 | type Tool interface { |
| 83 | Name() string |
| 84 | Description() string |
| 85 | Schema() json.RawMessage |
| 86 | Execute(ctx context.Context, args json.RawMessage) (string, error) |
| 87 | } |
| 88 | ``` |
| 89 | |
| 90 | - built-in tool 通过 `tool.RegisterBuiltin` 注册到进程级集合。 |
| 91 | - 每次运行创建独立 `*Registry`,由启用的 built-in 与插件工具组成;agent 只看到该 registry。 |
| 92 | - tool schema 在插入 registry 时 canonicalize;内置契约见[工具合约](./TOOL_CONTRACT.zh-CN.md),测试会校验文档与 canonical schema 不漂移。 |
| 93 | - `Execute` 自行解析原始 JSON 参数。错误作为结果返回给模型,让模型有机会自我修正,而不是直接终止进程。 |
| 94 | |
| 95 | ### 3.3 插件与 MCP(`internal/plugin`) |
| 96 | |
| 97 | 外部插件是配置中声明的 MCP server。协议统一为 JSON-RPC 2.0,传输由 `transport` 接口抽象: |
| 98 | |
| 99 | - `stdio`:本地持久子进程,每行一条 JSON 消息。 |
| 100 | - `http` / `streamable-http`:向远程 `url` POST,支持 `application/json` 和 SSE 响应,并复用 `Mcp-Session-Id`。 |
| 101 | - `sse`:兼容旧版 2024-11-05 HTTP+SSE;持久 GET 接收 server 公布的相对 POST endpoint、JSON-RPC 响应与 server 消息。为避免静态 header 泄漏,会拒绝跨域 endpoint。 |
| 102 | |
| 103 | `${VAR}` 与 `${VAR:-default}` 可用于 `command`、`args`、`env`、`url` 和 `headers`,使 secret 留在环境中。生命周期为 `initialize` → `notifications/initialized` → `tools/list`,调用使用 `tools/call`。 |
| 104 | |
| 105 | 存在工作区根目录时,初始化会声明 `roots` 能力,并用文件 URI 响应 `roots/list`。`tools/call` 会附带逐调用 `_meta.progressToken`;匹配的 `notifications/progress` 会进入现有工具进度事件链路。 |
| 106 | |
| 107 | 远程工具适配为 `Tool`,命名为 `mcp__<server>__<tool>`。`annotations.readOnlyHint` 映射为 `Tool.ReadOnly()`,默认 false;只有显式声明为只读的工具才进入并行读取与默认只读权限路径。MCP prompt 暴露为 slash command,resource 可通过 `@<server>:<uri>` 引用。 |
| 108 | |
| 109 | ### 3.4 Agent loop(`internal/agent`) |
| 110 | |
| 111 | `Session` 保存 `[]Message`。`Run(ctx, input)` 的主循环为: |
| 112 | |
| 113 | 1. 构建包含历史消息和 tool schema 的 `Request`。 |
| 114 | 2. 调用 `provider.Stream` 并实时输出 text delta。 |
| 115 | 3. 收集完整 tool call;若没有 tool call,则本回合结束。 |
| 116 | 4. 执行 built-in 或 plugin tool,把结果加入会话后继续,直到完成或达到安全边界。 |
| 117 | |
| 118 | `ctx` 贯穿调用链,Ctrl-C 可以取消进行中的请求。`Agent` 与 `Coordinator` 都实现 `Runner`,因此 CLI 不需要区分单模型或双模型执行。 |
| 119 | |
| 120 | ### 3.5 双模型协作(`Coordinator`) |
| 121 | |
| 122 | 当 `agent.planner_model` 与 executor 不同时,planner 与 executor 使用独立 session: |
| 123 | |
| 124 | - 宿主使用原始用户文本和可信回合元数据做确定性路由,不调用 classifier 模型,也不从 |
| 125 | controller 注入的 prompt block 猜测宿主状态;路由结果为 executor-only、Light、Full、 |
| 126 | plan-for-approval 或显式 plan-only,并用不含用户原文的 route/depth/reason 写入阶段详情; |
| 127 | - 显式 Plan Mode、synthetic turn、上下文短回复、明确单点小改和边界清楚的纯只读动作 |
| 128 | 不再调用第二个 Planner;跨面、结构化、模糊或高风险工作使用 Full;活跃 Goal 与 |
| 129 | Delivery 中的非原子修改工作同样升级为 Full,纯只读动作仍直达 Executor; |
| 130 | - Light 使用较小的单轮调研预算,输出紧凑目标、1–4 个有序步骤、候选触点和主要验证; |
| 131 | Full 使用较大的有界预算,区分已验证与候选触点,并补充风险、验收标准、命令级验证及 |
| 132 | 必要回滚;深度合约保持在同一个稳定 system prompt 中,单轮只追加很小的 |
| 133 | `<planner-turn>`;若 Planner 在有界调研和最终总结轮后仍未收敛,普通 |
| 134 | plan-and-execute 用原始任务降级到 Executor,plan-only 与 plan-for-approval 仍保持 |
| 135 | fail-closed;不完整的 Planner 回合会被回滚,不暴露成无法继续的手动续跑; |
| 136 | - 普通“先规划”在计划完成后直接交接 Executor;plan-for-approval 只用于明确要求等待 |
| 137 | 确认的请求,由宿主强制审批边界,批准后交接 Executor;headless 场景会保存计划供后续 |
| 138 | 回合继续;明确 plan-only 会保存计划并结束当前回合;上述两种执行边界下 Planner 失败 |
| 139 | 都不能降级执行;这些边界可位于任务子句之后,引号内的示例不改变路由; |
| 140 | - executor 在另一 session 中验证候选假设,并使用完整工具执行计划; |
| 141 | - 两条会话互不混合,prompt prefix 都只追加增长,避免切换模型破坏 prefix cache。 |
| 142 | |
| 143 | ### 3.6 上下文管理 |
| 144 | |
| 145 | Reasonix 通过低频 compaction 保持 cache-first: |
| 146 | |
| 147 | - 低于 `agent.tool_result_snip_ratio` 时不改写历史; |
| 148 | - 达到 snip ratio 后,归档并缩短较旧 tool result; |
| 149 | - 达到 `agent.compact_ratio` 后,先把旧 tool result 修剪为占位符,仍超阈值才调用摘要; |
| 150 | - 达到 `agent.compact_force_ratio` 后,可执行强制折叠; |
| 151 | - `context_window = 0` 会关闭该实例的 compaction。 |
| 152 | |
| 153 | 用户可用 `reasonix config compact-ratio [--local] [VALUE]` 查看或修改 65–85% 的自动 |
| 154 | 压缩阈值,内置默认值为 80%。项目级设置优先于桌面端与新 CLI 会话共用的用户全局配置。 |
| 155 | |
| 156 | tool result 的 snip/prune 不删除消息,确保 assistant `tool_calls` 与 tool result 配对。摘要只折叠 assistant/tool 工作;正常大小的用户回合和既有 digest 原样保留。被移除的原文归档到 `reasonix/archive/<timestamp>.jsonl`。 |
| 157 | |
| 158 | `history` tool 支持对 session 与归档进行 BM25 搜索;`memory` tool 用于检索自动记忆, |
| 159 | `remember` 与 `forget` 负责写入和归档。每个真实用户回合前,Reasonix 会用原始用户消息执行 |
| 160 | 有预算的 BM25 自动召回,把命中作为低权限 user-turn 后缀追加;泛化请求会被抑制,等价事实优先 |
| 161 | 项目级版本,stale 内容会降权。这不会修改稳定 system prompt 或工具 schema。 |
| 162 | |
| 163 | 拥有当前项目 store 的父 controller(包括顶层 headless)只有在新事实有界、非敏感、纯创建,且明确属于 project/reference 时才能 |
| 164 | 免确认保存。全局事实、偏好、feedback、更新、重复项、敏感/超长内容和所有 `forget` 仍需 |
| 165 | 新鲜人工确认,Auto、YOLO、Guardian、permission hook 或子智能体都不能代为批准;子智能体和 |
| 166 | 不拥有该作用域 controller 的 headless surface 会 fail closed。事实带有不变 ID、单调 revision、时间、type 与 scope;更新先快照旧版本, |
| 167 | restore 与 archive recovery 会创建更高 revision,并拒绝路径逃逸、符号链接、冲突和覆盖。 |
| 168 | 详细约定见 [`SESSION_MEMORY_RETRIEVAL.zh-CN.md`](SESSION_MEMORY_RETRIEVAL.zh-CN.md)。 |
| 169 | |
| 170 | ### 3.7 权限 |
| 171 | |
| 172 | 权限层按单次 tool call 返回 `Allow`、`Ask` 或 `Deny`: |
| 173 | |
| 174 | ```go |
| 175 | type Decision int |
| 176 | const (Allow Decision = iota; Ask; Deny) |
| 177 | |
| 178 | type Policy struct { Mode Decision; Allow, Ask, Deny []Rule } |
| 179 | func (p Policy) Decide(toolName string, readOnly bool, args json.RawMessage) Decision |
| 180 | ``` |
| 181 | |
| 182 | - rule 可以是 `Tool` 或 `Tool(specifier)`,例如 `Bash(go test:*)`、`Edit(docs/**)`;`Bash=<literal>` 是整条 Bash 命令的精确授权格式,其中 glob 与 Shell 元字符都按普通字符匹配。 |
| 183 | - 优先级为 `deny > ask > allow > fallback`;只读工具 fallback 为 Allow,写工具 fallback 使用 `Mode`。 |
| 184 | - 交互模式中的 Ask 由用户选择单次允许、session scope 允许、持久允许或拒绝;显式 Deny 在所有模式下都不可绕过。 |
| 185 | - 非交互 `reasonix run` 与无头子智能体没有审批界面:默认 Ask/manual 对普通 writer fallback 与显式 ask 规则失败关闭;Auto 只放行普通 writer fallback,显式 ask 仍拒绝;YOLO 可越过普通 Ask,但不能越过 deny、Sandbox 或强制新鲜人工审批。无人值守自动化需要普通 writer 自主执行时,使用现有的 `--auto` / `-y`。 |
| 186 | - 动态 Bash 分两级:参数/算术展开、赋值、不含嵌套执行的 heredoc、普通文件重定向与 Shell glob 不能复用裸 `Bash`、前缀或 glob Allow,保存时只生成 `Bash=<literal>`,但仍遵循普通 fallback,因此 Auto 与获批计划窗口可无提示执行。命令/进程替换、动态命令名、无法解析结构,以及 `eval`、`source`、Shell `-c`、PowerShell/cmd 命令字符串、运行时内联代码参数属于嵌套/间接执行;默认情况下交互 Ask/Auto 必须人工批准,Guardian 与 hook allow 不能代替,无头 Ask/Auto/DontAsk 直接拒绝,只有完全相同的 literal 或 YOLO 可以绕过。高级用户可设置 `[permissions] allow_dynamic_bash = true`,让 Allow fallback(包括 Auto)覆盖这类动态命令;显式 `ask` 与 `deny` 规则仍然优先。 |
| 187 | - 安装 MCP server 即授权其全部工具,不再有 server、raw tool、writer 或 destructive 的第二套审批策略;项目 `reasonix.toml` 与 `.mcp.json` 声明同样默认可信,不需要额外启动确认,显式全局 `deny` 仍然优先。全局安装写入用户 `config.toml`,项目声明保留在原项目文件;同名时项目覆盖全局,项目内部 `reasonix.toml` 高于 `.mcp.json`。编辑写回当前生效来源,删除高优先级声明后露出下一层。`readOnlyHint` 与 `destructiveHint` 仅用于调度、Plan/严格只读边界及缓存到实时安全分类复核,不会新增逐调用审批。严格只读子智能体 registry 仍仅暴露已授权且 `readOnlyHint: true`、无 `destructiveHint` 的 MCP;双模型 Planner 通过固定 `use_capability` 代理(从不暴露直接 `mcp__*` schema)调用已授权、非 destructive 的 MCP,不再要求 `readOnlyHint`,destructive 工具留给 Executor。Balanced 双模型的 Executor 使用独立 frontend 复用同一稳定代理,因此 Planner 发现的 capability ID 可在 handoff 后直接执行,同时保持两侧 ledger/audit 隔离。分发前代理会再次复核当前 controller 的 enable、授权和完整运行时连接身份;共享 Host 中仅 server 同名不构成复用权限。 |
| 188 | - Plan 是协作流程,不等于全工具只读。普通 built-in 与 Bash 仍走 Ask/Auto/YOLO 和 Sandbox;独立双模型 Planner 允许已授权、非 destructive 的 MCP(即使没有 `readOnlyHint`),但在规划阶段持续阻止 destructive 与未授权目标;没有独立 Planner 的单模型 Plan 仍阻止 MCP writer/destructive。 |
| 189 | - Plan 只能由用户显式选择进入,与当前工具审批姿态相互独立;普通聊天不会自动切换到 Plan。Auto/YOLO 不会回答 `ask`,也不会替用户批准 `exit_plan_mode`,获批计划的短期自动执行窗口也不会自动批准后续计划或嵌套/间接 Bash。 |
| 190 | - 桌面端协作模式分为 `normal`、`plan` 和 `goal`。Goal 会持续推进目标,直到完成、同一阻塞状态重复三次、用户停止或达到安全续跑边界。只有用户在输入框中选择 Goal 或运行 `/goal` 显式启动后,长周期研究、调试、优化或实现目标才可启用 AutoResearch;普通聊天不会隐式切换协作模式,也不会创建持久化 AutoResearch 状态。动态状态保存在 `.reasonix/autoresearch/.../`。 |
| 191 | |
| 192 | ### 3.8 Slash command |
| 193 | |
| 194 | Slash command 分为三类: |
| 195 | |
| 196 | - built-in action:`/compact`、`/new`、`/clear`、`/effort`、`/mcp`、`/help`; |
| 197 | - `.reasonix/commands/*.md` 与用户配置目录中的自定义命令; |
| 198 | - MCP prompt:`/mcp__<server>__<prompt>`。 |
| 199 | |
| 200 | 自定义命令支持简单 frontmatter、`$ARGUMENTS`、`$1…$N` 和 `$$`。加载失败的单个命令会被跳过,不应使应用整体退出。 |
| 201 | |
| 202 | Bubble Tea TUI 的 modal overlay 必须隐藏 composer;slash/`@` autocomplete 等 input-owned overlay 保留 composer。新增 overlay 时必须更新 `chat_tui.hideComposer()` 与 layout test。 |
| 203 | |
| 204 | ### 3.9 `@` 引用 |
| 205 | |
| 206 | - `@<server>:<uri>` 读取 MCP resource; |
| 207 | - `@<path>` 仅在本地路径真实存在时读取文件或目录,普通 `@mention` 与邮箱保持原文本; |
| 208 | - 文件内容有大小限制,binary 只标记不展开;目录按深度优先列出并跳过 `.git`、`node_modules` 等噪音; |
| 209 | - 解析异步进行,失败显示 notice 但不阻止本回合; |
| 210 | - autocomplete 每次只读取一层目录,避免在大型目录中递归遍历。 |
| 211 | |
| 212 | ### 3.10 子智能体 Profile |
| 213 | |
| 214 | 子智能体 Profile 是带 `runAs: subagent` 的 Skill。桌面端和 CLI 只允许修改简单、手动调用的 project/global profile;包含 `references/`、`scripts/` 或非托管 frontmatter 的丰富 Skill 不会被编辑器扁平化覆盖。 |
| 215 | |
| 216 | `reasonix subagent try` 使用只读 Skill runner;`reasonix subagent run` 使用常规权限与 Sandbox。`task` 支持 `profile`、`model`、`effort` 和 `write_paths`;`fleet` 在 session scheduler 上并发调度多个任务。详见[子智能体 Profile](./SUBAGENT_PROFILES.zh-CN.md)。 |
| 217 | |
| 218 | ## 4. 数据类型 |
| 219 | |
| 220 | provider 层的核心类型包括 `Role`、`Message`、`ToolCall`、`ToolSchema`、`Request` 和 streaming `Chunk`。`Message` 保留 `tool_calls`、`tool_call_id` 与 `name`;`Chunk` 区分 text、tool call、done 和 error。字段定义以英文规范及 `internal/provider` 源码为准。 |
| 221 | |
| 222 | ## 5. 配置 |
| 223 | |
| 224 | 配置优先级: |
| 225 | |
| 226 | ```text |
| 227 | flag > ./reasonix.toml > 用户 config.toml > 内置默认值 |
| 228 | ``` |
| 229 | |
| 230 | 从 v1.8.1 起,用户配置位于 macOS/Linux 的 `~/.reasonix/config.toml` 或 Windows 的 `%AppData%\reasonix\config.toml`。provider key 保存在 Reasonix home 的 `.env`;项目 `.env` 只用于 workspace 范围的非 provider 变量展开。完整路径见[配置路径](./CONFIG_PATHS.zh-CN.md)。 |
| 231 | |
| 232 | ```toml |
| 233 | default_model = "deepseek" |
| 234 | |
| 235 | [agent] |
| 236 | temperature = 0.0 |
| 237 | reasoning_language = "auto" |
| 238 | |
| 239 | [[providers]] |
| 240 | name = "deepseek" |
| 241 | kind = "openai" |
| 242 | base_url = "https://api.deepseek.com" |
| 243 | models = ["deepseek-v4-flash", "deepseek-v4-pro"] |
| 244 | default = "deepseek-v4-flash" |
| 245 | api_key_env = "DEEPSEEK_API_KEY" |
| 246 | context_window = 1000000 |
| 247 | max_output_tokens = 32768 # 正文、reasoning 与工具调用共用的总输出预算;0 使用 provider 默认值 |
| 248 | |
| 249 | [tools] |
| 250 | enabled = [] |
| 251 | bash_timeout_seconds = 120 |
| 252 | mcp_startup_timeout_seconds = 30 |
| 253 | mcp_call_timeout_seconds = 300 |
| 254 | |
| 255 | [permissions] |
| 256 | mode = "ask" |
| 257 | deny = ["Bash(rm -rf*)", "Bash(git push*)"] |
| 258 | allow = ["Bash(go test:*)", "Bash(git status:*)"] |
| 259 | |
| 260 | [sandbox] |
| 261 | # workspace_root = "" |
| 262 | # allow_write = ["/tmp"] |
| 263 | # forbid_read = ["${HOME}/.ssh"] |
| 264 | |
| 265 | [serve] |
| 266 | auth_mode = "none" |
| 267 | ``` |
| 268 | |
| 269 | 原生 CLI 更新器始终安装最新的严格 `vX.Y.Z` 正式版。1.x 期间仍解析旧渠道配置与 |
| 270 | 参数,但统一指向正式版,并在后续保存配置时省略这些字段。 |
| 271 | |
| 272 | `[sandbox]` 是权限策略之下的强制执行层。file writer 默认限制在 workspace root、Reasonix 用户配置目录和 `allow_write`;`forbid_read` 可阻止读取敏感路径。macOS 使用 Seatbelt,Linux 使用 bubblewrap;若声明 enforce 但平台 backend 不可用,Bash 应拒绝执行而不是静默降级。Windows 当前没有 OS 级 Bash sandbox,file tool 的路径限制仍然生效。 |
| 273 | |
| 274 | `[serve]` 控制 `reasonix serve` 的 browser frontend。默认 `auth_mode = "none"` 仅适合 loopback;暴露到其他机器时必须使用 token 或 password。只有位于可信 reverse proxy 后方时才能启用 `behind_proxy`。 |
| 275 | |
| 276 | 项目根目录的 `.mcp.json` 可使用 Claude Code 的 `mcpServers` schema;与 `reasonix.toml` 同名时,以后者为准。 |
| 277 | |
| 278 | MCP 启动与单次工具调用使用不同生命周期。调用方只短暂等待冷启动,而共享的进程启动、授权、 |
| 279 | `initialize`、`tools/list` 可在后台继续,最长由 `mcp_startup_timeout_seconds`(默认 `30`) |
| 280 | 限制;单个服务器可用 `startup_timeout_seconds` 覆盖。MCP 调用超时只在连接就绪后开始计算。 |
| 281 | |
| 282 | ## 6. 错误处理 |
| 283 | |
| 284 | - library code 使用 `fmt.Errorf("...: %w", err)` 包装并返回错误,不打印也不调用 `os.Exit`; |
| 285 | - 只有 `cli` / `main` 决定 exit code 和面向用户的信息; |
| 286 | - tool error 返回给模型,不直接终止 agent loop; |
| 287 | - network layer 应对 429 / 5xx 使用有界指数退避。 |
| 288 | |
| 289 | ## 7. 代码风格 |
| 290 | |
| 291 | - `gofmt`、`go vet` 必须通过; |
| 292 | - package name 使用小写,exported identifier 必须有文档; |
| 293 | - 注释解释“为什么”,而不只是复述“做了什么”; |
| 294 | - 避免过早抽象,优先清晰直接的实现。 |
| 295 | |
| 296 | ## 8. 分发 |
| 297 | |
| 298 | - 构建:`CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=$(VERSION)" -o reasonix ./cmd/reasonix` |
| 299 | - 目标矩阵:`darwin|linux|windows × amd64|arm64` |
| 300 | - 版本通过 ldflags 注入,来源为 `git describe --tags --always` |
| 301 | - 支持预编译二进制、`go install` 与 Homebrew。 |
| 302 | |
| 303 | ## 9. 路线图(当前范围之外) |
| 304 | |
| 305 | - 完成 Sandbox Phase 1 的 escape prompt:检测 sandbox 不可用或拒绝时,提供一次明确、受权限控制的非 sandbox 重试。 |
| 306 | - MCP long tail:OAuth 2.0、`headersHelper`、更多 `.mcp.json` scope、tool-search 延迟加载、`list_changed`、channel、elicitation、root,以及可提供 provider 的插件。 |
| 307 | - 增加 Anthropic-native provider kind,用于验证 registry 不依赖单一 wire format,并支持原生 prompt cache control。 |
| 308 | - 把“始终允许”规则持久化到项目配置,以及为 `reasonix run` 提供 session 级权限覆盖。 |
| 309 |