【免费下载链接】opencodexUniversal provider proxy for OpenAI Codex Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code项目地址https://gitcode.com/gh_mirrors/ope/opencodex点击查看免费下载导读本文讲解 opencodex 中cursor-web-approve工作项Phase 1的核心改造将 Cursor live transport 对webSearchRequestQuery/exaSearchRequestQuery/exaFetchRequestQuery三个交互查询的应答从rejected翻转为approved从而让经由 opencodex Cursor 适配器路径服务的模型如cursor/grok-4.5恢复被非交互式桥接拒绝扼杀的 Web 搜索能力。读完本文你将理解 Cursor agent 协议的 interactionQuery 审批门机制、为什么批准空应答能委托 Cursor 服务端执行搜索、对应的单元测试如何改造以及这一取舍背后的配额消耗与传输安全性考量。问题背景模型在 Cursor 路径上的联网能力为何是死的opencodex 是面向 OpenAI Codex 与 Claude Code 的通用 provider 代理其 Cursor 适配器src/adapters/cursor/目录负责把上游模型请求以 Cursor agent 协议转发给 Cursor 的服务端。计划文档 000_plan.md 记录了一个明确的现象通过该路径服务的模型cursor/grok-4.5无法使用 Web 搜索——opencodex 的 Cursor transport硬拒绝HARD-REJECTCursor 服务端的 Web 搜索审批门导致模型的联网能力是死的。用户日志为web_search - non-interactive bridge reject。根因不在模型本身而在 opencodex 的应答策略Cursor 服务端在模型发起联网检索前会以interactionQuery的形式向客户端即 opencodex 桥接层发送webSearchRequestQuery等权限审批请求而planInteractionQueryReply()此前对这三个查询一律回rejected理由为NON_INTERACTIVE_REASON。模型在 Cursor 端原生请求联网被拒于是整个 Web 能力形同虚设。核心机制interactionQuery 审批门approve/reject 协议planInteractionQueryReply()见 live-transport.ts是 opencodex 对 Cursor 服务端interactionQuery的唯一决策点。它是一个纯函数无 I/O可单元测试由handleServerMessage负责实际写帧与 liveness 维护。从生成的 protobuf 定义agent_pb.ts可以看到以WebSearchRequestResponse为例其result是一个oneofresult: | { value: WebSearchRequestResponse_Approved; case: approved } | { value: WebSearchRequestResponse_Rejected; case: rejected } | { case: undefined; value?: undefined }其中WebSearchRequestResponse_Approved是空消息Messageagent.v1.WebSearchRequestResponse_Approved {}即批准应答不携带任何参数。这与askQuestion、switchMode等带reason的拒绝应答形成鲜明对比——审批门没有结果字段可填批准即委托。协议语义如下批准approved客户端同意后搜索由 Cursor 的服务端执行结果在服务端注入到模型上下文模型答案以textDelta帧流回客户端拒绝rejected客户端拒绝后模型跳过搜索旧行为直接导致联网能力死亡结果帧同时以 display-plane 的ToolCall.web_search_tool_callunion 18与exa_*_tool_callunion 26/27InteractionUpdate形式到达但这些是 Cursor 原生native、非 MCP工具帧会被事件映射器安全丢弃详见下文传输安全性。Phase 1 实施三个审批门的翻转Phase 1010_phase1.md的改动范围被严格限制在三个文件src/adapters/cursor/live-transport.ts、tests/providers/cursor/cursor-interaction-query.test.ts文档写作时的相对路径为tests/cursor-interaction-query.test.ts实际位于tests/providers/cursor/下以及本计划单元本身。导入块变更schema 顶部约 17-25 行新增导入WebSearchRequestResponse_ApprovedSchema、ExaSearchRequestResponse_ApprovedSchema、ExaFetchRequestResponse_ApprovedSchema删除导入翻转后不再使用的WebSearchRequestResponse_RejectedSchema、ExaSearchRequestResponse_RejectedSchema、ExaFetchRequestResponse_RejectedSchema保留导入SwitchModeRequestResponse_RejectedSchemaswitchMode 仍保持拒绝。当前源码中三个 Approved schema 已就位见 live-transport.ts 的导入列表与计划完全一致。planInteractionQueryReply 文档注释更新约 176-189 行原注释中switchMode / webSearch / exaSearch / exaFetch: reject的行被改为webSearch/exaSearch/exaFetch现在approved空批准由 Cursor 服务端执行搜索并注入结果switchMode保持 rejected。注释同时明确记录取舍批准会消耗用户 Cursor 的 web-search/Exa 配额。当前源码 live-transport.ts 的注释完整描述了这一语义并补充说明合成 web_search sidecarsrc/web-search是另一个正交的代理侧路径仅在客户端发送 hostedweb_search工具时启用不覆盖 Cursor 原生 Web 搜索。三个分支体的翻转按计划文档翻转前的webSearchRequestQuery238-248 行区域为result: { case: rejected, value: create(WebSearchRequestResponse_RejectedSchema, { reason: NON_INTERACTIVE_REASON }) }, replyCase: webSearchRequestResponse:rejected,翻转后为result: { case: approved, value: create(WebSearchRequestResponse_ApprovedSchema, {}) }, replyCase: webSearchRequestResponse:approved,exaSearchRequestQuery249-259 行区域与exaFetchRequestQuery261-271 行区域形状相同分别使用ExaSearchRequestResponse_ApprovedSchema/ExaFetchRequestResponse_ApprovedSchemareplyCase对应为exaSearchRequestResponse:approved/exaFetchRequestResponse:approved。当前源码实现live-transport.ts中三个分支已全部落地为 approved 形态if (q.case webSearchRequestQuery) { return { response: respond({ case: webSearchRequestResponse, value: create(WebSearchRequestResponseSchema, { result: { case: approved, value: create(WebSearchRequestResponse_ApprovedSchema, {}) }, }), }), replyCase: webSearchRequestResponse:approved, }; } // exaSearchRequestQuery / exaFetchRequestQuery 同构分别使用 // ExaSearchRequestResponse_ApprovedSchema / ExaFetchRequestResponse_ApprovedSchema保持不变的分支askQuestion约 214 行保持 rejected理由为无人在回合中途应答代理必须自主继续AskQuestionRejectedSchemaNON_INTERACTIVE_REASONswitchMode约 227-234 行保持 rejected无非交互式模式切换createPlanRequestQuery保持 successagent 继续执行plan 文本作为可见输出暴露给 Codex 侧setupVmEnvironmentArgs与未知查询类型保持空应答 unsupported 标记避免抛错导致整个 gRPC 连接被failAndClear杀死见 live-transport.ts。其中NON_INTERACTIVE_REASON opencodex bridge is non-interactive; proceed without this interaction.live-transport.ts是拒绝路径的统一理由。测试改造拆分断言矩阵计划要求拆分原先用test.each约 58-69 行一次性断言 switchMode web exa 全部rejected的测试保留一个针对switchModeRequestQuery的测试单行 each 即可断言replyCase为switchModeRequestResponse、内部结果 case 为rejected新增针对 web/exa 三个 case 的test.each断言replyCase为*RequestResponse且内部value.result.case approvedid保持原样测试中为 11。当前测试文件 cursor-interaction-query.test.ts 已呈现拆分后的最终形态// switchMode 仍被拒绝 test.each([ [switchModeRequestQuery, SwitchModeRequestQuerySchema, switchModeRequestResponse], ] as const)(%s is rejected with the matching id, (queryCase, schema, responseCase) { const plan planInteractionQueryReply(query(11, { case: queryCase, value: create(schema, {}) } as InteractionQuery[query])); expect(plan.response.id).toBe(11); expect(plan.response.result.case).toBe(responseCase); const value plan.response.result.value as { result: { case: string } }; expect(value.result.case).toBe(rejected); }); // web/exa 是 approve/reject 权限门批准后由 Cursor 服务端执行搜索并注入结果 test.each([ [webSearchRequestQuery, WebSearchRequestQuerySchema, webSearchRequestResponse], [exaSearchRequestQuery, ExaSearchRequestQuerySchema, exaSearchRequestResponse], [exaFetchRequestQuery, ExaFetchRequestQuerySchema, exaFetchRequestResponse], ] as const)(%s is approved with the matching id, (queryCase, schema, responseCase) { const plan planInteractionQueryReply(query(11, { case: queryCase, value: create(schema, {}) } as InteractionQuery[query])); expect(plan.response.id).toBe(11); expect(plan.response.result.case).toBe(responseCase); const value plan.response.result.value as { result: { case: string } }; expect(value.result.case).toBe(approved); expect(plan.replyCase).toBe(${responseCase}:approved); });计划中特别注明测试不需要导入三个*RequestResponse_Approvedschema——测试通过读取plan.response.result.value.result.case断言只依赖响应对象的结构而非具体 schema 类型。当前测试代码正是这样实现的仅导入查询侧的 schema如WebSearchRequestQuerySchema。该测试文件还覆盖了其余交互分支的回归createPlansuccess plan 文本回显L29-43、askQuestionrejectedL45-55、setupVmEnvironment与未知查询的空应答 unsupported:*标记L83-101保证翻转不影响其他交互路径。验证单元测试 类型检查Phase 1 的验收标准来自 000_plan.md 的 accept criteria为c1webSearch/exaSearch/exaFetch 全部 approvedc2askQuestion switchMode 保持 rejectedc3cursor-interaction-query.test.ts更新且 Cursor 相关测试全绿c4改动文件的类型检查干净。对应的验证命令以当前仓库实际路径为准# 单测翻转后的交互查询应答矩阵 bun test tests/providers/cursor/cursor-interaction-query.test.ts # 回归整个 Cursor 适配器测试套件含 cursor-live-transport 等 bun test tests/providers/cursor/cursor-*.test.ts # 类型检查确认 live-transport.ts 与测试文件无类型错误 bun x tsc --noEmit # 改动范围核对仅 live-transport.ts 测试文件 计划单元 git diff --stat计划中同样明确了范围边界合成 web_search sidecar、并行工具调用截断、event-stream Transport-closed、上下文膨胀、其他适配器、配置开关、版本号 bump/发布等均不在本次改动范围内。传输安全性为什么批准后不会引发虚假的不完整工具调用批准空应答之所以安全得益于事件映射层的两道防护protobuf-events.ts 与 live-transport.tsmcpArgsFromToolCall()仅当toolCall.tool.case mcpToolCall且args.providerIdentifier OCX_RESPONSES_TOOL_PROVIDER即 opencodex 桥接的 Responses 工具时才返回参数对 Cursor 原生工具返回undefinedisClientToolFrame()toolCallStarted/partialToolCall/toolCallCompleted三类工具生命周期帧只有在其内部 ToolCall 是 ocx 桥接工具时才视为客户端工具活动Cursor 原生帧如readToolCall、web_search_tool_call、exa_*_tool_call被判定为 display-plane 帧不会撤销 pending 的 turn finalize。因此批准后服务端返回的web_search_tool_call/exa_*_tool_call结果帧会被事件映射器安全忽略——不会造成流停滞stall也不会产生虚假的不完整工具调用模型的最终回答textDelta照常流回。测试文件中readToolCall不撤销 client-tool finalize 的断言cursor-interaction-query.test.ts正是对这一防护的验证。代价与边界配额消耗与路径正交性实施该翻转必须接受一个明确的代价批准原生 web/exa 搜索会消耗用户 Cursor 账户的 web-search/Exa 配额搜索由 Cursor 服务端执行并计费。计划文档将其列为必须上报的 tradeoffplanInteractionQueryReply的注释也同步记录。另一个需要注意的边界是路径正交性opencodex 自带的合成 web_search sidecarserver/responses.ts中的planWebSearchrunWithWebSearch仅在客户端发送 hostedweb_search工具parsed._webSearch时触发本次改造涉及的是 Cursor 适配器路径上模型使用Cursor 原生 Web 搜索的场景两者互不覆盖。换言之本次翻转修复的是模型想联网、却被桥接层拒绝的死角而非替代代理侧的合成搜索能力。小结cursor-web-approvePhase 1 是一次精准的最小化修复通过把planInteractionQueryReply()中 web/exa 三个审批门从rejected翻转为approved配合测试矩阵拆分与类型检查让 Cursor 适配器路径上的模型恢复原生联网检索能力同时用事件映射层保证 native 工具帧被安全忽略、不破坏流式传输。整个改动以纯函数决策点 单元测试 类型检查为验收闭环未触碰任何其他适配器与配置面——这正是该工作项设计为单 PABCD 周期、单文件改动的初衷。赞分享【免费下载链接】opencodexUniversal provider proxy for OpenAI Codex Claude Code — use any LLM (Claude, Gemini, Grok, DeepSeek, Ollama…) with Codex CLI, App, SDK, and Claude Code项目地址https://gitcode.com/gh_mirrors/ope/opencodex点击查看免费下载相关推荐opencodex Cursor 适配器 Web Search 审批门控interactionQuery 从拒绝到批准的交互策略改造opencodex Cursor 适配器 Web Search 审批门控interactionQuery 从拒绝到批准的交互策略改造 本篇文章聚焦 opencopencodex 原生响应模式修复实录让路由模型推理遵循 hideThinkingSummary对齐 Codex 原生 wire 形态opencodex 原生响应模式修复实录让路由模型推理遵循 hideThinkingSummary对齐 Codex 原生 wire 形态 本技术指南以仓库开opencodex 原生 Codex 路由模型目录规约Phase 100 实现指南opencodex 原生 Codex 路由模型目录规约Phase 100 实现指南 opencodex 作为 OpenAI Codex 与 Claude Co上一篇FoxCMS黔狐内容管理系统打造企业级网站的终极开源解决方案下一篇Claudia布局系统打造灵活响应式界面的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考