Skip to content

fix(#329): protocol退场 + OpenCode路由修复 + 错误诊断增强#330

Merged
zts212653 merged 28 commits intomainfrom
fix/329-protocol-regression
Apr 7, 2026
Merged

fix(#329): protocol退场 + OpenCode路由修复 + 错误诊断增强#330
zts212653 merged 28 commits intomainfrom
fix/329-protocol-regression

Conversation

@mindfn
Copy link
Copy Markdown
Collaborator

@mindfn mindfn commented Mar 31, 2026

What

Core changes across 20+ files:

  • Protocol 退场(运行时/绑定决策): effectiveProtocol in invoke-single-cat.ts now solely provider-based, no longer reads account.protocol; validateRuntimeProviderBinding protocol matching removed; UI protocol selector removed from create form, edit form has collapsible advanced correction entry
  • OpenCode routing: safeProviderName() remaps openai to openai-compat to avoid OpenCode CLI builtin collision; deriveOpenCodeApiType() is sole authority for API type routing
  • Codex CLI model handling: uses --config model= instead of --model to bypass builtin metadata lookup; preserves multi-segment model slugs
  • Error diagnostics: fail-fast on missing API key; detailed error logging for provider binding validation
  • Env fix: OPENCODE_CONFIG instead of OPENCODE_CONFIG_DIR

Why — Protocol 为什么可以退场

核心推导:每个 client/provider 的协议是固定的,不是用户可以选的。

Provider Protocol 来源
anthropic anthropic Claude Code 只说 Anthropic 协议
openai openai Codex CLI 只说 OpenAI 协议
google google Gemini CLI 只说 Google 协议
dare openai Dare 用 OpenAI 兼容协议
opencode 由绑定账户决定 OpenCode 是唯一的多后端 client

结论:provider→protocol 映射是确定性的。用户在创建账户时选定 provider(如 anthropic/openai),系统就能推断出唯一正确的协议。Protocol 作为用户可填写的字段是冗余的——填对了等于没填,填错了只会造成运行时错误。

因此本 PR

  1. 运行时 env 注入(effectiveProtocol)不再读 account.protocol,改为 protocolForProvider[provider](OpenCode 例外:仍读绑定账户的 protocol,因为它需要知道目标后端)
  2. 绑定校验(validateRuntimeProviderBinding)不再做 protocol 匹配——protocol 是 provider 的内在属性,不是需要校验的外部输入
  3. UI 创建表单去掉 protocol 下拉——不需要用户选择系统可以推断的值
  4. 编辑表单保留折叠的「高级設置」——仅用于修正历史遗留的错配数据

Issue #329: manifest v3 sync regressed protocol dropdown and OpenCode provider routing.

Original Requirements

Plan / ADR

Tradeoff

Protocol field remains in the data model for backward compatibility but is no longer consulted for runtime/binding decisions. Full field removal deferred to avoid migration complexity in this PR.

Test Evidence

pnpm --filter @cat-cafe/web test # 1844 passed, 0 failed (266 files)
pnpm -r --if-present run build # Success (api + web)
pnpm lint # Passed (warnings only)
biome check # Passed (warnings only)

Open Questions

  • API test suite has pre-existing failure on main (exit code 127, missing CLI binary) — not introduced by this PR
  • resolveEnvFallbackKey(account.protocol) — retired in 6ae5946a, env fallback removed
  • resolveForClient legacy protocol read — data passthrough, non-decisional

本地 Review: [x] gpt52 已 review 并放行(active-path protocol 退场确认)
云端 Review: [ ] PR 创建后在 comment 中触发

@mindfn mindfn requested a review from zts212653 as a code owner March 31, 2026 16:46
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d9b6718cac

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 1, 2026

Addressed the remaining review comments in 8c81a4d2:\n- preserve hidden account protocol across baseUrl-only edits\n- support ocProviderName=openai-responses as the member-level override for generic openai accounts\n- added API and web regression coverage\n\n@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a617f865c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 11c04e037d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c246cc7158

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mindfn mindfn changed the title fix(#329): remove sync-regressed protocol dropdown, add openai-responses to provider suggestions fix(#329): protocol退场 + OpenCode路由修复 + 错误诊断增强 Apr 7, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ac6e1d900

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31d2ccb44a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1affd40b5f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 7, 2026

@codex review

@zts212653
Copy link
Copy Markdown
Owner

🐱 Cat Café Code Review — Two-Cat Synthesis

Reviewers: 布偶猫/宪宪 (Claude Opus) + 缅因猫/砚砚 (GPT-5.4)
Verdict: 🟡 Request Changes — valuable work, not ready for direct merge


What we agree on

The code quality is high. The PR body is exceptionally detailed with clear root-cause analysis, production validation (17 CLI calls, 6 routing scenarios), and well-structured tests. The five core changes are individually sound:

  1. OPENCODE_CONFIG_DIROPENCODE_CONFIG — confirmed correct per OpenCode docs
  2. safeProviderName() for openai builtin collision — clean solution
  3. ✅ Codex --config model= passthrough — avoids metadata lookup warning
  4. ✅ API key fail-fast — eliminates silent failures
  5. ✅ Debug logging with secret masking — good observability

Issues to resolve before merge

P1 — #329 is not fully fixed (砚砚)

The PR title says fix(#329) but the protocol dropdown UI still exists in the provider profile components. Specifically:

  • HubProviderProfileItem.tsx still renders the "API 协议" field in edit mode
  • hub-provider-profiles.sections.tsx still renders it in the create form
  • HubProviderProfilesTab.tsx still POSTs protocol

If protocol is being retired from OpenCode routing, the UI should either be removed or explicitly marked as "direct-client only". As-is, #329 ("protocol dropdown regression") is only partially addressed.

Suggestion: Either split into fix: OpenCode routing + diagnostics (this PR) + a separate PR for fix(#329): protocol UI cleanup, or add the UI changes here. Update the PR title accordingly.

P1 — inferProbeProtocol signature (宪宪)

provider-profiles.ts now calls inferProbeProtocol() with 5 arguments:

inferProbeProtocol(
  parsed.data.baseUrl ?? existing.baseUrl,
  undefined,
  parsed.data.models ?? existing.models,     // new
  parsed.data.name ?? existing.displayName,  // new
  parsed.data.displayName ?? existing.displayName, // new
);

The function definition is not in this diff. Please confirm the signature accepts these params — otherwise this is a compile error.

P2 — toTomlString import (宪宪)

CodexAgentService.ts uses toTomlString(cliModel) but no import is visible in the diff. Please confirm this function exists in scope.

P2 — Debug log volume (宪宪)

Every CLI event gets a debug log:

log.debug({ catId, eventIndex, type: evtType }, 'CLI event received');

Long sessions can produce hundreds of events. Consider trace level or sampling (e.g., log every 10th event + always log first/last).

P3 — OPENCODE_BUILTIN_NAMES scope (宪宪)

safeProviderName() only remaps openai. Worth adding a comment explaining why anthropic/google don't need remapping (their builtin behavior matches the intended SDK adapter).


Impact assessment (if intake to cat-cafe)

This is not a safe cherry-pick for cat-cafe. Key friction points:

  • Cat-cafe currently uses protocol > ocProviderName routing (per absorbed #292/#291)
  • Cat-cafe docs explicitly advise against using builtin names as ocProviderName
  • Intake would require: migration plan for existing profiles, doc updates, UI changes, and a design-gate discussion

For clowder-ai only: merge is safe after fixing the P1/P2 items above.


[宪宪/Opus-46🐾] + [砚砚/GPT-54🐾] — Cat Café Review Team

@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 7, 2026

逐项回复 — Review Findings

P1 — #329 is not fully fixed (砚砚) → ✅ 已修复

Protocol UI 有意保留已在 34fb7e97 中移除 Protocol UI

Protocol dropdown 已从 create/edit 表单中完全移除。protocol 字段由后端 inferProbeProtocol() 自动推断,用户无需手动选择。这与 deriveOpenCodeApiType(ocProviderName) 作为 OpenCode 唯一路由权威的设计一致——protocol 不再暴露给用户。

P1 — inferProbeProtocol signature → ❌ 不成立

函数定义使用了 rest parameter:

function inferProbeProtocol(
  baseUrl: string | undefined,
  selector: string | undefined,
  models: readonly string[] | undefined = [],
  ...nameHints: Array<string | undefined>
): 'anthropic' | 'openai' | 'google'

传 5 个参数完全合法,额外的参数由 ...nameHints 收集。pnpm lint 编译通过。

P2 — toTomlString import → ❌ 不成立

toTomlStringCodexAgentService.ts 文件内的局部函数(line 114),不需要 import。

P2 — Debug log volume → ⚠️ 已知,保留

"CLI event received" 日志来自 claude-agent / opencode-agent 模块(不是 CodexAgentService),level=20 (debug)。生产部署可通过 log level 配置静默 debug 日志。

P3 — OPENCODE_BUILTIN_NAMES scope → ✅ 已修复

已在 c50e7a1e 中补充注释说明为什么只列了 openai(anthropic/google builtins 已匹配对应 SDK adapter)。


总结:P1 #329 已修复(Protocol UI 移除)+ P3 注释已补充。其余 3 项 dismiss。

[宪宪/Opus-46🐾]

@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 7, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@zts212653
Copy link
Copy Markdown
Owner

🐱 Re-review — 布偶猫/宪宪 + 缅因猫/砚砚 综合

25 commits, 19 files, +806/-251。对照上一轮 review 逐项确认。


上轮 Finding 逐项追踪

# 上轮 Finding 状态 证据
P1 inferProbeProtocol 5参数签名 ✅ 已验证 第4参数是 ...nameHints: Array<string | undefined> variadic,5参数合法
P1 #329 protocol UI 没修 ✅ 已修 Create form 彻底移除 protocol selector;Edit form 折叠到"高级设置",默认隐藏(443fa166
P2 toTomlString import ✅ 已验证 定义在 CodexAgentService.ts:114 同文件内,不需要 import
P2 debug log 每个 CLI event ⚠️ 未改 作者回复"intentional, debug level only"——可以接受,不阻塞
P3 OPENCODE_BUILTIN_NAMES 注释 ✅ 已加 c50e7a1e 添加注释解释为什么只 remap openai

新增改动 Review

Protocol UI 折叠方案HubProviderProfileItem.tsx):

  • Create form: protocol 完全移除,后端 inferProbeProtocol 自动推断 ✅
  • Edit form: protocol 下拉框移到折叠的"高级设置"区域,默认隐藏,带说明"通常自动推断即可,若模型调用出现协议不匹配,可在此手动修正" ✅
  • Tag/model 快速编辑路径:不再发送 protocol(后端 preserve existing)✅
  • 测试更新:create form 断言 不包含 API 协议,edit form 断言先点"高级设置"再操作 select ✅

这是一个合理的折中——普通用户不碰 protocol,高级用户仍可手动修正。

hub-provider-profiles.sections.tsx

  • ApiProtocol type + PROTOCOL_OPTIONS 从 sections 移到 HubProviderProfileItem 本地 ✅
  • CreateApiKeyProfileSection 砍掉 protocol/onProtocolChange props ✅

最终判断

可以 merge。 所有 P1 已解决,P2 debug 日志量不阻塞。

唯一保留意见(不阻塞 merge):

  • Per-event log.debug('CLI event received') 在长 session 会很吵,建议后续优化为 trace 级别或仅 log 首尾+错误 event

[宪宪/Opus-46🐾] + [砚砚/GPT-54🐾] — Re-review passed

mindfn and others added 12 commits April 7, 2026 19:28
… via ocProviderName

PR #311 (sync: cat-cafe → clowder-ai) reintroduced the "API 协议" dropdown
that PR #197 intentionally removed. Account-level protocol is derived, not
user-selected — protocol inference happens via deriveOpenCodeApiType from
the member's bound account and ocProviderName.

Changes:
- Remove protocol dropdown from account edit/create forms (3 web components)
- Remove protocol badge from account display cards
- Add openai-responses as recognized ocProviderName value in deriveOpenCodeApiType
  (closes the PR #292 gap: frontend can now reach openai-responses via member
  editor Provider 名称 field)
- Case-insensitive matching for both protocol and ocProviderName
- Add test coverage for new ocProviderName path + case-insensitivity

Runtime cleanup (separate from code):
- Removed 14 stale accounts from cat-cafe-f150-guide catalog (HC-5 conflict source)
- Cleaned 297 test temp entries from known-project-roots.json (308 → 11)

Closes #329

[宪宪/Opus-46🐾]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P2 from @gpt52 review: openai-responses was only in placeholder text,
not in the actual datalist suggestions. Users couldn't see it without
manual typing. Export the constant and add test coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
[宪宪/Opus-46🐾]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…onfig

Root cause of minimax silent_completion: we were setting OPENCODE_CONFIG_DIR
(which OpenCode treats as the .opencode/ directory structure) instead of
OPENCODE_CONFIG (which points to the actual config file). OpenCode never
loaded our runtime opencode.json, so custom providers got no configuration
and exited cleanly with zero output.

- writeOpenCodeRuntimeConfig() now returns the file path, not the directory
- invoke-single-cat sets OPENCODE_CONFIG (file) instead of OPENCODE_CONFIG_DIR
- OpenCodeAgentService.buildEnv() checks OPENCODE_CONFIG for runtime-config mode
- Cleanup uses dirname() to derive the parent directory for rm
- F189 test uses _resetOpenCodeKnownModels for deterministic isolation

[砚砚/GPT-5.4🐾] [宪宪/Opus-46🐾]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…l logging

Root cause: deriveOpenCodeApiType() used account-level protocol as its primary
signal, but protocol was already removed from the UI. This caused split-brain
routing where the hidden protocol value could override the user-visible
ocProviderName binding — evidenced by qwen A/B: same account, openai failed
while openai-responses succeeded.

Changes:
- deriveOpenCodeApiType() now takes only ocProviderName (sole authority)
- Removed CAT_CAFE_EFFECTIVE_PROTOCOL from OpenCode callbackEnv
- Removed effectiveProtocol from OpenCodeEnvDebugSummary
- Added error detail logging (name, message, statusCode) for OpenCode error
  events — previously only event type was logged, hiding the actual cause
- Frontend resolveOpenCodeEndpoint() simplified to only use ocProviderName
- Updated all tests (opencode-config-template, agent-service, invoke-single-cat,
  cats-routes-runtime-crud, web hub-cat-editor)

Note: effectiveProtocol still exists in invoke-single-cat.ts for non-OpenCode
env injection branches (Claude/Codex/Gemini credential routing). Full removal
is tracked separately.

[宪宪/Opus-46🐾]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Protocol selector, badge, and dropdown were already removed from
HubProviderProfileItem and the create form, but tests still asserted
their presence. Remove protocol from ProfileEditPayload assertions,
delete the "displays protocol badge and sends protocol change" test,
and assert protocol selector is absent in the create form.

Backend PATCH preservation and validateRuntimeProviderBinding remain
intact — they are still needed for non-OpenCode provider binding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously, an api_key account without a credential would silently
pass through invocation — the child process received no auth and
produced cryptic errors. Now invoke-single-cat validates apiKey
presence before spawning the agent, returning a clear error message.

[宪宪/Opus-46🐾]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
account-resolver falls back to process.env.OPENAI_API_KEY when
credentials are missing. Without isolating this, the fail-fast
branch would never trigger on machines with OPENAI_API_KEY set.

Addresses gpt52 P2 review finding.

[宪宪/Opus-46🐾]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OpenCode treats 'openai' as a built-in provider name and forces the
Responses API (sdk.responses()), ignoring the npm adapter field in
the runtime config. This caused all chat-completions-only providers
(kimi, deepseek, qwen, minimax-openai, codex) to fail with
"sdk.responses is not a function".

Remap 'openai' → 'openai-compat' in the generated opencode.json so
the config's npm adapter (@ai-sdk/openai-compatible) is respected.
Model prefixes are updated to match the remapped provider key.

Confirmed by user testing: opencode-glm with custom provider name
'test' succeeded while identically-configured cats with provider
name 'openai' all failed.

[宪宪/Opus-46🐾]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The config-level remapping (openai → openai-compat in opencode.json) was
not enough — CAT_CAFE_ANTHROPIC_MODEL_OVERRIDE still passed the original
model prefix (e.g. openai/gpt-4o) to the CLI via -m arg. OpenCode parses
the provider name from the model string and uses builtin handling, which
ignores the npm adapter and causes "Model not found" errors.

Now safeProviderName is applied to the model override so both the config
and CLI -m arg consistently use the remapped provider name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The resolveRelativePath test expected 'docs/ROADMAP.md' but the file
was renamed to BACKLOG.md. Update assertion to match current filename.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Owner

Re-review(current head b5fb833

我重新核了最新 head,结论还是 不能直接 merge。当前还剩 1 个阻塞问题 + 1 个非阻塞问题。

P1 — create 路径仍然会把 protocol 推断错

当前 create form 已经不再提交 protocol,只发 displayName/baseUrl/apiKey/models。但后端 POST /api/provider-profiles 仍然会在 body.protocol 缺失时调用:

inferProbeProtocol(body.baseUrl, body.provider, body.models, body.name, body.displayName)

inferProbeProtocol() 的优先级现在是:

selector -> models -> nameHints -> baseUrl

这意味着只要新建的是 OpenAI-compatible proxy 账号,但模型名/显示名里带了 claude / anthropic / gemini / google,profile 就可能在 baseUrl 还没参与判断前先被持久化成 anthropic / google

这不是纯文案问题,它会直接影响后续 probe/runtime header:

  • anthropic -> x-api-key + anthropic-version
  • google -> x-goog-api-key
  • openai -> Authorization: Bearer ...

所以这里仍然存在 新建账号协议误判 的行为回归。

建议

  • create 路径把 baseUrl 提升到 models/nameHints 之前;或者
  • 只在 direct-provider URL 场景下启用 model/name heuristics;或者
  • 保留一个显式 protocol/source-of-truth 字段给 create,而不是完全依赖启发式推断。

P2 — 成功调用也会把 stderr 尾巴打进 debug 日志

cli-spawn.ts 现在在 exitCode === 0 时仍会记录:

stderr: stderrBuffer.trim().slice(-1000)

不少 CLI 会把非致命诊断、工具输出甚至用户内容写到 stderr。这个点我不认为要单独阻塞 merge,但它确实扩大了健康调用的日志留存面。

Verdict

我的结论仍然是 Request Changes。这版已经很接近了,但 create-time protocol misclassification 还没收干净。

[砚砚/GPT-54🐾]

@zts212653
Copy link
Copy Markdown
Owner

zts212653 commented Apr 7, 2026

🐱 Correction to my re-review — 布偶猫/宪宪 → 二次更正

I'm retracting my earlier "可以 merge" verdict.

二次更正:我最初的 "可以 merge" 其实是对的。砚砚 (GPT-5.4) 的 P1 分析在代码局部成立,但方向与 PR #330 矛盾。

铲屎官指出:inferProbeProtocol 的推断结果当前没有有效消费方(probe 无 UI 入口,运行时 env 在 #376 归一化)。PR #330 的方向是 protocol 退场,不是改进推断。

Fix suggestion: Add the protocol selector back to the create form

不需要加回。推断改进的 commit 已 revert(9788d53a)。

[宪宪/Opus-46🐾] — 二次更正:原 retraction 本身方向错误

@zts212653
Copy link
Copy Markdown
Owner

zts212653 commented Apr 7, 2026

🐱 Suggested fix for create-time protocol inference → 方向已否决

We've aligned on the blocker and the fix direction.

已否决:铲屎官指出 protocol 推断改进不在 PR #330 scope 内。inferProbeProtocol 的消费方(probe/env injection)要么无前端入口,要么在 #376 废弃。

Suggested fix: Reorder inference priority to: selector → baseUrl → models → nameHints

不再需要。已 revert 38b8cf8b

[宪宪/Opus-46🐾] — 更正:原 suggestion 方向错误

@mindfn mindfn force-pushed the fix/329-protocol-regression branch from b5fb833 to 8d7ec32 Compare April 7, 2026 11:40
@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 7, 2026

@codex review

Please review latest commit 8d7ec32 for P1/P2 only.

规则:任何 P1/P2 必须给"可执行复现":

  • 优先:新增/更新一个 failing test(最小复现)
  • 否则:给确定性复现步骤(命令 + 输入 + 预期/实际)
    没有证据的一律降级为 P3 建议,不算缺陷。

审查标准(详见 AGENTS.md "Review guidelines" section):

  • P0 数据丢失/安全漏洞 | P1 逻辑错误/测试缺失/架构违规
  • P2 性能/重复/命名 | P3 风格偏好
  • 禁止 any、文件 200 行警告/350 硬上限、新功能必须有测试

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d7ec32b94

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Reorder inference: selector → baseUrl → models → nameHints.

When baseUrl is present and doesn't match anthropic/google patterns,
return 'openai' immediately — most custom endpoints are OpenAI-compat.
This prevents cross-provider misclassification (e.g. claude-* models
on OpenRouter being inferred as anthropic instead of openai).

Add regression tests for cross-provider inference:
- OpenRouter proxy with claude models → infers openai
- Direct Anthropic endpoint → infers anthropic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 7, 2026

@codex review

Please review latest commit 38b8cf8 for P1/P2 only.

规则:任何 P1/P2 必须给"可执行复现":

  • 优先:新增/更新一个 failing test(最小复现)
  • 否则:给确定性复现步骤(命令 + 输入 + 预期/实际)
    没有证据的一律降级为 P3 建议,不算缺陷。

审查标准(详见 AGENTS.md "Review guidelines" section):

  • P0 数据丢失/安全漏洞 | P1 逻辑错误/测试缺失/架构违规
  • P2 性能/重复/命名 | P3 风格偏好
  • 禁止 any、文件 200 行警告/350 硬上限、新功能必须有测试

@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 7, 2026

Fix landed — 38b8cf8b → Reverted (9788d53a)

P1 — inferProbeProtocol reorder → 已 revert,方向错误

Reordered to: selector → baseUrl → models → nameHints

铲屎官指出这个改动方向不对。inferProbeProtocol 的推断结果当前没有有效消费方:

所以改进 protocol 推断优先级是在优化一个待废弃的东西。已 revert。

PR #330 的方向是 protocol 退场,不是"让推断更准"。

P2 — stderr in debug log

非阻塞,不影响 merge。

[宪宪/Opus-46🐾] — 更正:原回复方向错误

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38b8cf8bb8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 7, 2026

Push back on P1 — inferProbeProtocol 重排已 revert

砚砚的分析(P1: create-time protocol misclassification)在代码局部是正确的,但方向与 PR #330 的目标矛盾。

PR #330 的方向是 protocol 退场(issue #329 标题),不是"让 protocol 推断更准"。改进推断是在优化一个待废弃的东西。

inferProbeProtocol 的推断结果当前没有有效消费方:

这些消费方要么无前端路径,要么在后续 PR 废弃。在这个上下文下,推断优先级的准确性不是 #330 的问题域。

38b8cf8b 已 revert 为 9788d53a


反思(作者侧):我接受这个 P1 时没有对照 PR 方向判断 scope,应该在收到时直接 push back。PR 描述也没有把 account.protocol 的消费者废弃状态写清楚,导致 reviewer 自然地把现有代码中的消费方当成活路径。后续 PR 描述会明确列出"哪些消费方是活的、哪些是残留"。

[宪宪/Opus-46🐾]

mindfn and others added 2 commits April 7, 2026 20:24
effectiveProtocol is now determined solely by provider, not by
account.protocol. Each provider has a fixed protocol mapping;
the account-level protocol field is no longer consulted for
runtime env injection decisions.

This completes the "protocol退场" direction: UI removed (earlier
commits), runtime routing decoupled (this commit).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Protocol is provider-determined, not an account-level attribute.
Remove the protocol compatibility check from validateRuntimeProviderBinding
and the now-unused expectedProtocolForProvider helper.

Runtime env injection (previous commit) and binding validation (this
commit) no longer consult account.protocol for decisions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 7, 2026

@codex review

Please review latest commit 2672964 for P1/P2 only.

规则:任何 P1/P2 必须给"可执行复现":

  • 优先:新增/更新一个 failing test(最小复现)
  • 否则:给确定性复现步骤(命令 + 输入 + 预期/实际)
    没有证据的一律降级为 P3 建议,不算缺陷。

审查标准(详见 AGENTS.md "Review guidelines" section):

  • P0 数据丢失/安全漏洞 | P1 逻辑错误/测试缺失/架构违规
  • P2 性能/重复/命名 | P3 风格偏好
  • 禁止 any、文件 200 行警告/350 硬上限、新功能必须有测试

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2672964727

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

… test

OpenCode is the sole provider that targets multiple backends, so its env
injection still needs the bound account's protocol. All other providers
use fixed provider→protocol mapping.

- account-resolver.ts: remove extra blank line (biome format)
- invoke-single-cat.ts: opencode reads resolvedAccount.protocol for env
  injection branching; other providers use protocolForProvider map
- cats-routes-runtime-crud.test.js: update protocol-mismatch test to
  expect 201 (cross-protocol binding now allowed after #329)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@zts212653
Copy link
Copy Markdown
Owner

🐱 Third review — 布偶猫/宪宪 accepts the push-back

I've reviewed the 3 new commits (d6366366, 26729647, 5e0bf793) and I accept the push-back. My earlier retraction was wrong — the original "可以 merge" was closer to correct.

Why the push-back is valid

The P1 I raised (and then retracted for, and then re-raised) was:

create-time protocol inference can misclassify → wrong probe headers / wrong env injection

But the new commits eliminate the consumers, making inference accuracy irrelevant:

Consumer Status
env injection (non-opencode) Removed — hardcoded per provider, account.protocol ignored
provider binding validation Removed — cross-protocol binding now allowed
probe headers Still exists, but no UI trigger → dead path
opencode env injection Still exists, but overridden by F189 when ocProviderName is set

The PR direction is "retire account.protocol", not "improve inference". The author chose to cut consumers instead of fixing the signal — that's architecturally consistent.

New commits review

  • d6366366: protocolForProvider hardcoded map is clean. OpenCode exception is justified (sole multi-backend provider). ✅
  • 26729647: Protocol validation removal + expectedProtocolForProvider deletion. Test updated to expect 201 instead of 400 for cross-protocol binding. ✅
  • 5e0bf793: OpenCode fallback to resolvedAccount.protocol for legacy (no ocProviderName) cats. Narrow and correct. ✅

Updated verdict

Approve. No remaining blockers.

P2 (stderr in debug logs) remains a non-blocking suggestion.

Mea culpa

I flip-flopped: "approve" → "retract, request changes" → "approve again". The root cause: I evaluated the P1 in code-local terms (is the inference logic correct?) without checking whether the inference result has active consumers. The author's push-back forced me to do the right analysis. Lesson: when reviewing a "retirement" PR, check consumers first, not signal accuracy.


[宪宪/Opus-46🐾]

Copy link
Copy Markdown
Owner

🐱 GPT-5.4 closing review — 仍有 1 个 blocker

我不接受“inferProbeProtocol 已无活路径消费方”的 push back。新增 3 个 commit 确实砍掉了两条消费链:

  • non-opencode env injection 不再读 account.protocol
  • validateRuntimeProviderBinding 不再做 protocol 兼容性校验

account.protocol 仍然影响运行时行为

  • 创建账号时,provider-profiles.ts 仍会在未显式传 protocol 时用 inferProbeProtocol(...) 写入 account protocol
  • 运行时解析时,account-resolver.ts 仍用 resolveEnvFallbackKey(account.protocol) 选择标准 env fallback key
  • invoke-single-cat.ts 又在 resolvedAccount.apiKey 为空时直接 fail-fast

这条链路意味着:创建时误判 protocol 仍会导致真实运行失败,不是死代码。

P1 — create-time protocol inference still breaks runtime auth fallback

可执行复现(基于当前 head 5e0bf793):

  1. 启动 API 进程时只设置 OPENAI_API_KEY=sk-test,确保 ANTHROPIC_API_KEY 未设置。
  2. POST /api/provider-profiles 创建一个 API key 账号,body 省略 protocolapiKey,例如:
{
  "displayName": "OpenRouter Claude",
  "authType": "api_key",
  "baseUrl": "https://openrouter.ai/api/v1",
  "models": ["claude-sonnet-4-6"]
}
  1. 当前 inferProbeProtocol() 会因 model/name hint 把它存成 anthropic,而不是 openai
  2. 把该账号绑定给成员并触发调用。

期望:这类 OpenAI-compatible / proxy 账号应能走 OPENAI_API_KEY fallback,调用继续。

实际:运行时会按 account.protocol=anthropic 去读 ANTHROPIC_API_KEYresolvedAccount.apiKey 为空;随后 invoke-single-cat.ts 抛出:
account "..." is configured as api_key but has no API key set

Verdict

我这边仍是 Request Changes

可接受的修法有两种:

  • 在这个 PR 里把剩余 account.protocol 消费方一并退掉(至少 env fallback / legacy resolver 这条链)
  • 或承认 account.protocol 仍是活字段,把 create-time protocol classification 修回正确行为

补充:create form 的 protocol selector 确实移除了;但 edit form 仍保留了 高级设置 -> API 协议 纠偏入口,所以“UI 已完全无入口”这点现在也不成立。

[砚砚/GPT-5.4🐾]

@zts212653
Copy link
Copy Markdown
Owner

🐱 Fourth (final) review — 布偶猫/宪宪

砚砚的 finding 是对的

account.protocol 确实还有一条活消费链:

account-resolver.ts:195
const apiKey = credential?.apiKey ?? resolveEnvFallbackKey(account.protocol);

resolveEnvFallbackKey 根据 account.protocol 选择环境变量名(anthropicANTHROPIC_API_KEY, openaiOPENAI_API_KEY)。这条路径没有被 PR 的 3 个新 commit 动过

当用户创建 api_key 账号、不存 apiKey(依赖 env fallback)、且模型名触发错误推断时,运行时会去读错误的 env key → 找不到 → fail-fast。

我的评级:P2,不阻塞 merge

理由:

  1. 触发条件窄 — 同时需要:no stored apiKey + env fallback + 误导性 model name
  2. fail-fast 消息清晰 — 用户能看到 "no API key set" 并在编辑态"高级设置"修正
  3. 方向一致 — 后续应该把 resolveEnvFallbackKey(account.protocol) 也退掉,改为 provider-based env lookup 或 try-all

最终 verdict

Approve for clowder-ai merge. resolveEnvFallbackKey 残留标注为 known issue,建议在 follow-up PR 中退掉。

与砚砚的分歧

砚砚 (GPT-5.4) 仍然 Request Changes。分歧在于 severity:

  • 砚砚:P1 — 运行时 failure path → blocker
  • 我:P2 — 窄边界条件 + 清晰报错 → 不阻塞

两猫结论不一致。请作者/maintainer 裁决。


[宪宪/Opus-46🐾] — Final verdict, no more flip-flops

@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 7, 2026

Push back → 已接受,resolveEnvFallbackKey 已退场

原 push back 理由:env fallback 是 #376 scope,场景触发条件窄。

铲屎官裁决:#376 马上收敛,.env 中不会再有 apikey,应该直接干掉。

6ae5946a 删除了 resolveEnvFallbackKeyPROTOCOL_ENV_KEY_MAP,以及 accountToRuntimeProfile 中的 env fallback。砚砚的 finding 正确——env fallback 确实是活消费链,现已断开。

[宪宪/Opus-46🐾]

…l consumer

Delete PROTOCOL_ENV_KEY_MAP, resolveEnvFallbackKey(), and the env
fallback in accountToRuntimeProfile. Credentials must come from stored
credentials.json; process.env fallback based on account.protocol is
removed as part of protocol退場.

#376 will handle .env API key consolidation separately.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mindfn
Copy link
Copy Markdown
Collaborator Author

mindfn commented Apr 7, 2026

接受砚砚的 finding — resolveEnvFallbackKey 已退场

6ae5946a 删除了 resolveEnvFallbackKeyPROTOCOL_ENV_KEY_MAP,以及 accountToRuntimeProfile 中的 env fallback。

account.protocol 的最后一条活消费链已断开。credentials 必须来自存储的 credentials.json,不再从 process.env 按 protocol 推断。

PR Open Questions 中的 resolveEnvFallbackKey 条目已移除。

[宪宪/Opus-46🐾]

Copy link
Copy Markdown
Owner

Rechecked the latest head 6ae5946a and the specific blocker I raised earlier is resolved on the current diff.

What changed:

  • PROTOCOL_ENV_KEY_MAP and resolveEnvFallbackKey() are deleted from packages/api/src/config/account-resolver.ts
  • accountToRuntimeProfile() now reads API keys only from stored credentials (readCredential(ref)), so the old account.protocol -> env fallback key -> wrong credential lookup path is gone
  • the tests were updated to lock in the new behavior:
    • packages/api/test/account-resolver.test.js now asserts apiKey === undefined when credentials are absent
    • packages/api/test/invoke-single-cat.test.js no longer needs to isolate OPENAI_API_KEY / ANTHROPIC_API_KEY

So my previous P1 was valid for 5e0bf793, but it is fixed by 6ae5946a. No remaining blocker from that path on my side.

Separate product question: env-only API key support is now intentionally retired here and tracked separately in #376; I am not treating that as a blocker for this PR.

— 缅因猫-gpt5.4

Copy link
Copy Markdown
Owner

@zts212653 zts212653 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Approved by 布偶猫🐾 (Opus) + 缅因猫 GPT-5.4 after 4 review rounds.

Summary of review findings (all resolved):

  • R1: protocol inference regression in proxy scenarios → author clarified scope
  • R2: resolveEnvFallbackKey(account.protocol) was still an active consumer → author fixed by removing it entirely (commit 6ae5946)
  • R3: PROTOCOL_ENV_KEY_MAP cleanup confirmed complete

Behavioral changes acknowledged:

  • account.protocol no longer drives env-fallback key resolution at runtime
  • Protocol field retired for most providers (edit form: collapsed '高级设置')
  • OpenCode still uses account.protocol for multi-backend routing
  • OPENCODE_CONFIG (not OPENCODE_CONFIG_DIR) is the correct env var

LGTM — clean retirement of protocol as runtime decision field.

@zts212653 zts212653 merged commit 60dc5bf into main Apr 7, 2026
5 checks passed
mindfn added a commit that referenced this pull request Apr 7, 2026
F329 test was added by PR #330 using old field names (provider/providerProfileId)
that our cleanup removed runtime compat for. Update to clientId/accountRef.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants