mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-18 21:02:50 +03:00
d032431a5fc9c331d28dd8af62461d20f8d80c8d
129 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5455740faa |
fix(compression): log warnings for unreadable settings rows (#13522)
* fix(compression): log warnings for unreadable settings rows getCompressionSettings() silently skipped non-string (BLOB) and invalid-JSON settings rows, making it impossible to diagnose config drift between the panel and the runtime. Now logs a warn-level message for each unreadable row, including the key name and a remediation hint (re-save from the Storage panel). Also warns when the 'engines' row exists but yields no valid toggles, so operators know their panel-configured engines map is being silently replaced by the legacy fallback. Fixes #13456 * test(compression): cover getCompressionSettings warnings for unreadable rows The test for #13456 only asserted a stubbed console.warn recorded a message and never called getCompressionSettings(), so it never exercised the production change. Seed a BLOB row, an invalid-JSON row, and an 'engines' row that isn't a usable object, and assert the resulting warnings; also assert a legitimately empty (but valid) 'engines' map does not warn. Also stop warning on a valid-but-empty 'engines' row: parseStoredEnginesMap returns null both for an unreadable row and for a well-formed {} (an operator who deliberately disabled every engine), so only warn when the stored value isn't a usable object at all. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Koosha Pari <koosha@phenotype.ai> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
fde6241d41 |
test: close the database before removing temp DATA_DIR (#13290) (#13292)
* test: close the database before removing temp DATA_DIR (#13290) Tests that set their own DATA_DIR and removed it in test.after() failed on Windows with EPERM: nothing closed the SQLite connection, so the directory still had an open handle and the -shm/-wal sidecars kept it locked. maxRetries could not help because every retry hit the same open handle. Adds tests/_setup/tempDataDir.ts with cleanupTempDataDir()/createTempDataDir(), which close the DB singleton (lazily imported, so tests that never touch the database do not pull in the DB layer) and then remove the directory best-effort. Applies it to the five suites confirmed failing. The helper's own test proves the ordering matters: skipping the close makes it fail with 'cleanup must remove the directory'. * test: close the database before removing temp DATA_DIR (15 more suites) Converts the suites that measurably emitted EPERM during a full run to the shared cleanupTempDataDir helper from #13292. Measured on the same 15 files: base -> 22 fail, 40 EPERM lines branch -> 7 fail, 10 EPERM lines The 7 remaining failures are pre-existing and unrelated to teardown: rtk-learn-discover-routes and executor-map-golden already fail on a clean base (6 and 3 failures respectively). * test: close the database before removing temp DATA_DIR (final 9 suites) Completes the #13290 sweep. Two teardown shapes needed the helper: - after()/t.after() hooks that removed DATA_DIR directly - beforeEach() hooks that wiped DATA_DIR between tests while the previous test's connection was still open. These failed *before* the test body ran, so every test in the file reported the same EPERM path. Three of them already called core.resetDbInstance() right before rmSync and still leaked, which is the product-side connection leak tracked in #13303. Measured per file, EPERM lines now 0 across all nine. Remaining failures are pre-existing on a clean base (firefly 4->1, driverFactory 1, responses-* 1 each) and unrelated to teardown. * test: add the missing cleanupTempDataDir import to two responses suites The previous commit swapped rmSync for cleanupTempDataDir in these two files but did not add the import, so both suites died with ReferenceError: cleanupTempDataDir is not defined before running any test. responses-parse-once-4041: 0 pass / 1 fail -> 4 pass / 0 fail responses-route-early-keepalive-wiring: 0 pass / 1 fail -> 3 pass / 0 fail Both now report 0 EPERM. * test: close SQLite handles in three silently-leaking suites These three suites requested DATA_DIR cleanup but the delete failed on Windows because a SQLite connection was still open. They pass today, so the leak is invisible: they carry state between tests and would surface later as an unrelated-looking assertion, as #13303 already did in the Firefly suite (a 500 instead of a 401). agentbridge-mitm-router-key-6403 and agent-bridge-bypass-flow removed their own temp dir in test.after() without closing the DB first; both now use the shared cleanupTempDataDir helper, which closes the singleton before removing the directory. issue-agent-route-execution is a different case: it has no teardown at all, so the connection stayed open until process exit and the isolateDataDir cleanup hook then hit EPERM. It now closes the DB in test.after(). Verified with a probe on fs.rmSync: all three reported a failed delete before, and zero across three consecutive runs after, while the same probe still reports four leaks in the Firefly suite. * test: remove temp DATA_DIR in five suites that never cleaned up These five suites create their own mkdtemp DATA_DIR, open the SQLite DB and never remove the directory, so every run leaves a storage.sqlite behind in the OS temp dir. Each dir is private to its suite, so this leaked disk space rather than corrupting results - but the churn is pointless. Each now closes the DB and removes its directory through the shared cleanupTempDataDir helper. Verified with an exit-time probe that lists storage.sqlite* still present in DATA_DIR: it fired for these suites before the change and is silent after, with the same test counts (22/14/5/3/3 passing). |
||
|
|
9ce43850b7 |
fix(compression): preserve case, tags and negations through llmlingua engine (#13768)
Merged. Shielding XML tags and negation/absolute words from an uncased backend, restoring source casing and edge whitespace on re-stitch, and failing open on an empty reply are each the difference between "compressed" and "changed the meaning". The 6 fidelity tests make the negation list something a future reviewer can argue with in code instead of in the abstract. Validated as a combined board first (this PR merged with the 21 siblings of the same wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, i18n new-key coverage, docs-counts, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 176 passing / 0 failing focused node:test cases across the 25 test files the wave touches and the dashboard test under Vitest (2/0). Then re-validated alone on the fresh tip before this merge: conflicts re-resolved, file sizes rebaselined for this PR's own growth, eslint and this PR's focused tests re-run. Thank you. |
||
|
|
d4a1470e40 |
fix(compression): run user preservePatterns before built-in patterns (#13457) (#13525)
`cavemanConfig.preservePatterns` regions are captured before the built-in patterns. Previously the built-ins ran first and turned inline code, URLs and CONST_CASE identifiers inside the user region into sentinels, and `replacePattern` silently skipped the user match (#13457). Maintainer adaptation: after #13523 moved the instruction envelopes into a region pass that runs before fenced-code extraction, the user patterns now open that same pass. User regions win over the built-in envelopes, and fenced code inside a user region stays with the region too. This keeps the "run user patterns first" semantics of your change. Also fixed the new test's `../../open-sse` import path (it had never run) and three unused `tombstoned` bindings. Reset to the release tip plus only this commit (the branch carried ~2.6k unrelated commits), authorship preserved. All 5 new cases pass; full compression suite 1,531/1,531; budget gate clean. Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green. Thanks @KooshaPari! |
||
|
|
56fedf985e |
fix(compression): preserve polarity words and newlines in ultra heuristic (#13524)
The ultra heuristic no longer prunes polarity and modality words (`never`, `always`, `not`, `must`, `should`, `do`/`does`/`did`, contractions…). They now score 1.0 instead of the 0.1 stopword score, so "must never be deleted" can no longer collapse into "must deleted". It also collapses only runs of spaces and tabs, keeping the newlines that carry bullets, headings and fences (#13454). Maintainer fixes: (1) the new test imported `../../open-sse/…` from `tests/unit/compression/`, which does not resolve, so it had never run. It now passes with the rest of the compression suite (1,526 cases). (2) Dropped the unused `STOPWORDS` import. (3) `check:compression-budget` flagged the expected trade-off: ultra tokens-per-task goes prose 92→97, tool-output 116→117, json 117→126, the cost of keeping meaning-bearing words and line structure. The baseline was refreshed with `--update`, which also records a pre-existing caveman tightening (129/127/160 → 119/114/136). (4) The branch also carried the unrelated `feat(sveltekit)` commit and the #13523 commit, so it was reset to the release tip plus only this commit, authorship preserved. Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green. Thanks @KooshaPari! |
||
|
|
c46ca1dc75 |
fix(compression): preserve instruction blocks from lossy engines (#13523)
Lossy compression engines leave `<system-reminder>`, `<instructions>` and `<project-instructions>` envelopes byte-identical. Agentic CLIs inject these into user messages, and compressing them inverted negations, dropped emphasis and broke the tags (#13453). Maintainer fixes: (1) the new test imported `../../open-sse/…` from `tests/unit/compression/`, which does not resolve, so it had never run. With the path fixed, the main case failed: the envelopes were appended to the built-in list, so fenced code and inline code inside the reminder had already become sentinels, and `replacePattern` skips any match containing one. The envelopes now form a region pass right after frontmatter, before fenced-code extraction; all 4 cases and the full compression suite (1,518) pass, and the compression budget gate reports no regression. (2) Dropped an unused `tombstoned` binding. (3) The branch also carried an unrelated `feat(sveltekit)` commit (`apps/web/**`), so it was reset to the release tip plus only this commit, authorship preserved. Validated in one consolidated batch of this series (37 PRs boarded together on `release/v3.8.51`): `typecheck:core`, `check:open-sse-typecheck` and `check:dashboard-typecheck` clean; ESLint clean on every changed file; file-size, complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync and migration-numbering gates green (only the pre-existing `open-sse/utils/stream.ts` file-size red remains, inherited from the base); 3,743 focused `node:test` cases plus 34 vitest cases green. Thanks @KooshaPari! |
||
|
|
39abab6681 |
fix(dashboard): honor active compression profile in effective-pipeline preview (#12063) (#13287)
Merged as part of the 39-PR owner batch of 2026-09-11, validated as a unit. Boarded into one consolidated worktree cut from `release/v3.8.51` with the other 38 — zero conflicts between them. - ESLint over every changed file: no errors (the only finding was one suppression entry the batch emptied, pruned on #13243) - `typecheck:core` clean; `check:dashboard-typecheck` OK (206 pre-existing, within baseline); `check:changelog-integrity` OK - complexity 2821 / baseline 3218 and cognitive-complexity 1272 / baseline 1437 — both under baseline - 256 assertions green: 246 under node:test and 10 under vitest, which is where `tests/unit/**/*.test.tsx` actually runs - `check-file-size`: `chatCore.ts` rebaselined 6144 → 6146 for #13278 and #13276, annotated and landed on #13243 ⚠️ base-red inherited: #12732 — the provider count (356 in the docs vs the 358 the modules define) and `open-sse/utils/stream.ts` at 3115 > frozen 3098 both reproduce on the pure tip with zero contribution from this batch. |
||
|
|
3f62e43696 |
test(compression): assert idle eviction terminates at the resource level (#13371)
Merged as the credit vehicle for #12542. Reverse-TDD verified on the tip: 8/8 with the fix, 7/8 with `terminate()` disabled. |
||
|
|
85a5126dba |
fix(compression): terminate idle workers on eviction (#13091)
`remove(slot, false)` dropped the slot without `terminate()`, leaving the OS thread and its heap alive — invisible to RSS, which is why 55 orphaned `MessagePort`s took 16 h to surface. Removing the parameter rather than keeping it is the correct call: the pool was its only owner. --- Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them. - `typecheck:core` clean - complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline - 71 focused assertions green across the 13 test files this batch adds or touches ⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff. Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit. |
||
|
|
66330cc724 |
fix(compression): pass a URL object when spawning the LLMLingua worker (#13093)
`node:worker_threads` only treats a `URL` instance as a `file:` URL — a string must be a relative path. The silent `catch {}` in `pump()` made this degrade compression to a passthrough while still reporting success, which is the worst shape for it.
---
Validated in one consolidated worktree cut from `release/v3.8.51`, boarded together with the other 13 PRs of this batch — zero merge conflicts between them.
- `typecheck:core` clean
- complexity 2799 / baseline 3218 and cognitive-complexity 1265 / baseline 1437 — both under baseline
- 71 focused assertions green across the 13 test files this batch adds or touches
⚠️ base-red inherited: #12732 — `Docs Gates (fast-path)`, `Merge integrity`, `No new ESLint warnings`, `Unit Tests fast-path` and `Fast Quality Gates` all reproduce on the pure `release/v3.8.51` tip (provider count 356 vs the 358 the modules define, SKILL.md drift, and `open-sse/utils/stream.ts` at 3115 > frozen 3098). None of them touch this diff.
Thanks @anhtahaylove — the root-cause write-up, the measured before/after numbers and the red-before-green proof on every one of these made the batch reviewable as a unit.
|
||
|
|
e1a1290fde |
fix(compression): keep tool_result blocks first when aging annotates a turn (#12920)
Boarded with 13 sibling PRs into one worktree off release/v3.8.51 and validated as a set: 132 focused tests pass across all 15 test files in the batch, typecheck:core is clean, check-changelog-integrity reports no lost base bullets, and check-file-size is green. Your PR merged without conflict against its siblings. Thank you — the write-up made this reviewable: measuring the behaviour on the release tip and showing the before/after table meant the defect could be confirmed rather than taken on faith. |
||
|
|
c702a27eda |
perf(compression): OOM mitigations for large payload hashing, memoization, and token estimation (#7847) (#11844)
* perf(compression): memory and OOM mitigations for large payload hashing and token estimation * fix(compression): implement getMemoStats observability for result memo (#7847) Adds the missing memo observability layer referenced by tests/unit/compression/oom-memo-memory.test.ts and the monitoring API: - resultMemo.ts: lifetime hit/miss counters + bounded time-ordered ring buffer (10k entries, ~90KB) powering 1m/5m/15m/1h hit-rate windows; getMemoStats() reports size/capacity/hits/misses/hitRate + windows. - memoLookup() tags served results with stats.memoHit = true. - clearMemoStore() also resets counters and the ring. - compression/index.ts re-exports getMemoStats for the monitoring route. - types.ts: optional memoHit field on CompressionStats. - New GET /api/monitoring/compression route exposing the stats snapshot (lightweight, no DB) for operators to track cache-hit efficiency. * fix(compression): align memo contract with upstream #11727 — return caller object, reset lookup counter in clearMemoStore * fix(compression): restore unwrapEventEnvelope in stream payload collector summaries The OOM-mitigation commit accidentally replaced unwrapEventEnvelope(evt.data) with asRecord(evt.data) in the summary builders and live push, breaking translate-mode {event, data} envelope unwrapping (clientPayload type detection) and failing 2 stream-payload-collector tests. Restored upstream semantics; kept the jsonLength OOM optimization as the only delta in this file. * refactor(compression): break down writeValue and writeEncodedString to pass complexity ratchets Refactors jsonSha256 internal helpers (writeValue, writeEncodedString) into small, single-responsibility sub-functions under the complexity threshold (max cyclomatic 15, max cognitive 15). Preserves exact JSON.stringify parity, circular reference guards on both arrays and plain objects, and escape behavior (all 530 relevant tests pass). * test(compression): make oom-memo heap assertion robust without expose-gc The CI unit-test shard runner does not pass --expose-gc, so global.gc is undefined and heapUsed can still momentarily hold GC-pending transients (observed 53.4 MiB after a 3MiB body). Gate the retained-heap assertion on forced collection being available (3 forced cycles for array buffers) instead of skipping it silently, and keep it fully active when --expose-gc is present. * fix(compression): restore worker-pool offload path in runCompressionAsync The OOM-mitigation refactor dropped the isCompressionWorkerEligible / runCompressionInWorker dispatch at the top of runCompressionAsync, silently removing the base's worker-thread offload for eligible large payloads. Restore the block exactly as on release/v3.8.51, ahead of the result-memo path, keeping the memoization and hashing improvements intact. * docs(api): document GET /api/monitoring/compression and log route errors via pino Add the new monitoring endpoint to docs/openapi.yaml following the neighboring System entries, and replace the route's console.error with the repo-standard pino logger. * fix(skills): regenerate omni-resilience and add changelog fragment Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Co-authored-by: Andrian Balanescu <AndrianBalanescu@users.noreply.github.com> Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> |
||
|
|
a784b42060 |
fix: resolve compression worker file using runtime anchors instead of… (#12183)
* fix: resolve compression worker file using runtime anchors instead of import.meta.url Replace workerUrl() function that used import.meta.url with resolveWorkerFile() function that uses runtime anchors (process.cwd() and process.argv[1]) to locate the worker file. This fixes webpack module resolution in Next.js standalone bundles where import.meta.url is replaced with a stub pointing to build machine path. Also update Dockerfile to copy required worker-related scripts and adjust npm install flags for better compatibility. * fix(docker): restore base Dockerfile — keep npm ci --ignore-scripts supply-chain guard Revert every Dockerfile change from this branch back to release/v3.8.51: the branch dropped --ignore-scripts (reopening install-time script execution for all transitive deps), swapped the reproducible npm ci for npm install, invoked the nonexistent 'npm approve-scripts' command, and broke the better-sqlite3 smoke test with a stray space in ':memory: '. The worker-file fix does not need any Dockerfile change. * test(compression): export runtime-anchor helpers and cover worker-file resolution firstAncestorWith's doc already claimed 'exported for tests' without the export; export it together with resolveWorkerFile and add unit coverage for the runtime-anchor resolution: cwd anchor, dirname(argv[1]) anchor, bounded walk-up (8-level cap boundary), prod-first .js-over-.ts ordering, dev .ts fallback and the fail-open cwd fallback when nothing exists. Fixtures live in mkdtemp sandboxes only — the repo tree is never touched. --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
abbbca216d |
feat(sse): full language parity for output styles (es/de/fr/it/ru/zh + autoDetect) (#12289)
* feat(sse): add it/ru/zh caveman output instructions * fix(sse): expose the dormant terse-prose translations through the catalog * feat(sse): translate less-code to es/de/fr/it/ru/zh * feat(sse): translate ponytail to es/de/fr/it/ru/zh * feat(sse): translate i-have-adhd to es/de/fr/it/ru/zh * test(sse): anchor wave-2 output-style translations to their own language * feat(dashboard): offer every output-style language in the default-language selector * feat(sse): let autoDetect pick the output-style instruction language * docs(compression): consolidate the output-style tables and record full language parity |
||
|
|
3d4f3e4960 |
test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) (#11968)
* test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) Two shards on release/v3.8.51 went red in one day with the same signature — "ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only .github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass alone and on re-run: the cleanup races something still writing into the directory (SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner the window opens. 1154 test files do their own cleanup with fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries. One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record): every rm / rmSync / rmdirSync option object with `recursive: true` and no `maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292 files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included. Only the option object changes: no call site, assertion or import is touched. Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292 files; a random 20-file sample runs green (quota-redis-store hangs identically on the untouched tree — it needs a Redis on localhost, an environment matter). The four unit shards on this PR are the full run. * fix(quality): let check-forgotten-sibling-tests read a 1,000-file diff The gate shells out to `git diff` through execFileSync with Node's default 1 MB maxBuffer; the 1,292-file codemod in this PR is the first diff large enough to overflow it, and the gate died with `spawnSync git ENOBUFS` before comparing anything. 64 MB is far above any real PR and costs nothing when unused. |
||
|
|
a14a91dcfe |
perf(compression): avoid cloning memo misses twice (#11727)
Obrigado! Validado em lote combinado (8 PRs, release/v3.8.51):
- TDD claro: sem o fix, o teste focado falha nas asserções de contagem exata de lookup sync/async; com o fix, `tests/unit/compression/result-memo.test.ts` passa (34/34).
- Remove supressão eslint agora obsoleta (`no-unused-vars` no arquivo de teste).
- `⚠️ base-red inherited: #11449` reconhecido e verificado — não é responsabilidade desta PR (confirmado via probe-worktree do tip puro).
- Gates estáticos do lote OK.
|
||
|
|
04dba0460e |
fix(responses-continuation): recover a real id/output for passthrough and translate-mode replies (#11434)
Retargetado para release/v3.8.51 (release/v3.8.50 está congelada — freeze issue #11439). Validado em lote combinado (batch-0824h2, junto de #11435/#11436/#11437) contra o tip de release/v3.8.51: typecheck:core limpo, gates estáticos OK, 127/127 testes focados passando. Investigação sólida com repro real via container isolado, três causas independentes identificadas e corrigidas com testes de regressão dedicados para cada uma. Obrigado pela contribuição! |
||
|
|
8f390efffd |
feat(codex): self-contained codex app-server transport (executor + provider + sign-in) (#11205)
Merged after conflict resolution: the 5 conflicting test files were the base-red drains that #11201 already landed on the tip — kept the tip versions; the feature content is untouched. Validated on the combined batch board + this branch: codex-app-server + codex-gpt56-catalog 25/25, typecheck:core clean, docs-counts green (351 providers), provider-consistency 268/351/0. The opt-in codex-app-server transport (JSON-RPC-over-WS, turn/completed-awaited close, Responses SSE bridge) leaves the default codex path untouched. Thank you @arminanton — a 3.4k-line transport with the docs wave and tests to match! |
||
|
|
e52d2db449 |
fix(compression): CCR must not strand prompts for callers without the retrieve tool (#11084)
Validated on the combined batch board + this branch: ccr-non-mcp-full-prompt-loss + ccr-retrieval-ramp 20/20, file-size gate green with the ccr/index listing (1024, dated annotation — owner-authorized). The callerSupportsCcrRetrieve gate now skips the whole engine for callers whose tools[] cannot reach omniroute_ccr_retrieve — no more 15KB prompt arriving upstream as 112 tokens. Production-measured root cause, textbook TDD. Thank you @HouMinXi! |
||
|
|
45b42ecdee |
fix(compression): bump vendored GCF with numeric-domain and surplus fixes (#10807)
Validado no worktree combinado do lote: typecheck:core, lint, gates de qualidade e os novos testes gcf-numeric-domain/gcf-count-mismatch verdes (mais os já existentes do codec GCF). Fix de losslessness bem documentado e cirúrgico. CI vermelho neste PR é o base-red já rastreado em #9985. Obrigado! |
||
|
|
b661b71559 |
fix(sse,compression): graceful silent-close truncation + compressToolResults boolean guard (#10805)
Two fixes: (1) createDisconnectAwareStream now distinguishes graceful max_tokens truncation (partial content already reached the client, upstream closes without a terminal marker → clean stop, no error) from a real empty-content failure (still surfaces the 502). Fixes #7699, keeps #8649 intact. (2) liteEngine's compressToolResults now requires an explicit boolean before overriding step config, instead of letting a malformed value leak through the `??` chain. Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 4 files): - 68/68 tests pass (silent-sse-close-7699, compression/lite, empty-stream-no-content-8649, stream-handler). - check-file-size, check-changelog-integrity: OK. - typecheck:core: clean. - check-complexity / check-cognitive-complexity: OK, both under baseline. Note: the empty-content Claude error message text changed from "Upstream stream ended without a terminal marker" to "Provider returned empty content" (matches the OpenAI/Responses branch wording) — intentional, documented in the PR. Co-authored-by: minhlongs <minhlongs@users.noreply.github.com> |
||
|
|
118840131d |
fix(deps): upgrade @atjsh/llmlingua-2 to 2.0.5 and drop @tensorflow/tfjs (#10610)
Implements #10536: upgrade @atjsh/llmlingua-2 2.0.3 → 2.0.5 and drop @tensorflow/tfjs from the LLMLingua SLM optional stack. Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 20 files): - 48/48 focused llmlingua/colocate/docker unit tests pass (author-reported, reproduced). - check-file-size, check-changelog-integrity: OK. - grep confirms no remaining source imports of @tensorflow/tfjs. - typecheck:core: clean. - check-complexity / check-cognitive-complexity: OK, both under baseline. Co-authored-by: jonlwheat2-gif <jonlwheat2-gif@users.noreply.github.com> |
||
|
|
6d043674c2 | fix: skip expensive RTK compression stats computation on no-op runs (#10765) | ||
|
|
54b39690e5 |
fix(compression): bound RTK raw-output store growth and make pointer reads O(bucket) (#10659) (#10660)
Merged — locally validated together with related stanleytejakusuma PRs (typecheck:core clean, complexity/cognitive/file-size/changelog gates green, focused tests passing). Great incident writeup and clean fix. Thanks! |
||
|
|
14a480453c |
fix(compression): preserve unfenced raw code from Caveman prose normalization (#9144) (#10764)
Co-authored-by: Markus Hartung <mail@hartmark.se> |
||
|
|
ec4802d09c |
test(mcp): declara a precondição de env dos testes de principal do CCR/MCP (#10689)
`resolveCcrPrincipal` dá precedência a `resolveMcpCallerApiKeyId()`, que no transporte stdio cai em `OMNIROUTE_API_KEY`/`ROUTER_API_KEY`. Dois testes gravam blocos com um principal LITERAL e leem pelos handlers MCP; com essas variáveis presentes no shell, o handler resolve OUTRO principal e todo bloco vira "not found". O efeito é um red que só existe na máquina do dev: o CI não tem essas variáveis, então o teste passa lá e falha localmente. Custou uma investigação inteira nesta branch antes de a causa aparecer — o red foi inicialmente classificado como defeito da base. A precondição já existia, só não estava escrita. Agora está, no mesmo idioma de api-key-lifecycle.test.ts, cli-remote-mode.test.ts e do irmão ccr-mcp-principal-5649.test.ts (que aprendeu isso no #7883): salvar, deletar no topo, restaurar no `after`. Nenhum código de produção mudou — não havia defeito de produção. Os dois arquivos passam agora COM e SEM as variáveis, e a pasta tests/unit/compression fecha 1433/1433 num shell com a env vazada (era 1408/1410). Co-authored-by: Xiangzhe <bakryun0718@proton.me> |
||
|
|
04af8b1517 |
feat(compression): adota omniglyph 1.4.0, perfis semânticos e contabilidade com evidência (#10647)
* feat(compression): target-wire OmniGlyph stage and transport fidelity gate Roda o OmniGlyph depois da tradução para o wire real do provedor, em vez do corpo de origem. Um cliente OpenAI roteado para Claude deixava de comprimir com skip:source_format_not_claude porque o corpo ainda estava em formato OpenAI quando a engine era avaliada. - dispatch nativo por wire: Anthropic Messages, OpenAI Chat Completions e OpenAI Responses (input[] preservado, sem achatar para messages[]); - estágio target-wire pós-translateRequest, com guarda contra dupla compressão no caminho Claude→OpenAI; - preserveSystemPrompt do OmniRoute mapeado para compressSystem: false; - imageTransportPolicy: fidelidade de bytes/dimensões separada de supportsVision; só Anthropic/Claude tem recibo byte-preserving, o resto é fail-closed; - contagem de tokens de data URL PNG no wire OpenAI (marcador ;base64,); - README e i18n en/pt-BR com claims escopados ao caminho medido. * feat(compression): adota omniglyph 1.4.0 e tira o gate de modelo da env do host O 1.4.0 introduziu escopos de segurança e passou a resolvê-los dentro de isOmniGlyphSupportedModel() lendo process.env.OMNIGLYPH_PROFILE. Somado ao OMNIGLYPH_MODELS que já existia, duas variáveis do ambiente do host decidiam em silêncio o gate de TODO request do OmniRoute: passthrough desligaria a engine inteira e OMNIGLYPH_MODELS admitiria modelos sem recibo medido, enquanto a UI segue prometendo "Claude Fable 5 na rota direta medida". O adapter passa a usar isOmniGlyphSupportedModelForScope() com escopo explícito e fixa o escopo mais restrito como teto: a env só pode ESTREITAR a allowlist, nunca alargar. Os dois wires compartilham a mesma lista no pacote desde o 1.4.0, então uma checagem cobre Anthropic e GPT. - omniglyph ^1.3.1 -> ^1.4.0 (lock em 1.4.0); - testes de regressão para os dois caminhos de sequestro por env; - teste de contrato dos exports novos (escopo, perfis, accounting). O 1.4.0 também traz, sem mudança de código aqui: correção do glyph K que era lido como H, remoção do backtracking polinomial no secret-guard, overrides do pnpm em pnpm-workspace.yaml e as transitivas vulneráveis resolvidas. * feat(compression): expõe os perfis semânticos do omniglyph nos três wires O 1.4.0 trouxe perfis nomeados (coding-safe, balanced, aggressive, passthrough), mas só transformAnthropicMessages() os resolve sozinho: os transformadores OpenAI recebem TransformOptions cru e ignorariam o campo. Um perfil escolhido pelo operador valeria no wire Claude e sumiria no OpenAI. O adapter passa a mesclar o perfil com mergeCompressionProfileOptions() antes de chamar Chat Completions e Responses. O default segue aggressive — a política que os recibos publicados mediram. Medido nesta base: com coding-safe/balanced, uma sessão sem histórico acumulado para em below_min_chars e a engine não faz nada, porque os dois fixam minCompressChars no máximo e desligam system/tools/tool-results. Como a engine é opt-in, um default assim entregaria "ligado, 0% de ganho". O perfil é TETO, não piso: mergeCompressionProfileOptions não deixa um override do chamador reabrir uma lane lossy que o perfil fechou. Coberto por teste, por ser contra-intuitivo. Também fecha um caminho em que o OmniRoute violaria a própria política: o wire OpenAI do pacote não tem compressSystem — honra apenas compressTools, gptHistory, minCompressChars e reflow, e sempre troca a instrução por um ponteiro para a imagem. Com preserveSystemPrompt ligado, imagear assim queimaria o prefixo quente que a decisão cache-aware está protegendo, sem nada no corpo devolvido denunciando. A engine agora pula com skip:system_preservation_unsupported_on_wire. * feat(compression): contabilidade física do omniglyph com grau de evidência O adapter descartava o TransformInfo inteiro, então a UI mostrava um número de economia sem dizer de onde ele vinha — contagem do provider, estimativa ou só diferença de bytes. O 1.4.0 expõe normalizeAccounting(), que classifica essa evidência e resolve a semântica de cache por família: Anthropic reporta input, cache-create e cache-read em buckets DISJUNTOS, enquanto OpenAI e xAI reportam cached como SUBCONJUNTO do input. Somar à mão dá double-count silencioso. O novo omniglyphTelemetry.ts não filtra por denylist — MONTA um objeto novo, campo a campo, só com número e enum. TransformInfo mistura contadores inofensivos com material que não pode ser persistido: bytes PNG, imageSourceText(s), recoverable[].text, os sha8 de system/CLAUDE.md/primeira mensagem, nomes de tags observadas e o bloco env (cwd, branch, versões). Copiar o objeto inteiro transformaria telemetria de compressão em vazamento de prompt. O teste de negação prova que segredo, caminho do operador, texto do system e base64 não aparecem, e varre a allowlist exigindo que toda string seja de um enum conhecido. - provider threaded do chatCore e do bridge Codex WS até a engine; ausente vira `unknown`, que faz o upstream recusar adivinhar buckets de cache; - contabilidade propagada para o engineBreakdown do passo (o agregado do pipeline soma todas as engines e não serviria); - skip não emite contabilidade: zeros ali seriam indistinguíveis de "a engine nem rodou". * feat(compression): perfil do omniglyph configurável, persistido e documentado Fecha o caminho do operador: o perfil já existia no adapter, mas só como default de código. Agora atravessa schema Zod, normalizador do banco, API de settings e a página dedicada do engine. - OmniglyphConfig tipado + omniglyphConfigSchema (z.enum dos quatro perfis); - normalizeOmniglyphConfig: nome desconhecido vindo do storage cai para o default em vez de virar "roda com a política padrão"; - seletor na página do engine, com PATCH próprio — o perfil vive fora do mapa `engines`, e mandá-lo junto reescreveria o mapa inteiro (o store persiste o mapa como uma linha JSON só); - i18n en/pt-BR descrevendo o custo medido de cada perfil, não só o nome; - README e COMPRESSION_ENGINES.md com a regra do teto e o motivo de o default não ser o perfil mais seguro. Corrige de passagem um teste-irmão que ninguém via: o gate de transporte na UI deixou de dizer "direct Anthropic" quando os wires OpenAI nativos entraram, mas tests/unit/ui/omniglyphContextPage.test.tsx continuou afirmando a cópia antiga. O arquivo inteiro estava excluído do vitest.config.ts como "#8618 pre-existing failure", então a quebra passou silenciosa. Com a asserção alinhada o arquivo fecha 3/3, e a exclusão sai — o próprio comentário mandava removê-la quando corrigida. A doc não nomeia OMNIGLYPH_MODELS: o gate de docs fabricadas está certo em apontar que o OmniRoute nunca lê essa env — quem lê é o pacote. * fix(i18n): paridade do locale vi com as chaves novas do perfil do omniglyph `tests/unit/i18n-vi-completeness.test.ts` exige paridade ESTRITA de chaves entre en e vi — diferente do ratchet `i18n:check-ui-coverage`, que passa com 80%. As 11 chaves do seletor de perfil entraram só em en e pt-BR, e o gate de cobertura seguiu verde, então a quebra só apareceu na matriz completa do CI. --------- Co-authored-by: Xiangzhe <bakryun0718@proton.me> Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> |
||
|
|
397a0e351e |
fix(compression): gate de estágio não derruba o pipeline com engine sem metadata (#10655)
`assertValidEngine()` valida id, apply, compress, getConfigSchema e validateConfig — não exige `metadata`. Uma engine registrada sem esse campo é, portanto, um registro legal. Mas `canRunAtCompressionStage` lia `engine.metadata.executionStages` sem guarda, então essa engine legal derrubava o pipeline inteiro com `TypeError: Cannot read properties of undefined` em vez de falhar aberto, que é o contrato da compressão. Metadata ausente é o mesmo caso de "não declarou executionStages" e passa a cair no mesmo fallback documentado: só pre-translation. Isso destravava também `tests/unit/compression/pipeline-circuit-breaker.test.ts`, que registra uma engine de teste sem metadata e vinha 8/9 na base — agora 9/9. O teste novo torna o contrato explícito, em vez de deixá-lo dependendo de uma reprodução incidental noutro arquivo. Co-authored-by: Xiangzhe <bakryun0718@proton.me> Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> |
||
|
|
15386495c2 |
fix(compression): add i18n support for less-code and terse-prose (#10498)
* fix(compression): add i18n support for less-code and terse-prose Translates less-code output style to pt-BR, vi, ja, and id. Adds missing vi translation to terse-prose caveman mode. Removes less-code from English-only allowlist and updates matrix tests. Fixes #10426 * docs(compression): add output styles coverage table Adds the requested Output Styles matrix to the compression guide covering styles, supported languages, and intensity levels. Fixes #10426 |
||
|
|
497dd6f357 |
fix(memory): auto-check Qdrant health on mount and stop false-red badge (#10489)
* fix(memory): auto-check Qdrant health on mount and stop false-red badge The Qdrant engine card on /dashboard/memory?tab=engine showed a red "Error" badge after every page refresh even when Qdrant was healthy: the badge derives its state from a health check, but the mount effect only fetched settings + embedding models — health started as null and the render treated `health?.ok` (undefined) as a failure. Clicking "Test connection" (which runs the same server-side /readyz check) immediately turned it green, proving the connection was fine. Two changes: - Auto-run the health check on mount once settings load and Qdrant is enabled, so a refreshed page reflects the real state (verified live: /api/settings/qdrant/health returns ok:true in ~2ms on a healthy compose deployment). - While health has not been checked yet (null), render a neutral gray "Testing..." state instead of red — red is now reserved for an actual failed health check. Regression test added (fails on the old code): with enabled settings and a healthy mock, the card must hit /api/settings/qdrant/health on mount and show statusActive, never statusError. * chore(changelog): fragment for #10489 * Merge branch 'release/v3.8.50' into fix/qdrant-health-badge * test(fix): refresh expired alibaba quota sample validity and onnxruntime pin for v3.8.50 base - alibaba-free-tier-quota-fetcher.test.ts: sample quotaValidityPeriod (2026-08-16 16:00 UTC) is in the past, making every quota entry classify as expired/not_capable; bump to 2028-01-01 UTC so the text/merge classification tests exercise the intended path again. - optional-transformers-dependency.test.ts: onnxruntime-node pin assertion updated from ~1.24.3 to ~1.27.0 to match package.json (bumped by #10403); the regular-not-optional intent is unchanged. * test(fix): align optional-transformers-dependency with onnxruntime ~1.24.3 pin (base #10543) * docs(fix): sync 150-migration count and document PROXY_LOG_INCLUDE_IPS (base drift #10348/#10507) * fix(memory): re-check Qdrant health after saving settings save() optimistically flipped enabled and started the PUT while the mount effect could immediately GET /api/settings/qdrant/health against the OLD persisted settings. If that GET won, it returned not_configured/failed and - because health was non-null - the effect never retried after the PUT succeeded, leaving a healthy Qdrant red until a manual Test connection. Invalidate health (generation counter + setHealth(null)) at save start and after a successful PUT, then explicitly schedule a fresh check: setting health to null alone is not enough, React bails on the no-op when health is already null (the exact GET-wins ordering). Stale responses are dropped via the sequence guard so an in-flight pre-save check can never overwrite the post-save result. Adds a regression test covering enable ordering. Addresses PR #10489 review finding (issuecomment-5312271806). * fix: narrow omniglyph transform result union (merge base |
||
|
|
aa912c42a7 | docs: update omni route video guides ranking layout | ||
|
|
4b914ad297 |
feat(sse): bring i-have-adhd to vi/ja/id parity and guard the style × language matrix (#10425)
i-have-adhd shipped in #10271 with en + pt-BR only; it now covers vi/ja/id as well, matching ponytail. Every level keeps the SHARED_BOUNDARIES clause. Adds a per-MATRIX guard (output-styles-i18n-matrix.test.ts). Every existing test is per-style, which is how less-code stayed English-only since the 9router port without anyone noticing. The guard fails on: a new style without pt-BR, a style losing a translation it had, a translation missing an intensity level, a translation that dropped the boundaries clause, and a stale KNOWN_ENGLISH_ONLY entry. Proven by mutation, not just by passing: dropping less-code from the allowlist and renaming the vi key both turned it red with the expected messages. less-code stays English-only as declared debt (KNOWN_ENGLISH_ONLY + comment); the remaining coverage work is tracked in #10426. |
||
|
|
964a3fe442 |
feat(sse): add i-have-adhd output style to compression catalog (#10271)
Adds `i-have-adhd` as the 5th entry in OUTPUT_STYLE_CATALOG — a port of the github.com/ayghri/i-have-adhd skill (MIT), following the same integration shape as ponytail. Action-first output shaping: the next action leads, multi-step work is numbered, no preamble/recap/closers — which also trims output tokens. lite/full/ultra levels in en + pt-BR, each ending in SHARED_BOUNDARIES so code, paths, commands, errors and URLs stay verbatim. The agent-harness-specific upstream rules (restate plan state, time estimates) are reworded as conditionals so they hold for plain chat clients too. Per the D-A1 registry contract, one catalog entry is the whole change: the injector, the settings panel, the Zod schema and the telemetry all enumerate the catalog, so no other production file moves. Dedicated test mirrors ponytail-catalog.test.ts (7 tests). |
||
|
|
a9d6fd3d9a |
feat(api): add per-key prompt compression bypass (#10001)
* feat(api): add per-key compression bypass * docs(changelog): note per-key compression bypass * chore(db): renumber API key compression migration * fix(compression): preserve hard kill during adaptive planning * chore(db): refresh migration gap allowlist |
||
|
|
32f8340719 |
Fix custom tool output pairing during context compression (#8933)
* Fix custom tool output pairing during compression (#8932) * Bypass proxy compaction for native Codex context * fix(sse): extract Codex tool-call output repair to leaf module for file-size gate repairMissingCodexToolCallOutputs (added by #8932 for custom_tool_call pairing) pushed codex.ts past the frozen file-size baseline. Extract it to open-sse/executors/codex/toolCallRepair.ts, leaving only the wiring call in codex.ts. Rebaseline the test file's genuine +41 line growth from #8932's new custom_tool_call_output coverage. Co-authored-by: JxnLexn <JxnLexn@users.noreply.github.com> --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Co-authored-by: JxnLexn <JxnLexn@users.noreply.github.com> |
||
|
|
54bba33e2f |
maint: follow-up cherry-pick fix-in-place #9629 (conflict-resolved fallback) (#9885)
* fix(compression): add Lite tool truncation toggle * fix(antigravity): add missing antigravityProjectPersistence.ts module The quota-strategy engine (quotaStrategies.ts) imports from antigravityProjectPersistence.ts, but only antigravityProjectPersist.ts existed in the tree. Add the missing module with the expected preferAntigravityConnectionsWithStoredProject() helper and re-export the existing persistDiscoveredAntigravityProjectId(). Co-authored-by: diegosouzapw <diegosouza.pw@outlook.com> * fix(file-size): rebaseline strategySelector.ts for Lite truncation toggle The PR adds one line to threading options?.config?.lite into applyLiteCompression. Update the frozen size from 1060 to 1061. Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> Refs #9629 --------- Co-authored-by: Xiangzhe <xiangzhedev@gmail.com> Co-authored-by: xz-dev <xz-dev@users.noreply.github.com> Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
04b4690f84 |
cherry-pick(pr-9730): fix(compression): persist RTK renderer configuration (#9867)
* fix(compression): persist RTK renderer configuration * docs(changelog): add fragment for #9730 Adds the changelog.d/fixes/9730-persist-rtk-renderers.md fragment required by check:changelog-integrity for the RTK enableRenderers persistence fix in PR #9730. --------- Co-authored-by: Isaac <isaaclyons98@gmail.com> |
||
|
|
e0ce95c592 |
fix(ci): close remaining release-green gaps (#9835)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
41d2e4e7a1 |
test(compression): lock in stacked RTK+Caveman savings on redundant tool_result content (#9278)
Merge-train validated (tip 6ce4effef8). Vitest failures confirmed as base-red (#9679). |
||
|
|
c40d4b17ee |
fix(ci): clear the NEW base-reds from the 08-06 merge batch (migration collision #2 + broken import) (#9688)
* test(base): realign six suites with contracts that #9100/#8990/#9009 deliberately changed Continuing the base-red drain — every one of these reproduces on the pure tip. - tests/snapshots/provider/translate-path.json: regenerated via UPDATE_GOLDEN=1. The diff is ADDITION-ONLY — the unorouter block from #9009; no existing provider entry changed. 3/3. - tests/unit/provider-models-route.test.ts: |
||
|
|
8fbd331567 |
fix(compression): drop orphan custom_tool_call/local_shell_call/apply_patch_call on compaction restore (#8946)
Closes #8946 |
||
|
|
7f36b192f0 |
fix(compression): bound session-dedup suffix-block scan to prevent OOM (#8438)
Validated in post-merge-train sweep (boards clean on release/v3.8.50 tip) |
||
|
|
e7f6b1d130 |
feat(radar): flag-gated signed free-model catalog overlay (#9515)
* feat(dashboard): add RADAR_ENABLED flag (default off) * feat(db): radar feed cache + settings with encrypted supporter key * feat(radar): signed feed sync with pinned key and version floor - feedSchema.ts: Zod v4 schema mirroring the server feed format (discriminated union on budget.kind, enum constraints, etc.) - pinnedKeys.ts: Ed25519 SPKI-DER pinned key + env override for forks - verify.ts: signature verification over exact wire bytes, never throws - sync.ts: full download/verify/validate/cache pipeline with injectable deps, feature-flag gate, opt-in gate, version floor (numeric compare), and sanitized error reasons (no stack traces) - 40 tests covering: contract hash, key handling, sig verification, schema validation, version compare, all sync paths (disabled, opt_out, invalid_signature, invalid_schema, stale, updated, error), auth header injection, and cache-untouched assertions for every failure mode * feat(radar): read-time overlay merge rules over the free catalog Pure function applyFeed() merges the cached Radar feed over the static baseline catalog at read time, honoring 4 rules: 1. Feed never overwrites a local override field. 2. enabled:false disables the entry with disabledBy:"radar" provenance. 3. User-added entry NOT in the feed survives untouched. 4. User deletion tombstone prevents feed resurrection. getRadarCatalog() accessor in index.ts: flag off / no cache / corrupt payload all fall back to baseline. Valid cache applies the overlay and returns feed metadata (version, tier, fetchedAt). TDD: 19 tests (4 rules + dedup + origin + accessor flag/cache/corrupt/ valid/bad-feed + baselineToMergedEntries converter). * feat(dashboard): radar catalog and guided setup screens - API routes: GET /api/radar/catalog, POST /api/radar/sync, POST /api/radar/settings - All gated on RADAR_ENABLED flag (404 when off) - Error responses via buildErrorBody(), never raw stack/message - Settings never echoes clear supporter key (masked omr_****<last4>) - Sync delegates to syncRadar() server-side, never proxies feed URL - Dashboard pages: - /dashboard/radar: 4 states (flag off, opt-in pending, empty, populated) - /dashboard/radar/setup?provider=X: guided setup with steps, key URL, test connection - Uses existing Card component and next-intl patterns - Sidebar: radar entry in costs group with icon - i18n: pt-BR and en keys for radarPage and radarSetupPage namespaces - Tests: - radar-api-routes.test.ts: 11 tests (flag-off 404, flag-on shape, error sanitization) - radar-page-state.test.ts: 5 tests (pure state logic) - All 90 radar tests pass (including prior 74) * docs(radar): module doc and flag-off inertia test Add docs/frameworks/RADAR.md covering the flag gate, the separate data-sync opt-in and privacy promise, the Ed25519 signature/pinned-key security model, tiers, the read-time overlay merge rules, and the self-hosting env vars — plus index entries in CLAUDE.md/AGENTS.md/docs/README.md/REPOSITORY_MAP.md. Document RADAR_FEED_URL and RADAR_FEED_PUBKEY in .env.example and docs/reference/ENVIRONMENT.md to satisfy check:env-doc-sync, which was failing on this branch since the sync.ts commit added the reads. Add tests/unit/radar-inertia.test.ts as the single canonical place asserting the "RADAR_ENABLED off => zero behavioral delta" claim end to end: the three /api/radar/* routes 404, the flag resolves to the definition default with no override, getRadarCatalog() returns exactly the baseline without touching the cache, and computeFreeModelTotals() keeps its pinned values with the Radar module imported alongside it. * fix(db): renumber radar migration to 135 after collision with 134 The base branch introduced 134_proxy_logs_egress_ip while this branch carried 134_radar_cache_settings; the migration runner rejects duplicate numeric prefixes. This migration has never been applied to a real database (the PR is unmerged), so no retroactive isSchemaAlreadyApplied guard is needed. * i18n(radar): translate radar catalog and setup strings to all locales The UI-coverage ratchet measures (present - placeholder) / total_en, so the __MISSING__ sentinels that i18n:sync-ui writes do not count as covered — only real translations restore the metric. Scoped to this PR's namespaces (radarPage, radarSetupPage, sidebar.radar*) instead of a bulk sync, which would have pulled ~978 unrelated pending keys into this diff. Placeholders and code identifiers verified preserved across all 1682 strings. * fix(radar): trust the served-tier header instead of the signed body field The signed feed body always carries tier:"live" by design (one signed artifact per version — rewriting the field server-side per request would break the exact-bytes Ed25519 signature). The server now returns the tier ACTUALLY served via the x-omniroute-feed-tier response header, so free users on a delayed community snapshot no longer see "Ao vivo (tempo real)" in the UI. sync.ts now reads and validates that header (falling back to the body's tier only when the header is absent or holds an unrecognized value) and stores the served tier in the cache; index.ts already surfaces cache.tier to the UI unchanged. * test(combo): shorten an assert message that exceeded the line limit The assertion added by #9507 was 104 chars, so prettier reformatted it into five lines on the next commit that touched the file, pushing it past its frozen size (3449) and failing check:file-size. The message is shortened (the issue reference stays in the comment directly above); the assertion itself is unchanged, and the file is back to 3448 lines and prettier-clean. * i18n(radar): use the canonical zh-TW glossary terms The machine translation produced retired renderings the glossary gate blocks: 供應商 for provider (canonical 提供者) and 文檔 for documentation (canonical 文件). Fixed across the 11 affected radar strings; tests/unit/i18n-glossary-consistency-check.test.ts is back to 17/17. * fix(radar): point the default feed URL at the domain that exists radar.omniroute.dev was a placeholder for a domain that was never registered, so an out-of-the-box sync would fail DNS resolution for every user. The live feed is served from radar.omniroute.online (the subdomain the design always specified), now behind Cloudflare TLS. Forks still override it via RADAR_FEED_URL. --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com> |
||
|
|
22d0e60d4a |
fix(types): narrow CCR store rejections (#9091)
Validated in local merge-train (devbox-vm-06-dev002) @ combined-tip (FAST gates: typecheck/complexity/cognitive/changelog/vitest — only pre-existing audit.test.ts flake). Evidence: /home/diegosouzapw/dev/proxys/OmniRoute/.claude/worktrees/merge-train-20260805-222248-suite.log |
||
|
|
d291ce2b9f |
fix(sse): evict a principal's own CCR blocks before another principal's (#9146) (#9191)
Validated in local merge-train (devbox-vm-06-dev002) @ combined-tip (FAST gates green: static + changed tests + vitest — only pre-existing audit.test.ts flake). Evidence: /home/diegosouzapw/dev/proxys/OmniRoute/.claude/worktrees/merge-train-20260805-213228-suite.log |
||
|
|
a72e1656eb |
fix(routing): bare model ids route to codex first; validate synced candidates (#9275)
* fix(routing): bare model ids route to codex first; validate synced candidates
Two bare-model-routing bugs surfaced in the field when an OmniRoute
deployment had a codex subscription whose cookie quota was exhausted
(retry-after 429047s / ~5 days) AND an active kiro connection whose
upstream sync briefly advertised 'claude-opus-5' before kiro vendored
it into the static registry.
1. Bare 'gpt-5.6-sol' (and friends) routed to the codex provider even
when the user had explicitly configured 'agentrouter' as their
provider (via model_provider in codex CLI). With codex in cooldown,
every bare request 429'd. Fix: extend CODEX_NATIVE_UNPREFIXED_MODELS
to include the full gpt-5.6-sol tier set + gpt-5.5 + the related
codex-native ids. The Codex CLI default is now actually honored;
users can still prefix 'agentrouter/gpt-5.6-sol' to opt into a
specific provider.
2. Bare 'claude-opus-5' silently routed to 'kiro' when kiro's synced
/v1/models catalog had that id (likely from a transient upstream
quirk). kiro's static registry never cataloged claude-opus-5, so
the upstream call 404'd. Fix: validate activeSyncedProviders against
MODEL_TO_PROVIDERS before merging them into the candidate list.
Auto-discovery still wins when the model id has no static entry
(brand-new models from upstream keep working).
Bonus: when handleNoCredentials returns a 404 'No active credentials for
provider: X' error, surface the top-3 candidate aliases (e.g.
'anthropic/claude-opus-5, claude/claude-opus-5, agentrouter/claude-opus-5')
so the operator can pick a working prefix instead of staring at a wall.
Tests (all pass, 25 regression tests preserved):
- tests/unit/fix-bare-model-precedence.test.ts (7 tests)
- tests/unit/fix-synced-model-validation.test.ts (3 tests)
- tests/unit/fix-error-message-candidates.test.ts (3 tests)
- tests/unit/fix-bare-routing-fallback.test.ts (7 tests)
* fix(tests): replace lorem ipsum with neutral text to avoid agentrouter WAF
The agentrouter.org WAF blocks requests containing 'lorem ipsum' in
messages[].content. When Claude Code reads test files via the Read tool,
the content appears in tool_result blocks which can trigger the filter.
Replace 'lorem ipsum dolor sit amet' with 'example content for testing
purposes' in compression harness test to avoid false positives.
---------
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
|
||
|
|
d43e71613e |
optimize(chaos+ponytail): i18n ponytail, dedupl dispatch, provider diversity (#8264)
* feat(chaos+ponytail): parallel chaos-mode dispatch + ponytail output style (rebased on v3.8.49)
- Chaos mode: new auto/chaos variant fans the prompt out to the top-N
stable models in parallel and returns a single merged SSE stream.
- Progressive streaming: each panel model's answer is enqueued as it
lands (omni-chaos-part event), instead of awaiting the whole panel.
- withTimeout now aborts the underlying request (modelAbortSignal) on
timeout so the connection is released, not leaked.
- concatSseText parses both OpenAI and Anthropic SSE wire formats.
- autoPrefix/modePacks add the chaos-mode weight pack; virtualFactory
materializes auto/chaos with fusion strategy + chaos config flag.
- Ponytail (lazy-senior-dev mode) integrated into the existing
OUTPUT_STYLE_CATALOG registry (id 'ponytail') so it rides the production
output-style injector, instead of a bespoke duplicate module. Dev-only
scripts and the duplicate ponytail/ module are removed.
- Tests: chaosEngine/chaosVirtualCombo cover panel dispatch, progressive
broadcast, timeout abort, and Anthropic parsing; autoCombo pack count
updated to 6.
Rebased onto release/v3.8.49 (no provider-registry or validation changes —
those are split out per review).
* optimize(chaos+ponytail): i18n ponytail, dedupl chaos dispatch, provider diversity
- Ponytail: add vi/ja/pt-BR/id i18n with lite/full/ultra levels
- chaosEngine: extract dispatchOnePanelModel (shared), add onResult for
progressive SSE streaming, fix withTimeout anti-pattern
- virtualFactory: deduplicate chaos panel by provider, add tuning overrides
- dispatchChaosFromCombo: accept ChaosTuning, enforce minPanel
- Add/port 8 node-runner tests for ponytail i18n + catalog integrity
- Add muse-spark-web.ts to KNOWN_MISSING_ERROR_HELPER (pre-existing)
* fix(8264): use HandleSingleModel type in chaosEngine dispatch (base-drift)
---------
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
|
||
|
|
5e8b130e77 |
fix(compression): make memo key model-independent for non-vision engines (#8196)
* fix(ci): resolve upstream-inherited check failures * fix(compression): make memo key model-independent for non-vision engines (#8137) The compression result memo included `model` and `supportsVision` in the cache key for ALL deterministic modes. This was correct for the `lite` engine (which strips data:image URLs based on vision support) but unnecessary for model-independent engines like `rtk`, `caveman`, and stacked pipelines without a `lite` step. In the combo retry loop, the body and config are identical across targets — only the model changes each attempt. Including model in the key forced a fresh cache miss on every retry, re-running the full compression pipeline 5-8x per request instead of serving the cached result. Fix: `makeMemoKey` now only includes model + supportsVision when the compression pipeline actually uses a vision-dependent engine (lite, standard, or stacked containing lite). All other deterministic engines use a model-independent key. - Add `usesVisionDependentEngine()` helper to classify modes - `makeMemoKey` conditionally includes model/vision fields - 5 new tests covering rtk, caveman, stacked-with-lite, stacked-without-lite Closes #8137 --------- Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |
||
|
|
e7f965c9cc |
fix(compression): persist Headroom minRows (set 5 and reload keeps 5) (#8058)
* fix(compression): persist Headroom minRows (set 5 and reload keeps 5) Fixes diegosouzapw/OmniRoute#8056. Headroom detail settings had a Save-looking form but EngineConfigPage only persisted aggressive/ultra via SETTINGS_SUBOBJECT, so minRows always reseeded to the schema default (8) after reload. - Add HeadroomConfig + DEFAULT_HEADROOM_CONFIG (minRows: 8) - Accept headroom in compressionSettingsUpdateSchema (minRows 2..10000) - Normalize/store headroom in get/updateCompressionSettings - Register headroom in EngineConfigPage SETTINGS_SUBOBJECT so Save works - Merge settings.headroom into stacked stepConfig for runtime apply - Thread minRows through preview API + EngineConfigPage preview payload - Tests: schema/DB round-trip, engine apply, stacked merge, UI Save→PUT 5 * chore(quality): rebaseline compression.ts + strategySelector.ts own-growth (#8056 headroom minRows) --------- Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com> |
||
|
|
fb6ea295bf |
feat(compression): add Responses tool-output engine (#8010)
* Add Responses tool-output compression engine * fix: enable Codex Responses stacked steps * fix(compression): share Codex tokenizer and rebase UI * fix(compression): sync MCP engine selection * fix(compression): i18n parity for codex-responses mode + rebaseline The codex-responses compression engine already imports the shared countTextTokens/resolveTokenizerEncoding from tiktokenCounter.ts (no duplicate encoder) and CompressionSettingsTab.tsx already threads the new mode through the existing useTranslations()/labelKey pattern - both pre-existing on this branch tip after rebasing onto release/v3.8.49. What was missing after the rebase: the new compressionModeCodexResponses / compressionModeCodexResponsesDesc keys existed only in en.json. Filled en-fallback into all 42 locales via scripts/i18n/fill-missing-from-en.mjs and added real pt-BR/vi translations. Also rebaselined the three files whose own growth (new codex-responses mode wiring) crossed the frozen file-size caps: open-sse/mcp-server/schemas/tools.ts, open-sse/services/ compression/strategySelector.ts, and src/lib/db/compression.ts. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> |