- Exported public types and schemas —
server/exports/types.tsandserver/exports/schemas.tsexpose a stable set of runtime types and Zod schemas for use by external consumers. A dedicatedtsconfig.exports.jsonand updated build script generate the exports bundle.
- Updated model data — Refreshed
models-dev-data.jsonwith latest provider model entries (March 2026). (ENG-225) - Codex shim API key logic consolidated —
resolvedApiKey,apiKeySource, andisAuthConfiguredare now readonly getters onCodexShim, replacing scattered utility functions. Key priority order is explicit and tested:OPENAI_API_KEY>CODEX_API_KEY>~/.codex/auth.json. Unit and integration tests cover all priority combinations.
- Codex shim falls back to SDK bundled binary when
codexnot on PATH —resolveCodexPath()previously calledwriteStartupErrorifwhich codexfailed, blocking the SDK's ownfindCodexPath()from running. In CI, codex is not on PATH but is available via npm platform packages. Now passesnullto the Codex constructor so it can resolve the binary itself; self-test also triestryFindSdkBundledCodex()as a fallback. - Codex shim
WebSearchdeferstool_useemission untilitem.completed— WebSearch items arrive withquery: ""atitem.started; the real query only appears in later streaming chunks. Deferring emission ensures transcript logs record a non-empty query. - Codex warnings no longer thrown as errors — The shim previously threw on any non-fatal warning message from Codex, causing spurious failures. Warnings are now logged and ignored.
- Codon failure classification reads
msg.resultinstead ofmsg.error—ResultMessagehas noerrorfield; error text lives inmsg.result. Readingmsg.erroralways yieldedundefined, so every error result was classified as{ type: "unknown", retriable: false }, including retriable timeouts and rate-limit errors. - Loop codons funded via hank proportional shares now route correctly — When a hank used proportional allocation with a named share for a loop but the loop had no explicit
budgetfield,loopContext.loopBudgetwasundefined. The routing condition fell through toresolveHankScopedLimits, which looked up shares by codon ID instead of loop ID, yielding a $0 allocation. Now synthesizes an implicitloopBudget: {}so the correct loop-scoped path is taken.