Repository navigation
Conversation
The plugin now serves the opencode and opencode-go routes itself, through supported host paths only, and the hidden-route workaround is gone. A prepended internal/config listener hands pi-ai a copy of its config without the two plane entries, so pi-ai keeps every other route. On a commit the plugin reconciles: it builds one plane adapter, a subclass of the host's PiAiAdapter, over pure profiles (utils/plane-profile, plane-model, model-metadata and protocols) and registers only the planes that are declared. A Settings candidate has the plane entries it changes validated strictly. A read-only settings.describe graft puts the committed plane entries back, so the host still sees them. Deleted: responses-provider, responses-routes, google-bridge, pi-provider and utils/google-replay; the listing hide and the discovery decoration; the stream redirect; the D19 settings; and the hidden ids in DEFAULT_PROVIDERS and cordis.patch.yml. What users see: any saved id is accepted and served, and an id no catalog knows dispatches as completions. The protocol comes from the catalog and model info follows the user's own row first. A model whose reasoning is only on or off offers Off/On. The default endpoint is the per-protocol inference family. Keys resolve by pi-ai's own rule, and a plane lists its declared ids even when no key resolves. When several pi-ai rows declare planes, the row the plugin serves stays the same while it is live and still declares one, so no row is left stripped with nobody serving it. If the host's pi-ai module cannot be read, the plugin steps aside even before any plane is declared, so a later Settings save goes to pi-ai's own check. The characterization rows this changes are flipped to new baselines; every other row stays green unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
# Conflicts: # README.md # README.zh-CN.md # docs/redirect-planes.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
|
The fix already exists. Generated by Claude Code |
|
Generated by Claude Code |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The refreshed Go catalog serves its Gemini model as gemini-4-argon, so the plane-dispatch mock, the plane-model cases and the binary e2e's probe expectations name the new id; the Go Google base they assert is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The review ran three rounds (found 20, 5 and 8; upheld 19, 4 and 8). What it found and what changed: - A completions model whose effort list names none lost its Off. utils/model-metadata.ts now offers Off with none as its wire value on completions, as pi 1.1.0's own entries do; Responses unchanged. Pinned in model-metadata, plane-model and a new mock-gateway case. - A Go entry with no apiKeyEnv authenticates with OPENCODE_API_KEY (pi's lookup, D20), and nothing said so. README "Changed in 1.x" and step 3 now name it; host-planes.test.ts pins both cases. - A Settings save of a plane was refused as "cannot be validated yet" while the host modules were unloaded. host/route-claim.ts now judges it with the last modules that loaded; route-claim.test.ts pins it. - Fiber.update(config) with noSave false would persist the stripped config and drop both planes. Recorded as a constraint in route-claim.ts and the plan; no caller does it today. - Stale text: cordis-context.ts comments (stream redirect, isolate, root inject), deep-dive.md (pi's Go lookup, who answers Fetch), CONTRIBUTING (field count, the host-modules floor), and historical banners on opencode-parity-plan.md and upstream-findings.md. - The mock body-shape row asserted only body.model. It now checks each protocol's required fields and that the gateway answered 200, or the vendor's 404 for the unknown id. - Route-set rows: removing the last plane now pins replace([]) on the live handle, and "booting with none" waits for the plugin's row to be active instead of sleeping 50 ms. - The live protocol-routing suite now sends paid models through the plane adapter on a tier-A host, not only raw probes. - README's credential-isolation claim was false: the Go meter can send the Zen key. README.md, README.zh-CN.md, deep-dive.md, credentials.md and redirect-planes.md now give the real order and the 403 remedies; readme-parity.test.ts keeps both READMEs in step. - The keySource hint described Automatic backwards. settings-copy.ts (en and zh) now gives literal key, captured key, then reference. - The live adapter blocks dropped a protocol whose model was withdrawn. They now walk up to three candidates per protocol (support/live-walk.ts), pass a "model is unavailable" turn only when a raw probe confirms it (support/live-probe.ts), and skip naming a protocol no candidate reached. - A mock comment claimed a body passed shape checks the mock lacks. Declined: a toggle beside an effort list still offers no Off, which moves baseline facets and needs the owner's decision; the Models store's join stays a documented restatement rather than importing a client UI package; the PR description is edited outside the tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Second review of P-R1This phase's first reviews ran without the dedicated reviewer, which was unavailable at the time. It has now had that review: three rounds, through the correctness, rules and test-matrix lenses.
In total 31 findings were upheld and 23 fixed. Round 3's two lenses found the same two problems: the Chinese README was not ported, and the live walk dropped a protocol. Fixed
Declined
Commit: 0cceb81. Gate before the commit: check, 1387 unit tests, 63 mock tests, build and bundle gate, all passing. Generated by Claude Code |
…levels The live suite caught the Go plane's Gemini model refusing a thinking budget: the gateway answered that the model takes a thinking level and not a budget. Pi chooses a thinking level over a thinking budget only by matching the model id against its own pattern, so a model the pattern does not name was sent a budget it cannot accept. The plugin now makes that decision from the catalog, as D16 asks: a model whose catalog entry offers discrete effort levels on the Google protocol gets a thinking level. It builds that request through pi's public exports, on both the enabled and the disabled path, and hands every other model back to pi's own path unchanged. Tests pin it: the pure rule over the catalog, a differential check that the wrapper builds the same request as pi's own streamSimple for an id pi already recognises, the lazy loading of the pi pieces, and a mock-gateway case that the turn completes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
|
Fix pushed: Google's thinking level for Gemini models the catalog describes by level (4dbd22e) The live failure. The live suite caught a turn on the Go plane's Gemini model failing. The gateway refused a token thinking budget with 400
Cause. pi 1.1.0 picks Google's discrete Fix. The decision now comes from the catalog, as D16 requires. A model whose catalog entry offers discrete effort levels on the Google protocol ( Tests that pin it.
The full gate passed before the push: check, unit suite, mock suite, build and the bundle gate. The fix reaches the stacked PRs above through the sync. These reviews ran on the general model because the dedicated reviewer was unavailable: review:tests#1, review:correctness#1, verify#1.1, review:tests#2, review:correctness#2, verify#2.1, review:tests#3, review:correctness#3, verify#3.1. Generated by Claude Code |
…h' into claude/vigilant-mendel-bctz9n-p-r1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Three documentation defects the synced base carries into P-R1. None was caused by the merge (each is identical on the merge-base and on both parents), and each wants the same change on the lowest branch (#105) and, for the README date, on P-R2's cherry-pick of d13cbe2. - PRE-1, docs/redirect-planes.md: "The Go plane serves 12 models on shapes other than completions" restores the count its own table gives (5 openai + 5 anthropic + 1 google = 11, the Go shim's 30 less its 19 no-SDK models). The 12 was right when written (6 + 6) and a later refresh moved the cells but not the sentence. test/catalog.test.ts now holds the sentence to the table's non-completions Go sum, so the next refresh cannot strand it again. - PRE-2, README.md and README.zh-CN.md: the 81-model count is dated 2026-10-27, the refresh that produced it (d13cbe2); on 2026-10-23 the count was 82, so the date no longer matched the number. - PRE-3, README.md and README.zh-CN.md: both install snippets name "^1.4.1" (package.json's version), where they had drifted to ^0.14.0 and ^0.12.0, neither of which can resolve to a 1.x release. test/readme-parity.test.ts pins the two snippets to one range on package.json's major. Both new assertions were checked against the stale values: each fails on them and passes on the corrected text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
…-mendel-bctz9n-p-r1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Phase P-R1 of docs/improvement-plan.md, stacked on #112.
The plugin now serves the
opencodeandopencode-goroutes itself (the D13 design), using only supported host paths. The hidden-route workaround is gone.What changed
inject: [].src/host/route-claim.ts(new)internal/configlistener gives pi-ai's fiber a fresh copy of its config withproviders.opencodeandproviders["opencode-go"]removed. It runs the nested waterfall on that copy behind a re-entrancy guard. On a commit it schedulesreconcile. On a Settings candidate it strictly validates only the plane entries that changed. The served pi-ai row is sticky: it stays the same while it is live and still declares a plane.src/host/plane-provider.ts(new)reconcilebuilds onePlaneAdapter, a subclass of the host'sPiAiAdapter, and registers only the declared planes throughregisterAdapter/handle.replace. pi-ai keeps every other route.src/utils/plane-profile.ts,plane-model.ts,model-metadata.ts,protocols.ts(new)utils/purity gate covers them.src/host/describe-graft.ts(new)settings.describewrapper, built onpatchMethod, that grafts the committed plane entries back (D18).src/host/host-modules.ts(new)cordis-context.ts. pi-ai's module comes from pi-ai's own loader entry during its commit run, so nothing waits on a timer.responses-provider.ts,responses-routes.ts,google-bridge.ts,pi-provider.ts,utils/google-replay.ts(removed after a mock-gateway replay test showed a single provider label); frommodels-discovery.ts: the listing hide,decoratePlaneAdaptersanddecorateModelInfo; the stream redirect; the D19 settings; the hidden ids inDEFAULT_PROVIDERSandcordis.patch.yml.host-planes), contract (describe-graft,catalog-change) and mock (plane-dispatch) suites. Characterization rows flipped to new[BASELINE]values: 14S0_TODAYfacets, 14 namedhost-characterizationcases and 5 binary-e2e rows, each named under "Characterization rows this PR flips" below. Every other row stays green unchanged.Some details refine the plan text on purpose, and the plan's P-R1 entry records each one:
legacyuses pi's own base only when pi files the model under the same protocol and that base is a/zenroot.infothe way D17 does, so theINFO_NOT_FROM_CATALOGBASELINE guard stays intact.Behaviour changes
After this phase:
/inference/go/google/v1beta.apiKeyEnvnow sends the Zen key. pi's own lookup readsOPENCODE_API_KEYon both planes, so such an entry authenticates every Go protocol with it. Before, Go's Responses, Messages and Google turns defaulted toOPENCODE_GO_API_KEY(completions already took pi's rule). The meter follows: it prefers the key a Go turn carried, so after the first such turn it sends the Zen key to the Go usage endpoint, is refused, and falls back to the Zen pill or "subscription required". The fix for a user isapiKeyEnv: OPENCODE_GO_API_KEYon the Go entry; README step 3 and "Changed in 1.x" (both languages) say so, andtest/host-planes.test.tspins both cases.thinkingLevel, never athinkingBudget(src/host/google-levels.ts); Gogemini-4-argonrefused the budget live.Two changes from the last review round:
Characterization rows this PR flips
Every row below was
[KNOWN_DEFECT]or[INTENDED_CHANGE]on the base and is[BASELINE]here. No[BASELINE]row on the base moved.S0_TODAYfacets (test/support/host.ts; facet order info / callConfig / wire), 14:opencode/deepseek-v4-pro[INTENDED_CHANGE]/zen/v1/chat/completions/inference/openai/v1/chat/completionsopencode/gpt-5.3-codex-spark[KNOWN_DEFECT]user row ignored (128000),INVALID_CONFIGokopencode/not-in-any-catalog[KNOWN_DEFECT]INVALID_CONFIG, nothing sentok, sent as completions on/inference/openai/v1(D15, D16)opencode-go/deepseek-v4-pro[INTENDED_CHANGE]/zen/go/v1/chat/completions/inference/go/openai/v1/chat/completionsopencode-go/minimax-m3[KNOWN_DEFECT]toggle-only model offered Minimal…Maxopencode-go/gemini-4-argon[KNOWN_DEFECT]INVALID_CONFIGokopencode-go/minimax-m3.1-flash[KNOWN_DEFECT]INVALID_CONFIG, nothing sentok, sent on/inference/go/openai/v1opencode-go/not-in-any-catalog[KNOWN_DEFECT]INVALID_CONFIG, nothing sentok, sent on/inference/go/openai/v1The listing rows
keylessListing/keyedListingare replaced bylistingBeforeTurn/listingAfterTurn(every declared id, D21), andsaveRejectedUnknownis gone (D15).Named
host-characterizationcases, 14 (base title → head title):[INTENDED_CHANGE]registry: both planes are pi-ai's, six hidden routes →[BASELINE]registry: both planes are the plugin's adapter, no hidden route (D13)[INTENDED_CHANGE]keyless listing hides every paid model →[BASELINE]listing before any turn: every declared id (D21)[KNOWN_DEFECT]keyed listing: only pi-resolved ids →[BASELINE]listing after a turn: gap ids and the unknown id included (G1)[KNOWN_DEFECT]toggle-only model offered Minimal/Xhigh/Max →[BASELINE]offered Off / On, an untaken level refused (D1)[INTENDED_CHANGE]completions dispatch to pi 1.1.0's legacy bases →[BASELINE]completions dispatch to the modern family on both planes[KNOWN_DEFECT]the adapter decoration never runs in this order →[BASELINE]the plugin patches nothing on its ownPlaneAdapter[KNOWN_DEFECT]Zen gpt-5.3-codex-spark: user row ignored,resolveCallConfigrefuses →[BASELINE]user row reaches model info, sent on Responses (D17)[KNOWN_DEFECT]Go minimax-m3.1-flashINVALID_CONFIG→[BASELINE]validates, sent once as completions on Go's modern base (G1)[KNOWN_DEFECT]Go gemini-4-argon refused while served through the bridge →[BASELINE]validates, sent on Go's Google base with the Go key (F10)[KNOWN_DEFECT]a stored gap id blocks every save of its plane →[BASELINE]a stored gap id blocks no save; the unknown id is listed, validates and is sent (D15)[KNOWN_DEFECT]the probe's save sequence needs the gap ids dropped first →[BASELINE]the first append of the unknown id is accepted[INTENDED_CHANGE]the adapter decoration runs in pi-ai-first order →[BASELINE]pi-ai held the planes first, so the claim restarted it exactly once[KNOWN_DEFECT]the apply order decides one facet (Go gemini-4-argon) →[BASELINE]no facet depends on the apply order (todayRowsFor's override dropped)[KNOWN_DEFECT]the unknown id is what the save check names →[BASELINE]the save check accepts it and the file holds it (D15)Binary-e2e rows (
test/e2e/host-binary.e2e.ts), 5:[INTENDED_CHANGE]registry: the planes are pi-ai's, hidden routes registered →[BASELINE]both planes are the plugin'sPlaneAdapter, no hidden route (D13)[INTENDED_CHANGE]keyless listing hides every paid model →[BASELINE]listing before any turn: every declared id (D21)[KNOWN_DEFECT]keyed listing: the gap ids are missing →[BASELINE]listing after a turn on each plane: every declared id (G1)[KNOWN_DEFECT]a stored gap id blocks saving any new id →[BASELINE]a stored gap id blocks no save (D15)[KNOWN_DEFECT]a catalog-unknown id cannot be saved →[BASELINE]it saves, is listed, validates and is sent as completions on the modern base (D15, D16)The binary e2e also gained rows with no base counterpart: the plane provider is built from the host's own pi, the Go Gemini level model is sent a thinking level and no
thinkingBudget, and the Settings graft (below).Records the plan asks for
docs/improvement-plan.md(Test plan, P-R1) asks this PR body to record four things.auto=modern) dispatch: Zenhttps://opencode.ai/inference/{openai/v1, anthropic/v1, google/v1beta}, Gohttps://opencode.ai/inference/go/{openai/v1, anthropic/v1}andhttps://opencode.ai/inference/go/google/v1beta(the catalog's family map, elseMODERN_FALLBACKinsrc/utils/protocols.ts).legacy:/zen/v1and/zen/go/v1(/zen,/zen/gofor Messages), with Go's Google staying on/inference/go/google/v1betaunder every family. The keyless matrix (every PR in CI) asserts that no cell the default endpoint dispatches to answers404; the binary e2e records the URL each characterized model was sent to through the shipped bundle (the wire facets above), including Gogemini-4-argonon/inference/go/google/v1betawith a thinking level.describegraft.src/host/describe-graft.tsgrafts the committed plane entries back into pi-ai'ssettings.describevalue (D18). Held by the binary-e2e row "the Settings graft shows each plane's own entry, and the Models page reads both planes configured under their apiKeyEnv", by the[BASELINE]hasProviderApiKeyrow in both apply orders, and bytest/describe-graft.test.ts.PiAiAdapterowns them again), and pi-ai's own save check is back in force; dispose then apply hands them to the plugin again (test/host-planes.test.ts, tiers B and A). When the host's modules cannot be read the plugin stands down and says so; a failing handback restart is reported, not thrown (test/route-claim.test.ts).Review
In rounds 2 and 3, reviewers reported some of the same defects (the catalog-listener unsubscribe; the store-opt-in case reading the shell's
OPENCODE_API_KEY; the modules-unreadable refusal). Round 3's fixes:Three new route-claim cases fail under the old behaviour and pass with the fix.
The audit round's eight findings repeat four defects. The README.zh-CN.md credential wording, the live adapter walk (a withdrawn model now moves to the protocol's next candidate, a withdrawal is confirmed by a raw probe, and a protocol no candidate reached skips by name) and
docs/deep-dive.md's key-isolation claim were already fixed on this branch; a source comment insrc/go-discovery.tsstill claimed the isolation and is corrected; the description findings are this body's flipped-row and record sections and the D20 Go entry above.Verification
pnpm run checkvp test runvitest.mock.config.ts)vp test run --coveragepnpm run buildlib/index.mjs322.89 kB, build completescripts/check.ts(bundle gate)check passed, exit 0DSH_HOST_E2E=1)Before each commit, the full diff and the new files were checked for absolute local paths and anything shaped like a credential. Nothing was found, and the pattern was confirmed to match a known positive.
Note: the
catalog freshnesscheck now gates a pull request only on its own generator changes (D23, from the base), and it passes on this branch.Generated by Claude Code