Repository navigation
Cartethyia 2.7.1
Cartethyia 2.7.1
[2.7.1] - 2026-10-28
Fixed
-
Qoder: index-less continuation fragments and plain-text leak markers keep
their arguments. Two upstream shapes still dropped tool arguments after
the first fix: continuation chunks that omitindexentirely (no pinned id
to join, so encoders dropped the nameless fragment), and the INTL leak
marker[assistant 请求调用工具] …when it arrives as plain text rather than
DSML markup (it was never buffered, so it never reached recovery). The
translator now falls back to the most recently seen call id for index-less
fragments (mirroring the generic chat parser), and accumulates non-DSML
text so the leak marker is recovered at end of stream with full arguments —
the "assistant talks, then calls a tool with {} arguments" reports. -
Routing no longer redials an account whose cooldown deadline has elapsed
but has not been swept yet. A row still incooldownstatus used to read
as healthy the moment itscooldown_untilpassed, so requests landing in
the window between the deadline and the next 30s recovery sweep reproduced
the refusal that cooled the account — logging a freshcooldown → cooldownhealth row for each one (the "still hit a cooled-down account"
report). The snapshot builder now stamps acooldownrow regardless of
whether its deadline is expired; the sweeper remains the only exit, so the
account is not served again until it is genuinely recovered. -
Qoder billing-limit refusals no longer invalidate the credential. Qoder
answers an exhausted daily billing allowance with
{"code":"110","message":"Billing daily count exceeded"}on a 403 wrapper.
The status-only fallback read the 403 as a credential fault, so a healthy
account was parked asauth_invalidated— the console's "Re-login
required" — and operators were sent to re-authenticate a credential that was
never rejected. The110code and the "Billing daily count exceeded" phrase
now classify as quota (an account cooldown), and unknown numeric provider
codes are never treated as credential evidence — the safe unclassified
cooldown is the default for a code we do not recognise. -
Qoder streaming tool calls no longer lose their arguments. The upstream
identifies one streamed call by OpenAI chunk shape —id+namein the
first chunk, then only the wireindexon every continuation fragment. The
qoder translator synthesized a freshcall_<index>id per chunk instead of
pinning the first id, so every continuation arrived nameless under an
unestablished identity and was dropped by the encoders. Clients saw tool
calls whoseargumentswere always{}(agent harnesses rejected every
native bash/write/read call as "missing required field"). The translator now
pins the first id per wire index and hands retries the same behavior as the
generic chat parser. -
Capability degradation no longer strips tools as collateral. When the
only capability a route cannot serve was ordered aftertoolsin the
degradation chain (an image attached to a text-only model such as Mercury
3), the surviving cumulative variant had already lost the tool catalog — the
tool-capable model then received a transcript of[tool:<name>]text
placeholders with no tool definitions and imitated them instead of
structurally calling tools. Degradation now tries each single-capability
loss against the pristine request first, so the smallest surviving
degradation wins (e.g. drop only the image, keep tools) before the
cumulative chain runs as fallback. -
Trae stream refusals are classified, not blanket-routed. Upstream
1001 "we are not able to authenticate you"now surfaces as an account-scoped
authentication_failed(401 + credential evidence) instead of a generic 502
platform fault, and4008 "exceeded the quota"asquota_exceeded(429).
Error objects carried in frame bodies (HTTP 200 with{"code":…,"message":…})
are detected too, not only namederrorevents — the wrong classification was
hiding the real cause and could drag the proxy pool into the story. -
The health classifier recognizes Trae's 1001 verdict. A numeric upstream
code was previously excluded from the credential arm, so1001fell through
to theunknownfallback that never mutates — the account stayed in rotation
and was re-dialled on every request despite the upstream's auth verdict. Trae
1001is nowauth_invalidated(account-scoped, routing-visible), so the
account cools/excludes and failover moves to a sibling. -
Trae prompt-cache estimation is re-adopted and scoped per (session,
model). Trae's agent surface never reports cache usage, so repeated
prefixes are estimated locally from per-session history; two models sharing a
conversation id cannot cross-hit, and the assessed usage rides the terminal
event so non-streaming telemetry records real counts. -
Buddy desktop device-token follows the official
token || error
contract. The desktop client sendsX-Device-Tokenon every realm (Intl
included) and never omits both it and the error header: when no real token
exists it sendsX-Device-Token-Error: sdk_not_initialized. The buddy
providers now do the same instead of dropping the header (or sending it only
on CN). -
A first-event timeout is a deadline, not a client cancel. The
empty-stream watchdog used to classify a silent upstream (e.g. a provider
holding the request in a queue) astransport_closed499 — "cancelled by
client" — hiding a real upstream wait behind a cancellation. It is now
deadline_exceeded504 with upstream origin, matching the streaming
stall/first-chunk watchdogs. -
Qoder recovers single-object leak echoes. The INTL leak marker could be
followed by one JSON object ({"name":…,"arguments":…}) instead of an array;
that shape was rejected, so the whole block was released as prose and the
marker leaked to the client as visible text.parseLeakedToolCallsnow
normalizes a single object into a one-entry array.
Added
-
Provider Detail "Activity Matrix" usage card. A collapsible card at the
top of the provider page condenses the last 1–366 days of usage into a
calendar heatmap (Mon–Sun weeks,GET /console/api/providers/:providerId/usage/matrix?days=N, aggregated directly
fromtelemetry_events— no migration). Summary strip totals requests,
tokens, cached tokens, and estimated cost. Cells are tinted by the day's
dominant model with a stable per-model color (hashed hue), hover shows a
native day tooltip broken down per model, and an expandable "Usage by model"
list ranks models by tokens with colored share bars. -
Inception Labs (Mercury 3) as a bundled free-tier provider.
inception
ships withmercury-3pinned to the free tier — one million token context,
131072 max output, reasoning and tool calling, zero price. It appears under
Free Limited Providers in the console. A 429 from the per-minute window
cools the account for one minute (upstreamRetry-Afterstill wins) instead
of the generic fifteen-minute guess, so a throttled account recovers as soon
as the window resets. -
A
randompool-group selection strategy. Pool selection previously only
knew the ordered scans, so a burst always hit the same first pools and
burned them one by one while siblings sat idle — the "all pools cooling,
fresh ones unused" pattern.randomshuffles the eligible pools per request
(still skipping cooldown/blocked pools and falling through full ones). Set
it per tenant via the pools strategy endpoint; migration0025ships for
both backends. -
Pool cooldown and probe verdicts survive refresh and restart. A probe
429 now writes a cooldown marker the console shows, markers persist across
restarts, and blocked egress verdicts are restored at boot — a spent or
retired egress is not re-picked after a restart. -
Qoder daily check-in (streak / 100 Credits per day). Qoder accounts now
ride the same daily check-in machinery as the Buddy family: the quota sweep
claims the grant once per day, and a manual trigger is available through the
account quota surface. Legacy/sash/.../daily-check-instatus/claim is
tried first; when the international realm does not expose it (404), the
CREDITS-kind activity campaign is claimed instead. Streak days and the 100
credit grant are reported in the sweep log. -
OpenCode free tier aligned 1:1 with the reference bridge (v2). Flat
/inferencedispatch paths, the minimal free-tier prompt, native proxy
egress, and pool bulk actions in the console. -
Validated direct egress uses native fetch by default, with per-provider
opt-in and opt-out. -
The TuringShield SDK is bundled under
vendor/turing-sdk/(WorkBuddy
desktop 5.8.4 extract) so buddy providers generate realX-Device-Token
values without any environment setup —CARTETHYIA_TURING_SDK_PATHremains
an override. Per-provider payload profiles (WorkBuddy vs CodeBuddy product
identity on channel 400111) make tokens differ between providers, and the
Dockerfile ships the vendor directory into both builder and runtime stages. -
Per-account device binding (
qimei36). Buddy desktop chat now sends
X-Device-Id-List: {"qimei36":"…"}resolved per account — explicit
auth_state.qimei36wins, otherwise a deterministic sha256 of the account id
(32 hex, stable across restarts, no DB write). Every pooled account presents
a distinct device alongside the machine-bound Turing token. -
Mistral catalog expanded to models verified live. The static roster was
re-checked against a live key:codestral-2508/latest,
mistral-code-latest, the ministral family,voxtral-small-2507/latest,
themistral-medium-latestaliases,mistral-vibe-cli-*, and
magistral-*now ship, while rows that answer 403
(tier_not_allowed— e.g.mistral-large*) or 400 (invalid_model—
Labs, embed/OCR/transcribe/TTS/realtime) are excluded, so every model the
catalog offers can actually serve.
Fixed
-
Qoder (INTL) tool calls and structured output are more robust. Adapted
from the reference bridge: DeepSeek multi-turn replaysreasoning_content
on every assistant turn once the conversation has a reasoning trace (absent
is refused upstream — the source of occasional failures that passed on
retry); tool-call echoes are recovered in every marker variant the model
emits, with trailing DSML tags stripped; structured tool turns fall back to
prose when a call lacks an id; and image parts are sent as structured
image_urlparts instead of being silently dropped (the upstream reads
message content, not the top-level field). Verified live on
qoder/deepseek-v4.1-flashandqoder/qwen3.8-flashonly. -
Health events no longer depend on database defaults. SQLite lacks
gen_random_uuid()/now(), so ids and timestamps are now supplied in code
— event rows survive and the activity panel stays truthful in Lite mode. -
bun run devstays quiet while the backend restarts. A refused Vite
proxy connect answers a graceful 502 JSON instead of a wall of
ECONNREFUSEDlogs;VITE_NO_BACKEND_PROXY=1removes the proxy entirely
for pure dashboard work. -
Egress blocks are scoped to IP-limited providers. A free-tier refusal
only retires the egress address for providers whose limits follow the
address; for everyone else the refusal belongs to the account dialed, so a
shared proxy is never sidelined for an account-keyed refusal. -
OpenCode registry wire-path maps synced to the v2 family segment, and
the system-one surface stays on the flat/inferencepath (verified live). -
Upstream 429 free-limit probes are labelled as rate limits, not generic
failures, so the health panel draws them correctly. -
DSML tool-call recovery matches the reference bridge's edge semantics
with parity tests. -
Boot path hardening. The 30s task cadence is staggered and the prior
listener stops on hot reload; task registration is idempotent, shutdown
flush bypasses backoff, the install-id knob is governed, a single share
store is used, and the inflight catalog store is cached. -
Buddy providers no longer hit load-time import cycles (leaf CN catalog
module). -
The gateway surfaces the upstream's full error prose instead of
truncating it to 500 bytes, and an upstream socket drop is classified as
502/upstream— not500/cartethyia. -
Vercel relay deployments are public again. The deploy flow left the
project under Vercel's SSO Protection, so a deployed relay answered 401 for
anonymous traffic — "deploynya sukses tapi gak public". Deploys now set
ssoProtection: null(via the project settings PATCH) so the front door
serves any caller. -
Hosted relay dials are direct fetches, not CONNECT. A relay front door
(Vercel/Workers/Deno) answers application HTTP and refuses CONNECT, but the
validated egress handed the relay URL to the runtime fetch as itsproxy
option, so every relay dial failed with "Proxy refused to open a tunnel:
400 Bad Request" — including pool health checks and the Add Proxy "Test
URL" probe. Relay dials now fetch the relay directly, carrying the target
origin/path inx-relay-target/x-relay-path, and the relay worker strips
content-encoding/content-lengthfrom the upstream response — Vercel
Edge already decodes the stream it hands back, so re-sending gzip headers
made callers decompress twice (ZlibError on gzip targets). Verified live:
the canary probe reports healthy and gzip/OpenCode targets return 200
through a deployed relay.
Changed
-
Network egress unified on one native-fetch path — the node dialer is
gone; validated direct egress and proxy egress share the same transport. -
Transport wiring consolidated. One
GatewayHandlerDepsand one
nativeCompletionBase; dead dispatch fields and an unused
providerAdaptersfallback were removed. -
Quota shared cache and refresh moved to the providers layer; transport
and workers no longer reach into console code. -
Protocol layer now owns translation, reasoning, and upstream parsing —
the protocol-to-transport/provider inversions are gone. -
Backend monoliths split: pure classification and catalog helpers were
extracted from the large services. -
Dashboard monoliths split: Proxy, Usage, and Accounts pages are section
components; the dead toolbar and EffortPicker were dropped and usage periods
are single-sourced. -
Test layout aligned with the
srcmodule tree.
Removed
- Buddy redeem, travel, lottery, and trial-claim worker tasks.
- The request-lifecycle-derived in-flight gauge.
- The model-abuse ban surfaces the strike layer owned.
- Orphaned start and watch-traffic scripting.
- The
reports/artifact directory.
Performance
- Client identity is resolved once per request, and the route snapshot is
warmed at boot instead of on first request. - The worker scheduler injects a lease-acquire timeout.
Tests
- Dropped heavy e2e gateway suites, low-value suites with sibling coverage,
duplicated assertions, a stale unit bucket, and donor-referencing provider
suites; CLI port-startup moved to integration scope.
Documentation
- README polished with a development/testing section; skill pages point the
engineering completion section at the verification owner; stale test
references and architecture wording fixed; references refreshed for the
relocated translation, quota, and governance paths.