Same root cause as commit 49fe356b9: ChatPlayground filtered models
with m.id.startsWith(...) which crashed on null/undefined ids returned
by /v1/models (synthetic combo entries). Apply the same defensive guard
and dedupe used in the parent page.
The /v1/models endpoint can include synthetic entries (combos, locals,
in-progress imports) with a null/undefined id. The playground used to
call m.id.split("/") in the provider-discovery loop, which threw on the
first non-string entry; the surrounding .catch(() => {}) silently
swallowed the error, so the provider/model/account dropdowns ended up
empty even though /v1/models returned thousands of valid entries.
- Skip entries without a string id before split/startsWith.
- Log the rejection in the .catch handler so future regressions are
visible in DevTools instead of silently emptying the UI.
The /v1/models endpoint can return the same model id twice (e.g., when a
model is listed by both an alias and its canonical provider), which made
the <Select> emit two <option> elements with the same key — triggering
"Encountered two children with the same key, codex/gpt-5.5".
Replace the chained filter + map with a single pass that skips ids
already added.
Adds providerDetailMyClaudeAccountPlaceholder and
providerDetailPathAutoDetected — the final user-visible labels in the
providers/[id] page that the round-5 subagent rewrote to t() calls
without yet adding to en.json.
Real missing keys: 0 (6 remaining are exampleTemplates.tsx false
positives — t is passed as a parameter so the audit cannot resolve the
namespace; keys do exist at translator.templatePayloads.*).
Round-5 agent began processing the remaining smaller dashboard files.
Added 5 more keys to en.json for providers/[id]/page.tsx OAuth flow
labels and the cross-OS auto-detection hint.
Pages affected:
- providers/[id]/page.tsx (5 keys)
Hardcoded total: 140 → 136. Real missing keys: 0.
Round-4 subagent + manual key-resolution refactored remaining strings in
3 high-traffic settings/API tabs, plus extracted English values for
keys that were already added as t() calls but lost during the previous
en.json race-condition resets.
Pages affected:
- api-manager/ApiManagerPageClient (7 → 0 missing key)
- settings/CompressionSettingsTab (8 → 0 missing key)
- settings/MemorySkillsTab (8 → 0 missing key)
- settings/ResilienceTab (4 more keys recovered)
Hardcoded total: 164 → 140. Real missing keys: 0 (6 remaining are the
exampleTemplates.tsx false positives — t passed as parameter).
Replace hardcoded labels in compression and resilience settings with
translation lookups to continue the dashboard i18n cleanup.
Add the v3.8.0 dashboard shakedown runbook to document the manual
smoke-test process and known dev environment pitfalls.
Replace useState+lazy-initializer with useSyncExternalStore so the server
snapshot (() => false) and client snapshot (() => navigator.onLine) are
declared separately. React hydrates with the server value and switches to
the real online status client-side without a mismatch.
`bun add -g omniroute` (and any runtime that does not reliably run the
better-sqlite3 postinstall script) leaves the *.node binary undownloaded.
The `bindings()` loader then throws "Could not locate the bindings file"
BEFORE any DLOPEN happens, which the previous detector missed — the user
saw a generic 500 page instead of the friendly "Run npm rebuild
better-sqlite3" guide.
Extended isNativeSqliteLoadError() to recognise:
- "Could not locate the bindings file" (bindings module preflight)
- "Cannot find module 'better-sqlite3'" (package not even installed)
- code === "MODULE_NOT_FOUND" (Node loader fallback)
isNativeSqliteLoadError is now exported so tests can pin the contract; the
detector covers both the "wrong NODE_MODULE_VERSION" and "binary never
shipped" failure modes operators have hit in practice.
The kilocode provider uses OAuth device flow + direct HTTPS to api.kilo.ai
and never depends on the local kilocode CLI binary at runtime. The connection
test was hard-failing with "Local CLI runtime is not installed" even when the
OAuth token itself was perfectly valid, blocking all kilocode setups on hosts
where the CLI binary was not also installed.
Removed kilocode from CLI_RUNTIME_PROVIDER_MAP and extracted the constant to
a dedicated module so unit tests can pin the contract without dragging the
full Next.js route + DB initialization into the test runtime.
CLI Tools integration (/api/cli-tools/kilo-settings, used to configure the
Kilo VSCode extension to point at OmniRoute) keeps its own runtime check
since it actually does need the CLI binary to be present.
Remove the unsupported `timelineItems` field from `ghIssueView`, add
`ghPrSearchOpen` wrapper, and synthesize `issue.timelineItems` in the
main loop so `classifyIssue` stays unchanged.
Sync body of .agents/skills/implement-features/SKILL.md and
.claude/commands/implement-features-cc.md with the canonical
.agents/workflows/implement-features-ag.md (Phase 0 pre-flight
triage + comment templates already applied in the workflow file).
SKILL.md Codex Execution Notes section preserved.
Closes the gap between the v3.8.0 cut (PR #2323) and the current state of
release/v3.8.0 by listing 24 PRs that landed after the initial release and
crediting every contributor that was missing from the Hall of Fame table.
Added entries cover:
- 4 security hotfixes (CodeQL #243-247 + Kiro Google OAuth)
- 4 dependency bumps (mermaid, electron, prod/dev groups)
- 9 dashboard/CLI/codex features
- 3 Claude/OAuth fixes
- README acknowledgment restore
Extended Hall of Fame credits new contributors @terence71-glitch, @TF0rd,
@slider23, @t-way666, @Rikonorus, @8mbe and updates PR tallies for
@oyi77, @backryun, @thepigdestroyer, @mrmm, @dhaern, @hartmark, @gleber,
@congvc-dev, @herjarsa, @payne0420, @InkshadeWoods.
CodeQL re-flagged the HMAC variant from #2394 at high severity (#247) —
its data-flow analysis still sees an OAuth bearer reaching .update(token)
and applies js/insufficient-password-hash, regardless of whether the hash
primitive is createHash or createHmac.
Stop hashing the token at all. The session-pool Map is keyed by the
token verbatim, falling back to "anonymous" when the input is missing
or empty. This is safe because:
- The token is already held in CopilotSession.cookies for every pool
entry, so the Map key adds no new in-memory exposure.
- The pool is bounded by MAX_POOL_SIZE with LRU eviction, so memory
stays bounded regardless of how many distinct tokens appear.
- bcrypt/scrypt/argon2 — the only forms CodeQL accepts here — are the
wrong tool, since their slowness exists to thwart brute-force of
low-entropy human passwords we do not have.
Tests
- Replace the "16-char hex" shape assertion with verbatim-equality
assertions and an explicit "empty string → anonymous" case.
- Keep the regression guard that fails if anyone ever re-introduces
createHash/createHmac on the token (catches the alert reappearing
before CodeQL does).
- 16/16 copilot-web tests pass.