主要技术:OpenCode、Effect、SQLite、Drizzle ORM
项目目录
项目简介
OpenCode 是一套基于 TypeScript 的开源编程智能体,提供终端、桌面和 Web 交互入口。开发者可以围绕现有仓库提出问题,让它搜索代码、解释调用关系、修改文件并运行检查命令,工具结果会回到会话中参与下一步判断。
例如,用户要求“找到首页按钮并修改颜色”,它会先搜索组件、阅读候选文件,必要时询问修改目标,再进行编辑。Plan 用于讨论方案,Build 用于实施;长任务还有会话持久化、上下文压缩与服务重启后的继续执行机制。

项目亮点
- 模式约束与实际授权分层:
Plan的提醒和默认编辑规则约束分析阶段;工具执行时再按动作、路径或命令检查权限,支持允许、拒绝和等待确认。工具可见与一次操作能否执行分别判断。 - 输入受理与会话推进分离: 用户输入先进入持久待处理队列,再由会话执行器消费。同一会话的多次唤醒在进程内协调,避免仅依赖客户端连接维持任务。
- 按模型预算压缩上下文: 每次请求前依据模型窗口、已知用量和新增内容判断是否压缩;采用原生压缩或任务摘要,并保留近期消息,溢出反馈与手动压缩走不同入口。
- 重启后的状态结清与续跑: 通过持久执行标记识别遗留任务,将未完成工具记录标为中断,再重建模型输入继续。恢复采用至少一次语义,外部副作用仍需核对。
技术架构
交互与会话受理: 终端、桌面或 Web 客户端向服务提交输入;会话消息、待处理输入及执行状态保存在 SQLite,执行协调器负责唤醒和串行推进当前会话。
模型与工具执行: Runner 组装持久历史、模式提醒与工具定义,经模型适配层发起流式请求;工具注册、权限检查与文件/命令执行共同完成动作,结果写回会话,压缩服务控制后续请求规模。
| 代码位置 | 主要职责 |
|---|---|
packages/core/src/session/inbox.ts、packages/core/src/session/run-coordinator.ts |
受理输入、协调会话执行 |
packages/core/src/session/runner/ |
组装模型请求、处理流和工具结果 |
packages/core/src/plugin/plan.ts、packages/core/src/permission.ts |
模式规则、动作授权与确认 |
packages/core/src/session/compaction.ts、packages/core/src/session/history.ts |
压缩与模型上下文重建 |
packages/core/src/session/execution/restart.ts |
重启后识别和恢复遗留执行 |
如何写进简历
补丁工坊 · SaaS 接口缺陷修复助手 | 企业软件场景示例
项目描述:面向企业软件研发团队的接口缺陷修复助手,结合缺陷说明、请求日志与现有用例推进代码排查、修改和检查,交付补丁及执行记录。
技术栈:TypeScript、OpenCode、Effect、SQLite、Drizzle ORM
主要工作:
- SDK 接入与分析改码分层:通过
OpenCode SDK嵌入修复会话,按仓库组织缺陷日志、源码与复现输入;结合Plan/Build及动作、路径权限划分分析与改码阶段,控制接口排查中的修改范围。 - 持久输入与中断续接:以
SQLite持久化Inbox、消息事件和执行标记,通过会话级串行推进消化补充线索;进程恢复时识别遗留执行并结清中断工具状态,续接尚未完成的接口排查。 - 上下文水位与长排查压缩:结合模型窗口、已知用量与新增消息估算压缩水位,用摘要和近期交互重建请求;在长日志排查中延续接口约束、已排除原因与待处理步骤,控制历史输出进入后续上下文的规模。
- 复现工具与补丁交付:通过插件封装项目现有接口复现与检查入口,将退出状态、失败断言和日志位置回传修复会话;按仓库基线回收代码差异与检查记录,供研发审查补丁。
脚本医生 · 游戏脚本调试助手 | 游戏开发场景示例
项目描述:面向游戏研发的脚本调试助手,结合关卡触发条件、运行日志和脚本配置推进多轮排查,输出代码改动和本地检查结果。
技术栈:TypeScript、OpenCode、Effect、SQLite
主要工作:
- 脚本调试与工具反馈循环:嵌入
OpenCode SDK,将关卡触发条件、运行日志和脚本配置交给调试会话,串联代码读取、文件修改与项目检查;按工具执行结果继续排查,并回收实际代码差异。 - 调试输入持久化与串行推进:通过持久
Inbox保存新增关卡日志与调试要求,在步骤边界优先处理steer,空闲时再消费后续输入;用会话级唤醒合并和串行推进协调多次提交,让排查接续新线索。 - 长调试上下文管理:按模型窗口与新增输入触发压缩,使用摘要和近期工具交互继续处理长运行日志;围绕触发条件、已尝试方案和下一步检查延续调试,减少过期输出反复占据输入窗口。
- 脚本与命令执行边界:基于
allow、deny、ask规则约束脚本目录、工程外路径和检查命令,运行时核对工具可用性;将脚本解析、检查输出与需人工进入游戏确认的结果分别交付。
面试题与回答
3 题Plan Mode 与执行模式的本质区别及工具权限差异是什么?
展开收起
核心回答
OpenCode的Plan和Build都可以读取代码、调用模型和使用工具,区别在于任务目标及有效工具权限:Plan用于分析和形成方案,默认限制普通文件编辑;Build用于实施,但具体操作仍要通过权限检查。切换模式改变的是 Agent 配置和行为约束,并不会自动把进程变成操作系统级只读沙箱。实现上有三层:模式提醒告诉模型当前应做什么;工具注册表过滤被整体禁用的工具;真正读取、编辑或执行命令时,再按动作和资源调用
Permission.assert()。最后这一层决定操作是否允许、需要确认,或直接拒绝。Plan 默认限制了什么
在
OpenCode 2.1.2中,Plan 插件为planAgent 追加普通edit拒绝规则,并为用户主目录下的.opencode/plan计划目录保留编辑例外。提示词同时要求默认在对话中讨论计划,只有用户明确要求时才保存计划文件;实现任务应由用户切换 Agent 后继续。Plan 配置与提醒。例如用户要求“先分析重构方案”,模型可以读取调用链、整理涉及文件;按默认 Plan 编辑规则,直接改普通源码文件会被拒绝,而计划目录是明确例外。切换到
Build后,默认 Agent 可以按配置执行工具,仍需检查当前会话和目标资源的权限。Build 定义。这里的
edit限制不能推成“任何途径都无法写文件”:Shell 命令、插件和外部工具有各自的动作权限,最终还取决于合并后的配置。若要求严格只读,需要同时收紧这些入口;文件系统或进程隔离承担的又是另一层职责。为什么隐藏工具还不够
Tool.snapshot()会根据权限过滤被整体禁用的工具,但可见性无法表达某次调用的全部条件。同一个编辑工具可能只允许操作计划目录,因此工具仍可见,普通源码路径却不允许写。工具可见性过滤。实际授权以动作和资源匹配规则。规则求值采用最后匹配项;权限服务合并 Agent 与会话规则后,先检查明确拒绝,再处理保存的允许规则及需要确认的请求。
deny返回拒绝,allow继续,ask则建立待处理请求并等待决定。这样模型即使提出了一个可见工具的调用,也不能只凭工具名取得任意资源的访问权。规则匹配、权限求值与确认。权限检查怎样落到真实操作
- 读取文件:读取工具先调用
FileAccess.authorizeRead(),解析目标路径,必要时检查工作目录外访问,再以read动作和目标资源调用Permission.assert(),通过后才交给读取器。读取入口、路径与读取授权。 - 编辑文件:编辑工具解析匹配位置、准备差异预览,再用
edit动作对目标路径申请权限;通过后还会检查匹配是否唯一等编辑条件,才进入写入。权限允许与补丁可正确应用是两个判断。编辑授权。 - 执行命令:Shell 工具扫描命令和涉及目录,检查外部目录访问,并按命令资源申请权限;确认后还会重新核对工作目录,再启动进程。因此允许
read不等于允许执行所有命令,禁止edit也不会自动禁止所有可能写文件的命令。Shell 执行前检查。
模式切换与方案取舍
切换 Agent 时,Plan 插件注入进入或退出提醒,并在后续上下文中补齐缺失提醒,避免压缩后模型仍受旧模式提示影响。它负责行为连贯性,具体动作仍由权限服务判断。模式提醒维护。
这种设计能复用同一套模型与工具,在分析和实施之间调整能力范围,也支持路径级例外。代价是需要逐个工具落实授权,并分别验证直接编辑、Shell 写入、外部目录和模式切换;不能只看到界面显示 Plan 就认定所有副作用已被隔离。
参考资料
- 国内:JavaGuide:Harness Engineering:参考模型提出动作、运行系统负责工具和约束的职责划分,解释权限为何不能仅由提示词保证。
- 国内:zero2Agent:Agent 架构与规划设计:参考计划与实施共享任务信息、切换后继续处理既定目标的组织;具体默认权限以本文源码为准。
- 国内:WeThinkIn:Agent Harness 工程:参考任务允许动作与运行环境分别定义,解释权限模式和执行环境不是同一层能力。
- 国内:WeThinkIn:工具权限、沙箱和人工审批:参考工具策略、用户确认和沙箱分别解决的问题,说明应用授权与操作系统隔离的区别。
- 国外:Claude Code:Choose a permission mode:以另一产品的 Plan 权限及命令审批作为对照,说明具体能力由产品版本与配置决定。
- 国外:Anthropic:Building effective agents:参考模型按反馈选择工具的运行方式,解释规划能力与运行时允许动作可以分别设计。
- 读取文件:读取工具先调用
上下文压缩在什么条件下触发?
展开收起
OpenCode 2.1.2的会话执行器在每一步模型请求之前检查自动压缩,也支持模型拒绝超长请求后的恢复,以及用户主动提交的压缩请求。三种入口共用压缩服务,但触发条件不同。自动压缩依据模型预算
SessionRunner先加载当前会话,再调用compaction.compact({ reason: "auto", ... })。压缩服务用模型的input上限计算水位,没有独立输入上限时使用context窗口。该版未显式设置
buffer时,默认预留窗口的15%;窗口至少为32k时,预留量还不低于16k Token。例如窗口为100k时,默认水位为84k。这是一版源码的策略,不能照搬为所有模型的最佳阈值。当前大小优先以同一提供商返回的有效用量作基准,再估算此后新增的消息与工具结果;缺少有效用量时,估算系统指令、工具定义和消息。模型窗口未知时不会自动按比例触发,只能依赖手动操作或服务端溢出反馈。刚完成压缩时,执行器先重建请求;原生压缩产生的不透明窗口,也要等新用量反馈后再判断,避免拿旧计数连续压缩。
溢出恢复与手动压缩
当请求实际被判断为上下文溢出时,执行器尝试
reason: "overflow"的压缩;成功后重新加载历史发起请求,并限制同一步的再次溢出恢复。压缩请求自身过大时,还会逐步降低目标大小,无法再缩减则返回失败,不无限重试。自动压缩关闭时,普通水位检查和溢出恢复都会跳过压缩;用户的手动请求仍可进入压缩服务。因此,“关闭自动压缩后,超长时一定会自动兜底”并不符合这版实现。
压缩后怎样继续任务
支持原生压缩的模型走提供商原生路径;其他模型生成包含目标、要求、决定、工作状态和下一步的摘要,并按
keep保留近期上下文。该版keep默认预算为15k Token,与触发水位分别配置。普通摘要通过消息标识关联保留片段,后续会话历史据此重建请求。这样既能减少反复携带旧过程,又保留近期任务细节;是否丢失约束仍需检查摘要及后续执行,不能仅凭压缩成功状态判断。
Session 中断后重新启动时如何恢复上下文?
展开收起
核心回答
OpenCode是能够阅读仓库、修改文件和执行命令的编程助手。以该版TypeScript会话实现为例,它把用户输入受理、会话执行和模型输入重建分开:先将输入写入持久待处理队列,按会话串行推进步骤;服务重启后检查遗留执行标记,核对未结束的工具记录,再从持久消息重建上下文继续。这恢复的是任务的推进依据。它不会接回旧模型请求的进程现场,也不会仅凭会话恢复就撤销或去重所有文件、命令和外部系统的副作用。
输入先持久受理,再唤醒执行
Session.prompt先按输入 ID 核对已有记录,再准备并受理输入,之后才唤醒执行器。resume: false可以只受理而暂不执行。相同会话、相同输入类型和相同 ID 的重试沿用第一次受理的内容,因此重新发送并不意味着替换原输入。待处理输入保存在
session_inbox中;投递时消费待处理行,并在同一事件事务中插入可见消息。这样,恢复时可以区分“已经受理、尚未投递”和“已经成为会话消息”的输入,而不依赖客户端是否收到了响应。重启标记与执行协调各管一件事
执行器在开始执行时,随开始事件的事务写入持久执行标记。正常完成、失败或用户主动中断会释放标记;服务关闭导致的中断保留标记,进程突然退出也可能留下它。
受管理服务启动时,恢复扫描识别这些遗留会话,持久增加恢复尝试次数,再安排继续执行;超过预算则记录失败。这个标记用于识别未结束的执行,不是分布式租约。
同一会话的多次唤醒由进程内协调器合并,同一会话串行推进,不同会话可以并发。重启恢复假定旧进程已经退出,不能据此允许多个进程共享数据库并同时接管同一会话。
先结清遗留记录,再重建模型输入
Runner 开始推进时,会把遗留的
streaming/running工具记录标为中断失败,把遗留的执行中压缩记录标为压缩失败。这段处理让后续模型能看到调用没有取得正常结果,避免历史永远停在“执行中”;它没有证明实际动作未发生,也没有恢复那个动作的执行现场。之后每一步重新选择会话上下文并加载持久历史,再进行一次模型流式请求。工具调用及结果进入会话记录,下一步从更新后的历史继续,而不是依赖一个只在内存里保存消息的无限工具循环。
长历史采用压缩时,本地摘要路径会保留任务目标、要求、决定、工作状态和下一步等内容,并通过
keepID引用近期消息;历史加载再取回这些近期消息组成文本,与摘要及后续消息一起重建模型输入。原生模型压缩还有模型兼容条件,不能将任意压缩结果直接用于任意模型。哪些状态仍要另行核对?
最关键的情况是:工具已经修改文件或完成外部请求,但进程在结果落库前退出。恢复后即使工具记录被标成中断,动作也可能已经发生。输入 ID 的受理幂等与会话串行,都不能替代业务动作的幂等和结果核对。该版重启恢复明确采用至少一次的执行语义。
同样,遗留后台 Shell 的取消或完成通知,是对后台任务状态的处理;不能把通知当成任意子进程、外部服务或业务资源已经恢复的证据。文件快照与显式回退也不能撤销任意命令的外部副作用。
验证时应分别在输入受理后、工具动作完成但结果未写入时、模型输出中途和压缩过程中终止进程,再检查恢复后的输入数量、消息顺序、工具状态及实际文件或业务结果。还要区分用户主动停止与服务退出,确认不会把用户已取消的任务自动重启。