sync: upstream v2.50.0 (2d4d7a223) into dev - #330
Draft
cursor[bot] wants to merge 343 commits into
Draft
Conversation
…6-recovery fix(codex): reconcile manual reset cooldowns with owned fresh usage
…close docs: archive six-item bug stack delivery proof
[WRONG BRANCH] release: promote 2.48.0 to main
chore(release): open dev at 2.49.0 before releasing 2.48.0
…cord docs(devlog): archive the 2.48.0 release train record
(cherry picked from commit 7e56b63)
(cherry picked from commit f651611)
(cherry picked from commit 18530f8)
(cherry picked from commit 4b705e9)
The carried CodeBuddy commits predate the tests/ modularization, so their two test files landed at the tests/ root where the layout guard rejects them. Move them under tests/providers and register both in layout.json and the expected-layout fixture.
(cherry picked from commit 4ac98bd)
Same drift as the CodeBuddy layer: the carried Qoder commit predates the tests/ modularization. Move both files under tests/providers and register them in layout.json and the expected-layout fixture.
…rors The Qoder catalog branch was written against the pre-lidge-jun#3601 shape of provider-fetch.ts, so it merged cleanly while dropping captured.effectiveAlias from all four hint applications; a Qoder row would carry whatever alias the cached row already held instead of the decision captured at flight admission. The shared coding-agent result classifier matched the bare word authentication anywhere in vendor text, turning transient upstream wording into a 401 credential verdict that drives reauth messaging and key-pool rotation. Anchor it to credential phrasing; the existing 'Not logged in; invalid token' fixture still classifies as 401.
(cherry picked from commit a4e8050)
…idge-jun#3989) Configure `sourcePreservingYaml: { path: ["providers", "opencodex"] }` for Hermes Agent in `INTEGRATION_CLIENTS`. Hermes Agent is a multi-provider autonomous agent whose configuration (`~/.hermes/config.yaml`) typically hosts multiple providers, vision models, and comments. Previously, Hermes was registered without source preservation, causing `classifyIntegration` to fail-closed (`state: "conflict", reason: "foreign-edit"`) whenever a user modified sibling providers or comments. In the dashboard UI, this locked the toggle and threatened a destructive full-file restore ("Replace"). By giving Hermes source-preserving YAML semantics (joining OMP and DSH): - OpenCodex mutates only `providers.opencodex` in-place. - Sibling provider configurations, user comments, and formatting remain untouched. - Unit and invariant test suites updated with dedicated regression coverage. - Public documentation in `guides/integrations.md` updated accordingly. Closes lidge-jun#3989 (cherry picked from commit a1fe9ca)
…ss Gemini and CCA
Google Gemini (via Cloud Code Assist / Antigravity, Vertex AI, and AI Studio)
rejects requests whose conversation history ends on a model turn with HTTP 400
INVALID_ARGUMENT ("Requests ending with a model turn are not supported.").
Previously, the continue nudge was only attached for Claude-on-Antigravity under
the assumption that Gemini natively accepts model-tail histories. In practice,
autonomous multi-turn loops (such as subagent orchestration and continuation turns
in Codex CLI) produce histories ending in an assistant message, triggering the 400.
This patch:
- Injects a synthetic user `(continue)` nudge in `messagesToGeminiFormat` whenever
the reconstructed `contents` ends with `role: "model"` or is empty.
- Broadens the post-replay Antigravity tail check to cover all Google models.
- Updates unit tests to verify that Gemini 3.7 Flash, Gemini 3.8 Flash, and
AI Studio endpoints all receive the continue nudge on model-tail contexts.
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
(cherry picked from commit 1837b8f)
qoder and qoder-cn landed without an icon alias or display name, so the dashboard drew them as an initial tile with a raw config id underneath; codebuddy and codebuddy-cn had the same gap. Every other first-class provider carries both. Qoder ships its own square symbol as the declared site icon, byte-identical across qoder.com, qoder.cn, qoder.com.cn and the schema.org Organization logo, and neither terms document restricts its use; it is committed verbatim and wired to both ids. CodeBuddy keeps the initials tile on purpose: section 9.3 of the Tencent service agreement forbids using Tencent brand features under any circumstances without written consent. Both decisions, with sources and clauses, are recorded in the provider-icons README so the refusal is not silently re-litigated. Also publishes the Qoder Global/CN section in the docs-site providers guide (the repo-level docs/qoder-cli-provider.md is not built) and lists the qoder adapter in the configuration reference.
…ervation The English guide now lists Hermes with OMP and DSH as source-preserving YAML writers; the French and Traditional Chinese pages still named it among the clients whose switch locks on sibling edits, contradicting runtime behaviour.
The carried change added the nudge to messagesToGeminiFormat for every Google mode and also widened the post-replay Antigravity check from Claude-only to all models, leaving two injection sites on one request. The second is only a no-op while nothing between them reshapes the tail; the signature sanitizer can. Drop the post-replay copy so the formatter owns the invariant.
feat(provider): add official CodeBuddy Global and CN providers (carry lidge-jun#3340)
…lobal feat(provider): add Qoder Global PAT provider (carry lidge-jun#3349)
feat(provider): add Qoder CN PAT provider (carry lidge-jun#3350)
feat(gui): give Qoder its brand mark and name the CLI providers
…yaml feat(integrations): support source-preserving YAML for Hermes Agent (carry lidge-jun#3990)
…tail fix(google): guard model-tail histories with user continue nudge across Gemini and CCA (carry lidge-jun#3988)
…hput Logs reported one rate. tokensPerSecond() never subtracted TTFT, tokPerSecondResult passed the full duration, and the MetricSource Pick did not even include firstOutputMs, so a decode-rate metric could not be computed at all -- even though TTFT is already recorded per request and per combo attempt. It was never a calculation bug: the end-to-end metric is documented as end-to-end. It is a missing metric. The history is why this is not a straight re-land. Contributor PR lidge-jun#4040 implemented the same metric and was closed unmerged by the reporter, on the grounds that proxy TTFT and the provider's own generation window do not line up and that a small post-TTFT remainder makes the estimate explode. The issue stayed open, so the repository held both an acceptance criterion and a rejection of the same feature. MIN_DECODE_WINDOW_MS answers the rejection directly. Below one second the window is dominated by TTFT jitter, proxy buffering and timestamp granularity, so a window under the floor yields NO value instead of a four-digit number. The second half of the answer is honesty rather than arithmetic: the value is always marked estimated, because proxy TTFT is when the first byte reached the proxy and no window length makes that the provider's generation start. Strictly additive. tokensPerSecond, tokPerSecondResult, filterLogs, RequestLogEntry and usage.jsonl are untouched, the end-to-end rate keeps its meaning, the speed filter still sorts on it, and this stays out of /api/request-history. The parent uses the request's own TTFT and each attempt uses its own attempt-relative one; a combo parent never borrows a child's. Closes lidge-jun#4038.
Maintainer integration on dev under the MAINTAINERS.md policy that lets a maintainer with maintain or admin access land a PR on dev without a second approval, recording the decision and the exact-head CI evidence. Lane A item 3 of the round-2 stack, planned in devlog/_plan/260910_post249_round2/040_4141_launchctl_bootout.md. The decision this carries, stated plainly rather than buried: bootout kills the live gui job. That is the repair the issue asks for, and it is also why the existing code only printed the command instead of running it. It is scoped so a healthy job is never touched - it runs inside installLaunchd only, never in ocx service start, and only after load -w has already failed. launchctlLoadFailed itself is unchanged, because that regex is the silent-success guard and the fix is to recover from the condition rather than stop detecting it. Sequenced deliberately after PR lidge-jun#4152, which landed as 9ba04b6 and rewrote the same runLaunchctl runner. This adopts the seam that PR established instead of introducing a second one. Exact-head CI at ae057c4, verified by exit code: gh run view 34418986812 --exit-status -> 0 Cross-platform CI gh run view 34418986816 --exit-status -> 0 Service lifecycle gh run view 34418986897 --exit-status -> 0 React Doctor gh run view 34419067873 --exit-status -> 0 Enforce PR target branch Latest conclusion per check name at that SHA: twenty-six success, none failed, none cancelled, none pending. NOT RUN, and worth naming for this change in particular: no launchctl, no ocx service, no ocx start/stop/restart was executed anywhere while producing it. A live proxy is running on this machine and a separate task owns it, so the behaviour is proven by stderr fixtures against the injection seam and by remote CI. Also NOT RUN: bun run test, bun run typecheck, bun run build, bun run lint:gui, bun run privacy:scan, bun install.
… path too deriveEntry builds a served entry twice over: once by cloning a cached template and once from scratch when none is available. The inactive-reason stamp landed only on the templated path, so whether a quota-exhausted row carried the field depended on whether a template happened to be cached -- the same row would be marked or unmarked across rebuilds. The regression test caught it because it passed template: null, which is the fallback path. It now runs both paths explicitly rather than one, so a stamp added to one branch and not the other fails here instead of shipping.
…d pin the built entry Two gaps in the same field. listManagementModelRows REBUILDS custom rows from config.customModels instead of spreading the CatalogModel, and the slug dedup then drops the gather-derived row it replaces. So a custom model whose provider is out of credit was the one row on the page that could never show as inactive, while routed and combo rows beside it did. The reason gather already computed for that slug is now carried across. The regression test covered the predicate and deriveEntry, but nothing built a catalog. Every one of those cases would stay green if a refactor moved the stamp out of the entry builder. The new case seeds the routing cache, lets the predicate read it, and builds through buildCatalogEntries -- asserting the exhausted row is present, still visibility "list", and marked, while a funded provider's row beside it carries no field at all.
…est history Two gaps. The attempt table still showed only the end-to-end rate, while the parent detail and the list stacked both. The DTO already carried a per-attempt decode rate measured on that attempt's own TTFT, so this was a rendering gap rather than a data one. /api/request-history shares requestLogDto but not its contract, and the field was riding along. requestLogDto now takes includeDecodeRate, defaulting to true so /api/logs is unchanged, and both history call sites opt out. Worth stating plainly: the value would have been MEANINGFUL there, not noise -- firstOutputMs does survive into a persisted-usage row, so history entries would have carried real numbers. The exclusion is therefore a scope decision rather than a correctness one. The decode rate is a Logs-page metric, and widening a separate endpoint's response shape is not this change's business. Flipping it on later is one argument. Both halves are asserted: the DTO omits the field on parent and attempts under the opt-out while keeping tok/s and cost, the default still includes it, and a source check pins that the route passes the flag at every call site -- otherwise a correct flag and a route that never set it would both look fine.
Typecheck failure at b19fa9e. /api/logs built its rows with .map(requestLogDto), which is fine for a one-parameter function and a type error the moment a second one exists: Array.map passes (value, index, array), so the element INDEX was being handed to the new options object. Type 'number' has no properties in common with type '{ includeDecodeRate?: boolean }' An explicit arrow fixes it and keeps the default, which is what /api/logs wants. The two request-history call sites were already explicit because they pass the opt-out. This is the whole cost of the flag -- one call site that happened to be point-free -- so the exclusion stays rather than being traded for a documented deviation.
Lane A is complete. lidge-jun#4141 landed as 95a3f6a, joining lidge-jun#4129 and lidge-jun#4148, and with lidge-jun#4147 and lidge-jun#3859 that is five issues delivered and closed. The remaining four are code-complete and audited PASS, and each sits at twenty-three green checks with enforce-target as the only failure. Its message is literally "missing UI screenshot". Satisfying it needs a GUI build, which this round forbids, so the choice is the maintainer's: allow a build for screenshots, integrate past the gate with admin rights, or carry these four forward. It is not a false positive. PR lidge-jun#4162 changed nothing but documentation and tripped the same gate merely by quoting the trigger token in its description; rewording made it pass. On four PRs that do change the dashboard, the requirement is real. Records the second-round audit and its four findings, all since fixed, and the answer to the question worth asking about lidge-jun#4165's earlier CI failure: the repair filled a missing stamp on the deriveEntry fallback and extended the new test to both derivation paths, rather than relaxing an existing catalog equality to go green. Also records a direct cost of this round's constraints. Fixing the last finding broke the typecheck, and with local typecheck forbidden that was only discoverable from the remote gates job, whose failing step has to be read out of the workflow rather than seen locally. NOT RUN: local test suite, typecheck, build, lint. Remote CI is the gate.
…close Maintainer integration on dev under the MAINTAINERS.md policy that lets a maintainer with maintain or admin access land a PR on dev without a second approval, recording the decision and the exact-head CI evidence. Documentation only: confined to devlog/_plan/260910_post249_round2/, and nothing in the build, typecheck or test path reads from devlog/. Exact-head CI at 7945635, verified by exit code: gh run view 34423303084 --exit-status -> 0 Cross-platform CI gh run view 34423303095 --exit-status -> 0 React Doctor gh run view 34423303033 --exit-status -> 0 Enforce PR target branch gh run view 34423302981 --exit-status -> 0 PR hygiene Latest conclusion per check name at that SHA: eight success, ten skipped, none failed, none cancelled, none pending. NOT RUN: bun run test, bun run typecheck, bun run build, bun run lint:gui, bun run privacy:scan, bun install. The maintainer set a no-local-suite constraint for this round and remote CI is the only gate.
The Free switch is offered only where discovery returned per-token prices, but the operator's choice is component state that outlives the rows it was made against. When the evidence goes away -- a refresh that comes back without pricing, a re-auth, a discovery fallback to a static catalog -- pricingKnown goes false and the control disappears, while the stale true keeps filtering. Every row is then unclassified, filterFreeModelRows keeps none of them, and the list empties under a switch that is no longer drawn. The empty-state copy makes it worse: models.noFreeMatch tells the user to turn off Free only, naming a control that is not on screen. Gate the filter on the same condition that renders the switch. freeOnlyInForce lives beside the predicate in models-shared so the Models page group and the provider workspace inventory cannot drift, and both surfaces bind their empty-state message to the derived flag rather than the raw state. Local checks: NOT RUN, per this round's constraint. Exact-head remote CI gates.
feat(catalog): classify discovered model pricing and filter free models
fix(models): explain the discovery dependency when model sync fails
feat(logs): show an estimated decode rate alongside end-to-end throughput
lidge-jun#4156 and lidge-jun#4158 landed on dev while this branch was open, and both touch the same catalog surfaces. Every conflict was additive -- the same declaration list, the same locale catalog, the same row type -- so each one keeps both sides: quotaInactiveReason (lidge-jun#1711) beside pricingStatus (lidge-jun#3666) on ModelRow and on the parsed catalog row, and models.inactiveNoCredit beside models.discoveryFailedDependency in all nine locales. Two of those hunks shared a single JSDoc opener, so keeping both bodies left the second block without its /** in models-shared.ts and parsing.ts. Restored. Local checks: NOT RUN, per this round's constraint. Exact-head remote CI gates.
feat(catalog): mark quota-exhausted models and combos inactive
Round 2 closed with Lane B dammed by the enforce-target screenshot gate. This records how it was cleared, what an independent audit changed before the merge, and the disposition of every finding that was not fixed.
…lane-b-landing docs(devlog): record the Lane B landing and the screenshot-gate waiver
…e-jun#4186) * docs(devlog): record the Cursor ACP -> OpenCodex feasibility investigation Answers whether cli-jaw's Cursor ACP integration can be attached to OpenCodex. Transport yes, provider no: ProviderAdapter is a model port and ACP delivers an agent. Cursor-the-model is already integrated over HTTP. Corrects a stale finding: 800_agent-fabric/110_protocol_boundaries.md defers ACP on the grounds it merged into A2A, but that is IBM/BeeAI's Agent Communication Protocol. Cursor speaks Zed's Agent Client Protocol, a separate spec still at v1. Records that the unit's original central premise -- that ACP gives a client no way to constrain the agent -- was falsified during audit by Cursor's documented read-only plan/ask modes, and that the conclusion survived while its premise did not. Three audit rounds, 16 findings, zero rebutted. Docs only; no src/, gui/ or docs-site/ change. * docs(devlog): close the Cursor ACP residuals with a live trace Runs the cursor-agent acp handshake the unit could not run earlier, on 2026.09.08-6caf4ff in a scratch directory. ask mode held: an explicit "do it now" write instruction produced no modification. agent mode did not: the same prompt edited the file after issuing zero session/request_permission and zero fs/write_text_file calls, against a client instrumented to deny everything. That reverses a retraction. Cycle 1 withdrew the claim that Cursor mutates without offering a refusal point because it could not be demonstrated; it can now. The reviewer was right about the ACP specification and wrong about Cursor's implementation of it. Also records a measured model roster (38 rows, context 200k/272k/300k, no advertised 1M, no Max Mode) replacing an earlier unproven claim. Four reviewer blockers folded, all scoping conclusions to what two runs support. Docs only. * docs(devlog): correct the ACP model-surface measurement and add ACP-D5 The earlier trace omitted the client capability both existing ACP implementations send, and measured a degraded model surface as a result. t3code (CursorProvider.ts:71-75) and cli-jaw (cursor-session.ts:64) both set _meta.parameterizedModelPicker at initialize. With it, Cursor returns clean base model ids instead of 38 bracketed ones, branded display names, and per-model config options carrying the legal effort and context values - including 1m context on claude-opus-5 and gpt-5.6-sol. Three claims are withdrawn: "no advertised 1M-context variant", "no observed way to vary effort or context", and "one fixed configuration per listed model". Neither the ACP spec nor Cursor's docs mention the flag; reading t3code is what surfaced it. Adds ACP-D5: run cursor-agent acp as a metadata discovery probe for the existing HTTP provider rather than as an inference path. No turn crosses the ACP boundary, so tool ownership, missing usage and unsupervised writes never arise, and it replaces hand-maintained effort/context tables with values the vendor advertises. Docs only. * docs(devlog): record the workspace constraint and tidy the unit for review Marks the superseded model-surface section in 070 instead of leaving it to contradict the correction below it, and records the constraint that actually decides an ACP inference path: OpenCodex has no workspace to give the agent. ACP requires a cwd at session/new. OcxParsedRequest has no working-directory field, IncomingMeta carries none, and coding-agent/turn.ts passes no cwd when it spawns, so a child inherits the proxy's directory rather than the caller's project. That also explains why the coding-agent precedent needs no cwd: with --tools "" the child never touches the filesystem. Adds a 060 addendum as the current entry point, since two later passes changed conclusions after that summary was written.
…in-01a08a81 [WRONG BRANCH] release: promote verified 2.50.0 product tree to main
Co-authored-by: SB Yoon <yansigit@users.noreply.github.com>
Co-authored-by: SB Yoon <yansigit@users.noreply.github.com>
Co-authored-by: SB Yoon <yansigit@users.noreply.github.com>
Co-authored-by: SB Yoon <yansigit@users.noreply.github.com>
Co-authored-by: SB Yoon <yansigit@users.noreply.github.com>
|
PR automation (bot-owned)
|
⏳ DRAFT
What to do
This pull request was already a draft. Its draft status will be preserved after every issue above is resolved. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Merge upstream release v2.50.0 (
2d4d7a22381a2e497c2442902104619e25f937c7) intoorigin/dev(785424295fc5d8ef0a66fb8ce94a55fe3b054471).The fork sync Action stopped at decision-handoff after 42 textual conflicts. This coordinator branch resolves them per
docs/fork/OWNED.md, re-applies fork preservation deltas, and leaves a draft PR for human merge (no squash/rebase).Provenance
v2.50.02d4d7a22381a2e497c2442902104619e25f937c7origin/dev):785424295fc5d8ef0a66fb8ce94a55fe3b054471d86db7996bfe4df9bf036e184312bf9b3e93a7b5Conflict decision table
package.json(version, scripts, identity)@yansigit/opencodexname, non-decreasing semver, forkprepush/audit:highscriptsmergePackageJsonrecipebun run typecheckbun.lockbun installbun installsrc/codex/quota.ts, etc.)--theirs)src/server/responses/core.tsimports (enrichOpenCodeZen*, web-search bridge)googleProviderOptionsRouteError,mediaBridgeWillRun,resolveCcaInTurnGroundingbun test tests/responses/responses-context-overflow.test.tscore.tsshadow interceptcredentialDomainWasRewrittenmodels auth-domain rewriteshadowInterceptedfeeds V2 bridge admission loggingbun test tests/codex-integration/multi-agent-compat.test.tscore.tspassthrough SSE bodyupstreamSseBodyupstreamSseBodyelse branch + forknormalizedPassthroughSseBodyrewritebun test tests/server/server-xai-responses-streaming.test.tscore.tspassthrough remembertextincl.modeltext/modelshape behind fork guardbun test tests/responses/responses-context-overflow.test.tssrc/usage/log.tstransportPhase/terminalSourcepersistenceagentKind, v2-bridge telemetrybun test tests/ci-workflows/test-home-guard.test.tssrc/server/responses/fetch-helpers.tsUpstreamRedirectError, TLS/test fetch chaincore.tsredirect handlingbun test tests/server/server-xai-responses-streaming.test.tssrc/server/responses/collaboration.tssyncCodexSubagentDefaults/nativeDefaultStateplaceholdersbun test tests/codex-integration/multi-agent-compat.test.tssrc/codex/routing.tspin releasesaveConfigPreservingClaudeCodeon pin drainpersist=falsetry/finallybun test tests/codex-integration/codex-routing.test.tssrc/codex/inject.ts#3978client compaction providerpublicOriginTLS injectioninject.ts(blocker: compaction test still red)clientCompactionfrom upstream into fork injectbun test tests/codex-integration/codex-inject.test.tssrc/claude/inbound.ts#3922explicitstrict: falseon toolsbun test tests/claude-integration/claude-inbound.test.tstests/codex-integration/codex-routing.test.tsorigin/devtest filebun test tests/codex-integration/codex-routing.test.tstests/responses/responses-context-overflow.test.tsgoogleProviderOptionsRouteErrormay 400 earlyorigin/devtest filebun test tests/responses/responses-context-overflow.test.tsVerification
Remaining focused failures (human)
tests/responses/responses-context-overflow.test.ts—non-streaming callers retain the upstream 413 status and bodytests/codex-integration/codex-inject.test.ts—Codex client compaction opt-in (#3978)(needs upstreamclientCompactionmerged into forkinject.ts)tests/codex-integration/codex-routing.test.ts—WHAM preserves the 5h, weekly, and Spark weekly windows(needsfiveHour*quota fields without breaking upstreamcore.tssignatures)Checklist
src/orgui/src/— 3 focused regressions remain; see blockers above.vendor/mainv2.50.0 /2d4d7a223— donesrc/server/responses/core.tsperOWNED.md— done (manual)origin/dev— pendinggh pr view --json mergeableci/enforce-target/hygiene— pending exact-head runsDraft for human merge — do not squash/rebase.