Skip to content

feat(providers): resolve LinkCode Gateway as a Codex-native endpoint - #477

Merged
lucas77778 merged 5 commits into
masterfrom
yihong/code-603
Aug 27, 2026
Merged

feat(providers): resolve LinkCode Gateway as a Codex-native endpoint#477
lucas77778 merged 5 commits into
masterfrom
yihong/code-603

Conversation

@xiaoland

@xiaoland xiaoland commented Aug 24, 2026

Copy link
Copy Markdown
Member

Client half of CODE-603. The server half is arcboxlabs/linkcodehq#50.

LinkCode Cloud now serves POST /v1/responses at the same base URL, so the LinkCode Gateway
service gains an openai-responses variant and Codex resolves it as native instead of
reporting an incompatible protocol.

Codex ≥ 0.122 rejects wire_api = "chat" outright, so this variant is the only thing standing
between Codex and the Gateway; nothing else in the client hard-codes chat-only for this service,
since the settings incompatibility notice derives from resolveBinding.

Verification

see https://github.com/arcboxlabs/linkcodehq/pull/50#issuecomment-5433835876

Note

The server serves Responses for 46 of the 75 catalog models (read CODE-626 for more), while /v1/models still advertises all of them, so the model picker can offer a model that returns 404 model_not_found for Codex.
That is tracked in CODE-620 and needs a client-side decision as well as the server field.

LinkCode Cloud now serves POST /v1/responses at the same base URL, so Codex binds
natively instead of reporting an incompatible protocol.
Copilot AI lite review requested due to automatic review settings August 24, 2026 14:28
@linear-code

linear-code Bot commented Aug 24, 2026

Copy link
Copy Markdown

CODE-603

This comment was marked as outdated.

@greptile-apps

greptile-apps Bot commented Aug 24, 2026

Copy link
Copy Markdown

Greptile Summary

The PR makes LinkCode Gateway a Codex-native OpenAI Responses endpoint and adds protocol-aware model discovery and filtering.

  • Adds a protocol-specific Gateway model-list source.
  • Persists each probed model’s supported protocols in the shared account schema.
  • Filters account models against the protocol selected for the active agent binding.
  • Updates the model-probe request path and wire protocol version.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up-review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
packages/foundation/providers/src/catalog.ts Adds per-variant model-list overrides and declares the Gateway’s OpenAI Responses endpoint and filtered catalog URL.
packages/foundation/providers/src/enabled-models.ts Resolves each enabled account once and narrows explicitly tagged models to the agent’s selected protocol.
packages/foundation/schema/src/model/account.ts Extends account models with optional protocol-availability metadata while retaining compatibility with older records.
packages/foundation/schema/src/wire/message.ts Advances the wire protocol version for the additive account-model schema change.
packages/host/engine/src/agent/model-probe.ts Probes distinct model-list URLs across service variants and merges model IDs with their supported protocols.
packages/host/engine/src/agent/request-handler.ts Passes the complete catalog service descriptor into protocol-aware model probing.
packages/host/engine/src/tests/engine-model-probe.test.ts Verifies shared-list protocol tagging and multi-list Gateway result merging.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Probe Gateway account] --> B[Fetch service-level model list]
  A --> C[Fetch Responses-specific model list]
  B --> D[Merge models and protocol tags]
  C --> D
  D --> E[Persist selected account models]
  E --> F[Resolve binding for agent]
  F --> G[Filter models by bound protocol]
  G --> H[Offer compatible models]
Loading

Reviews (4): Last reviewed commit: "fix(schema): bump wire version for model..." | Re-trigger Greptile

…n agent can't reach

Corresponds to CODE-603's client-side half: the gateway now serves a different
model roster per protocol (GET /v1/models?protocol=), so the client needs to learn
and act on that instead of showing every account's full catalog to every agent
regardless of which wire it actually binds on.

ServiceVariant gains an optional models override, so one service can serve a
different list per variant while every other catalog entry (which serves the same
ids on every variant) keeps using the service-level default unchanged. LinkCode
Gateway's openai-responses variant now points at the protocol-filtered list.
modelListSourceForProtocol resolves the effective list for one protocol; the
existing modelListSource (service-level, used only as an existence check today)
is untouched, so its test and the add-flow "can this be probed" gate are unaffected.

AccountModel gains optional protocols: AccountProtocol[]. Absence must mean "offer
it" — a pre-catalog bare key, a hand-typed custom account, or a model probed before
this field existed all carry no data, and a stored account must not go dark on
upgrade. enabledAccountModels narrows a picked model to the protocol the agent
actually binds that account with (reusing the ResolvedBinding already computed for
the enabled/unavailable check, rather than resolving twice), and treats an unknown
protocol on either side — the model's or the binding's — as "offer", not "hide".
config.probe-models now resolves the EndpointService and calls the new
probeServiceModels instead of a single service-level modelListSource lookup.
Most services still make exactly one request (no variant overrides an
otherwise-shared list), but a service like LinkCode Gateway — whose
openai-responses variant lists a strict subset of the plain list — makes one
request per distinct list and merges the results, tagging each returned id with
every protocol whose list actually named it.

A protocol the service has no variant for at all must not fall through to the
service-level list by accident of `??` — fixed before it shipped, caught by a new
test asserting the merge across two distinct lists rather than just the
single-list case every other service exercises.
Copilot AI review requested due to automatic review settings August 25, 2026 09:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings August 27, 2026 03:54
@xiaoland
xiaoland marked this pull request as ready for review August 27, 2026 03:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important

Two things to settle before merge: the wire version isn't bumped for the new AccountModel.protocols field, and the daemon's default-model fallback doesn't apply the new narrowing, so it can start codex on a model the picker just hid.

Reviewed changes — full review of all 9 files at da14af63, covering the catalog variant, the fan-out probe, and the picker narrowing.

  • LinkCode Gateway gains an openai-responses variant — same https://gateway.linkcode.ai/v1 base URL, so resolveBinding(gateway, 'codex') flips from unavailable / protocol-unsupported to native.
  • Per-variant model listsServiceVariant.models overrides the service-level list; the gateway's Responses variant points at /v1/models?protocol=openai-responses. New modelListSourceForProtocol reads override-then-default.
  • probeServiceModels replaces probeEndpointModels as the probe entry point — dedupes distinct list URLs across a service's variants, fans out with Promise.all, merges by model id, and tags each id with every protocol whose list returned it.
  • AccountModel.protocols — new optional field on the schema; absent means unknown, and only an explicit set narrows anything.
  • enabledAccountModels narrows per binding — picked models are filtered to those reachable on the protocol the agent actually binds the account with; enabledAccounts is refactored onto a shared resolvedAccounts helper with no behaviour change.

The backward-compatibility direction is right throughout: undefined on either side of modelReachable means "offer", so pre-catalog keys, custom accounts, and accounts probed before this field existed keep working unchanged. Both new tests genuinely fail without the change — the codex assertion would pick up anthropic/claude-sonnet-5, and the probe test pins exactly two relay paths. Scoped suite is green locally (5 files / 54 tests across providers, both engine model-probe suites, and the wire config contract).

ℹ️ A gateway deployed before the server half silently produces positively-wrong protocol tags

?protocol=openai-responses is a query parameter on the same /v1/models path the service-level list already uses. A gateway that predates linkcodehq#50 will not reject it — it will ignore it and return the full list, at which point both fan-out requests return identical data and every model gets tagged ['openai-chat', 'openai-responses']. Unlike absence, an explicit set is trusted by modelReachable, so codex would then be offered chat-only models with confidence. That degrades to exactly the CODE-620 state the PR body describes rather than to something worse, so this is a merge/deploy-ordering note rather than a code change: the client must not reach users ahead of the server.

Technical details
# Deploy ordering: client narrowing depends on a server that honours `?protocol=`

## Affected sites
- `packages/foundation/providers/src/catalog.ts:179` — the Responses variant list URL differs from the service-level one only by a query string, so an old server returns the same body for both.
- `packages/host/engine/src/agent/model-probe.ts:283` — merges protocols additively with no cross-list check that the Responses list is actually a strict subset.
- `packages/foundation/providers/src/enabled-models.ts:43-47``modelReachable` trusts an explicit set unconditionally.

## Required outcome
- Confirm `arcboxlabs/linkcodehq#50` is deployed to `gateway.linkcode.ai` before this ships, so the two lists genuinely differ.

## Open questions for the human
- Does the deployed gateway currently 4xx on unknown query parameters? If it does, the failure is loud instead of silent and this note is moot — but it also means the fan-out probe fails outright until the server lands (see the `Promise.all` comment).

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

Comment thread packages/foundation/schema/src/model/account.ts
Comment thread packages/foundation/providers/src/enabled-models.ts
Comment thread packages/host/engine/src/agent/model-probe.ts
Comment thread packages/host/engine/src/agent/model-probe.ts
Comment thread packages/foundation/providers/src/catalog.ts
Copilot AI review requested due to automatic review settings August 27, 2026 11:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ℹ️ The wire bump itself is exactly right. One note on the contract test that came with it — it passes with or without the field it is meant to guard.

Reviewed changes — the da14af631907e145 delta, which is two things: the wire version constant and one added contract-test case.

  • WIRE_PROTOCOL_VERSION bumped 78 → 79 — correct per Invariant 1, and MIN_COMPATIBLE_WIRE_VERSION correctly stays at 76 since AccountModel.protocols is purely additive. Nothing else in the repo hardcodes the version; every other site imports the constant, so the bump is self-contained.
  • Added a config.get.result case to the wire contract suite — asserts a config.get.result frame carrying models: [{ id, protocols }] parses.

The remaining threads from the da14af63 review are untouched by this commit and stay open — notably applyProviderDefaults at provider-config.ts:125, which is still the one blocking item.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

Comment thread packages/foundation/schema/tests/contract/wire/config.test.ts

@lucas77778 lucas77778 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@lucas77778
lucas77778 merged commit a79b917 into master Aug 27, 2026
13 checks passed
@lucas77778
lucas77778 deleted the yihong/code-603 branch August 27, 2026 11:58
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.

3 participants