Commit Graph

3579 Commits

Author SHA1 Message Date
hkfires
7ab999a80f fix(config): add NormalizeHomePort function and update home port handling 2026-08-30 08:59:04 +08:00
Luis Pater
02e3d33c49 chore(codex): update codex client model definitions
- Add missing model schema fields including `multi_agent_reasoning_effort`, `requires_sandboxed_review`, `persistent_instructions`, `guardian_v2`, and `confirmation_policies`.
2026-08-30 01:29:03 +08:00
Luis Pater
c1f3b48999 Merge pull request #5331 from router-for-me/fix-home-refresh-logging
Fix home refresh logging and error handling
2026-08-30 00:46:39 +08:00
Luis Pater
5ff4a31eaf feat(claude): support advisor tool beta header
- Add `advisor-tool-2026-03-01` beta header support when advisor server tools are declared or explicitly requested.
- Ensure proper ordering of the advisor tool beta before advanced tool use and effort betas.
- Inject and preserve advisor tool beta across token counting and fingerprint preservation paths.

Closes: #5330
2026-08-29 14:47:51 +08:00
hkfires
6a489fa84d fix(auth): prefer errors from upstream attempts
Track when executor calls cross an upstream transport boundary and use that
signal to keep model/provider errors from being replaced by later local
preparation, selection, or internal failures.

Mark HTTP, websocket, relay, and usage-tracked transports as upstream
attempts, while avoiding marks for local validation, logging, missing
sessions, and successful websocket handshakes before request send.

Parse relative auth expiry metadata and adjust Antigravity refresh timing.
2026-08-29 12:50:46 +08:00
hkfires
bc918ab276 fix(runtime): log safe home refresh error types 2026-08-29 12:50:45 +08:00
hkfires
e4a8f98913 fix(logging): enhance error diagnostics and logging for home refresh operations 2026-08-29 12:50:45 +08:00
hkfires
9a2201c36a fix(auth): forward Home unauthorized upstream errors
Stop refreshing Home-owned OAuth credentials after upstream 401s.
Preserve marked upstream response bodies for direct responses, usage
records, request logs, and websocket handshake failures.
2026-08-29 12:50:44 +08:00
Luis Pater
c350d3f520 fix(claude): strip trailing thinking blocks from assistant messages
- Strip trailing `thinking` and `redacted_thinking` blocks from the final assistant message during OpenAI responses translation.
- Drop the assistant message if no content blocks remain after stripping trailing thinking blocks.

Closes: #5321
2026-08-29 05:07:19 +08:00
Luis Pater
6f25b9a149 fix(claude): drop unsupported assistant prefill for opus-5 and sonnet-4-6
- Strip trailing assistant prefill messages for Opus 5 and Sonnet 4.6 model families in addition to Fable models during OpenAI responses translation.

Closes: #5319
2026-08-29 04:58:59 +08:00
Luis Pater
07d8156375 fix(claude): collapse consecutive thinking blocks in openai responses translation
- Replace consecutive thinking blocks with the latest reasoning item in assistant messages.
- Flush pending tool use parts upon encountering subsequent reasoning items to maintain proper block separation.

Closes: #5317
2026-08-29 04:26:46 +08:00
Luis Pater
dd5f9e74e4 fix(kimi): normalize tool and function parameter schemas
- Inline local `$ref` pointers and strip `$defs` and `definitions` from tool parameter schemas for Moonshot compatibility.
- Ensure tool parameter root objects declare an explicit `type: "object"`.

Closes: #5316
2026-08-29 01:22:22 +08:00
sususu98
4b2beb3da1 feat(executor): measure effective TTFT with protocol-aware token classification (#5313)
* feat(executor): measure effective TTFT with protocol-aware token classification

- Introduce protocol-aware streaming token classification under internal/runtime/executor/helps
- Implement responses_ttft_helpers.go for OpenAI Responses / Codex WebSocket & SSE streaming
- Filter out container metadata, rate limits, handshake headers, and tool execution outputs
- Implement dual-track firstPacketDuration fallback for abnormal / zero-token stream terminations
- Add TrackHTTPClientRoundTripOnly to bypass false TTFT triggers on first response body reads
- Optimize UsageReporter.ObserveTokenEvent hot path with fast-path RLock short-circuiting (0 B/op)
- Align terminal event completions across SSE, WebSocket streaming, and non-streaming executions
- Add EnsurePublished metric recording fallback for successful terminal turns lacking usage blocks
- Add stub helpers with comprehensive specifications for Chat, Claude, and Gemini protocols

* fix(executor): normalize SSE response.done and terminate incomplete WS streams

* fix(usage): capture first-packet fallback on initial body reads with TrackHTTPClientRoundTripOnly
2026-08-29 01:22:21 +08:00
Luis Pater
be1763e59e fix(claude): fallback to array index when tool call index is omitted
- Fall back to array index in tool calls accumulator when the `index` field is missing.
- Ensure parallel tool calls without explicit indices are accumulated properly during streaming.

Closes: #5058
2026-08-29 01:22:20 +08:00
Luis Pater
677dbe1dc5 fix(claude): emit message_delta when openai streaming finish reason is omitted
- Ensure `message_delta` is emitted on tool call usage chunks and stream completion even if `finish_reason` is missing or null.
- Add `terminalOpenAIFinishReason` fallback to map missing finish reasons to standard stop reasons.
- Consolidate content block finalization into `finalizeOpenAIAnthropicContentBlocks` and delta emission into `emitAnthropicMessageDelta`.

Closes: #5308
2026-08-29 01:22:18 +08:00
Luis Pater
f0de1d008f Merge pull request #5273 from esokullu/codex/add-webbrain-project
docs: add WebBrain to related projects
2026-08-29 01:20:46 +08:00
Luis Pater
d9cea8904b docs: remove BmoPlus sponsorship
- Remove BmoPlus sponsor listings from English, Chinese, and Japanese README files.
- Remove unused `bmoplus.png` image asset.
v7.2.145
2026-08-28 17:29:47 +08:00
Luis Pater
3db0a86c60 fix(management): synthesize persisted auth record before invoking post-persist hook
- Synthesize the saved auth file from disk using `SynthesizeAuthFile` after persisting token records.
- Pass the canonical synthesized auth record to `postAuthPersistHook` instead of the raw input record.

Closes: #5290
2026-08-28 17:28:27 +08:00
Luis Pater
8dd78042e0 fix(proxyutil): enforce HTTP/1.1 ALPN and configure TLS dial context for HTTPS proxies
- Add `buildHTTPSProxyDialTLSContext` to handle HTTPS proxy TLS handshakes with configurable timeout and TLS options.
- Restrict ALPN `NextProtos` to HTTP/1.1 for HTTPS proxy connections to prevent HTTP/2 negotiation issues during proxy CONNECT tunneling.
- Support custom TLS configuration in `httpConnectDialer` for HTTPS proxy connections.

Closes: #5287
2026-08-28 16:50:33 +08:00
Luis Pater
b5cde4ba42 fix(gemini): add default items schema for tool array definitions
- Populate a default string `items` schema on tool array definitions missing an items property for Gemini and Antigravity.
- Add helper to identify array declarations across single strings and type lists during schema normalization.

Closes: #5292
2026-08-28 13:21:50 +08:00
sususu
3e20d662a7 Revert "fix(home): allow home config to override default port"
This reverts commit 1502ac826d.
2026-08-28 11:38:17 +08:00
sususu
7bc16ee3db fix(auth): preserve round-robin successor across ready view rebuilds
Keep round-robin rotation stable in the scheduler fast path when
credentials enter cooldown or are removed. Replace the numeric readyView
cursor modulo normalization with ID-based successor binary search,
aligning readyView with RoundRobinSelector.
2026-08-28 09:17:57 +08:00
Luis Pater
d36b776c79 fix(test): improve cross-platform compatibility across unit tests
- Support CRLF line endings and skip hidden dot directories in nocopy invariant tests.
- Handle environment variable overrides and skip case-sensitive token priority tests on Windows.
- Use static OS and architecture values in Claude header fingerprint assertions for deterministic test results.

Closes: #5295
v7.2.144
2026-08-28 05:01:42 +08:00
Luis Pater
f8c45c30c5 feat(codex): map claude output_config format to text format
- Map Claude `output_config.format` with `json_schema` type to Codex `text.format`.
- Preserve custom schema name and strict configuration with appropriate defaults.

Closes: #5280
2026-08-27 18:19:04 +08:00
Luis Pater
8ee9add75d fix(claude): drop trailing assistant prefill for fable models
- Drop unsupported trailing assistant prefill messages when converting requests for Claude Fable models.
- Fall back to an empty user message turn when dropping assistant messages leaves the request empty.

Closes: #5279
2026-08-27 18:04:56 +08:00
Luis Pater
95f83a8d96 fix(claude): treat allowed_warning as allowed in unified rate limit checks
- Treat `allowed_warning` status as allowed for shared 5h and 7d rate limit windows.
- Ensure Fable-only rejections with warning-level shared windows remain model-scoped instead of credential-scoped.

Closes: #5275
2026-08-27 17:32:07 +08:00
Luis Pater
06997df44a fix(gemini): cache trailing text thought signatures instead of emitting carriers
- Cache trailing thought signatures associated with preceding text blocks via best-effort cache.
- Suppress emitting detached thinking carrier blocks after visible text in streaming and non-streaming responses.

Closes: #5272
2026-08-27 17:21:58 +08:00
Luis Pater
fcea738f74 fix(codex): use codex status error for websocket handshake rejections
- Use `newCodexStatusErr` when handling HTTP handshake rejections in WebSocket execution and streaming.
- Ensure rate limit retry-after metadata and error payload details are properly parsed from handshake responses.

Closes: #5270
2026-08-27 17:09:28 +08:00
Luis Pater
1cc72b9d13 fix(codex): filter extended reasoning levels for older client versions
- Filter `max` and `ultra` reasoning effort levels for Codex client versions prior to `0.144.0`.
- Extract and forward the `client_version` query parameter across model catalog response handlers.
- Add dotted version parsing and comparison utilities to verify extended reasoning level compatibility.

Closes: #5262
2026-08-27 16:51:33 +08:00
Luis Pater
4b5f1eab25 feat(plugin): support observing upstream websocket response events
- Introduce `WebSocketResponseObserver` capability and bump plugin ABI schema version to 4.
- Forward upstream WebSocket response frames from Codex and xAI executors to configured observers.
- Wire `WebSocketResponseObserver` across API handlers and plugin host dispatchers.

Closes: #5248
v7.2.143
2026-08-27 05:30:19 +08:00
Luis Pater
b7f6c15f83 fix(pluginhost): detach context and log rpc failures in usage handling
- Detach cancellation from context before dispatching usage records to plugins.
- Log debug messages when RPC `usage.handle` calls fail.

Closes: #5244
2026-08-27 04:35:51 +08:00
Luis Pater
6f6856e784 fix(gemini): map cached content tokens to claude cache read usage
- Deduct `cachedContentTokenCount` from `promptTokenCount` for `usage.input_tokens`.
- Set `usage.cache_read_input_tokens` when cached tokens are present in streaming and non-streaming responses.

Closes: #5238
2026-08-27 04:19:22 +08:00
Luis Pater
4fa1de2f9b feat(claude): support server-side web search translation for openai responses
- Map Claude `server_tool_use` and `web_search_tool_result` content blocks to OpenAI Responses `web_search_call` items in streaming and non-streaming modes.
- Support replaying `web_search_call` items and text search annotations back to Claude server tool blocks and citations.

Closes: #5236
2026-08-27 04:04:11 +08:00
Luis Pater
cb8746fb63 fix(claude): use zero-based sequential index for streamed tool calls
- Track tool call indices independently using a sequential counter instead of reusing Claude content block indices.
- Set the sequential tool call index when emitting streaming delta chunks.

Closes: #5229
2026-08-27 02:39:02 +08:00
Luis Pater
adac1e5816 fix(gemini): improve schema normalization for unions and unsupported constraints
- Avoid overwriting parent properties when resolving `anyOf`/`oneOf` unions on object schemas, merging branch properties instead.
- Add `contains` to unsupported constraints and preserve object/array constraint hints in descriptions.
- Strip `required` arrays when the schema does not define a `properties` object.

Closes: #5219
2026-08-27 02:14:40 +08:00
Luis Pater
9b88808fc7 fix(xai): fold namespace tools and restore dispatcher tool calls
- Introduce `xaiNamespaceRestorer` to track and restore folded dispatcher tool calls across SSE and WebSocket response events.
- Support unwrapping dispatcher tool calls and arguments in `output_item.added` and `function_call_arguments.done` events.
- Normalize historical input namespace tool calls to dispatcher format when namespace folding is active.

Closes: #5214
2026-08-27 01:08:35 +08:00
Luis Pater
2555cde2f1 fix(xai): inline local refs and broaden codex app tool normalization
- Export `InlineLocalRefs` utility to resolve local JSON Pointer references.
- Inline local definitions and remove `$defs`/`definitions` in tool function parameters.
- Support `mcp__` prefixes and `codex_apps` namespace variations when identifying Codex app automation update tools.
- Handle `$ref` entries when inspecting and normalizing root union schema branches.
2026-08-26 23:41:44 +08:00
Emre Sokullu
d198817c61 docs: add WebBrain to related projects 2026-08-26 18:15:38 +03:00
sususu
1502ac826d fix(home): allow home config to override default port 2026-08-26 15:17:16 +08:00
Supra4E8C
ba200aefa0 fix: update Infistar registration links in README files 2026-08-26 13:54:53 +08:00
sususu
1f53b2eb03 fix(auth): keep credential rotation fair when candidates are filtered
Both built-in rotation strategies lost their position whenever the candidate
set shrank, which happens on every retry that excludes an already tried
credential and on every cooldown transition.

Smooth weighted round-robin reset every accumulated credit as soon as the
weight vector differed from the previous call. A transient subset is not a
configuration change, so the reset fired constantly. With all credits back at
zero and equal weights, the strict `>` comparison always resolves ties to the
first entry in slice order, and candidates are sorted by auth ID, so every
retry restarted the cascade at the alphabetically first credential. Credits are
now reset only when a credential's configured weight actually changes, and the
accumulator is bounded so permanently removed credentials cannot leak entries.

Plain round-robin indexed a monotonic counter into the filtered slice via
`available[index%len(available)]`. Once the slice shrank, the modulo re-seated
the rotation instead of resuming it. Rotation now continues from the identity
of the previous pick, resolved with a binary search over the sorted ring.

Measured over 9 equally weighted credentials with a realistic mix of long
sessions, new sessions and retries, the busiest-to-quietest ratio drops from
11331x to 1.1x. Distribution is exact for equal weights, matches configured
ratios for unequal weights, and tracks the theoretical optimum within 3% under
random credential unavailability.
v7.2.142
2026-08-25 18:55:00 +08:00
sususu98
998dcfeba2 fix(antigravity): safely synthesize terminal finish reasons (#5230)
The Antigravity backend sometimes ends a 200 stream without ever emitting
finishReason. Evidence from local request logs: 25 of 18,346 captured
cloudcode-pa streams have no finishReason at all (gemini-3.7-flash x23,
gemini-3.6-flash x2). Gemini and OpenAI chat clients then never see a
terminal event and wait forever.

Only synthesize on a clean end of stream
- The [DONE] tail is now translated only when scanner.Err() is nil. A
  truncated upstream stream previously still produced a terminal event:
  replaying a cut stream to a Claude client emitted the full
  content_block_stop / message_delta / message_stop sequence, so the
  truncation was reported as a completed message.
- Only Antigravity translators synthesize on [DONE], so the other
  executors that emit the tail before checking scanner.Err() cannot leak a
  fake terminal event and are left unchanged.

Never finalize a stream that produced nothing
- Synthesis requires at least one chunk carrying candidates or token
  accounting. Both translators share the same check, and presence alone is
  not enough: `{}`, `{"response":{}}` and `{"response":{"candidates":[]}}`
  leave the stream unstarted.
- Without that guard the synthetic chunk defeats the existing empty_stream
  detection in sdk/cliproxy/auth/conductor_stream.go, which only fires when
  the executor produced no chunk at all. An empty 200 would be reported as
  a successful empty completion instead of a failure.

Synthetic chunks mirror the observed upstream shape
- All 18,321 real terminal chunks carry candidates/usageMetadata/
  modelVersion/responseId with a model-role candidate whose parts are
  [{"text":""}]. The Gemini synthetic chunk now reproduces that shape and
  key order instead of a bare finishReason candidate.
- The last known usage snapshot is carried into the synthetic chunk.
  Without it the final chunk a client sees reports no tokens, because
  FilterSSEUsageMetadata renames non-terminal usage to cpaUsageMetadata
  and the Gemini path restores it per chunk.
- The OpenAI chat path keeps the latest cpaUsageMetadata as pending usage
  and emits it on [DONE] for the same reason.

Do not mistake an intermediate chunk for the terminal one
- A chunk carrying usage but no finishReason stays non-terminal.
  FilterSSEUsageMetadata forwards real usageMetadata on such a chunk only
  after an earlier chunk already carried finishReason, which the existing
  condition covers; finalizing on usage alone would cut the stream short.
- finish_reason and native_finish_reason are resolved by one shared
  helper, so the upstream terminal chunk and the synthesized [DONE] chunk
  cannot drift apart.
- The non-stream Gemini conversion defaults a missing finishReason for
  every candidate rather than only the first one.

Also fixes the unreachable alt != "" branch, which parsed an always-nil
buffer, and replaces an unchecked param type assertion.

Verified by replaying byte-exact upstream bodies extracted from request
logs through a mock backend, comparing this change against the unmodified
branch point: clean streams keep exactly one terminal event, streams
without finishReason gain one carrying the last usage snapshot, a stream
cut mid-chunk surfaces the read error with no terminal event, and an empty
200 now fails with empty_stream instead of reporting a successful empty
completion.
2026-08-25 15:13:04 +08:00
Luis Pater
f2b1996b3f fix(gemini,antigravity): align parallel tool results with preceding tool calls
- Add `AlignClaudeToolResults` to order `tool_result` blocks to match the preceding `tool_use` IDs while preserving other content parts.
- Apply tool result alignment in Claude-to-Gemini and Claude-to-Antigravity request translators.
- Preserve mixed non-response parts when normalizing and reordering parallel function responses in Antigravity executor.

Closes: #5199
2026-08-25 14:57:46 +08:00
Luis Pater
80de901550 fix: preserve multi-reference video durations 2026-08-25 12:44:00 +08:00
Chén Mù
adf052984f fix(antigravity): remove cross-endpoint fallback (#5209) (#5228)
Fixes #5209
2026-08-25 11:51:17 +08:00
Luis Pater
e1bf893956 feat(github): resolve GitHub token for release checks and asset updates
- Add `util.ResolveGitHubToken` to resolve GitHub API tokens with priority order (`GITHUB_TOKEN`, `github_token`, and `GITSTORE_GIT_TOKEN` for GitHub repositories).
- Set GitHub `Authorization` headers in management version check and asset updater requests when a token is resolved.

Closes: #5189
2026-08-25 04:22:46 +08:00
Luis Pater
ba510f85a2 fix(pluginhost): add plugin quiesce handling with safe rollback during hot reload
- Add new `plugin.quiesce` ABI method and propagate RPC error codes from plugin call failures.
- Invoke quiesce on the replaced plugin before loading a new version, then only activate replacement after quiesce succeeds.
- Improve hot-reload safety by serializing lifecycle transitions, cleaning up failed/canceled loads, and rolling back to the previous plugin state when replacement fails or is canceled.

Closes: #5134
2026-08-25 01:32:57 +08:00
sususu98
ca601db05d feat: observe upstream provider quota signals (#5211)
Codex and Claude already emit credential-level quota watermarks on ordinary
responses. CPA used to drop them. Keep the latest watermark in memory and
return it from the management auth-file API.

Hard rule: this is observation only. It must not change scheduling, cooldown
selection, or auth-file persistence.

Snapshot, not accumulation
- QuotaState now has ObservedAt and a bounded Signals map. MarkResult fills
  them from the response headers already recorded on the request.
- Signals is the current response, not a union of earlier ones. Retry-After
  and "limit reached" only appear on the response that produced them; merging
  across responses would keep an expired value forever.
- A response with no quota header (transport failure, 5xx, unrelated endpoint)
  leaves the previous snapshot in place.
- ObservedAt is the time of the current snapshot. It advances even when the
  values did not change, so a consumer can tell a fresh reading from a stale
  one.
- When two model states merge, keep the newer snapshot. Do not union keys
  captured at different times.

What is observed, and what is not
- One predicate, ProviderSupportsQuotaObservation, decides the provider set.
- Keep Codex and Claude. Drop Kimi, xAI/Grok, Antigravity, and the Gemini
  family (gemini/vertex/aistudio): their ordinary headers are not a reliable
  credential-level remaining quota.
- Count-tokens reuses the credential but is not generation traffic.
  ExecuteCount sets SkipQuotaObservation so those headers cannot replace the
  last generation snapshot. Cooldown and success/failure accounting still run.

Cooldown must not overwrite the last snapshot
- Observation writes only ObservedAt and Signals.
- Cooldown writes only Exceeded, Reason, NextRecoverAt, and BackoffLevel,
  through applyCooldownFields. Never assign a fresh QuotaState{...} over a
  live value: that would zero the snapshot on 429, Cloudflare, credential-
  scope sibling updates, and cooldown clears.
- If a credential-quota cooldown is still active, MarkResult still observes
  an already-present model state. It does not create scheduler state just to
  record a watermark.
- .cds files persist cooldownFieldsOf(Quota) only. Restore keeps the newer
  ObservedAt, so reloading cooldown cannot clobber a newer in-memory snapshot.
- cooldownQuotaEqual still ignores observation fields, so a watermark change
  cannot by itself persist cooldown or move the scheduler.
- The management payload omits every cooldown field, so it cannot be mistaken
  for scheduler state or wired back into scheduling.
- Manual ResetQuota still clears the full QuotaState.

Codex websocket events
- Codex WS reports quota as codex.rate_limits frames, not HTTP headers.
  ParseCodexQuotaEventHeaders turns one event into the same bounded header
  shape, and MergeResponseHeaders folds it into the request-scoped holder.
  additional_rate_limits is accepted as an object (websocket) or an array
  (/wham/usage).
- Parse only through AppendCodexAPIWebsocketResponse. The shared
  AppendAPIWebsocketResponse is also used by xAI, and xAI error frames really
  do carry x-ratelimit-* headers. Parsing every frame as Codex quota would
  forge Codex headers into another provider's request log.
- Also capture code_review_rate_limits.
- A malformed active-limit name drops only that one header, not the window
  watermarks parsed from the same event.
- The type discriminator scans a bounded frame prefix, not every byte of
  every frame.
- HTTP namespaces an extra limit by short name (x-codex-bengalfox-*); WS
  namespaces it by limit name (GPT-5.3-Codex-Spark). The two paths cannot
  emit the same header names. The X-Codex-Additional- prefix marks the WS
  origin, and snapshot replacement keeps the two spellings from piling up.

Hardening
- Reject observed values with control characters. These strings reach the
  plain-text request log, and Limit-Name is upstream-controlled, so CR/LF
  could forge a header line.
- When the header cap is hit, keep plan/credits/primary ahead of
  additional-limit namespaces, then sort names so truncation is deterministic.
- QuotaState.Clone deep-copies Signals and is used by Auth.Clone and
  ModelState.Clone.
- Token stores still serialize credential metadata only, so observation adds
  no auth-file writes.
2026-08-24 17:15:37 +08:00
lzt404
dc3c3b1ec3 Merge pull request #5212 from router-for-me/apimart-sponsor
Apimart sponsor
v7.2.141
2026-08-24 16:35:18 +08:00
lzt404
cd445f2be8 Merge remote-tracking branch 'origin/dev' into apimart-sponsor 2026-08-24 16:31:23 +08:00