Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
f87698c
feat(xai): enable Priority Processing on the API-key transport
olddonkey Aug 18, 2026
057f93e
docs(devlog): capture the xAI Fast pricing UI evidence
olddonkey Aug 18, 2026
6e89ffc
Merge commit '63bfd149dd04ffbe448f43004a17e371a514eb38' into codex/fa…
olddonkey Aug 19, 2026
1d7d817
fix(xai): address B2 pricing review
olddonkey Aug 19, 2026
d887a4f
fix(gui): translate estimated cost labels
olddonkey Aug 19, 2026
c13981b
docs(xai): clarify API key transport
olddonkey Aug 19, 2026
33e1c3e
docs(xai): separate OAuth gateway rows
olddonkey Aug 19, 2026
68d929a
Merge remote-tracking branch 'upstream/dev' into codex/fastwire-b2-xai
olddonkey Aug 20, 2026
d4023ae
docs(xai): separate the OAuth gateway row in the remaining locales
olddonkey Aug 21, 2026
6c74866
fix: enable call_id thought-signature replay for Claude Code
Hsia97 Aug 21, 2026
b31f3db
test: cover Claude Code thought-signature replay scope
Hsia97 Aug 21, 2026
df16e0a
fix(responses): lower apply_patch for upstreams that reject custom tools
olddonkey Aug 21, 2026
88ffe32
fix(responses): build the routed compaction body last
olddonkey Aug 21, 2026
2785aa2
test(responses): assert the terminal SSE marker on namespace replay
olddonkey Aug 21, 2026
398b7ad
test(responses): lower apply_patch on noncanonical forward destinations
olddonkey Aug 21, 2026
b12307b
Merge remote-tracking branch 'upstream/dev' into codex/fastwire-b2-xai
olddonkey Aug 21, 2026
e8c62a9
test(fastwire): expect xAI key-auth chat to forward Fast
olddonkey Aug 21, 2026
4430742
scripts: add Windows Codex desktop full-restart helper
lidge-jun Aug 21, 2026
6c33ea5
devlog: record provider verification and PR fallback for restart helper
lidge-jun Aug 21, 2026
c0cbe49
Merge pull request #2293 from lidge-jun/codex/windows-restart-helper
lidge-jun Aug 21, 2026
6d5f0cf
fix(codex): recover zero-byte coordinator remnants
Ingwannu Aug 21, 2026
4c7b3ce
fix(release): reject credential-bearing SSH remotes
Ingwannu Aug 21, 2026
71598fa
test(release): close SSH target log bypasses
Ingwannu Aug 21, 2026
1d70993
devlog: vision external-backend roadmap (160-190) under sidecar-selec…
lidge-jun Aug 21, 2026
7317dde
devlog: bug merge-train roadmap (260821) — triage, dependency analysi…
lidge-jun Aug 21, 2026
728ca1e
Merge pull request #2295 from lidge-jun/ingw/fix-zero-byte-coordinato…
lidge-jun Aug 21, 2026
030cc77
Merge remote-tracking branch 'upstream/dev' into codex/fastwire-b2-xai
olddonkey Aug 21, 2026
21aec54
feat(vision): routed describer backend — options, gates, namespaced i…
lidge-jun Aug 21, 2026
3161904
feat(vision): routed describe executor via loopback self-fetch (#2188…
lidge-jun Aug 21, 2026
3ff19c3
feat(vision): GUI/CLI routed surfaces + GET reports the routed descri…
lidge-jun Aug 21, 2026
a211e6d
devlog: record vision routed-backend live delivery evidence (190)
lidge-jun Aug 21, 2026
362377a
test(vision): pin routed GET verbatim reporting (live-found regression)
lidge-jun Aug 21, 2026
a228ed7
devlog: vision routed dropdown screenshot (PR evidence)
lidge-jun Aug 21, 2026
401c24f
Merge pull request #2306 from lidge-jun/codex/vision-routed-sidecar
lidge-jun Aug 21, 2026
c9c818d
fix(cursor): settle clean Connect terminal without HTTP EOF
Ingwannu Aug 21, 2026
72df5e0
fix(codex): bind Desktop reconnects to one pool account
Ingwannu Aug 21, 2026
0e5a434
fix(codex): align Desktop affinity preview
Ingwannu Aug 21, 2026
948fb5d
fix(service): restart existing installations without re-registering
Ingwannu Aug 21, 2026
2df92a2
fix(service): fail closed on unknown installation state
Ingwannu Aug 21, 2026
56bff34
test(cursor): harden clean terminal teardown
Ingwannu Aug 21, 2026
97bedc4
Merge remote-tracking branch 'upstream/dev' into codex/fastwire-b2-xai
olddonkey Aug 21, 2026
7616660
fix(cursor): preserve drained terminal on clean end
Ingwannu Aug 21, 2026
fcc3f5c
fix(cursor): keep mixed tool terminals fail-closed
Ingwannu Aug 21, 2026
8535f08
Merge pull request #2072 from olddonkey/codex/fastwire-b2-xai
Ingwannu Aug 21, 2026
64cd6e5
fix(xai): normalize Responses web search tools
goodwilliam0126 Aug 20, 2026
9399d1d
Merge remote-tracking branch 'origin/dev' into ingw/fix-cursor-clean-…
Ingwannu Aug 21, 2026
69907dd
Merge pull request #2312 from goodwilliam0126/fix/xai-responses-web-s…
Ingwannu Aug 21, 2026
a1dc169
Merge remote-tracking branch 'origin/dev' into ingw/fix-cursor-clean-…
Ingwannu Aug 21, 2026
e672b0f
Merge remote-tracking branch 'origin/dev' into ingw/fix-app-pool-affi…
Ingwannu Aug 21, 2026
7f00202
devlog: 2295 cycle — full-suite rerun green after gui deps fix (14175…
lidge-jun Aug 21, 2026
584a3e3
devlog: triage matrix — mark #2295 merged on the train
lidge-jun Aug 21, 2026
f745083
Merge remote-tracking branch origin/dev into merge train
lidge-jun Aug 21, 2026
ffa6d46
Merge pull request #2315 from lidge-jun/codex/merge-train-260821
lidge-jun Aug 21, 2026
aea77b8
devlog: 2294 cycle plan — live-head scope and gate sequence
lidge-jun Aug 21, 2026
441564c
Merge PR #2294 head (71598fa45) into merge train
lidge-jun Aug 21, 2026
2cdfba2
fix(release): reject credential-shaped scp-like hosts and colon-beari…
lidge-jun Aug 21, 2026
08bd086
devlog: 2294 security review round — blocker fixed, re-verdict pass
lidge-jun Aug 21, 2026
f52de33
devlog: triage matrix — mark #2294 hardened and merged on the train
lidge-jun Aug 21, 2026
3a3f556
Merge pull request #2319 from lidge-jun/codex/merge-train-260821
lidge-jun Aug 21, 2026
c142cc7
devlog: 2296 cycle plan — live-head scope, inherited-model reviewer
lidge-jun Aug 21, 2026
7abf112
Merge PR #2296 head (e672b0fd0) into merge train
lidge-jun Aug 21, 2026
698228e
fix(codex): derive subagent preview quota scope from the route model
lidge-jun Aug 21, 2026
d832221
devlog: 2296 security review round — major fixed, re-verdict pass
lidge-jun Aug 22, 2026
c16d5ff
devlog: triage matrix — mark #2296 hardened and merged on the train
lidge-jun Aug 22, 2026
ec3d032
Merge pull request #2324 from lidge-jun/codex/merge-train-260821
lidge-jun Aug 22, 2026
6e1202f
Merge PR #2289 head (2df92a270) into merge train
lidge-jun Aug 22, 2026
d846ad4
devlog: 2289 cycle plan — live-head scope after author rebase
lidge-jun Aug 22, 2026
174f03b
docs(lifecycle): sync Windows bare-service fail-closed caveat across …
lidge-jun Aug 22, 2026
7957756
devlog: 2289 review round — locale parity fixed, re-verdict pass
lidge-jun Aug 22, 2026
5bbca70
devlog: triage matrix — mark #2289 hardened and merged on the train
lidge-jun Aug 22, 2026
6e2a6b6
Merge pull request #2325 from lidge-jun/codex/merge-train-260821
lidge-jun Aug 22, 2026
3bbe4e4
devlog: 2270 cycle plan — PR-ref merge strategy for fork
lidge-jun Aug 22, 2026
668512a
Merge PR #2270 head (398b7ade4) into merge train
lidge-jun Aug 22, 2026
ec32a8d
test(responses): pin canonical forward custom-tool passthrough agains…
lidge-jun Aug 22, 2026
65c0fd3
devlog: 2270 review round — boundary pin added, re-verdict pass
lidge-jun Aug 22, 2026
c7f341a
devlog: triage matrix — mark #2270 hardened and merged on the train
lidge-jun Aug 22, 2026
7d1b2ca
Merge pull request #2327 from lidge-jun/codex/merge-train-260821
lidge-jun Aug 22, 2026
0fb80bd
devlog: 2281 cycle plan — merge-ref strategy with stacked normalization
lidge-jun Aug 22, 2026
471d4b6
Merge remote-tracking branch 'pr/2281' into codex/merge-train-260821
lidge-jun Aug 22, 2026
bc6d6b5
fix(responses): normalize Claude Code prompt_cache_key through anthro…
lidge-jun Aug 22, 2026
3b18d28
devlog: 2281 review rounds — both reviewers pass
lidge-jun Aug 22, 2026
c836ffb
devlog: triage matrix — mark #2281 hardened and merged on the train
lidge-jun Aug 22, 2026
34b04ad
Merge pull request #2328 from lidge-jun/codex/merge-train-260821
lidge-jun Aug 22, 2026
b08ea71
fix(cursor): classify bare 0-token resource_exhausted as context over…
lidge-jun Aug 22, 2026
fd06058
fix(cursor): close HTTP/2 after turnEnded so a held-open response can…
lidge-jun Aug 22, 2026
b513a91
fix(cursor): unknown exec replies with ExecClientThrow + streamClose …
lidge-jun Aug 22, 2026
a69d291
fix(cursor): stop native Auto from echoing [Tool Result] as chat (#2318)
jeongjin0 Aug 22, 2026
d5bed36
Merge pull request #2307 from Ingwannu/ingw/fix-cursor-clean-connect-end
lidge-jun Aug 22, 2026
d79b1b4
perf(cursor): add HTTP/2 session pool for discovery calls (#2332)
lidge-jun Aug 22, 2026
5255686
feat(cursor): add weighted credential router with cooldown failover (…
lidge-jun Aug 22, 2026
6b889c3
devlog: round-2 Cursor stabilization research and roadmap lock (docs-…
lidge-jun Aug 22, 2026
764ef32
Merge pull request #2336 from lidge-jun/codex/cursor-round2-docs
lidge-jun Aug 22, 2026
994e5ba
fix(cursor): fail silent and heartbeat-only streams at the transport …
lidge-jun Aug 22, 2026
94c1dc2
Merge pull request #2337 from lidge-jun/codex/cursor-stream-health-wa…
lidge-jun Aug 22, 2026
ce15bf9
fix(cursor): fail OAuth polling on terminal statuses and shut the dis…
lidge-jun Aug 22, 2026
77c15ff
Merge pull request #2338 from lidge-jun/codex/cursor-oauth-failfast-h…
lidge-jun Aug 22, 2026
d6b8f8b
devlog: round-3 live-probe evidence and lock (docs-only)
lidge-jun Aug 22, 2026
24a36bd
Merge pull request #2340 from lidge-jun/codex/cursor-round3-probe-docs
lidge-jun Aug 22, 2026
ab6a54e
fix(cursor): fold display aliases in textual pseudo tool-call markers…
lidge-jun Aug 22, 2026
896cb57
Merge pull request #2341 from lidge-jun/codex/cursor-2305-text-marker
lidge-jun Aug 22, 2026
f3a7cd4
fix(cursor): keep provably-small bare resource_exhausted on the 429 c…
lidge-jun Aug 22, 2026
8f3ac5f
Merge pull request #2342 from lidge-jun/codex/cursor-bare-re-size-prior
lidge-jun Aug 22, 2026
5eb5640
devlog: 290 post-landing status — 230/231 and 260 landed, final CI ga…
lidge-jun Aug 22, 2026
1af7a1e
Merge pull request #2343 from lidge-jun/codex/cursor-290-postlanding
lidge-jun Aug 22, 2026
7b9dd62
devlog: release-readiness unit (inventory, risk matrix, lock) + probe…
lidge-jun Aug 22, 2026
4cd23b2
Merge pull request #2345 from lidge-jun/codex/release-readiness-docs
lidge-jun Aug 22, 2026
831810c
feat(cursor): expose the Opus Fast families with live-verified effort…
lidge-jun Aug 22, 2026
a012a46
Merge pull request #2346 from lidge-jun/codex/cursor-opus-fast-catalog
lidge-jun Aug 22, 2026
ffba5d9
devlog: 310 executed — maxMode NOOP; discovered ~1MiB per-message cap…
lidge-jun Aug 22, 2026
67b5fa0
Merge pull request #2347 from lidge-jun/codex/cursor-310-results
lidge-jun Aug 22, 2026
4bfc451
devlog: WP4 consolidated audit findings — 0 P0/P1, 2 new P2s, full su…
lidge-jun Aug 22, 2026
2b4ddf3
Merge pull request #2348 from lidge-jun/codex/wp4-findings
lidge-jun Aug 22, 2026
b7d3b6e
devlog: GO verdict — dev promotion-ready at 2b4ddf3b0
lidge-jun Aug 22, 2026
ced9a85
Merge pull request #2349 from lidge-jun/codex/go-verdict
lidge-jun Aug 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ Qwen Cloud, SiliconFlow, and more. Full list: `ocx init` or the
ocx init # interactive setup (writes config, wires Codex, offers the shim)
ocx start [--port 10100] # start the proxy in the foreground
ocx stop # stop + restore native Codex
ocx service [install|start|stop|status|uninstall|remove] # background service
ocx service [install|repair|restart|start|stop|status|uninstall|remove] # background service
ocx codex-shim install # start the proxy on demand whenever `codex` launches
ocx health [--json] # check immediate proxy liveness
ocx ready [--json] [--wait [--timeout <seconds>]] # check post-sync readiness
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions devlog/_plan/260818_fastwire_b2_xai/evidence/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# FastWire B2 (xAI) — UI evidence

`010_logs_priority_lower_bound.png` — Logs & Debug, three seeded `xai/grok-4.6` rows
that exercise every branch of the new pricing path:

| Row | Situation | Cost cell |
| --- | --- | --- |
| `req-standard` | no Fast requested | `~$0.0300` |
| `req-priority` | response-confirmed priority, prompt under the long-context threshold | `~$0.0600` — exactly the documented 2x premium over the row above |
| `req-longctx-priority` | response-confirmed priority, prompt at or above 200k | `≥$0.8760` — the published long-context rate, marked a lower bound because xAI publishes no combined price |

The `≥` prefix is the visible change: a cost that is a known floor rather than an
estimate now says so instead of rendering as `~$`. The detail drawer explains why
via the `priority_lower_bound` estimate reason.

Captured against a local proxy with a seeded `usage.jsonl`; no live xAI request was
billed to produce it.
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# 160 — Vision external-backend research (xai Grok / Antigravity Gemini describers)

Continuation of #2188. Web-search shipped four external backends (L6-L9, docs
060-090); the vision sidecar still dispatches only openai-forward and
anthropic-OAuth. GUI evidence: the vision dropdown lists only Codex/Claude
rows while the web-search dropdown already lists Grok/Gemini.

## Current vision dispatch inventory

- Types: `OcxVisionSidecarConfig.backend?: "openai" | "anthropic"` (src/types.ts).
- Union: `VisionSidecarBackend` (src/vision/eligibility.ts:30) — 2 arms.
- Candidate mapping: `visionBackendForCandidate` (eligibility.ts:150-165) —
native/openai → openai; anthropic only via the resolved OAuth provider name.
- Options: `visionEligibleModelOptions` (eligibility.ts:201+) iterates
`["openai","anthropic"] as const` and injects `BASELINE_VISION_MODELS`.
- Enabled backends: `enabledVisionBackends`
(src/server/management/vision-sidecar-options.ts:31-43); empty-auth fallback
returns both universal sides.
- Write gate: `visionDescriberIsProvablyBlind` (vision-sidecar-options.ts:94+)
probes ONLY the openai/anthropic vendor tables.
- PUT validation: config-routes.ts:594-596 rejects backends outside the two
literals; hint fall-through at :623; claude-code override near :738-740.
- Runtime plan: `planVisionSidecar` (src/vision/index.ts) — anthropic arm and
openai-forward arm only. `resolveVisionBackend`: explicit > anthropic-if-auth
> openai.
- GUI: `SidecarBackend = "openai" | "anthropic"` (gui/src/pages/
dashboard-shared.ts:62, claude-manual-env.ts:8). NOTE: this type is shared
with WebSearchModelOption and is ALREADY stale — the server emits
xai/gemini/exa web rows today.

## Wire research (from shipped web-search executors, probe-verified 2026-08-20/21)

### xai describe wire

Mirror src/web-search/xai-executor.ts: POST `https://api.x.ai/v1/responses`
(origin pinned; provider baseUrl honored only on same origin), stored OAuth
bearer via `getValidAccessToken`, `redirect: "manual"`. Body for describe:

```json
{
"model": "<settings.model>",
"instructions": "<describe instruction>",
"input": [{ "role": "user", "content": [
{ "type": "input_text", "text": "<context>" },
{ "type": "input_image", "image_url": "<data: or https: url>" }
]}],
"reasoning": { "effort": "<low|medium|high>" },
"stream": true
}
```

SSE reduction: reuse the `response.output_text.delta` / `.done` handling
shape from parseXaiResponsesSSE, without the citation/source machinery.
Grok Responses accepts `input_image` with data URLs (same shape the OpenAI
forward describer already posts — describe.ts builds input_image parts).

### Gemini (Antigravity CCA) describe wire

Mirror src/web-search/gemini-executor.ts: POST
`{registry base}/v1internal:generateContent`, `ANTIGRAVITY_REQUEST_UA`,
token + projectId via `getValidAccessTokenSnapshot`, envelope:

```json
{
"model": "<wireModelId from resolveAntigravityEffortWireModel>",
"userAgent": "antigravity", "requestType": "agent",
"project": "<projectId>", "requestId": "agent-<uuid>",
"request": {
"systemInstruction": { "role": "user", "parts": [{ "text": "<describe instruction>" }] },
"contents": [{ "role": "user", "parts": [
{ "text": "<context>" },
{ "inlineData": { "mimeType": "<mime>", "data": "<base64>" } }
]}]
}
}
```

inlineData shape matches src/adapters/google.ts:972/:1233. Response mapping:
`candidates[0].content.parts[].text` join (mapCcaGroundedResponse shape,
minus grounding). https: image URLs cannot be inlined without proxy-side
fetch — REJECTED for gemini describe (data: URLs only, documented delta,
same stance as anthropic-describe's stricter base64 rule).

## Metadata facts

- xai vendor table: bare grok-2/grok-3/grok-4 are `text`-only; grok-4.x
fast/4.3/4.5/4.6 and grok-2-vision are `text,image`.
- No bare model id collides across the four vendor tables (openai 48,
anthropic 26, xai 32, google 43; collision scan 2026-08-21: zero) — the
"vendor tables never disagree" premise of visionDescriberIsProvablyBlind
survives widening to four families.

## Audit deltas folded into this unit (sol-medium audit, 2026-08-21)

- **Blocker A**: `BASELINE_VISION_MODELS` is a TOTAL
`Record<VisionSidecarBackend, string>`; widening the union without a
decision breaks typecheck. Decision → doc 170: baselines become
descriptor-owned (only openai/anthropic carry one).
- **Blocker B**: `visionDescriberIsProvablyBlind` collapses non-anthropic
hints to openai and probes two families; a bare grok id absent from
candidates would slip the gate. Decision → doc 170: probe all four vendor
families.
- Empty-auth fallback stays `["openai","anthropic"]` — never offer
xai/gemini unauthenticated.
- GUI shared `SidecarBackend` must split (web-search has exa; vision does
not).
- New executors: `sidecarEnter("vision")` (NOT "web-search"),
`signalWithTimeout` + `cancelBodyOnAbort`, `redactSecretString` on all
error paths, timeout-bounds.ts as single authority.

Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# 170 — Backend union: "routed" describer (wp2, REVISED)

Depends on: 160. REVISION 2026-08-22: user directive — vision does not need
per-backend executors. Any picker-visible model with image input can describe;
the proxy's own router already speaks every provider wire. The earlier
xai/gemini backend literals were implemented but never released; this revision
replaces them before any push.

## Design

- `VisionSidecarBackend = "openai" | "anthropic" | "routed"`.
- "openai"/"anthropic" arms unchanged (forward Responses / OAuth Messages) —
they carry auth semantics loopback routing cannot replicate (forwarded
headers, OAuth beta fences), and their defaults must not drift.
- "routed": the describer is ANY routed model, dispatched through the proxy's
own /v1/chat/completions on loopback (pattern: src/claude/gateway-cache.ts
self-fetch). One executor, every provider.

## Filter (#2188 rules, unchanged shape)

1. Picker-visible ∪ auth slots (pickerVisibleSidecarCandidates).
2. − provably text-only (modelAcceptsImageInput === false drops the row).

visionBackendForCandidate: native/openai → openai; resolved-OAuth anthropic
row → anthropic; ANY OTHER provider row → "routed". Routed option values are
NAMESPACED ("provider/model") so routeModel is unambiguous; legacy sides keep
bare ids (GUI/current-value compatibility).

## Gate

visionDescriberIsProvablyBlind keeps the four-family probe widening AND
learns namespaced ids: split on first "/", probe that provider's config row +
metadata family. Bare ids keep the existing all-family probe.

## Runtime

- planVisionSidecar routed arm requires: cfg.backend === "routed", explicit
cfg.model, and plan-time modelAcceptsImageInput !== false for the target.
- Recursion safety: the loopback request re-enters the vision planner only if
the routed model is provably text-only; the plan-time check excludes exactly
that set, so describe recursion is structurally impossible.
- resolveVisionBackend: explicit honored; unset default order UNCHANGED.

## Files (wp2 scope, revised)

- src/vision/eligibility.ts: union, visionBackendForCandidate routed arm,
namespaced option values, BASELINE narrow-key record (kept from r1).
- src/vision/backends.ts (r1 descriptor table): SIMPLIFIED — descriptors for
openai/anthropic/routed; xai/gemini entries dropped.
- vision-sidecar-options.ts: enabledVisionBackends offers "routed" whenever
any routed row exists; gate learns namespaced ids.
- config-routes.ts + agent-settings-routes.ts: literal sets accept "routed"
(xai/gemini literals removed).
- types: backend unions.
- tests: vision-backend-union.test.ts rewritten for routed.


## Audit round 2 amendments (2026-08-22, sol-medium)

- **Recursion fence is a MECHANISM, not a predicate claim.** The loopback
describe request carries a terminal marker header
`x-opencodex-vision-describe: 1`. The Responses plan site treats a marked
request as terminal: images are STRIPPED, never described (depth cap 1).
This holds under predicate drift (modelInputModalities is invisible to a
row-less plan-time target) and combo re-resolution (router.ts:625-631 can
land a different sibling). Belt-and-braces: the routed arm also requires
`!isModelTextOnly(resolvedRoute.provider, resolvedRoute.modelId)` at plan
time — the exact re-entry predicate on the resolved route.
- **PUT-gate coherence:** a namespaced model with backend openai/anthropic is
REJECTED (forward executor POSTs the string verbatim — web-search F1
selector/slug failure); backend "routed" REQUIRES a namespaced id.
- **GUI inference:** `value.includes("/") → "routed"` in
visionSidecarBackendForModel's fallback; persisted backend keeps traveling
as currentBackend.
- Known limitation (recorded, not fixed here): a non-loopback-only bindHost
where 127.0.0.1 does not answer — same latent limitation gateway-cache has.
- handleNativeChatCompletions fast path has no vision handling; the marked
describe request must not regress it (marker check lives at the Responses
plan site the bridge replays into).

Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# 180 — Routed describe executor + dispatch (wp3, REVISED)

Depends on: 170 (revised).

## src/vision/routed-describe.ts (new)

Loopback POST http://127.0.0.1:{config.port}/v1/chat/completions:

```json
{ "model": "<namespaced routed id>", "stream": false,
"messages": [
{ "role": "system", "content": "<describe instruction>" },
{ "role": "user", "content": [
{ "type": "text", "text": "<context>" },
{ "type": "image_url", "image_url": { "url": "<data:/https:>" } }
]}]}
```

- Auth: none on loopback binds (resolveApiAuth admits loopback without a
token); when OPENCODEX_API_AUTH_TOKEN is set, send it as Authorization
bearer (auth-cors.ts:399-400 accepts bearer on /v1/chat/completions).
- signalWithTimeout(settings.timeoutMs) + cancelBodyOnAbort;
sidecarEnter("vision"); redactSecretString on error paths; response text
from choices[0].message.content; DESC clamp caller-side (existing).
- validateImageUrl reused (data: mime allowlist + 20MB, https passthrough).
- The chat inbound translates image_url → input_image and every adapter
compiles its own wire (anthropic blocks, CCA inlineData, xai Responses),
so provider coverage is the router's, not this file's.

## planVisionSidecar routed arm

VisionPlan gains { backend: "routed", routedModel: string }. Arm requires
explicit model + plan-time modelAcceptsImageInput !== false (recursion
fence). executeDescription routed arm calls describeImageRouted.

## Tests

vision-routed.test.ts: wire shape against a mock loopback server; recursion
fence (text-only target never plans routed); timeout/error taxonomy;
redaction. E2E: routed describer via a second mock provider.


## Audit round 2 amendments (2026-08-22)

- **Admission ladder (blocker 2):** token =
configuredApiAuthToken() || loadServiceTokenFromFile(env) || first
config.apiKeys entry; sent as `x-opencodex-api-key` (never Authorization —
gateway-cache.ts:77-86 rule); omitted entirely on loopback binds where
isApiAuthRequired is false.
- **Terminal marker:** executor sets `x-opencodex-vision-describe: 1`; the
core.ts plan site checks it and strips images instead of planning vision.
- Executor also passes stream:false and reads choices[0].message.content;
non-2xx → {error} with redacted body slice.


## Audit round 3 amendment (2026-08-22) — marker propagation

The chat→responses bridge rebuilds headers from the FORWARD_HEADERS allowlist
(chat-completions.ts:198-203, openai-responses.ts:28-36), which would DROP
`x-opencodex-vision-describe` before the plan site — on exactly the one path
recursion lives. Therefore:

- The marker is detected AT THE CHAT SURFACE (raw req.headers before the
bridge) and carried as an explicit option/flag into handleResponses
(`visionDescribeTerminal: true`), not as a header the bridge must
preserve. The Responses surface ALSO honors the raw header directly for
native /v1/responses callers.
- Regression test drives the FULL chat-surface path: marked POST to
/v1/chat/completions with an image + text-only routed model → assert the
plan site STRIPS (no describe dispatch, no recursion), while the same
unmarked POST plans normally. A predicate-only test is insufficient and
would stay green with the marker broken.

Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 190 — Surfaces, live proof, delivery (wp4 cycle)

Depends on: 180.

## GUI

- Split the shared SidecarBackend (dashboard-shared.ts:62): web-search side
keeps its server-provided backend strings (already emits xai/gemini/exa —
stale type fixed by the split); vision side gets
VisionBackend = "openai" | "anthropic" | "xai" | "gemini".
- visionSidecarBackendForModel fallback stays server-provenance-first;
catalog inference (anthropic-vs-openai guess) only for legacy rows.
- claude-manual-env.ts SidecarOverride backend union widens for vision.
- No new dropdown UI: options arrive from visionModels server list already.

## CLI

- src/cli/agent.ts: usage already names xai|gemini; verify backend values
pass through PUT unvalidated client-side (server gate authoritative);
vision --list renders new backends' rows.

## Live proof (acceptance 3-5)

- GET /api/sidecar-settings on live :10100 shows visionModels containing
xai/gemini rows (auth present on this machine for both — web-search rows
prove it).
- PUT vision {backend:"xai", model:"grok-4.3"} → 200; PUT model grok-4
(bare) → 400 provably-blind; restore original settings after proof.
- GUI screenshot of the vision dropdown listing Grok/Gemini rows.

## Delivery

- Small commits per layer (backends table / eligibility+gate / executors /
GUI+CLI / tests+devlog), full bun run typecheck + bun run test green at
final head, push directly to dev (user-authorized, no PR).
- devlog docs 160-190 land with the same push train; unit stays in _plan
until the release train closes it.


## Delivery evidence (2026-08-22, wp4)

- Live dev server (commit 3ff19c33e, port 11100, copied auth home):
- GET /api/sidecar-settings visionModels: 25 rows — legacy openai/anthropic
sides + 17 namespaced [routed] rows (xai/grok-4.6,
google-antigravity/gemini-3.7-flash, cursor/kimi-k3, zenmux/…,
alibaba…/qwen3.8-max, …). Rule 2 confirmed live: no text-only rows.
- PUT gates live: routed+xai/grok-4.6 → 200; routed+xai/grok-3 →
400 provably-blind; openai+namespaced → 400 coherence.
- GET after PUT reports the routed model verbatim
({"model":"xai/grok-4.6","backend":"routed"}) — fixed the legacy-collapse
display bug found during this verification.
- GUI screenshot: vision dropdown lists namespaced routed rows; current
selection renders as xai/grok-4.6.
- CLI: `ocx agent sidecar vision --list` prints the same 25 rows with
[routed] backend tags (server-computed list, no drift).
- LIVE describe e2e: POST /v1/chat/completions with a 64x64 red PNG to
xai/grok-composer-2.5-fast (noVisionModels) with routed describer
xai/grok-4.6 → main answer "red"; request history shows the inner
grok-4.6 describe call followed by the outer composer call. (A 1x1 probe
earlier failed with xai invalid_image min-8px — upstream constraint, not
a pipeline defect; the graceful degradation path handled it and the main
call still succeeded.)
- Verification-side effect handled: the 11100 dev server rewrote
~/.grok/config.toml to port 11100 during startup sync; restored to 10100
via production `ocx ensure` and confirmed (27x base_url 10100, zero
11100). Temp verify home moved aside (/tmp/trash-ocx-vision-verify-*).
- privacy:scan green; root+gui tsc clean; focused suites green (185 pass).
- Full-suite run at final head queued behind another worktree's runner
(scripts/test.ts exclusive-run queue); recorded separately below when it
lands.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading