OpenChamber Control Service 深度解析:类型化控制契约、动作校验与双适配器架构

发布时间:2026/9/25 7:10:42
OpenChamber Control Service 深度解析:类型化控制契约、动作校验与双适配器架构 AI Agent人工智能代码智能体交互助手【免费下载链接】openchamberAgentic Development Environment based on OpenCode AI agent项目地址https://gitcode.com/gh_mirrors/op/openchamber点击查看免费下载OpenChamber Control Service 是 OpenChamber CLI 与托管 OpenCodeopenchamber工具之间唯一的类型化控制契约它将项目、会话、定时任务、浏览器与文件查看五大域收敛为一套固定的动作白名单并在服务端统一完成参数校验、模型/代理解析、会话目录推导与超时/取消语义。本文从该模块的 DOCUMENTATION.md 出发结合 service.js、actions.js、routes.js 与托管工具运行时 runtime.js 的源码实现逐条展开服务边界、动作契约与设计不变量帮助你理解一次校验、双端复用的控制面设计以及会话等待、截图落盘、文件展示等关键动作的真实行为。一、服务定位一份契约两个适配器1.1 为什么需要独立的控制服务在 OpenChamber 的架构中控制能力有两种截然不同的消费方CLI 调用方通过经过认证的 HTTP 路由直接调用托管 OpenCode 工具以openchamber会话/项目/定时任务、openchamber_web浏览器、openchamber_memory记忆三个工具的形式注入到受管的 OpenCode 子进程中由 Agent 自主调用。两种适配器都必须委托给同一个createOpenChamberControlService()见 service.js#L146任何一个适配器都不得调用或派生出另一个。这一约束保证了动作的校验与执行路径唯一CLI 与 Agent 看到的行为完全一致能力开关如禁用浏览器工具在契约层生效而不是在某个适配器内部被绕过动作白名单只需维护一份两个适配器共享同一套语义。从源码结构看actions.js 中的OPENCHAMBER_ALL_ACTIONS聚合了三组定义而 runtime.js#L15 在托管侧用Set([...OPENCHAMBER_AGENT_TOOL_ACTIONS, ...OPENCHAMBER_WEB_ACTIONS, ...OPENCHAMBER_MEMORY_ACTIONS])收紧为 Agent 可触达的子集正是同一契约、窄化暴露的实现落点。1.2 服务装配位置服务的依赖注入点在 index.js#L1566createOpenChamberControlService接收sessionService、scheduledTaskService、browserControl、fileOpen、agentMemoryActions等域服务然后在 feature-routes-runtime.js#L225 通过registerOpenChamberControlRoutes(app, { controlService })挂载到/api/openchamber/control路由。依赖项browserControl、fileOpen、agentMemoryActions允许为null——对应能力未启用时execute()会返回 503而不是静默失败见 service.js#L502-L523。二、边界划分谁校验谁执行文档将模块边界划分得非常清晰结合源码可以拆解为四层文件职责关键点service.js校验并执行固定的项目、模型、会话、定时任务动作白名单唯一执行入口execute(action, input, contextDirectory, options)actions.js定义全部动作及参数说明用agentExposed: false标记 CLI-only 动作routes.js经过认证的 CLI HTTP 适配器一次转发一个动作传播取消信号agent-tool/runtime.js托管工具适配器结果包装为带版本号的工具信封使用独立的一次性回环凭据2.1 CLI 适配器单动作转发与取消传播routes.js#L5-L35 中POST /api/openchamber/control接收{ action, input, contextDirectory }结构将整段逻辑包在AbortController中客户端断连req.once(aborted)或响应关闭res.once(close)时调用controller.abort()取消信号随execute()的options.signal传入waitForIdle会立即以 499 错误终止等待见 service.js#L260失败时保留partial细节如果动作在部分执行后失败响应体中会带上partialAction、sessionId、directory供调用方恢复现场。2.2 托管工具适配器版本化信封与回环凭据agent-tool/runtime.js 是另一侧适配器有几个值得注意的实现细节插件注入materializePlugin()把生成的index.jspackage.json写入dataDir/agent-tool/openchamber-agent-tool/OpenCode 通过动态 import 加载工具设置热变更时重新生成已运行的子进程会自动重载一次性凭据createChildEnv()为每个受管子进程生成 32 字节base64url随机 token通过OPENCHAMBER_AGENT_TOOL_URL与OPENCHAMBER_AGENT_TOOL_TOKEN注入环境回调地址指向实际的绑定地址通配监听时回退127.0.0.1双重校验authorize()同时校验来源地址必须是回环地址或本机实际绑定地址与Bearertoken用crypto.timingSafeEqual常数时间比较动作消歧模型常省略命名空间把memory.read发成readresolveAgentToolAction()以调用方工具自身的动作集为起点解析裸名并在无法消歧时返回可用动作列表而非单纯的 unsupported见 actions.js#L111-L132。三、动作契约总览三组工具、三类意图文档强调两个能力两个工具的设计原则会话控制与页面驾驶是不同意图合并成一个工具描述会让模型更容易调错拆开后关闭其中一个工具即可连同其参数一并从 schema 中消失。动作定义全部集中在 actions.js3.1openchamber控制工具15 个动作OPENCHAMBER_CONTROL_ACTION_DEFINITIONS定义了项目、模型、会话与定时任务动作动作说明参数要点projects.list列出已配置项目无参数models.list展示模型偏好返回 default/favorite/recentsession.list列出会话directory、limit默认 10、all、withStatussession.create创建会话prompt可选默认当前目录session.send向会话发送新提示sessionId必填projectId/directory限定范围session.fork派生会话messageId指定分叉边界prompt可选session.status查询会话状态默认当前会话目录session.messages读取纯文本消息role、limit默认 10schedule.status查询调度器状态CLI-onlyagentExposed: falseschedule.list列出定时任务返回scheduler状态 tasksschedule.create创建定时任务需要name、prompt、model与一个调度选择器schedule.run立即运行任务taskId必填schedule.delete删除任务taskId必填schedule.toggle启用/禁用任务需要布尔型disabledfile.open在用户文件面板展示文件路径相对会话目录解析3.2openchamber_web浏览器工具10 个动作OPENCHAMBER_WEB_ACTION_DEFINITIONS覆盖browser.open/snapshot/click/type/scroll/back/forward/inspect/capture/resize。浏览器动作在服务端先行校验见 service.js#L361-L495非法调用直接以 400 使用错误返回避免唤醒客户端后再经历一次往返。其中browser.open使用独立的 45 秒超时页面导航需要时间其余动作共享 20 秒viewport仅允许mobile、tablet、desktop、fill。3.3openchamber_memory记忆工具4 个动作OPENCHAMBER_MEMORY_ACTION_DEFINITIONS提供memory.read/list/save/delete。记忆工具独立成组的原因与浏览器一致跨会话记忆是与控制单个会话截然不同的意图且需要在 schema 层面干净地整体关闭。3.4 参数契约托管侧 JSON Schema 生成在托管侧参数由 runtime.js#L48-L109 的ALL_PARAMETER_PROPERTIES统一定义再按工具裁剪控制工具参数排除WEB_PARAMETER_NAMES与MEMORY_ONLY_PARAMETER_NAMES浏览器工具只携带url、selector、text、value、submit、direction、viewport、label生成的 schema 仅用oneOf承载 per-action 描述刻意避开enumoneOf组合——某些 OpenAI 兼容网关会拒绝该组合并返回空补全而非错误见 runtime.js#L165-L168。关键参数说明来自 runtime.js#L48-L94 的托管参数注释modelprovider/model格式session.create时从models.list的 favorites/recents 中挑选session.send/fork时应省略以复用会话原模型wait默认省略仅当用户要求或下一步需要已完成结果时才置真timeout等待超时秒数范围 1–86400默认 600必须与wait一起使用goal/goalTokenBudgetGoal 模式开关与 token 预算1000–100000000预算依赖goal: trueworktree/branch/startRef/setUpstream隔离工作树创建相关未提交改动不会带入新工作树disabledschedule.toggle专用true禁用、false启用。四、会话动作的执行语义等待、分叉与目录解析文档中的不变量在 service.js 里有完整实现以下是会话域最核心的几条。4.1 消息只取有序文本部分extractTextMessages()service.js#L53-L72兼容 v2 消息结构用户消息取text字段助手消息从content[]中过滤type text项并拼接最后按createdAt升序重排拉取时order: desc分页返回前再排序回时间正序。每条消息附带providerID/modelID组合的模型标识与createdAt/completedAt时间戳。4.2 等待绝不把初始空闲当作完成waitForIdle()service.js#L256-L279实现了文档的核心不变量默认轮询间隔 500ms默认超时 600 秒上限 86400 秒requireActivity在派发已确认时置真result.promptDispatched true空闲但未观察到活动时检查最近一条助手消息若其completedAt晚于基线非基线 ID 且不早于开始时间才视为完成——初始空闲响应永远不算完成超时500与取消499都被视为失败绝不作为权威的空闲结果返回。4.3 派发确认promptDispatched只报告已观察到执行session.send后若附带wait服务会通过 service.js#L342-L352 确认新用户消息确实抵达会话确认失败时返回promptDispatched: false与promptError而不是声称成功。baselineAssistantMessageId作为内部字段在返回前被删除不会泄漏给调用方。4.4 会话目录的权威解析v2 的prompt_async使用调用方上下文目录会命中错误实例目标会话可能位于其他 worktree。因此resolveSessionDirectory()service.js#L286-L296从全局会话列表中按session.location.directory解析目标会话目录显式projectId或directory优先于托管工具当前会话目录的回退回退永不创建冲突的第二作用域send/fork 未显式指定模型/代理/变体时先复用目标会话最后一次用户消息的选择再回退到配置默认值只有session.create直接解析默认值。4.5 默认代理的解析顺序默认代理按所属项目 → 全局设置 → OpenCode 默认的顺序解析文档不变量目录型请求在创建 worktree 前先识别项目已存在的链接 worktree 通过 Git 主 worktree 根解析。配置的模型 ID 与 effort 偏好即使目录中缺失对应条目也会保留绝不静默换模型派发。五、定时任务域调度器状态与动作校验schedule.*动作全部经由 scheduled-tasks/service.js 组合进控制服务。校验要点在execute()内service.js#L527-L559schedule.run、schedule.delete、schedule.toggle要求taskIdschedule.toggle强制要求布尔型disabled这是它取代独立 enable/disable 两个动作的契约依据schedule.list额外返回scheduler字段调度器状态即文档所述schedule.list也以scheduler形式返回调度器状态项目作用域二选一projectId与directory只允许提供其一resolveProjectID()会校验项目确实存在于设置中404schedule.create的调度选择器四选一dailyHH:mm、weekly0周日…6周六 time、onceYYYY-MM-DD time、cronservice.js#L94-L116提供多个或零个都报 400weekly会去重并排序可选timezoneIANA 名称。六、file.open与browser.capture两个反直觉的设计6.1file.open只负责请用户看不等待反馈file-open.js 完整实现了文档不变量相对路径必须相对会话目录解析显式directory优先完全没有目录时的相对路径直接 400检查目标是存在的文件ENOENT报 404目录报 400 Not a file校验通过后把{ path, directory, sessionId }交给注入的emit由 index.js 写入所有 UI 控制流事件为openchamber:file-open-request没有回包打开标签页不会在客户端静默失败因此到达的客户端数量就是信号——emit返回 0 时抛 503无窗口连接绝不声称成功工作区外路径有意放行截图与录制常落在临时目录查看器本就支持读取这类文件。6.2browser.capture服务端落盘只回路径screenshots.js 说明截图写入.openchamber/screenshots/项目目录下SCREENSHOT_DIRECTORY常量返回项目相对路径而非图片字节。原因在源码注释中非常明确拍照的客户端可能不在持有仓库的机器上而回答、提交、评审能引用的是路径base64 工具结果三者都不是Agent 提供的label经screenshotSlug()降级为文件名片段只保留小写字母数字其余替换为-截断 48 字符..、路径分隔符与前导点无法存活screenshots.js#L29-L37文件名带 ISO 时间戳冒号与点替换为-按时间排序且可读结果额外携带hint字段明确提示 Agent在回答中写![](path)才能把图片渲染在消息下方——保存文件只是展示的一半service.js#L485返回的路径统一使用 Posix 分隔符因为该路径会写进 Markdown 与提交信息Windows 分隔符在其中是转义字符screenshots.js#L80-L82。七、错误契约一次说清缺什么error.js 定义了统一的OpenChamberControlError携带statusCode与可扩展 details与asControlError()转换器。文档的用法错误点名缺失/冲突输入不变量贯穿全程session.send缺sessionId→sessionId is required400browser.open缺url或协议不是 http(s) → 明确指出timeout或lastAssistant未配wait→timeout requires waitgoalTokenBudget未配goal→goalTokenBudget requires goalschedule.create调度选择器数量 ≠ 1 →Provide exactly one of daily, weekly, once, or cron校验顺序保证保护副作用显式请求的模型/代理/变体在创建任何会话、worktree 或 goal之前即对照目录自身的 OpenCode agent 与 provider 列表校验因为prompt_async会接受不可用的选择、只在事件流上失败查找失败或空结果绝不把有效选择变成拒绝service.js#L34-L39 的注释与文档不变量一致。错误经asControlError包装后由 CLI 路由以对应statusCode返回 JSON{ error, partial?, ... }托管侧则按状态码 4xx/5xx 映射为usage/runtime错误类别runtime.js#L426-L430使 Agent 能区分改参数重试与环境故障。八、契约的刻意边界什么不在动作表里文档最后一部分强调了几类刻意不做的能力这是契约边界的另一面会话/工作树的破坏性删除与项目路径注册不属于动作契约——Agent 无权删除用户数据或扩增项目配置session.status与session.messages的状态来源是官方目录级 OpenCode APIsession.list的 archive 信息由 OpenChamber 自有存档覆盖v2 已无设置Session.time.archived的路由未接线存档即无归档且单个目录状态查询失败只对该目录产生unknown不会抹掉其他会话结果service.js#L573-L586会话动作timeout的取值上限 86400 秒与browser.open的 45 秒预算差异service.js#L453都写死在服务端调用方不可越权。结语OpenChamber Control Service 的价值不在于动作数量多而在于把谁校验、谁执行、谁适配的边界收敛得足够干净一份动作表同时驱动 CLI 路由与托管工具插件校验集中在执行入口之前取消与超时语义在服务端统一且刻意拒绝了删除类与注册类危险操作。如果你要扩展 OpenChamber 的控制能力正确的入口是修改 actions.js 的动作定义并在 service.js 的execute()中登记执行分支同时保持两个适配器继续委托同一个服务——这正是文档所述架构约束的直接实践。赞分享AI Agent人工智能代码智能体交互助手【免费下载链接】openchamberAgentic Development Environment based on OpenCode AI agent项目地址https://gitcode.com/gh_mirrors/op/openchamber点击查看免费下载相关推荐Canopy Wallet 配置驱动架构实战chain.json 与 manifest.json 双契约深度解析Canopy Wallet 配置驱动架构实战chain.json 与 manifest.json 双契约深度解析 Canopy Wallet 是 Canopy区块链后端PocketPal AI 自动化桥接层src/__automation__深度解析__E2E__ 构建门控、DCE 契约与 CI 包体校验PocketPal AI 自动化桥接层src/__automation__深度解析 __E2E__ 构建门控、DCE 契约与 CI 包体校验 在 Pock人工智能AI 应用大模型本地部署移动开发语音TanStack Query Lit 适配层 CreateMutationOptions 类型解析Lit 变更控制器的完整选项契约TanStack Query Lit 适配层 CreateMutationOptions 类型解析Lit 变更控制器的完整选项契约 CreateMutatio前端缓存状态管理上一篇为什么React Native Skeleton Placeholder是提升应用用户体验的最佳选择下一篇HIXL Python 接口完全指南从初始化、建链到内存传输与状态管理创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考