mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-08-24 10:14:19 -05:00
31ac8a6ecae593ec08770568aa9ea651595eb162
100
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
31ac8a6eca |
perf: stop busting the prompt cache and slim per-turn context (#2142)
* perf: stop busting the prompt cache and slim per-turn context Benchmarked against a comparable harness on grok-4.6 (identical one-shot coding task), OpenClaude used 84k total tokens per task with near-zero cache reuse. Root causes and fixes: - Auto-memory now defaults off in non-interactive (-p) sessions (src/memdir/paths.ts). The conversation-arc append gated behind it rewrote the system prompt every request (Date.now()-relative durations, running token counters, per-turn RAG retrieval), which invalidates implicit prefix caches from byte one on chat-completions providers. An explicit settings opt-in (autoMemoryEnabled / memory.autoWrite) still enables it; also drops the ~3.2k-token memory protocol section from one-shot runs. - The OpenAI shim no longer runs compressToolHistory for providers with implicit prefix caching (OpenAI, xAI, DeepSeek, Kimi/Moonshot, Codex) (requestPreparation.ts, both call sites). Its end-relative window retro-edits already-sent tool results each turn, mutating the middle of the request prefix — the native Anthropic transport already guards against exactly this (claude.ts shouldCompressNativeToolHistory). - Remove the wall-clock-relative "Ns ago" line from the multi-turn tracking block (conversationArc.ts) — it changed on every request. - Ship the ~1.7k-token git commit/PR protocol in the Bash tool description only when the session is inside a git repository (gitSettings.ts). The probe is cached per cwd, not per process, since worktree tools and daemon/SDK processes change directories mid-life. - Add a code-robustness bullet to the Doing-tasks system prompt section: derive timing-sensitive logic from elapsed time, and wire up every element introduced (prompts.ts). Measured on the same benchmark (8 runs): 84k -> ~46.6k total tokens per task (-45%), 89s -> ~60s wall clock, per-call cache reads up from a constant 128 tokens to 12k-29k, baseline context 16.8k -> 11.7k tokens. Tests: 573 targeted tests pass, including new coverage for the non-interactive memory default and the per-cwd git probe; tsc --noEmit clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: address review findings on the prompt-cache changes - Correct the prefix-caching route ids ('moonshot'/'kimi-code', not 'kimi'/'codex' — the latter never matched a real routeId) and replace the unanchored host regex with parsed-hostname comparison so path-routed gateways are not misclassified. - gitSettings: an explicit includeGitInstructions settings value now always wins over the repo probe (recourse for bare-repo/GIT_DIR layouts), and the probe reuses the existing LRU-memoized findGitRoot on getCwd() instead of a second process.cwd()-keyed implementation — fixing stale results after Bash `cd`, `git init`, and in daemon/SDK processes serving multiple directories. - Memory gate: env-provisioned memory (CLAUDE_COWORK_MEMORY_PATH_OVERRIDE, CLAUDE_CODE_REMOTE with a mounted memory dir) counts as explicit opt-in, so Cowork/remote sessions keep extraction and indexing. - Multi-turn tracking block: render only completed turns and drop the running token totals — the in-progress turn's tool-call list and the aggregate counters changed between model requests, still rewriting the system prompt mid-turn. - Update the Kimi K3 compression test to assert the new policy (history kept uncompressed on implicit-prefix-caching hosts) and extend gitSettings tests to cover settings overrides and session-cwd tracking. 569 tests pass, tsc --noEmit clean, benchmark re-run confirms metrics hold (45.1k total tokens, 62.8s, 3 calls). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: green CI and address CodeRabbit review on the prompt-cache changes CI: the conversation-arc suites assumed the multi-turn tracking block renders with only an in-progress turn, and that auto-memory is on in the (non-interactive) test process. Both now seed a completed prior turn, mark the session interactive where they exercise interactive behavior, and assert the new cache-stability invariants directly: the in-progress turn is never rendered, and no Duration/token-total lines appear. CodeRabbit findings: - Codex transport now skips tool-history compression too: Codex talks to OpenAI Responses backends with implicit prefix caching, and the end-relative compression window rewrites already-sent tool results, busting the cache (mirrors the openaiShim/requestPreparation skip). Its compression test now pins the uncompressed behavior. - New regression tests for both compression decision paths: an implicit-prefix-caching host skips compression on chat-completions and Responses requests, while a non-caching custom endpoint still compresses. - New byte-stability test: the same turn rendered twice with an advanced clock and a grown in-progress tool-call list produces an identical system prompt. - gitSettings: cover the CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS=0 defined-falsy override outside a repository. - Focused system-prompt test asserting the new timing/wiring guidance without snapshotting the full prompt. bun run check (smoke, deadcode, full suite) passes; tsc --noEmit clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: isolate CLAUDE_CODE_SIMPLE in the doing-tasks prompt test getSystemPrompt short-circuits to a minimal prompt when CLAUDE_CODE_SIMPLE is truthy; save, unset, and restore it around the test so the full prompt path is always exercised regardless of process-level state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
6e3590303b |
feat(partners): add Concentrate and Exa to partner roster (#2141)
Adds Concentrate (concentrate.ai) and Exa (exa.ai) to the README partners table and the web landing page, with light/dark logo variants self-hosted under docs/assets/ and web/public/partners/. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
575b407275 |
feat(partners): add ApiSmart, refresh Novita AI logo (#2121)
- Add ApiSmart (https://www.apismart.ai) to the README partners table and the web partner strip, with a dark-theme logo variant (near-black wordmark recolored to white, white matte removed). - Replace the Novita AI PNG logo with the new SVG wordmark plus a generated dark variant, wired through the same prefers-color-scheme <picture> pattern (README) and logoDark field (web). Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
7b03ad19a4 |
feat(opengateway): add Ling 3.0 Tiny :free — Day-0 launch, free until August 13 (#2112)
* feat(opengateway): add Ling 3.0 Tiny :free — Day-0 launch, free until Aug 13 inclusionai/ling-3.0-tiny:free (7.9B MoE, ~1.3B active, 262k ctx) joins the picker via the gateway's OpenRouter wiring. The gateway time-boxes it (free through Aug 13, rate limited) and delists it server-side when the window closes. * test(opengateway): Ling Tiny gateway mapping test + explicit window note Addresses CodeRabbit review on #2112: - new ling-tiny.test.ts (macaron.test.ts pattern) asserting the opengateway-ling-3.0-tiny-free entry maps both apiName and modelDescriptorId to inclusionai/ling-3.0-tiny:free, plus descriptor capabilities and runtime limits - catalog note now dated explicitly ('Free through August 13, 2026') and the entry's lifecycle documented: the gateway time-boxes the id server-side and 400s after the window; this static catalog has no expiry mechanism (Ling Flash precedent), so the entry is removed or updated at window close * feat(integrations): availableUntil catalog-entry expiry + Tiny lifecycle guard Addresses CodeRabbit round 2 on #2112: - new optional ModelCatalogEntry.availableUntil (ISO-8601): entries past the cutoff are dropped in getCatalogEntriesForRoute, the single choke point behind the model picker, gateway catalogs, and runtime limits; the pre-existing (previously unenforced) hidden flag is honored in the same filter; unparseable dates fail open - the Ling Tiny entry sets availableUntil to the gateway's window end (2026-08-13T10:00:00Z), so the picker drops it the instant the gateway starts rejecting the id — no client release needed - boundary regression test on both sides of the cutoff, and the picker expected-list test pins the clock inside the window (setSystemTime) so it stays deterministic after the date passes - ling-tiny.test.ts now also asserts supportsPreciseTokenCount: false * test(integrations): exact-cutoff + hidden + malformed-date coverage; fix stale lifecycle comment Addresses CodeRabbit round 3 on #2112: - ling-tiny.test.ts asserts the boundary at exactly 2026-08-13T10:00:00Z (cutoff is exclusive: entry already gone at that instant) - registry.test.ts covers the two previously-untested filter branches: hidden entries dropped, availableUntil expiry (before / at / after cutoff), and a malformed availableUntil failing open - the catalog comment above the Ling Tiny entry no longer claims the static catalog has no expiry mechanism — availableUntil is the guard Validation commands run locally: bun run integrations:generate bun test src/integrations src/commands/model/model.test.tsx src/utils/model bunx tsc --noEmit 558 tests pass, typecheck clean. * fix(model): route static picker entries through the availability filter Addresses jatmn's P1 on #2112: model.tsx read catalog.models directly, bypassing the availableUntil/hidden filter that only lived in getCatalogEntriesForRoute — so after 2026-08-13T10:00Z the /model picker would still offer inclusionai/ling-3.0-tiny:free and selecting it would persist an id the gateway 400s. - registry.ts exports filterAvailableCatalogEntries (shared with getCatalogEntriesForRoute) - model.tsx filters the static entries AND the static+discovery merged list, so discovery-sourced entries with their own markers are covered - routeMetadata.ts getRouteDefaultModel's catalog fallback filters too, so an expired entry can never become the implicit default - new picker regression test pinned just past the cutoff asserts the expired entry is gone while the rest of the catalog is untouched Validation: bun run integrations:generate; bun test src/integrations src/commands/model/model.test.tsx src/utils/model (559 pass); bunx tsc --noEmit (clean). * fix(model): merge raw static entries so expired ones mask cached duplicates Addresses CodeRabbit round 4 on #2112: filtering static entries before mergeRouteCatalogEntries let a cached discovery entry with the same apiName (and no availableUntil marker) re-enter the merged list, where the post-merge filter could not remove it. The merge now takes the RAW static list — the expired static entry wins the apiName dedup and the post-merge filter then drops it, so neither copy survives. The filtered list still drives the non-discovery path. Regression tests in routeCatalogOptions.test.ts cover the cached duplicate after the cutoff (including documenting the buggy pre-filter order) and the masking inside the window. Validation: bun run integrations:generate; bun test src/integrations src/commands/model/model.test.tsx src/utils/model (561 pass); bunx tsc --noEmit (clean). * test(integrations): default-model fallback skips hidden and expired entries Addresses CodeRabbit round 5 on #2112: getRouteDefaultModel's catalog fallback changed in the availability-filter fix but had no focused coverage. New routeMetadata.test.ts case (self-contained registry mutation with the shared lock, mirroring registry.test.ts) verifies a hidden default-marked entry and a past-cutoff availableUntil entry are both skipped in favor of the remaining valid entry, and that a catalog with nothing valid yields undefined rather than a rejected id. Validation: bun test ./src/integrations/routeMetadata.test.ts (63 pass); bun run integrations:generate; bun test src/integrations src/commands/model/model.test.tsx src/utils/model (562 pass); bunx tsc --noEmit (clean). * test(integrations): release shared mutation lock even if registry restore throws Addresses CodeRabbit round 6 on #2112: the fallback test's finally block ran _clearRegistryForTesting/ensureIntegrationsLoaded before releaseSharedMutationLock, so a throw there would leave the lock held and block later tests. Nested try/finally, matching registry.test.ts's afterEach shape. Validation: bun test ./src/integrations/routeMetadata.test.ts (63 pass); full related suites 562 pass; tsc clean. --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
54b9cd8389 |
feat(opengateway): free retirement — paid Ling id, dual Nemotron, Macaron Venti (#2108)
The gateway retired its free models on 2026-08-10, keeping Nemotron 3 Ultra :free as the one free model (rate-limited by OpenRouter's shared pool) and adding its paid throttle-free sibling as a separate entry. Ling 3.0 Flash moves to its paid id (the :free id is aliased server-side for older clients), Macaron V1 Tall is now paid, and Macaron V1 Venti (748B MoL on GLM-5.2, 1M ctx) joins the catalog. The ling entry id keeps its historical -free suffix so saved selections resolve; HY3's stale Free label removed. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
5844d1fe8a |
Feat/ultracode blue spinner (#2096)
* feat(ultracode): add blue/cyan spinner and effort visual treatment - Add EFFORT_ULTRACODE (◆) figure for effort display surfaces - Add ultracode/ultracodeShimmer theme colors across all 6 theme variants - Wire ultracode case into effortLevelToSymbol() for icon rendering - Use blue-cyan RGB shimmer for "thinking" text when ultracode is active - Set spinner color override in REPL when displayed effort is ultracode * feat(ultracode): tint prompt border and unify shimmer to theme tokens Add a persistent cyan-blue prompt border whenever ultracode is the active effort, reacting immediately to /effort and ranking below bash/teammate overrides. Derive the spinner thinking-shimmer from the ultracode/ ultracodeShimmer theme tokens (with an ANSI/daltonized fallback) instead of a divergent hardcoded cyan, so border, spinner, and shimmer share one source of truth. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * test(spinner): cover ultracode shimmer color selection and ANSI fallback Extracts the thinking-shimmer color computation into an exported getThinkingShimmerColor helper (renderToString strips ANSI color, so the selection logic is only observable through a direct call) and adds focused tests for ultracode rgb() token interpolation, the ansi:* fallback endpoints, and the non-ultracode gray interpolation. Addresses CodeRabbit review on #2096. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
77c82829c4 |
docs(readme): add npm monthly downloads badge (#2069)
Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
e636f7d1cb |
feat(opengateway): add Macaron V1 Tall to the gateway catalog (#2067)
* feat(opengateway): add Macaron V1 Tall to the gateway catalog Served by opengateway via direct Novita (model is not on OpenRouter). Free launch window; the gateway delists it 2026-08-10. Adds the model and brand descriptors and regenerates integration artifacts. * test(opengateway): add Macaron regression coverage + picker expectation Adds macaron.test.ts (descriptor capabilities/limits, gateway catalog apiName/modelDescriptorId wiring, runtime limits — tencent.test.ts pattern) and includes mindai/macaron-v1-tall in the /model picker's expected opengateway option list. --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
56a920196d |
feat(web): replace favicon/logo with Ember Block O brand mark (#2065)
The site icon was still the 2026-06 terminal-face + git-fork circuit
mark, predating the ember identity the product now leads with (the
ANSI-Shadow startup logo and the orange pixel wordmark in the README).
Replace it with the Ember Block O: the startup screen's figlet "O"
letterform re-plotted as pure SVG rects — five ember gradient bands
(#ffb15f → #be5008, the exact stops from StartupScreen.palettes.ts)
with the wordmark's thin offset outline shadow, on a dark rounded tile.
Reads as a crisp orange O at 16px and matches CLI, README, and site.
- openclaude-logo.svg: new mark (same filename, Head.astro untouched)
- openclaude.png: 512px transparent-corner render (PNG favicon and the
nav/footer images, which already reference this path)
- og/{default,docs,commands,buddy}.png: all four social cards
regenerated with the new mark; layout, copy, and grid unchanged
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
c2030bbb2b |
fix(web): make web/ build standalone — stop importing the repo-root p… (#2061)
* fix(web): make web/ build standalone — stop importing the repo-root package.json vercel --prod deploys only the web/ directory, so site.ts importing ../../../package.json (and verify-dist.ts reading it) broke every Vercel build with ts(2307) while local builds passed. - SITE.version now derives from latestVersion, the newest entry in src/data/releases.ts — committed data inside web/, so builds are deterministic and need nothing outside the directory - verify-dist gains a best-effort npm freshness guard: fails the build only when registry.npmjs.org reports a newer @gitlawb/openclaude than releases.ts; unreachable registry or malformed responses skip the check, and site-ahead-of-npm is allowed for release PRs - verify-dist.test.ts covers the guard via injected fetch (no network) Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(web): reject leading-zero semver from the npm registry Number() would normalize a malformed '01.2.3' to 1.2.3; require strict semver components so malformed registry values skip the freshness check instead of being silently coerced. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
0f76b5490b |
feat(web): v0.26 refresh — buddy page, changelog, partners, provider … (#2060)
* feat(web): v0.26 refresh — buddy page, changelog, partners, provider catalog - Single-source the site version from the root package.json (never stale again) - New /buddy/ page: all 7 hero sprites rendered as animated SVGs generated from src/buddy/pixelSprites.ts, attack descriptions, commands, hatch lore, plus a dedicated 1200x630 OG image composed from the real sprites - New /changelog/ page: curated release highlights 0.19 -> 0.26 from a typed releases.ts data file - Landing: buddy teaser section, partners strip (GitLawb, Bankr, Atomic Chat, Xiaomi MiMo, Atlas Cloud, AI/ML API, Novita AI) with self-hosted logos, community links, refreshed provider strip, node >= 22 fix - Providers docs rebuilt as grouped catalog (39 providers: subscriptions, gateways, vendors, local, custom) incl. xAI OAuth, AI/ML API, Cloudflare Workers AI, NVIDIA NIM, Kimi K3, GPT-5.6, Opengateway free models - Data refresh vs v0.26.0 source: 16 new slash commands, pdf skill, new CLI flags + 10 subcommands, modelLimits/providerFallbackChain/agentRouting settings, corrected env vars (GEMINI_API_KEY, OPENGATEWAY_API_KEY, ...) - Nav/footer/docs sidebar link the new pages; JSON-LD breadcrumbs on both Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(web): address CodeRabbit review — flag description + dist verification - Correct --disable-slash-commands description: the flag empties the entire slash-command list (REPL.tsx filters all commands), not just skills; the upstream help string "Disable all skills" is the misleading one - Add scripts/verify-dist.ts, wired into `bun run build` (so the existing CI web job runs it): asserts SITE.version matches the root package.json in the rendered pages, nav exposes /buddy/ and /changelog/, every release renders with its GitHub URL, every hero renders with its sprite asset, partner and community links render on the landing page, and the sitemap covers the new routes Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(web): harden verify-dist per review — empty-page guard, rendered-nav check, tests - page() now records a failure for a present-but-empty file, so '' is only ever returned alongside a recorded failure and skipped assertions can no longer mask a blank page - assert the rendered docs sidebar (dist/docs/) links every docsNav route, not just the source data array and the landing nav - extract pure verifyDist(dist) and add 9 fixture-based bun tests covering missing/empty pages, lost sidebar links, missing sprites, stale partner links, missing release URLs, and sitemap regressions; discovered by the root `bun test` run in CI, no workflow changes needed Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * test(web): derive the missing-sprite fixture from heroes data Hard-coding robinhood.svg would make the test throw during fixture mutation if that hero were renamed, instead of exercising verifyDist(). Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
3c5856a004 |
feat(integrations): add Ling 3.0 Flash free to the Opengateway catalog (#2057)
* feat(integrations): add Ling 3.0 Flash free to the Opengateway catalog inclusionai/ling-3.0-flash:free — 124B MoE reasoning model, 262K context, 32K max output, tool calling verified through the gateway. Free window on the gateway runs until 2026-08-03; the gateway delists it automatically after that. * test(model): include Ling 3.0 Flash in the Opengateway picker expectation The static descriptor picker asserts the exact Opengateway catalog; inclusionai/ling-3.0-flash:free now sits between Nemotron and HY3. --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
12994f2c97 |
feat(ui): single-row centered startup logo with ANSI Shadow wordmark (#2053)
* feat(ui): single-row centered startup logo with ANSI Shadow wordmark Render OPEN and CLAUDE side by side as one centered 6-line block on the pre-Ink startup screen, redrawn in ANSI Shadow letterforms (consistent shadow corners, D-shaped D, clean N). Terminals narrower than the 94-col row fall back to two stacked blocks, each centered as a unit so rows stay aligned. The tagline, provider box, and version line are centered to match. The Ink welcome panel wordmark (constants/brand.ts) becomes a matching single row: letter-spaced caps flanked by shade-gradient accents, keeping the shimmer/brand two-tone split. Adds layout unit tests (one-row vs stacked switchover, block centering, box centering) and brand wordmark invariant tests; updates the D-shape glyph regression for the new font. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * test(ui): address CodeRabbit review — wordmark render test, row centering asserts Extract the LogoV2 wordmark row into a WordmarkRow component and add a focused render test asserting segment order and the shimmer/brand color split (left accent + OPEN in brandShimmer, CLAUDE + right accent in brand), via renderToAnsiString with chalk pinned to truecolor. Extend the startup-screen layout test to assert the tagline and version rows are centered, alongside the existing provider-box check. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
158bdd0dcf |
docs(readme): rename Sponsors to Partners, add AI/ML API and Novita AI, new wordmark (#2054)
- Rename the Sponsors section and nav link to Partners - Add AI/ML API and Novita AI to the partners table with local logo assets; AI/ML API ships light/dark SVG variants behind a <picture> element so the wordmark stays readable on both GitHub themes - Replace the green SVG header wordmark with the orange pixel-art OPENCLAUDE wordmark PNG Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
fff83a1a7f |
feat(onboarding): first-run experience for third-party providers (#1864)
* feat(onboarding): first-run experience for third-party providers Two gates in showSetupScreens were keyed on usesAnthropicAccountFlow(), so users of any non-Anthropic provider skipped onboarding entirely: - Onboarding (theme + security notes) now runs for all providers. The component already drops its preflight/OAuth steps when Anthropic auth is not enabled, so third-party users get theme -> security notes -> terminal setup with no login screens. - The trust dialog now runs for all providers. Workspace trust is orthogonal to the API provider — an untrusted repo is exactly as dangerous over a local model as over Anthropic. (The block comment even said "always show"; the inner gate contradicted it.) Also: the login-method screen now detects OPENAI_BASE_URL+OPENAI_MODEL in the environment and offers "Use current environment configuration" as the first (default) option. Selecting it saves and activates a provider profile via addProviderProfile — env vars alone do NOT activate the OpenAI route (resolveActiveRouteIdFromEnv requires CLAUDE_CODE_USE_OPENAI or a saved profile), a gap previously masked in manual testing by a stray legacy .openclaude-profile.json in the cwd. Verified live (tmux, scratch config dir, mock OpenAI server): fresh 3P first run walks theme -> security -> trust -> REPL; env option saves "Local OpenAI-compatible", the session completes a real turn against the env endpoint, and the profile persists across relaunch. Second launch shows no onboarding. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(onboarding): address review — testable gating seam + env-profile dedup - The first-run screen decisions move into src/utils/setupScreenGates.ts, a provider-free importable seam (showSetupScreens' import chain cannot be loaded under bun test — the same constraint and pattern as the dev-channels registration seam). Behavioral tests cover the gate matrix (fresh install, completed install, theme-missing re-show, trust independence, claubbit skip); the bugfixes.test.ts checks now assert the wiring (both dialogs consult the seam, no provider gate at the call sites) instead of only regexing for the removed string. - The "use current environment configuration" onboarding option dedupes: an existing profile matching the env base URL + model is re-activated via setActiveProviderProfile (which also re-applies profile env and syncs the startup profile file) instead of appending a near-identical profile on every pass through the flow. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(onboarding): refresh reused profile credentials + accurate env var label - The reuse branch now refreshes the stored credential from the environment before activating: a rotated OPENAI_API_KEY would otherwise leave the flow running on the profile's stale key. Falls back to the existing key when the env no longer carries one, so a working credential is never blanked. Status text says "Activated" for reuse and keeps "Saved" for a newly created profile. - The environment option's label names the variable the value actually came from (OPENAI_BASE_URL vs OPENAI_API_BASE) instead of hardcoding the former, so troubleshooting points at a variable that is really set. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(onboarding): preserve profile fields and verify the credential refresh Follow-up on the reuse path added last round: - updateProviderProfile REPLACES the profile (toProfile builds a fresh object rather than merging), so passing only name/baseUrl/model/apiKey silently dropped any configured apiFormat, azureStyle, authHeader, authScheme, authHeaderValue, customHeaders, or maxContextLength. Spread the existing profile and override only the refreshed credential. - A null return from updateProviderProfile (env values failing profile validation) no longer falls through to activation: reporting "Activated" while still running on the stale key is worse than routing the user to guided setup, which is what the create path already does. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(onboarding): redact credential-bearing endpoints before display OPENAI_BASE_URL / OPENAI_API_BASE are credential-bearing in the wild (userinfo like https://user:pass@host/v1, or ?token=/?api_key= query params), and both the option label and the completion status message rendered the raw value straight into terminal scrollback. The derivation moves into src/utils/envProviderOption.ts, which owns the disclosure boundary explicitly: `displayBaseUrl` is passed through the codebase's existing redactUrlForDisplay and is the only form the UI may render, while the raw `baseUrl` is retained for profile creation and activation so the saved profile still authenticates. Both rendered sites now use the redacted value. Regression coverage: envProviderOption.test.ts asserts userinfo and sensitive query params never reach displayBaseUrl (including via the non-URL fallback path) while baseUrl stays intact, plus var-name and availability cases; a wiring guard in bugfixes.test.ts fails if either rendered site is ever pointed back at the raw endpoint. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
83d54b0ac8 |
feat(perf): tier1 token optimization — universal tool compression, doom loop detection, configurable compaction (#1869)
* feat(perf): tier1 token optimization — universal tool compression, doom loop detection, configurable compaction - Extend compressToolHistory to Anthropic-native transports (firstParty/ bedrock/vertex/native-GitHub), gated to runs where prompt caching is inactive: rewriting old messages as they cross tier boundaries diverges the request prefix every call, so cached native sessions keep relying on the cache-aware microCompact instead. Shim-routed traffic (OpenAI-compatible env providers, per-agent providerOverride, Codex) still compresses at its own layer, where the local fast-path opt-out applies. compressToolHistory is now idempotent (skips its own stub/truncation markers) so layered call sites can never re-mangle output. - Add doom loop detection: blocks after 3 consecutive identical tool calls (same name + input signature). State is keyed per agent (main thread and each subagent separately) so concurrent subagents neither trip nor reset each other's counters. Resets at the start of each agent's query turn. - Add configurable compactTailTurns in GlobalConfig, wired into autoCompact's relevance pruning (default: 3, clamped to positive) and exposed in the /config UI next to the other compaction settings. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(perf): address review — tier-aware idempotency, full-input signatures Post-rebase reconciliation with #1958's compressToolHistory rework (the guard's extractText call no longer even typechecked against the new signature) plus the CodeRabbit findings: - Tier-aware idempotency replaces the blanket already-compressed skip, which permanently blocked mid→old upgrades: stubs stay terminal, and a truncated block left alone while mid-tier still upgrades to a stub when later exchanges age it into the old tier. The upgraded stub's omitted-chars count is recovered from the truncation marker (visible length + marker's omitted count = exact pre-truncation length), so it reports the tool's real output size — asserted equal to what a fresh single-pass stub would have produced. Regression tests cover the aging upgrade, recovered length, and same-input no-op. - computeSignature hashes the FULL serialized input (sha-256, fixed-size stored signature) instead of comparing a 2KB prefix, which treated distinct calls sharing a long prefix (e.g. Write calls differing only in trailing content) as identical — a false-positive block on legitimate work. Regression test included. - DEFAULT_COMPACT_TAIL_TURNS shared constant replaces the `3` duplicated across autoCompact, pruneByRelevance's default, and the /config UI. - Doom-loop block path: added a tengu_doom_loop_blocked analytics event (false-positive rates become observable for threshold tuning) and the nudge now tells the model a deliberate repeat is fine once something observable has changed. The blocked yield's message shape mirrors the sibling pre-execution error paths, preserving tool_use/tool_result pairing. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(perf): normalize compactTailTurns everywhere + native-routing coverage Second CodeRabbit round: - normalizeCompactTailTurns (relevancePruning.ts, next to the shared default) is now the single rule for the hand-editable config: finite values >= 1 floor to integers, everything else falls back to the default. The /config UI displays AND persists through it, so the picker shows exactly what autoCompact preserves (a hand-edited 2.5 no longer displays as 2.5 while running as 2; 0/negatives no longer display as selected while running as 3). Also fixes a real edge in the previous inline clamp: 0.5 passed the `> 0` check and floored to a ZERO-message tail, pruning everything. Unit tests cover the boundary matrix. - shouldCompressNativeToolHistory extracted from queryModel and exported: the request-mutating routing decision is now parameterized-tested across all four native transports (first-party, Bedrock, Vertex, GitHub-native-Anthropic) x caching on/off, the providerOverride exclusion, and non-native providers — queryModel itself needs a live client, so the predicate is the honest testable seam. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(perf): guard custom first-party endpoints + strict config coercion Third CodeRabbit round: - shouldCompressNativeToolHistory now requires an Anthropic first-party base URL before accepting the firstParty provider as native, mirroring the exact guard getPromptCachingEnabled uses. Without it, a custom ANTHROPIC_BASE_URL (proxy / compatible endpoint) reported firstParty with caching disabled and had every request's messages compressed — an assumption we cannot make about arbitrary endpoints. Test added for the custom-base-URL exclusion. - normalizeCompactTailTurns only coerces numbers (persisted config) and strings (the /config picker channel); other hand-edited shapes no longer smuggle a tiny tail through Number() coercion (true → 1, [2] → 2) and fall back to the default instead. Boundary tests added. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
cb460e516b |
feat(codex): add GPT-5.6 family models and fix saved-model rehydration (#2014)
* feat(codex): add GPT-5.6 family models and fix saved-model rehydration Add the GPT-5.6 family (gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna) to the Codex OAuth provider: alias map with default reasoning efforts, /model picker options, display names, and Codex-route context metadata. Bare `gpt-5.6` resolves to the flagship Sol tier at parse time (Codex CLI convention) — matched on the base name so a ?reasoning=/?thinking= query suffix cannot defeat the rewrite — keeping context sizing and display on real descriptor metadata. Context windows reconcile with the #1961 direct-OpenAI routing: the gpt.ts descriptors pin the ~272k effective Codex input cap (issue #1118 precedent; the Codex base URL resolves to a catalog-less route that reads descriptors), while the openai vendor catalog keeps the true 1.05M window for direct api.openai.com /v1/responses traffic. The gpt-5.6 alias-default reasoning effort is likewise Codex-transport-only: OPENAI_API_BASE gateways do not inherit first-party effort metadata (explicit /effort and ?reasoning= picks still flow everywhere). Fix startup rehydration for Codex profiles: profileSupportsModel is now authoritative for Codex-backend profiles — it accepts every Codex alias and Codex-eligible gpt-5.x free-text pick (shared isCodexEligibleGpt5Model predicate), so a /model choice (e.g. gpt-5.6-terra) survives restart instead of silently reverting to codexplan/gpt-5.5. A trailing [1m] tag is normalized off before matching, so tagged picks stick too. Foreign leftovers (kimi-k2.6) and API-only tiers the backend does not serve (gpt-5-mini/-nano) still fall back to the profile default instead of 400ing. Also: generalize the picker's custom-model recovery to keep curated labels for all Codex models across provider switches ([1m]-tolerant, single lookup), and add GPT-5.6 cases to the display-name maps. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(codex): address CodeRabbit review — [1m] tag parsing + coverage - parseModelDescriptor now strips a trailing [1m] context tag (whole-string or base-id position) before parsing: the tag is a client-side 1M opt-in, never a wire model id, so tagged aliases keep their mapping and effort defaults and resolvedModel never leaks the bracket suffix to the backend (pre-existing gap for e.g. gpt-5.5[1m], now fixed at the parse layer). - The bare-gpt-5.6 rewrite keeps a [1m] tag TRAILING after a preserved query (gpt-5.6?reasoning=medium[1m] → gpt-5.6-sol?reasoning=medium[1m]); the previously emitted tag-before-query form broke the request-time base-model split. End-to-end regression tests cover parse + request. - New coverage per review: alias effort defaults are asserted suppressed on a custom OpenAI-compatible gateway (non-Codex transport) while explicit ?reasoning= overrides flow; picker-recovery tests assert a persisted gpt-5.6-terra[1m] under a non-Codex provider keeps its exact tagged value with the curated label/description instead of a "Custom model" entry. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
ca7a7e0791 |
feat(install): enforce and guard the zero-warning npm install contract (#2019)
* feat(install): enforce and guard the zero-warning npm install contract
`npm install -g @gitlawb/openclaude` is verified zero-warning today, but
nothing kept it that way: the runtime deps were caret ranges resolved
fresh on every user install (the published tarball ships no lockfile),
no CI step ever installed the package, and registry-side drift (a
transitive dep deprecated after we ship) is invisible to file-based CI.
Static contract (fast, offline, every PR via `bun run build`):
- Pin the 3 runtime deps to exact versions so the verified resolution IS
the shipped resolution.
- New validators in scripts/externalsValidation.ts (unit-tested):
dependencies must equal RUNTIME_DEPENDENCY_CONTRACT exactly (no ranges,
no unreviewed additions), no consumer-run install hooks or funding
field, engines.node pinned. Wired into validate-externals.ts.
Runtime verification (scripts/verify-clean-install.ts, `install:verify`):
- Tarball mode (release gate) and published mode (registry watch), each
running cold-install and upgrade-over-previous scenarios in throwaway
prefixes with a cold cache and normalized env/flags.
- Strict output whitelist (summary lines only) with network failures
retried and reported as infra (exit 2), never as a hygiene verdict.
- Structural authority over the installed tree: any package declaring
install scripts fails, the installed manifest must match the static
contract, tarball payload/size asserted.
- Boot must be silent: --version prints the exact packed version;
--help (which, unlike the --version zero-import fast path, loads the
real bundle) must exit 0 with empty stderr.
CI: release publishes only after the verify passes on Node 22 (npm 10,
the supported floor — warning phrasing and EBADENGINE behavior differ
from npm 11) and Node 24, plus a final gate on the publishing machine
replacing `npm pack --dry-run`. A daily install-hygiene workflow
re-verifies the published @latest on {ubuntu, macos, windows} x
{Node 22, 24} — the only defense against post-release registry drift,
and the OS matrix covers the per-platform @vscode/ripgrep packages.
Found-by-the-guard fix: a fresh machine printed "Warning: ignoring saved
provider profile. OPENGATEWAY_API_KEY is required..." on every command
(even --help) because the injected fresh-install Opengateway default
fails validation without a key (#1651 chose ignore+warn). The default
env is still ignored, but the warning now only fires for genuinely
persisted profiles; published 0.24.0 carries the old noise, so the
verify script exempts exactly that version until the next release.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(install): address CodeRabbit review on the install-hygiene guard
- release.yml: pin install-verify to least-privilege `contents: read` and
disable credential persistence on its checkout; same persist-credentials
hardening on the install-hygiene cron checkout.
- verify-clean-install: previousPublishedVersion now follows the same
retry/infra discipline as installWithRetry — transient registry failures
retry and then exit 2 (infra) instead of silently skipping the
upgrade-scenario coverage; a clean not-published answer still skips.
- providerProfile: the fresh-install warning suppression now keys on
explicit provenance (persisted profile resolved once in
applyStartupEnvFromProfile) instead of sniffing the
DEFAULT_STARTUP_PROVIDER_ENV_VAR marker, which a persisted profile's
env can inherit from a parent CLI process; regression test covers the
marker-collision case.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* test(install): cover previousPublishedVersion retry/skip/infra branches
CodeRabbit follow-up: the branches deciding whether the upgrade-install
scenario runs, skips, or aborts as infra were untested. Extract the loop
as resolvePreviousPublishedVersion with injected effects (runView,
onRetry, onInfraFailure) per the repo's dependency-injection testing
convention, guard main() behind import.meta.main so the test import does
not launch a real verification, and add regression tests: first-try
success, transient-infra retry then success, clean E404 → null skip
without retries, persistent infra → onInfraFailure (exit 2 in the real
wiring), and unparseable version output → null.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
---------
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
14648213a6 |
Chore/readme cleanup (#1976)
* docs: README cleanup, green wordmark header, Trendshift badges Header: the startup wordmark (src/constants/brand.ts half-block art) rendered as a green two-shade SVG (docs/assets/openclaude-wordmark.svg, textLength-pinned so rows align in any monospace font), with the three Trendshift badges (daily/monthly/repository) centered beneath it. Cleanup (536 -> ~430 lines, nothing lost): - Agent routing, maxSteps limits, and GitHub Copilot sub-agent tuning moved to docs/agent-routing.md; headless gRPC server moved to docs/grpc-server.md; README keeps linked summaries. - Build/test/validation commands were repeated in three sections — consolidated into one Development section; Contributing links to it. - New "Meet Your Buddy" section documenting the companion heroes and their /buddy commands; added to What Works and Why OpenClaude. - Star History moved from the header flow down beside Community. - Setup Guides indexes the new docs pages; fixed a missing blank line before Repository Structure and a curly quote. All relative links, image paths, and internal anchors validated. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * docs: pure-rect wordmark for crisp rendering; drop broken Star History The wordmark SVG previously drew the half-block art as monospace <text>, which rendered raggedly (font-dependent glyph stretching and seams). Regenerated as pure SVG rects computed from the brand.ts wordmark grid — no font dependence, pixel-crisp at any size, same two-shade green split. Star History chart removed: the badge endpoint errors and displays a broken image. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * docs: render the wordmark at full README column width Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
d683e85395 |
feat(buddy): hero pixel-art companions with signature Enter animations (#1972)
* feat(buddy): hero pixel-art companions with signature Enter animations Rebuild the buddy system as heroes-only. The 18 legacy rolled species are removed; the hatch pool is now 7 hero forms — robinhood, kaio, strawhat, merlin, kage, ember, corsair — each hand-pickable via /buddy set. Every hero has 22x16 truecolor half-block pixel art (idle + action poses), a line-art fallback for low-color terminals, a narrow-mode face, and a signature effect that fires on every message submission: arrow with impact thunk, charging full-width energy wave, stretchy punch that extends and snaps back, twinkling sparkle stream, spinning shuriken, gradient fire cone, and cannonball with smoke trail. Engine: companion animation moves from a raw 500ms setInterval to the shared animation clock (useAnimationFrame; pauses when hidden, respects prefersReducedMotion), with a one-shot 50ms burst driver (useShotClock, arm-then-anchor to avoid stale-tick draw-phase skips) and a general ActionEffect system (pure draw/travel/impact functions, frame-tested). Effects travel right-to-left toward the prompt — matching where the sprite actually stands. Commands: /buddy set <form|random>, /buddy name <name>, muted-buddy feedback (silent no-op pets now explain themselves), and a hatch-message fix so the announced species always matches the displayed sprite (the message previously rolled with a different seed). BREAKING: existing rolled pets transform into a hero on upgrade (name and personality persist; speciesOverride pins are unaffected). Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(buddy): address CodeRabbit review on PR #1972 - useShotClock: consume an in-flight shot when playback becomes ineligible mid-flight (mute/reduced-motion/resize), so re-enabling can't resume a stale animation. - /buddy unmute: emit a greeting reaction — the sprite reads companionMuted non-reactively and its clock is paused while hidden, so a config-only unmute left it invisible until an unrelated re-render. - /buddy name: strip ANSI escapes and control/format characters before saving, and cap by display width (stringWidth) instead of UTF-16 length. - CompanionSprite: track bubble age in sync-render state instead of an effect-updated ref, so a fresh reaction can't render pre-faded. - companion_intro already keyed on name+species (prior commit); tests now pin exact faces for all seven heroes, separate idle/shoot pixel frame counts, and decode-guard the charCode species constants. - CompanionActionFX tests: deterministic companion fixture via complete-config module mock; raw (untrimmed) output compared against a rendered-null baseline so a spurious blank FX row fails. - companion.test: re-register the real config module in afterAll (mock.restore does not undo mock.module). - Types: SPECIES_COLORS and FORM_FLAVOR are full Records (compile error on a colorless/flavorless future hero); dead RARITY_COLORS removed. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * test(buddy): regression coverage for bubble age reset on reaction change Renders CompanionSprite against a fake shared clock: ages the first bubble past the fade threshold, swaps the reaction WITHOUT advancing the clock, and asserts the fresh bubble renders unfaded. Fading is detected structurally (border and text collapse to one color when fading) so the test is independent of the active theme's exact values. Requested by CodeRabbit on PR #1972. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
780f703747 |
fix(installer): gate native-binary install behind NATIVE_PACKAGE_URL (#1838)
* fix(installer): gate native-binary install behind NATIVE_PACKAGE_URL openclaude install inherited the upstream native installer, which downloads the first-party Claude Code binary from the GCS bucket, symlinks ~/.local/bin/openclaude to it, and uninstalls the npm package the user is running. Gate every native-installer surface behind hasNativeDistribution() so npm-only builds never touch the native path; setting NATIVE_PACKAGE_URL at build time re-enables it unchanged. Also gate background cleanupOldVersions(): the versions/staging/locks directories under ~/.local/share/claude (etc.) are shared with a coexisting first-party native Claude Code install, and the protection logic only recognizes our own launcher symlink — an npm-only build kept only the newest VERSION_RETENTION_COUNT binaries and could delete the version a user's pinned `claude` launcher still points to. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(installer): keep npm-only guidance on npm paths * test(installer): share macro mock helper * fix(installer): clean stale native launcher in npm fallback * fix(update): clean stale native launcher in slash update * test(update): reuse shared macro helper --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> Co-authored-by: jatmn <the@jat.mn> |
||
|
|
2edec9a140 |
fix(deps): ship a zero-warning, minimal install (#1784)
* fix(deps): ship a zero-warning, minimal install
The published package declared 62 runtime `dependencies`, but `dist/cli.mjs`
is a fully-bundled esbuild output that inlines almost all of them. End users
therefore installed ~476 transitive packages — including three subtrees the
bundle never needs at install time, each emitting an install warning:
- node-domexception (deprecated) via google-auth-library
- protobufjs (allow-scripts) via @grpc/* (already bundled into dist)
- sharp (allow-scripts) native image module
The repo's `overrides`/`allowScripts` silence these locally, but those are
root-only npm settings and are ignored when the package is installed as a
dependency — so end users saw the warnings.
Core changes:
- package.json: runtime dependencies trimmed 62 -> 3 (@orama/orama,
@orama/plugin-data-persistence, @vscode/ripgrep). Bundled packages, plus
the optional sharp/google-auth-library, move to devDependencies so they
are built/tested but not shipped.
- package.json: @anthropic-ai/sdk, @modelcontextprotocol/sdk, react and
react-reconciler declared as OPTIONAL peerDependencies — externalized by
the ./sdk bundle but bundled into the CLI. Optional peers keep the CLI
install minimal and warning-free while still resolving for ./sdk consumers.
- externals.ts: sharp, google-auth-library and @anthropic-ai/bedrock-sdk
marked OPTIONAL_RUNTIME_EXTERNALS (loaded on demand, not shipped).
- validate-externals.ts: runtime deps validate against externals; bundled
deps validate against dependencies + devDependencies.
- client.ts: load @anthropic-ai/bedrock-sdk via the runtime importer so
esbuild no longer inlines it and hoists its static @aws-sdk import into
the CLI bundle (that was a startup crash for default installs).
Optional-dependency UX (consistent, actionable errors):
- New src/utils/optionalRuntimeModule.ts exports importRuntimeModule and
importOptionalRuntimeModule. The optional variant translates a missing
package (code === 'ERR_MODULE_NOT_FOUND', specifier present in message)
into "<feature> requires "<pkg>" ... Run `npm i -g <pkg>`". Generic so
typed call sites keep their module types.
- Routed ALL optional-package load sites through it (previously only one
did): google-auth-library (client.ts, auth.ts, geminiAuth.ts),
@anthropic-ai/foundry-sdk + @azure/identity (client.ts), and the
@aws-sdk/* Bedrock paths (model/bedrock.ts, tokenEstimation.ts, aws.ts).
- imageProcessor.ts: sharp-missing error now says `npm i -g sharp`.
- docs/advanced-setup.md: new "Optional provider packages" table and a
Vertex note documenting the on-demand installs.
- Unit test for the helper (friendly error, success path, specifier match,
raw passthrough).
- knip.json: ignore google-auth-library (now loaded via runtime string).
Verified on the current tree:
- tsc, build/validate-externals, knip, and tests all pass.
- npm pack + install --omit=dev adds 8 packages, zero deprecation/
allow-scripts/funding warnings; --version/--help/mcp list run.
- With packages absent, CLAUDE_CODE_USE_BEDROCK and CLAUDE_CODE_USE_VERTEX
print the friendly `npm i -g <pkg>` error (verified end-to-end).
- ./sdk imports once its optional peers are present (24 exports, no warns).
- Bundled ajv + ajv-formats validate with no ajv installed; no unguarded
native runtime requires (fsevents absent in chokidar 4; bun:sqlite Bun-only).
Trade-off: image reads, AWS Bedrock, Azure Foundry and GCP/Vertex now prompt
a one-time `npm i -g <pkg>` instead of being shipped to every user.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
Review fixes (CodeRabbit + jatmn):
- validate-externals: the INTENTIONALLY_BUNDLED exemption is now scoped per
bundle. The CLI exempts every bundled package; the SDK does NOT exempt
packages declared as peerDependencies (keyed on package.json, an independent
source of truth) so dropping react/@anthropic-ai/sdk from SDK_EXTERNALS now
fails validation instead of silently passing. Added an explicit minimal-
install contract check: bundled packages must be devDependencies-only — never
in `dependencies`, and only the SDK-external subset may be optional peers.
Validation logic extracted to scripts/externalsValidation.ts + tests.
- FileReadTool oversized-image fallback now loads via the shared
getImageProcessor() (not a raw import('sharp')) and re-throws
ImageProcessorUnavailableError, so a missing processor surfaces the
`npm i -g sharp` install hint instead of returning an over-budget image.
- optionalRuntimeModule: match the missing specifier as a QUOTED token, not a
raw substring, so a missing transitive package whose name contains the
requested one (sharp vs sharp-libvips, @aws-sdk/client-bedrock vs
@aws-sdk/client-bedrock-runtime) no longer triggers the wrong install hint.
Predicate extracted to isMissingSpecifierError() with regression tests.
- docs/advanced-setup.md: the Vertex auth section now shows both documented
paths (gcloud ADC and a GOOGLE_APPLICATION_CREDENTIALS service-account file).
Review fixes (round 2, CodeRabbit):
- validate-externals: assert the optional-peer install contract — every
peerDependency must be { optional: true } in peerDependenciesMeta
(validateOptionalPeers), so losing that flag fails the build instead of
silently reintroducing install warnings.
- validate-externals: hard-check OPTIONAL_RUNTIME_EXTERNALS placement
(validateOptionalRuntimeexternals). Anything esbuild can see statically must
stay external in BOTH bundles (dropping sharp/google-auth-library now fails);
the runtime-indirection-only subset (new RUNTIME_INDIRECTION_ONLY_EXTERNALS)
must stay OUT of externals so esbuild never re-exposes their static imports.
- Deeper-dig fix: @anthropic-ai/foundry-sdk was misclassified as
INTENTIONALLY_BUNDLED, but it is loaded only through the Function indirection
(esbuild never sees it, so it was never actually bundled) — its sole presence
in dist is the specifier string. Per the PR's own "Azure Foundry now prompts"
trade-off it is on-demand, so it now lives in OPTIONAL_RUNTIME_EXTERNALS +
RUNTIME_INDIRECTION_ONLY_EXTERNALS (mirroring bedrock-sdk). sandbox-runtime is
genuinely statically imported, so it stays bundled.
- Provider-routing coverage (scripts/optionalRuntimeSpecifiers.test.ts): a
static scan asserts every importOptionalRuntimeModule specifier is a declared
OPTIONAL_RUNTIME_EXTERNAL and never also INTENTIONALLY_BUNDLED — the
invariant that keeps a provider's optional package loadable on demand.
- All new validators extracted to scripts/externalsValidation.ts with tests.
Review fixes (round 3, CodeRabbit):
- client.ts: gate the Vertex google-auth-library import behind the non-skip
branch. CLAUDE_CODE_SKIP_VERTEX_AUTH (proxy/test) uses a mock GoogleAuth and
must not require the optional package; it was loaded unconditionally before.
- optionalRuntimeModule: drop the hard-coded `npm i -g`. The helper backs both
the global CLI and project-local ./sdk consumers, so the hint is now
context-neutral ("npm install <pkg>" / add -g for the global CLI).
- validate-externals: every SDK_ONLY_EXTERNALS entry must STAY a
peerDependency (a dropped peer leaves runtimeDeps while the SDK still
externalizes it); and OPTIONAL_RUNTIME_EXTERNALS must never be shipped (fail
on overlap with dependencies/peerDependencies). Both with tests + live-verified.
- optionalRuntimeSpecifiers.test: pin the EXACT set of optionally-loaded
specifiers instead of a >=5 count (a count passes even if a provider path
regresses).
- attachments: extract tryReadEditedImageAttachment() — background watched-file
image attachments DEGRADE to null on any failure (incl.
ImageProcessorUnavailableError) so a missing optional package never aborts a
turn, while the explicit FileReadTool path still surfaces the install hint.
Deterministic regression test (bad path -> null).
- docs: Bedrock row notes profile-based auth also needs
@aws-sdk/credential-providers; install-hint wording matches the new message.
Review fixes (round 4, CodeRabbit):
- attachments: stop sending the raw file path through the analytics
bypass-cast (tengu_watched_file_compression_failed). Send only the safe
file extension via getFileExtensionForAnalytics, matching the existing
tengu_file_read_dedup pattern, so no usernames/project paths can leak.
- externals.ts: corrected the OPTIONAL_RUNTIME_EXTERNALS header comment,
which still claimed all entries "remain in COMMON_EXTERNALS" — no longer
true since the indirection-only subset (bedrock/foundry) must stay OUT of
the externals lists.
(Other CodeRabbit comments on this push re-surface items already addressed in
prior commits: the peerDependenciesMeta-optional check (validateOptionalPeers),
the SDK-peers-present and optional-not-shipped validator rules, the
exact-specifier-set test, the attachments degrade contract + test, and the
context-neutral install hint are all present. The "assert every optional
external is a devDependency" suggestion is intentionally NOT applied: @aws-sdk/*
and @azure/identity are transitive devDeps via bedrock-sdk/foundry-sdk, so a
blanket assertion would be incorrect; source resolution is covered by the
build + tests that import these packages.)
Review fixes (round 5, CodeRabbit):
- attachments: stop leaking file paths via logError in the background-image
degrade path. readImageWithTokenBudget can throw path-bearing messages
(e.g. "Image file is empty: <path>") and logError persists message/stack, so
log only the error TYPE name now. (Analytics payload was already sanitized.)
- attachments: tryReadEditedImageAttachment takes an injectable reader so the
degrade contract is tested for the EXACT error types — ImageProcessorUnavailableError
and a path-bearing read error both degrade to null (not just ENOENT) — plus a
success case. No mocking.
- validate-externals: enforce the source-install half of the optional contract.
Non-transitive OPTIONAL_RUNTIME_EXTERNALS must be devDependencies so `bun
install` source builds resolve them. The new TRANSITIVE_OPTIONAL_EXTERNALS
documents the exemption (@aws-sdk/* via @anthropic-ai/bedrock-sdk, @azure/identity
via @anthropic-ai/foundry-sdk — provided transitively, not direct devDeps). A
blanket "all optionals are devDeps" check would have wrongly failed on those.
Tests + live-verified (dropping sharp from devDependencies now fails).
Review fixes (round 6, CodeRabbit + jatmn):
- optionalRuntimeSpecifiers.test: the call-site scan regex missed
generic-annotated calls (importOptionalRuntimeModule<...>(...)) in
model/bedrock.ts and tokenEstimation.ts, so the exact-set assertion was
incomplete. Regex now allows an optional generic; EXPECTED_SPECIFIERS adds
@aws-sdk/client-bedrock and @aws-sdk/client-bedrock-runtime (7 total).
- importOptionalRuntimeModule default generic is now <T = unknown> (was any),
so destructured imports are no longer silently any. Every call site now
supplies its module type — typeof import('<pkg>') where the package is
type-resolvable (bedrock-sdk, foundry-sdk, @aws-sdk/credential-providers,
google-auth-library), and a named minimal-shape alias for @azure/identity
(not a direct devDep, so typeof import can't resolve it). This gives
compile-time verification of each provider's module contract (export names,
shapes) — the structural answer to the "cover the provider branches" ask.
- attachments: tryReadEditedImageAttachment takes injectable {read,log,track};
a new test asserts the sanitized-telemetry contract directly — the logError
payload is path-free and the analytics payload carries only `ext`, never the
edited-image path.
* fix(deps): address optional runtime review findings
* test(deps): isolate optional runtime importer mocks
* fix(deps): clarify AWS optional auth labels
* fix(deps): close optional runtime review gaps
---------
Co-authored-by: jatmn <the@jat.mn>
|
||
|
|
2a506f9f38 | added tencent hy3 to opengateway available models (#1876) | ||
|
|
203f05538e |
fix(build): shim jsxDEV when bundling production React — TUI rendered nothing (#1863)
Since
|
||
|
|
77c0a0d780 |
feat(ux): honest feedback pass — visible retries, statusline truncation marker, hint grace period (#1862)
* feat(ux): honest feedback pass — visible retries, statusline truncation marker, hint grace period
Three fixes with one principle: never look frozen, never silently hide state.
- SystemAPIErrorMessage: retries were fully hidden until attempt 4, so
transient rate limits / overloads were indistinguishable from a hang.
Attempts 1-3 now render a compact dim line ("Rate limited — retrying
in 4s… (attempt 2/10)") with a live countdown; the full error block
is unchanged at attempt >= 4. The transcript already keeps only the
last api_error message and hides it on the next non-error message,
so early visibility adds no stacking. New briefAPIErrorReason()
classifies 429/529/5xx/connection failures, including the
OpenAI-compat shim's plain-text transport errors that carry no cause
chain. Component rewritten from react-compiler output to plain React.
- BuiltinStatusLine: fitSegments dropped rate-limit -> cost -> context
silently on narrow terminals. Segments now degrade to short forms
first (ctx 37% -> 37%, $1.23 -> $1), and anything still dropped is
marked with a trailing dim "…" so hidden data is visible as hidden.
The marker is best-effort: at extreme widths the bare model name
beats showing nothing.
- PromptInputFooter: "? for shortcuts" was suppressed whenever a status
line rendered — the default state since the builtin statusline
shipped, killing the hint's discoverability path entirely. New users
(numStartups <= 10) keep the hint alongside the status line;
established users get the quieter footer.
- docs: BASH_MAX_OUTPUT_LENGTH env var documented on the website env
reference (default 30000, cap 150000).
Verified live in the TUI (tmux + mock OpenAI endpoint): compact retry
line from attempt 1 against a dead endpoint, full block at attempt 4,
Esc interrupts cleanly; statusline at 100/32/24 cols shows full /
degraded / "test-model · 2% · …"; hint present at numStartups=2,
suppressed at 50.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(review): address CodeRabbit feedback — custom statusline yields immediately, timeout test, docs wording
- shouldSuppressShortcutsHint: a custom status line is explicit user
configuration, so it now always wins over the discoverability grace
period; only the builtin status line grants new users the hint.
Test added to lock the semantics.
- Test the ETIMEDOUT -> "Request timed out" branch. Note: CodeRabbit's
suggested test shape (plain object with a cause) would not exercise
the branch — extractConnectionErrorDetails only walks Error
instances — so the error itself carries the code.
- Docs: soften "full output saved" to reflect the persisted-file cap.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
---------
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
2083d1cdff |
fix(openai-shim): recover GLM/Qwen XML tool calls emitted as text (#1791)
* fix(openai-shim): recover GLM/Qwen XML tool calls emitted as text GLM/Qwen-family models routed through OpenAI-compatible gateways emit tool calls as XML text (`<tool_call><function=…>`) instead of structured `tool_calls`. The shim had no parser, so these leaked into visible prose and never executed — the turn ended with no tool_use block and the agent appeared to "forget" and stop mid-task. Add `parseXmlToolCalls` covering the three dialects seen in the wild (function/parameter, GLM-native arg_key/arg_value, Hermes JSON), wired into both the streaming and non-streaming paths. The streaming path holds back text from `<tool_call>` onward (incl. an opener split across SSE deltas), converts it to tool_use blocks at finalize, and flips the finish reason to tool_calls — mirroring the existing Ollama fallback. Structured tool_calls and false-positive prose are handled losslessly. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * Update src/services/api/openaiShim.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> |
||
|
|
13f7401541 |
perf(repl): batch streaming text, cache normalize, coalesce config writes (#1744)
Three independent runtime hot-path wins for long sessions and fast streams:
- Streaming text (REPL.tsx): the Ink root is a LegacyRoot, so every
per-delta setStreamingText committed a synchronous REPL render. The
preview hides the in-progress trailing line, so deltas between newlines
changed nothing on screen yet still re-rendered. Hold the full text in a
ref and publish to state only when the newline-truncated preview changes
(or on clear), dropping those no-op renders under 100-300 delta/sec
streams. Displayed text is byte-identical; the Esc-interrupt path reads
the ref so no trailing partial line is lost.
- Incremental normalize (messages.ts + Messages.tsx): normalizeMessages
flat-maps the whole transcript on every append (O(n) over 2,800+
messages). normalizeMessagesCached memoizes per-message output keyed on
(message identity, isNewChain entry flag) in a WeakMap, so unchanged
messages are reused with stable object identity (reviving downstream
memo/WeakMap bailouts and cutting GC pressure). It is an allocation/
identity optimization — the call still scans the list, it is not an O(1)
append. Proven equivalent to normalizeMessages in tests.
- Coalesced config writes (config.ts): saveGlobalConfig does a sync
lock+reread+backup+fsync per call. saveGlobalConfigDeferred queues the
updater, write-throughs the cache for read coherence, and folds the
batch into one locked write on a 500ms debounce (flushed on cleanup and
process exit). Auth-loss guard, lock and backup stay on the disk path.
Also fixes handleMessageFromStream's input_json_delta to update tool input
in place instead of reordering the updated tool to the array tail.
Review fixes (CodeRabbit + jatmn):
- REPL onStreamingText updates streamingTextRef BEFORE the showStreamingText
guard so reduced-motion / cursor-up-yank-bug terminals preserve partial
assistant output on Esc. The publish decision is extracted to a pure
helper (streamingTextPublish.ts) with streamingTextPublish.test.ts, and
replStreamingTextClear.test.ts source-scans REPL.tsx to assert both
turn-boundary paths fully clear the streaming refs (no stale re-append).
- saveGlobalConfigDeferred primes the cache (getGlobalConfig) before
enqueueing, and a direct saveGlobalConfig flushes pending deferred writes
first. Together these keep same-process reads coherent: the first deferred
counter update is visible immediately even on a cold cache, and a direct
save can no longer clobber a queued delta with a disk snapshot. Regressions
in deferredConfigWrites.test.ts.
- Extracted the deferred-write queue/debounce/write-through/drain into a
generic, disk-free engine (deferredConfigWrites.ts) with injected
storage/scheduler; deferredConfigWrites.test.ts exercises the real branch.
- config.deferredWrite.test.ts loads config via a query-suffixed specifier
so a leaked mock.module('./config.js') can no longer silently turn its
assertions into no-ops; it always exercises the real path.
- messages.streamingToolUses.test.ts covers interleaved input_json_delta
order preservation; messages.normalizeCached.test.ts now covers the
reused-message entryFlag true<->false cache transition.
- Corrected the normalizeMessagesCached comment (O(n) scan / allocation
optimization, not an O(1) append).
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
a723540163 |
perf(build): minify the CLI bundle (whitespace + syntax, keep identifiers) (#1743)
dist/cli.mjs shipped unminified at 21.7MB; whitespace+syntax minification cuts it to ~16MB (-26%) and shaves V8 parse time on every invocation. Identifier mangling stays off because the codebase matches constructor.name (errors.ts, toolExecution.ts, useCanUseTool). The SDK bundle stays unminified — its React/Ink leak check greps import syntax that minification would rewrite. The bundle guard's missing-module tripwire relied on Bun's `// missing-module-stub:<path>` module-boundary comments, which minification strips. The stub loader now also emits the marker as a side-effecting string push (survives treeshaking and syntax-minify), and the guard parses both forms. Review fix (CodeRabbit + jatmn): the marker parser previously truncated paths at the first backslash or space, so a JSON-escaped Windows marker like "missing-module-stub:C:\\Users\\Jane Doe\\...\\src\\...\\foo.js" was captured as a useless `C:` (or `C:\\Users\\Jane`) fragment and canonicalized to the wrong key — letting a newly stubbed module slip past the tripwire on Windows/spaced build hosts. Parse each marker form to its correct terminator instead: the string literal runs to its matching (back-ref) closing quote consuming escaped pairs, and Bun's comment runs to end of line. Extract canonicalStub() + the parser into scripts/stubMarkerGuard.ts so the logic is unit-testable, and add regression tests for Windows, spaced, comment-form, and multi-marker-per-line cases. Verified: build green, bundle ~16MB minified, guard passes against the real bundle, stub-guard tests pass, --version works through the minified bundle. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
a1a3cfc31e |
perf(integrations): load descriptor catalog lazily on first registry read (#1742)
Split the generated output into integrationManifest.generated.ts (plain preset metadata, type-only deps) and integrationArtifacts.generated.ts (the ~10k-line descriptor graph). Add a setRegistryLazyLoader() hook in registry.ts so read getters evaluate the catalog on first access instead of eagerly at module load — cutting startup cost on paths that never read the registry (index.ts previously eval'd 60+ descriptor modules at import). Review fixes: - registry.ts ensureLoaded() keeps the lazy loader armed until it succeeds (re-entrancy guard + clear-on-success), so a throwing lazy import retries on the next read instead of leaving the registry empty. - providerSecrets.ts no longer statically imports the heavy descriptor artifacts. It sat on the bootstrap startup chain (bootstrap -> providerConfig -> providerProfile -> providerSecrets), so the static import undercut the lazy-loading goal. Import PROVIDER_PRESET_MANIFEST from the lightweight manifest module and lazily require() the descriptor arrays inside readDescriptorCredentialEnvKeys() (cached by getKnownProviderSecretEnvKeys(), so it runs at most once). - artifactGenerator.test.ts resolves artifacts by full src/integrations/generated/<name> path instead of tuple order; the last order-dependent test now uses splitGeneratedArtifacts(). - Added registry + providerSecrets regression tests (loader retry on failure; no static descriptor import on the bootstrap path). Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
b8c34645c9 |
chore(deps): clean npm install — fix CVEs, silence warnings (#1782)
* chore(deps): clean npm install — fix CVEs, silence warnings - bump undici 7.24.6 → 7.28.0 (7 high CVEs: TLS bypass, header injection, DoS, cache poisoning, SameSite downgrade, cross-origin routing) - bump ws 8.20.0 → 8.21.0 (2 high CVEs: uninitialized memory disclosure, memory exhaustion DoS) - add allowScripts for sharp + protobufjs to silence install-script warnings - vendor node-domexception shim (re-exports native DOMException) and override the deprecated polyfill pulled transitively by google-auth-library → gaxios → node-fetch@3 → fetch-blob Result: `npm install` reports 0 vulnerabilities, 0 warnings. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * chore(deps): update bun.lock for undici/ws bumps and node-domexception override CI runs `bun install --frozen-lockfile`, which requires bun.lock to match package.json. The previous commit bumped undici/ws and added the node-domexception shim override but didn't include the regenerated lockfile, causing frozen-lockfile CI to fail. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(publish): include vendor/node-domexception-shim in npm tarball The file: override in package.json points at vendor/node-domexception-shim, but the files array didn't list vendor/, so npm pack excluded it. End-user npm installs would fail resolving the override. Add vendor/node-domexception-shim/ to the files array. Verified via npm pack --dry-run: tarball now contains both shim files (12 → 14 files). Addresses reviewer finding #1. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
d32b6f0476 |
fix(update): stop false "development build" block on npm installs with NODE_ENV=development (#1781)
`/update` and `openclaude update` reported "Auto-update is unavailable for
a development build." even when OpenClaude was correctly installed from npm,
as long as the launching shell had `NODE_ENV=development` exported.
Root cause: `getCurrentInstallationType()` checked `NODE_ENV === 'development'`
as its first branch, before any path-based detection. A user's shell env var
then downgraded a real npm install to 'development', which routed
`resolveUpdateStrategy()` to `{ action: 'blocked', reason: 'development' }`.
Two-part fix:
1. doctorDiagnostic.ts — move the `NODE_ENV === 'development'` check to a
fallback position after all real-install path markers (bundled mode, local
npm, npm-global paths, /npm/, /nvm/, `npm config get prefix`). Path
detection runs first; NODE_ENV only classifies as 'development' when no
install path matches (i.e. an actual source-tree `bun run dev` run).
2. bin/openclaude — the heap-sizing relaunch previously used
`fileURLToPath(import.meta.url)`, which resolves symlinks. After relaunch,
`process.argv[1]` pointed at the real file target (repo path for
`npm install -g .`, package path inside node_modules for real installs),
defeating path-based detection. Preserve `process.argv[1]` (the original
invocation path, e.g. /usr/local/bin/openclaude or nvm bin symlink) so
npm-global path markers can match correctly.
Verified: `bun run typecheck` passes; `openclaude doctor` now reports
npm-global (not development) with NODE_ENV=development set on a real
npm global install.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
adafde30fa |
feat(integrations): add GLM 5.2 as an Opengateway-routed model (#1772)
* feat(integrations): add GLM 5.2 as an Opengateway-routed model Add an `opengateway-glm-5.2` catalog entry (apiName `z-ai/glm-5.2`) to the gitlawb-opengateway gateway, reusing the existing `glm-5.2` model descriptor. Routes GLM 5.2 through the credit-billed Opengateway (OpenRouter upstream) alongside the existing direct Z.AI vendor path. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * test(model): add z-ai/glm-5.2 to the opengateway picker fixture The model-picker option-order assertion enumerates the gitlawb-opengateway catalog; add the new z-ai/glm-5.2 entry (after qwen/qwen3.7-max) so it matches the catalog. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
1b1dfcfdbc |
Feat/ads sponsored tips (#1674)
* initial commit * feat(ads): passive per-turn earning tips + mask earn code - Gitlawb earning tips now appear on a per-turn cadence (default every 2nd tip slot, OPENCLAUDE_ADS_TIP_EVERY-tunable) for opt-in users, bypassing the per-startup sponsored gate that only ever showed one ad per session. - Each shown earning tip fetches a real impression and confirms after the dwell, crediting opengateway credits; fails silent so ads never break the CLI. - Mark /ads isSensitive so the earn code is redacted from history. * feat(ads): mask earn code via paste dialog; never accept it inline /ads on always opens a masked TextInput dialog (mask="*") — the code is never typed inline, because the terminal echoes inline args as you type (isSensitive only redacts history after submission, not the live echo). An inline /ads on <code> now also opens the dialog and warns that the typed code is exposed and should be rotated. Converts /ads to a local-jsx command. * fix(ads): address CodeRabbit review on PR #1674 - ads.ts: hard 5s timeout (AbortController) on both fetchNextTip and confirmTip so a stalled connection can never hang the spinner-tip path; the abort timer is unref'd. (fetchWithProxyRetry forwards init.signal and treats AbortError as non-retryable.) - gitlawbEarn.ts: unref the best-effort confirm timer so it can't keep a short-lived CLI run alive for the dwell window. - ads.tsx: clear the stored earnCode on `/ads off` (it's a credential, no reason to keep it at rest after opt-out); fix the `/ads on` doc comment to match the always-masked-dialog behavior. - ads.test.ts: restore ADS_BASE_URL in afterEach; add submit/cancel coverage for the masked dialog (enables + persists code / cancellation message) and assert `/ads off` clears the code. - gitlawbEarn.test.ts: restore ADS_BASE_URL + OPENCLAUDE_ADS_TIP_EVERY in afterEach to stop env leaking across suites. - tipScheduler.test.ts: cover the earning-tip branch precedence in getTipToShowOnSpinner (driven via the existing config mock + env, no module mock — avoids the bun mock.module cross-file leak). Testing: tsc clean; tips+ads suite 36 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(ads): contextual sponsored tips — share the latest prompt for ad matching Switches the earning tips from generic to contextually-matched ads. When the viewer enables sponsored tips (the /ads on dialog now discloses this), the client sends their most recent prompt to the ads service, which matches a relevant ad via Gravity's contextual endpoint. - types.ts: TipContext gains `latestUserMessage` (opt-in earning tip only). - REPL.tsx: extract the latest user message at the spinner-tip pick site and thread the full TipContext into tip.content() (previously only {theme}). - gitlawbEarn.ts: pass ctx.latestUserMessage to fetchNextTip. - ads.ts: new sanitizeForAds() redacts secrets/JWTs/emails/long hex and truncates to 500 chars; fetchNextTip POSTs { context:{messages:[user]} } when a prompt is present (else GET, identity-only). - ads.tsx: /ads on dialog + enable message disclose that the recent prompt (secrets redacted) is shared with the ad partner — consent folded into enabling. Privacy: explicit disclosed consent, minimal context (last prompt only), sanitized client-side (the ads service re-bounds size server-side too). Testing: tsc clean; new ads.test.ts (7 sanitize cases); ads+tips suite 43 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ads): show the real advertiser + ad click URL in earning tips renderEarningTip hardcoded the Gitlawb name + gitlawb.com URL, so every served ad rendered as "Sponsored · Gitlawb — … gitlawb.com" and discarded the actual ad's `name` and `link`. Pass the served ad's advertiser name and click URL (Gravity's tracker — required for click attribution/payout) into the renderer; fall back to Gitlawb only for the static no-ad line. Testing: tsc clean; tips suite 16 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(ads): hyperlink the advertiser name instead of printing the tracker URL Sponsored/earning tips render the advertiser as a terminal hyperlink to the ad's click URL (Gravity's tracker) via a shared renderSponsorLink helper, rather than printing the long tracker URL inline. Clicks still route through the tracker, so attribution/payout are unchanged. Testing: tsc clean; tips/ads suite 46 pass (incl. tipLink); smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ads): address CodeRabbit review on the contextual ads changes Security: - tipLink.ts: sponsor name/url are advertiser-controlled (untrusted). Strip C0/C1 control chars from the name and accept only http(s) URLs (parsed via URL()), preventing terminal escape-sequence injection and javascript:/file: click targets. - ads.tsx: soften the consent copy to "best-effort secret redaction" (both the dialog and the enable message) — sanitizeForAds is heuristic, not absolute. Correctness: - ads.ts: clamp dwell_ms to a finite, non-negative integer (malformed values no longer yield NaN/Infinity in the confirm-delay math). - gitlawbEarn.ts: never point a third-party advertiser name at the Gitlawb URL — a real ad uses only its own click URL; the Gitlawb fallback is reserved for the static no-ad line. Tests + isolation: - ads.test.ts: cover fetchNextTip/confirmTip (POST-with-context vs GET, !ok→null, ad:null→null, dwell clamp, confirm normalization) via a stubbed fetch. - tipLink: renderSponsorLink takes an injectable `hyperlinks` flag so both branches are deterministically tested; added control-char/unsafe-URL cases. - gitlawbEarn.test.ts: restore global `ads` config in afterEach. - tipScheduler.test.ts: preserve OPENCLAUDE_ADS_TIP_EVERY in cleanup. Note: CodeRabbit's packages/memory/* findings are moot — that stray package was removed from the branch. Testing: tsc clean; ads+tips suite 56 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ads): address CodeRabbit follow-up review (round 2) - ads.tsx: remove warmOneEarn — it fetched AND confirmed a tip during /ads on that the user never saw, crediting an unshown impression (and could confirm after a quick opt-out). Earning now happens only on the per-turn rendered-tip path. Also drop the unsupported "Run /ads for your balance" instruction. - ads.ts: normalize confirm amounts (earned_micro/balance_micro) to finite integers via toFiniteInt, mirroring the dwell_ms clamp. - gitlawbEarn.ts: degrade to the static fallback when tip_text is blank, so a malformed ad never renders an empty line and credits a blank impression. - ads.test.ts (commands): restore global ads config in afterEach; assert the inline-code exposure warning (warnExposed) is shown. - ads.test.ts (services): build the AWS-shaped fixture at runtime so it isn't flagged by security:pr-scan; add a success-path test for buildEarningTip() rendering a fetched ad + a blank-ad fallback test. Testing: tsc clean; ads+tips suite 58 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ads): address CodeRabbit full review (round 3) + comments Blocking: - ads.ts: gate the serve response on a string `token` (the signed impression), not on an `ad` field. A served tip carries NO `ad` key, so CodeRabbit's suggested `data.ad == null` would have suppressed every real tip; the empty slot (`{ ad: null }`) and malformed responses both lack a token, so the token check covers them correctly. Documented the contract. - gitlawbEarn.ts: drop the dead try/catch around fetchNextTip — it is contractually non-throwing (catches everything, returns null), so the wrapper was unreachable/misleading. - ads.ts: make the base64-blob redaction boundary reliable — \b is meaningless around + and / (both \W), so bound the run with explicit look-around instead. Comments / clarity (per review): - ads.ts: note withAbortTimeout is a per-CALL deadline (shared across retries), not per-attempt. - tipLink.ts: clarify the CONTROL_CHARS_RE comment — the ESC/BEL literals above are intentional terminal-sequence constants, not part of the matcher. - config.ts: document that GlobalConfig.ads is managed via /ads and intentionally excluded from GLOBAL_CONFIG_KEYS (earnCode is a credential). (resetEarningCadenceForTesting export is an accepted in-repo pattern — no change.) Testing: tsc clean; ads+tips suite 58 pass; smoke green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
4dee44a542 |
feat(ux): long-turn visibility + default-on stream hang safety net (#1758)
Long-running turns looked frozen ("are you still working?") and a dropped
network stream could hang for the full 5-minute QueryGuard idle timeout.
Three focused changes:
- Spinner: show the elapsed-time counter at 5s instead of 30s (split the
timer gate from the token-count gate). The timer is wall-clock derived,
so it keeps ticking during long tool calls — proof of life even when no
tokens stream.
- Spinner: surface the currently-executing tool name in the status line
(reusing the spinnerSuffix channel; stop-hook progress still wins). A
long subagent/typecheck now reads "(↓ Bash · 1m 20s)" instead of a bare
spinner.
- claude.ts: enable the stream idle-timeout watchdog by default, matching
the always-on read-timeout already used by the OpenAI/Codex shims. A
silently dropped Anthropic-family stream now aborts and falls back to a
non-streaming retry within STREAM_IDLE_TIMEOUT_MS (90s) instead of
hanging to 5 minutes. Opt out with CLAUDE_DISABLE_STREAM_WATCHDOG=1.
Testing: tsc --noEmit clean; bugfixes.test.ts (31) and src/services/api
(835) pass; bun run smoke builds + runs.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
bcf9421824 |
feat(permissions): allow npm/bun/tsc --version as read-only (#1759)
`node --version` and `python --version` are auto-approved as read-only Bash, but `npm`, `bun`, and `tsc` version queries were missing from the allowlist, so they fell through to a permission prompt. Add them in the same exact-anchored form (no trailing args) so a version flag can't smuggle a script-running suffix past the check (the `node -v --run <task>` class of bypass). Closes the only read-only gap that the now-superseded #787 classifier covered, without a parallel classification surface. Testing: new readOnlyValidation.test.ts (18 cases — allows -v/--version, rejects install/suffixed forms); tsc clean; BashTool suite 117 pass. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
dc6a7781bf |
feat: auto-detect and persist project conventions to wiki (#1010)
* feat: auto-detect and persist project conventions to wiki Adds a convention scanner that reads project config files (package.json, tsconfig.json, eslint, prettier, Dockerfile, CI workflows, lockfiles) on startup and saves extracted conventions to .openclaude/wiki/pages/conventions.md. Includes a fingerprint cache to avoid redundant writes and a /wiki scan command for manual re-scans. New modules: - src/services/wiki/conventions.ts — scanner + cache + save - src/services/wiki/identity.ts — project identity (name, languages, monorepo) - src/services/wiki/conventions.test.ts — 7 tests Modified: - paths/types/init/status — extended wiki infrastructure for conventions - wiki.tsx/index — added /wiki scan command - main.tsx — fires scan via startDeferredPrefetches - init.test.ts/status.test.ts — updated for new conventions page/fields Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(wiki): address PR #1010 review — trust gate, cache, pre-init, indexing Resolves the three blockers raised by @jatmn and @Vasanthdev2004, plus a post-rebase integration gap. 1. Trust gate (blocker): the startup convention scan ran git (via project identity) before workspace trust was established. Now gated behind the same check as prefetchSystemContextIfSafe — runs only when non-interactive (implicit trust) or the trust dialog has been accepted. 2. Cache fingerprint (blocker): computeFingerprint hashed only the detected config sections, so identity-only changes (project name, language counts, monorepo, default branch) left conventions.md stale. Identity inputs are now folded into the fingerprint. (Hashing the rendered markdown isn't viable — it embeds a "Last scanned" timestamp.) 3. /wiki scan pre-init crash (blocker): forceScanConventions caught the page write failure but still wrote the cache, throwing ENOENT before /wiki init. It now only writes the cache on a successful page write and reports saved=false; /wiki scan surfaces "run /wiki init first". 4. Index integration: the conventions page is written to pages/, but the wiki index (rebuildWikiIndex, added after this PR was opened) was never refreshed, so the new page wasn't listed. Both save paths now reindex on success. Testing: 3 new regression tests (identity-only re-save, pre-init no-crash/no-cache, reindex); wiki suite 14 pass; tsc clean; smoke + knip green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(wiki): address CodeRabbit review on PR #1010 - main.tsx: add a terminal .catch(logError) to the deferred conventions-scan promise chain so a failed dynamic import or scan can't become an unhandled rejection on the startup path. (Major) - conventions.ts: narrow both page-write catch blocks to ENOENT (= wiki not initialized → skip); rethrow other failures (EACCES, EROFS, …) instead of masking them as "not initialized". (Major) - identity.ts: replace blocking execFileSync('git', …) with async execa (the service-layer subprocess convention); getProjectIdentity is now async, awaited in scanProjectConventions. Keeps the startup scan off the event loop. - commands/wiki/index.ts: restore `ingest` in the argumentHint — `[init|status|scan|ingest <path>]` — it's still an implemented subcommand. Testing: tsc clean; wiki suite 14 pass; smoke + knip green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
c2467eedad |
feat(atlas-cloud): add GLM 5.2 to vendor catalog (#1755)
Adds zai-org/glm-5.2 with the same 202,752 context window as GLM 5.1 so the model is selectable through the Atlas Cloud provider. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
c4aa756689 |
feat(commands): add /update command with package-manager auto-detection (#1687)
Adds a `/update` slash command that updates OpenClaude to the latest published version, routing by how the running install is actually managed so it updates the installation the user is running. `globalPackageManager.ts` detects the owning package manager (npm, yarn, pnpm, bun) for npm-style installs and maps it to the correct global-install command. `installGlobalPackage()` and `getLatestVersion()` now consume it, so the legacy `openclaude update` CLI and the background auto-updater gain yarn/pnpm support; `getLatestVersion()` also falls back to a direct npm-registry HTTP lookup when npm isn't on the PATH. `updateStrategy.ts` factors the install-type routing and the third-party-build guard out of `src/cli/update.ts` (now shared by both entrypoints). `/update` uses it to refuse development/third-party builds, point package-manager/native/local installs at their safe update paths, and only do a global npm install when that's what's actually running — instead of always installing a stray global package. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
a36ef463ce |
docs(readme): add social links and clarify license line (#1660)
- Add Discord (discord.gg/k68zFR6AcB) and X (x.com/gitlawb) as shields.io badges in the top badge row and as descriptive links in the Community section. - License section now notes contributor modifications are MIT while the derived Claude Code remains Anthropic's, with a "See more" link to LICENSE. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
00ff6de4ca |
fix(suggestions): stop slash-command dropdown freezing on a throwing getter (#1657)
Typing a multi-character slash command (e.g. /provider) left the dropdown frozen on the bare "/" list with /simplify highlighted. Root cause: the /sandbox command's `get description()` read `SandboxManager.checkDependencies().errors`, but checkDependencies() returns null at runtime, so `.errors` threw. That getter runs for every command while building the Fuse search index (only for non-empty queries — bare "/" returns before the index is built), so the throw rejected the whole updateSuggestions() call and the list never narrowed. Fixes: - sandbox-toggle: null-guard checkDependencies() so the getter can't throw. - commandSuggestions: make index building resilient — a single command whose description/isHidden/aliases getter throws degrades gracefully (safe fallback) instead of breaking suggestions for every command; a command whose name can't resolve is dropped. Broken getters now leave a one-time `warn` debug breadcrumb instead of failing silently. - useTypeahead: command results re-rank every keystroke, so the highlight snaps to the top/best match (selectedSuggestion = 0) instead of following the previously selected command by id (which made it stick to /simplify). Tests: - New sandbox-toggle getter tests (null/present/missing deps, via spyOn). - New commandSuggestions tests: narrowing, throwing description/isHidden/name getters, broken command still listed, and best-prefix-match-first ranking. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
f4c3be850e |
chore: remove dead code and add knip gate to CI check (#1612)
Delete 32 unreferenced source files (~4,000 lines) verified dead by import-specifier grep and knip: test-only token utilities, orphaned hooks (useTaskListWatcher, useSkillImprovementSurvey + its component), the removed DevBar and ConfigTool UIs, unregistered bundled skills (stuck, verifyContent), unused analytics sinks, the benchmark command, and stale migrations/helpers. Remove unused dependencies code-excerpt, stack-utils, and tsx from package.json plus their entries in build stub/external lists. Add knip with a tuned knip.json (entrypoints, build-time stub targets, subprocess-launched fixtures, and runtime-string-imported SDKs ignored; providerAutoDetect kept intentionally as provider pre-wiring) and wire `bun run deadcode` into the `check` script so dead code stays dead. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
a3a3c3659d |
perf(cli): restore --version fast path with dynamic provider imports (#1611)
The static imports of providerProfile.js and providerValidation.js at the top of cli.tsx side-effect-loaded the entire integrations graph (~11.7k lines of vendor/gateway/model descriptors) at module evaluation, defeating the zero-import --version fast path. Convert them to dynamic imports at their use sites, matching the file's existing convention. --version: ~0.47s median (0.35-0.60s) -> steady 0.26s. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
d08593de92 |
feat(web): rebuild landing as Astro static site with gitlawb theme and full docs (#1606)
* feat(web): rebuild landing as Astro static site with gitlawb theme and full docs Replaces the Vite+React SPA in web/ with a fully static Astro 6 site: - gitlawb-aligned design system: dark-default monochrome surface tokens, Geist Mono, hairline grids, [light]/[dark] toggle — keeping the orange #ff7a1a openclaude accent (#e85d00 in light mode for AA contrast) - 9-page docs section: installation, quickstart, providers, slash commands (all 69 user-facing commands with argument hints), CLI reference (every non-hidden flag), configuration, keybindings, skills — rendered from typed data files seeded from the CLI source - SEO: per-page canonicals/OG/Twitter, JSON-LD (SoftwareApplication, TechArticle, BreadcrumbList), @astrojs/sitemap, robots.txt, and generated 1200x630 OG cards, all on https://openclaude.gitlawb.com - zero framework JS: theme toggle, copy buttons, mobile nav, and TOC highlighting are small vanilla scripts - CI-compatible: same typecheck/build script names, bun.lock regenerated Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(web): redesign logo as gitlawb-aligned circuit mark White terminal face (hollow node eyes, >_ prompt mouth) on a black square with an orange git-fork trace descending to two commit nodes — same stroke language as the gitlawb mark. Adds the SVG source as the favicon and regenerates all three OG cards with the new logo. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(web): address review feedback on copy button a11y and css lint - CopyCommand: generic aria-label (component is reusable, not install-specific) and a visually-hidden role="status" live region so screen readers announce the copied state - global.css: lowercase text-rendering keyword, blank line before color-scheme, kebab-case fade-up keyframe name Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
2002e4c116 |
UI/upgraded UI (#1605)
* feat(ui): OpenClaude brand identity — orange accent, wordmark, dot-pulse spinner Give OpenClaude a real visual identity instead of reskinned upstream visuals: - theme.ts: add brand/brandShimmer keys and repoint the claude family (claude, claudeShimmer, clawd_body, briefLabelClaude) to the gitlawb orange #ff7a1a across all 6 themes — darkened variants for light backgrounds, luminance-separated variants for daltonized themes, redBright floor for 16-color ANSI themes. Values stay rgb() strings (parseRGB in Spinner/utils.ts silently fails on hex). - brand.ts (new): BRAND_NAME, BRAND_TAGLINE, accent constant, and a 2-row Unicode half-block OPENCLAUDE wordmark split for two-tone render. - LogoV2: full logo shows the wordmark + centralized tagline; CondensedLogo drops the duplicated OPEN CLAUDE header and renders a brand-colored name+version line; Clawd mascot body now clawd_body. - Startup splash: new 'ember' gradient palette (brand orange) as the default (/logo keeps sunset et al.); tagline centralized from brand.ts. - Spinner: dot-pulse glyph sweep (· ∘ ○ ◎ ◉ ●) replacing the asterisk set (drops Ghostty/darwin special-cases); defaults switched to brand/brandShimmer. Reduced-motion and stall interpolation unchanged. - figures.ts: TEARDROP_ASTERISK marker → ◎ to match the new glyph family. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(ui): built-in default statusline Ship a status bar that renders when no custom /statusline command is configured: `model · ctx % · session cost · rate limit`. Previously this slot was empty unless the user wired up their own shell command. - BuiltinStatusLine.tsx (new): pure segment builders (buildBuiltinStatusSegments / fitSegments — drops lowest-priority segments right-to-left as the terminal narrows) plus a memoized component reusing the exact data pipeline the custom StatusLine feeds to user commands (getRuntimeMainLoopModel, getCurrentUsage, calculateContextPercentages, getTotalCost, getRawUtilization). Pure in-process computation — no subprocess, no debounce. - Context % colors warning ≥70 / error ≥90 (aligned with auto-compact warnings); rate limit shows the worst of the 5h/7d windows and is absent (not 0%) for API-key users; cost hidden at $0. - PromptInputFooter: custom statusline always wins; the built-in bar also suppresses the "? for shortcuts" hint like the custom one does. - defaultStatusLineEnabled global-config toggle (default on) surfaced in /config settings. - Unit tests for segment building, narrow-width fitting, threshold colors, and custom-statusline precedence. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(ui): polish pass — fuzzy match highlighting, completion flash, diff gutter, dialog hints - Fuzzy pickers: new highlightFuzzyMatch (contiguous-run or greedy- subsequence, bold so the focused row's color survives) applied to Quick Open and prompt-history results. FuzzyPicker now passes the live query as a third renderItem argument so memoized render callbacks can't capture a stale query. - Select: inline option descriptions now truncate with an ellipsis on narrow terminals instead of clipping at the edge (row made shrinkable, description wrapped in truncate-end). - CompletionFlash (new): static `✓ Done · 12s` row for ~1.5s after a response completes. Keys off the isLoading transition (the spinner also hides mid-turn for streaming text); same row footprint as the spinner; suppressed for sub-second turns, brief mode, open permission/prompt queues, running teammates, and reduced motion. - Diff gutter: line numbers on +/- lines are now dimmed (previously full decoration intensity competed with content, worst on light themes); +/- sigils keep full intensity. Mirrored in the non-highlighted Fallback renderer. - Dialog: optional showNavigationHint prepends "↑/↓ navigate" to the default input guide; opted in from Select-hosting dialogs (IdleReturn, DevChannels, WorktreeExit). Dialog rewritten as plain React (was react-compiler output) to take the new prop safely. - Spinner: the ↑/↓ request-direction glyph now leads the status parens whenever any status shows — previously buried in the tokens part, which only appears after 30s; width gating reserves its space. - Replaced the stale `/`-search TODO in ScrollKeybindingHandler with a pointer to the shipped REPL transcript search. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ui): address review — explicit statusline default, narrow-terminal thinking gate - createDefaultGlobalConfig now sets defaultStatusLineEnabled: true explicitly, matching the factory's other default-true booleans (read sites already coalesced to true; no behavior change). - SpinnerAnimationRow: the width gate reserves mode-glyph space that a thinking-only spin never uses; on narrow terminals where nothing else renders, re-try the thinking gate with that space returned so '(thinking)' shows instead of nothing. CompletionFlash NaN guard was reviewed and not applied: both refs are useRef(0) — initialized numbers by type and construction — and the active→inactive transition guard guarantees the start time was set before the elapsed math runs. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ui): address review round 2 — hint accuracy, flash suppression, coverage - Dialog: the cancel shortcut hint now renders only when isCancelActive, matching the keybinding it advertises. - PromptInputFooter: new exported resolveFooterStatusLine(settings, guards, config?) is the single source of truth for which status line renders — custom wins over builtin, and all render guards (prompt mode, short fullscreen, exit message, pasting) force null. suppressHint and the render now share it, so '? for shortcuts' is suppressed only when a status line actually renders. - CompletionFlash: suppression now clears an already-active flash (the effect resets state and the render guard skips the commit-gap frame) instead of only preventing new ones. - REPL: flash suppression reuses the existing hasActivePrompt aggregate, which covers the sandbox, worker-sandbox, and elicitation queues the hand-rolled expression missed. - builtinStatusLineShouldDisplay takes an injectable config (defaults to getGlobalConfig()) so the config-off path is testable without module mocks; tests added for config-off, custom-wins-regardless, and the resolver's full variant x guard matrix. Validation: typecheck exit 0; full suite 3733 green; smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(ui): drop fit-budget floor, cover reduced-motion render gap - BuiltinStatusLine: remove the artificial 10-column floor on the fit budget — fitSegments returning [] is the signal that nothing fits, and the empty branch already handles it (row-reserve in fullscreen, null otherwise). The floor forced a truncated segment onto panes narrower than the model name. - CompletionFlash: the render guard now also skips the one frame between reducedMotion flipping on and the effect clearing the flash, same as the suppressed case. Component-level mount tests for PromptInputFooter were considered and deliberately not added: the footer needs 10+ mocked contexts to mount, mock.module harnesses leak across bun test files in this repo, and the branching under review is exactly the pure resolveFooterStatusLine contract already pinned by tests. Validation: typecheck exit 0; full suite 3733 green; smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
94d2a6a503 |
ci: split typecheck into its own PR-checks job (#1599)
The Typecheck step lived inside the smoke-and-tests job and typecheck:type-tests ran inside `bun run check`, so type errors were buried mid-job and serialized behind the build. They now run as a dedicated parallel `typecheck` job (tsc --noEmit + the focused type tests) with its own status check, and `check` slims to smoke + test:full so nothing runs twice in CI. Local scripts (typecheck, typecheck:type-tests, hardening:strict) are unchanged. Review feedback: the new job's checkout sets persist-credentials: false (no credentials needed), and CONTRIBUTING.md now documents typecheck as a CI-enforced check instead of a recommended-local-only one. Validation: workflow YAML parses (jobs: smoke-and-tests, typecheck, web); typecheck exit 0; type-tests green; `bun run check` green. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
9755550137 |
Typecheck/zero tsc errors (#1597)
* ci(typecheck): add error-count ratchet toward zero tsc errors tsc --noEmit currently reports 697 pre-existing errors (issue #473), so PRs cannot be gated on a clean typecheck yet. This adds scripts/typecheck-ratchet.ts and a per-file baseline: CI fails when the count rises above the baseline (listing exactly which files regressed), passes at or below it, and --update lowers the baseline to lock in gains. Wired into pr-checks as its own step; once the baseline reaches zero the step becomes a plain `bun run typecheck`. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): mechanical sweep — 697 → 624 tsc errors Type-only fixes with no runtime behavior change, except the deliberate NODE_ENV restorations: - Restore process.env.NODE_ENV comparisons that the source snapshot had baked into the literal "production", making the conditions constant (AutoUpdater dev/test skip, useTypeahead, ink devtools injection, interactiveHelpers onboarding skip, TestingPermissionTool.isEnabled — the last now correctly enables under bun test, +3 tests run green) - Type stream read helpers in openaiShim/codexShim as Bun.ReadableStreamDefaultReadResult<Uint8Array<ArrayBuffer>> and annotate throwClassifiedTransportError as never-returning, clearing the reader/response undefined cascades (29 errors) - Delete 14 stale @ts-expect-error directives - Widen useState/useRef/array generics inferred from null/[] literals - as-const notification priority/color literals to match Priority - Accept readonly Tool[] in checkLocalModelContextLoad/getCombinedTools Baseline lowered via typecheck:ratchet --update; full suite green (3690 tests), smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): recreate missing modules — 624 → 415 tsc errors The open snapshot never mirrored ~60 modules; the bundler noop-stubs them at build time (() => null named exports), so every recreated module here is runtime-inert by construction: no import-time side effects, gated features stay off (isAssistantMode/isSkillSearchEnabled → false, tools isEnabled → false, dialogs render null), lookups return empty, telemetry no-ops. Types are honest and derived from importer usage — no any. Highlights: - sdk: runtimeTypes re-exports/aliases, sdkUtilityTypes (NonNullableUsage), settingsTypes.generated; coreTypes.generated usage fields regenerated as a self-contained structural type (the consumer package ships without sdkUtilityTypes/@anthropic-ai/sdk, so the generated file must stay dependency-free — generator override updated to match, package-consumer-types tests green) - services: contextCollapse operations/persist/stats, compact cachedMicrocompact state/types + reactiveCompact, skillSearch (7 modules), oauth/types, lsp/types, sessionTranscript - cli/server/daemon: Transport interface, parseConnectUrl, server/* (7), daemon/*, bg/templateJobs/runners; assistant/* (KAIROS), ssh/* - tools/components: WorkflowTool trio, ReviewArtifact pair, OverflowTest/TerminalCapture/VerifyPlanExecution/DiscoverSkills, WebBrowserPanel, task dialogs, message variants, ink events/cursor - types: statusLine, fileSuggestion, notebook, messageQueueTypes; SerializedMessage rebuilt as distributed Omit-union so transcript guards narrow again; vitest-compat.d.ts mirrors Bun's runtime 'vitest' → 'bun:test' aliasing - TS2304 names: ant-model helpers imported from existing antModels.ts, inert Ultraplan/Gates/LogoV2 stubs, PromiseWithResolvers local type - build.ts: ACCEPTABLE_RUNTIME_STUBS emptied — both grandfathered bundle-reaching stubs (MonitorMcpDetailDialog, VerifyPlanExecutionTool/constants) are now real typed modules, so the degrade-on-use debt the guard tracked is retired Validation: full suite 3690 green, smoke + bundle guard green, typecheck:type-tests green, sdk package-consumer tests green; baseline lowered via typecheck:ratchet --update. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): reconstruct Message discriminated union — 415 → 342 tsc errors src/types/message.ts was a stub where all ~40 message type aliases were 'export type X = any'. Bare-any aliases break the one thing the union is for: narrowing. Type predicates like isHookAttachmentMessage collapsed to 'never' in guard chains, cascading TS2339/TS2345 through utils/messages.ts, messageFilters.ts, groupToolUses.ts, collapseReadSearch.ts, REPL.tsx, compact.ts, stopHooks.ts and the message components. Envelope design (permissive-body discriminated union): - Each variant declares its literal discriminant(s) — message.type for the envelope union (user/assistant/attachment/progress/system), subtype for the 17-variant System family — plus the properties constructor functions in utils/messages.ts actually populate, with '[key: string]: any' as an escape hatch so unreconstructed properties never error. - UserMessage<C> / AssistantMessage<T> are generic over content shape so NormalizedUserMessage / NormalizedAssistantMessage<T> reuse the envelope without Omit (Omit over an index-signature type collapses keyof to string and silently drops the discriminant, breaking narrowing). - AssistantMessage.message is a structural AssistantMessageContent<T>, not the SDK's BetaMessage: synthetic constructors don't populate every SDK-required field (stop_details), and SDK-facing consumers need assignability to Record<string, unknown>-style bodies. - AttachmentMessage<T = Attachment> / ProgressMessage<T = Progress> stay generic over their payloads (utils/attachments.ts and Tool.ts types). - UI wrappers (GroupedToolUseMessage, CollapsedReadSearchGroup, CollapsibleMessage, RenderableMessage) and stream/control envelopes (StreamEvent over BetaRawMessageStreamEvent, RequestStartEvent, TombstoneMessage, ToolUseSummaryMessage) reconstructed from call sites. - logs.ts SerializedMessage switched from the Omit<Message, never> trick (only sound against an any stub) to an Extract-based distributed union, keeping TranscriptMessage assignable to Message. All other touched files are type-level-only adjustments (annotations on evolving arrays that inferred never[], predicate types, casts in SDK wire adapters and test fixtures) — no runtime logic changed anywhere; the full bun test suite passes 3690/0 before and after. Result: 415 → 342 tsc errors, every never-cascade in the message pipeline resolved, no file above its per-file baseline (ratchet updated). Part of issue #473. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): narrow unknowns and fix signature drift — 342 → 94 tsc errors Clears every remaining non-test error. Honest fixes dominate: evolving array/let/useState/useRef annotations (the repo's noImplicitAny:false disables evolving types), real type guards over unknown wire payloads, hoisted react-compiler-style params annotated with their components' real Props, and callee signature corrections (useRegisterOverlay optional param, generic useVoiceState<T>, growthbook shim's accepted refresh-interval param) that each cleared several call sites. Targeted reason-commented casts only at SDK/stub/wire boundaries; no any, no new suppressions. Runtime deviations are confined to already-broken paths: benchmark.ts imported a function name that never existed (module-load crash), caches.ts called stub methods unguarded (TypeError for ant-gated users), messageActions returned undefined from a string function; CACHE_EDITING_BETA_HEADER is a best-effort reconstruction of a squash-lost constant, reachable only behind feature-gated first-party paths (flagged for review). Also: ConnectorTextBlock gains its wire-proven optional signature field; MCP server factory ambient types gain close(); ink render-node-to-output's nodeType cast fixed (intersection was collapsing the intended widening); upstreamproxy relay normalizes the socket data union. Validation: full suite 3690 green, smoke + bundle guard green; remaining 94 errors are all in test files (PR 5). Baseline lowered via typecheck:ratchet --update. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): clean test typing, gate CI on zero tsc errors — 94 → 0 Closes the typecheck burn-down (issue #473): bun run typecheck now exits 0 across the whole repo and CI fails on any new error. Test typing: new src/test/typedMocks.ts centralizes the two bun:test gaps (asMockFetch — Mock<T> lacks fetch.preconnect; callArgs — argless-signature mocks collapse mock.calls to []). Beyond the helpers, fixes are honest: discriminated-union narrowing before member access, fixture typing with boundary casts, assertion-type corrections, and two tests realigned to production signatures they had drifted from (requestLogging logApiCallEnd args, incrementalTokenCounter tokenBudget rename) with identical assert outcomes. No assertion semantics changed; all touched suites pass. CI: the ratchet served its purpose and is retired — pr-checks now runs a plain `bun run typecheck` step; ratchet script and baseline deleted. Burn-down summary across the series: 697 → 624 (mechanical sweep) → 415 (recreate ~60 missing modules) → 342 (Message discriminated union) → 94 (narrowing + signature drift) → 0 (this PR). Validation: tsc --noEmit exit 0, full suite 3690 green, smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(typecheck): reconcile with upstream parallel typecheck fixes Upstream landed #1591/#1592/#1595 while this series was in flight, fixing some of the same errors differently. Rebase resolutions prefer upstream where it is authoritative: their CACHE_EDITING_BETA_HEADER value ('cache-editing-2025-12-01', unconditional) replaces this series' feature-gated reconstruction; their cachedMicrocompact stub shapes (with their new test file) replace ours, with boundary casts in claude.ts where the stub's unknown[] edits meet the local pinned delete-edit shape; their reader/ReadResult stream typing in openaiShim replaces ours. MessageWithoutProgress now matches its name (Exclude<NormalizedMessage, ProgressMessage>), reconciling upstream's RenderableMessage GroupingResult with this series' message union; the @ts-expect-error upstream added for settingsTypes.generated is removed since the module now exists. tsc exit 0; full suite 3697 green (incl. upstream's new cachedMicrocompact tests); smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(sdk): keep result usage counters required, fix assistant stub exports Addresses jatmn's and chioarub's review on the typecheck PR: 1. SDK usage contract restored: the generated result types' usage now keeps input_tokens, output_tokens, cache_creation_input_tokens, and cache_read_input_tokens as REQUIRED numbers — result messages are populated from QueryEngine.totalUsage (initialized from EMPTY_USAGE), so they are always present at runtime and strict consumers may sum them without undefined guards. The richer nested metadata (cache_creation, server_tool_use, service_tier) is modeled explicitly instead of hiding behind the index signature; the nested objects carry no index signature so the SDK's interface types stay assignable. Generator override updated and artifacts regenerated; a new package-consumer type test sums the counters and reads the nested fields so this contract cannot silently regress. The sessionHistory test fixture now carries all four counters, matching runtime shape. 2. Assistant install wizard stub mismatch fixed: dialogLaunchers imported NewInstallWizard/computeDefaultInstallDir through a module shape cast, but the assistant stub only exported default — a guaranteed runtime crash if the gated path lit up. The stub now provides real typed exports: a wizard that cancels immediately (so the launcher resolves null/user-cancelled instead of hanging on an empty dialog) and an inert computeDefaultInstallDir; the unsafe cast in dialogLaunchers is gone. Validation: tsc exit 0; full suite 3698 green (incl. the new consumer counters test); smoke + bundle guard green. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
5040c491da |
feat(opengateway): surface the gateway's "auto" smart-routing model in /model (#1588)
Opengateway now supports a virtual model "auto": the gateway scores each request (context size, tools, code, reasoning) and routes it to the cheapest model expected to handle it, escalating on upstream failure. Add it to the static catalog so the /model picker offers it (listed first) when the opengateway provider is active. - catalog entry only — defaultModel stays mimo-v2.5-pro until the routing heuristics are calibrated - no MODEL_ALIASES change: "auto" validates through the normal live sideQuery path, which the gateway accepts - exempt the virtual entry from the shared-descriptor test (no concrete model descriptor exists for a server-side router) Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
5b01c2b595 |
feat(provider): add Atlas Cloud as official OpenAI-compatible provider (#1585)
New top-level vendor descriptor, env key wiring, startup screen detection, and provider flag support for Atlas Cloud AI. Catalog mirrors https://www.atlascloud.ai/models/list/llm (48 models, namespaced IDs) and the preset is covered by compatibility, ProviderManager preset-order, applyProviderFlag, route-credential, activation, and restart-relaunch tests. Endpoint: https://api.atlascloud.ai/v1 Auth: Bearer (standard) via ATLAS_CLOUD_API_KEY (dedicated key required; no OPENAI_API_KEY fallback anywhere). Scoped to Atlas Cloud only — venice/xiaomi-mimo/xai keep their documented OPENAI_API_KEY compatibility fallback unchanged. Credential hardening (atlas-cloud only): setup.dedicatedCredentialsOnly (new descriptor flag) stops route credential resolution from falling back to OPENAI_API_KEY; validation requires the dedicated key; the --provider flag path replaces stale known base URLs via applyOpenAIBaseUrlDefault (custom proxy URLs preserved), prefers the dedicated key over a lingering OPENAI_API_KEY, and clears the generic key when the dedicated key is absent; buildLaunchEnv carries dedicated vendor keys across restarts (live shell value wins over persisted) so relaunched profiles stay authenticated. Review feedback: OAuth picker entries anchor to DeepSeek by value with an append fallback when the anchor is absent; startup-screen base URL check moved into the authoritative base-URL section with the model regex dot escaped. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
03c8e224dc |
Feat/opengateway nemotron free (#1539)
* feat(opengateway): NVIDIA Nemotron 3 Ultra free model Add the OpenRouter :free Nemotron 3 Ultra endpoint (550B MoE, 1M context, 65K output, tools + reasoning) to the opengateway catalog. Bills $0 and bypasses the gateway credit gate, so it works for every user — no credits or premium plan needed. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(model): surface catalog notes as a picker tag; tag Nemotron free Wire ModelCatalogEntry.notes into the route catalog description so the Nemotron 3 Ultra entry shows "Free · Provider: Gitlawb Opengateway" in the model picker. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
b1a80267a5 |
feat(xiaomi): retire deprecated MiMo V2 Pro and V2 Omni (#1538)
Xiaomi deprecated mimo-v2-pro and mimo-v2-omni upstream — requests now 404 with a migrate-to-v2.5 message. Remove them from the opengateway catalog, the Xiaomi MiMo vendor catalog, model descriptors, brand ids, and the legacy model picker list. mimo-v2-flash remains (still served). Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
ea091768ae |
feat(opengateway): Gemini 3.1 Flash Lite GA model id (#1537)
The model left preview — OpenRouter and the gateway now serve google/gemini-3.1-flash-lite as the canonical id. Swap the opengateway catalog entry, model descriptors, and brand ids to the GA id and drop the -preview variants; pricing is unchanged ($0.25/1M in, $1.50/1M out). Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
73a2833819 |
feat(sponsors): add Atlas Cloud sponsor and sponsored tip (#1536)
Add Atlas Cloud (atlascloud.ai) to the README sponsors table with its banner asset, and add an Atlas Cloud sponsored tip to the tip catalog. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
8705cd35f7 |
feat(opengateway): add MiniMax M3 and Qwen 3.7 Max to the model catalog (#1515)
Two new models reachable through the Gitlawb Opengateway unified route, both served upstream via OpenRouter (gateway-side routing lands separately in the opengateway repo): - MiniMax M3 (`minimax/minimax-m3`): reuses the existing minimax-m3 descriptor — 1M context, 131k output, reasoning/coding. - Qwen 3.7 Max (`qwen/qwen3.7-max`): new descriptor — 1M context, 65k output, text-only per the OpenRouter catalog (no vision), so it skips the qwenModel helper's vision defaults. The /model picker test's exact-list assertion gains both entries. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
96ddec7183 |
fix(test): stop use-input test from leaking a global stdin mock (#1501)
The use-input.test.ts added in #1198 broke the full `bun test` run two ways: 1. It imported `@testing-library/react-hooks`, which was never installed and is React 16/17/18-only (incompatible with this repo's React 19), so the file errored on load. 2. Its top-level `vi.mock('./use-stdin.js', …)` registered a module mock that leaks across every later file in the same `bun test` process. The fake eventEmitter's `.on` was a no-op, so `useInput` silently registered no listener and dropped all keystrokes — surfacing as timeouts in MonitorPermissionRequest and the agent-menu/wizard TextInput tests (which passed in isolation but failed in the full suite). Rewrite the test to inject the stdin handle via StdinContext.Provider (no leaking global mock) and render through the real ink root (no @testing-library/react-hooks). Drop the now-dead react-hooks and react-test-renderer devDependencies and reconcile the lockfile. Full suite: 3429 pass, 0 fail (was 9 fail + 1 error). Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
e2fa248376 |
feat(minimax): add MiniMax M3 model with 1M context window (#1470)
* feat(minimax): add MiniMax M3 model with 1M context window M3 is MiniMax's next-gen flagship with coding/agentic capabilities, 1M token context (1,048,576), and benchmark performance on par with Opus 4.7 on SWE-bench. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(minimax): add M3 model definition to integrations models The integrations/models/minimax.ts was missing the minimax-m3 model definition that was added to the vendor config. This caused the model picker to not show MiniMax M3 as a selectable option despite it being present in the picker list. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(minimax): show full catalog in /model picker and default to M3 When a MiniMax provider profile was active, the /model picker collapsed to the single model pinned in the profile (e.g. MiniMax-M2.7), hiding the rest of the catalog — including M3. mergeActiveProfileModelOptions now surfaces the complete catalog for native vendor routes (which ship a curated static catalog), while gateways keep the profile model list as a whitelist. Added isNativeVendorCatalogRoute() to draw that distinction. Also harden isMiniMaxProvider() to detect the MiniMax host on ANTHROPIC_BASE_URL (anthropic-proxy transport), not just OPENAI_BASE_URL, and switch the vendor defaultModel to MiniMax-M3. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * test: fix 13 cross-file isolation failures in the full suite These tests passed individually but failed under `bun test` because bun does not unregister mock.module() overrides on mock.restore(), so stubs leaked into later files. Root-caused and fixed each leak at the source: - providerFallback: inject settings/profiles into getProviderFallbackChain and resolveNextFallbackProviderFromState; the test now uses DI instead of mock.module on settings.js/providerProfiles.js (fixed 7 attribution fails). - apiPreconnect: accept an injected apiProvider (defaults to getAPIProvider); the test passes it explicitly so a leaked providers.js mock can't force 'firstParty' (fixed 3 preconnect fails). - preflightChecks: build a COMPLETE axios stub (defaults + interceptors) and re-register the real module in afterEach, so the partial stub no longer breaks proxy.ts's axios.defaults usage in tests/sdk/query-lifecycle (fixed 2 query-resume fails). - flagSettings: realpath the temp dir so the macOS /tmp -> /private/tmp symlink doesn't mismatch the canonicalised --settings path (fixed 1 fail). Full suite now green: 3343 pass / 0 fail under both `bun test` and `bun test --max-concurrency=1`. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(minimax): default env-only MiniMax sessions to M3 The descriptor/profile path and --provider minimax already default to MiniMax-M3, but getDefaultMainLoopModelSetting() still fell back to MiniMax-M2.7 for env-only sessions (only MINIMAX_API_KEY / a MiniMax base URL set, no explicit model env). That left part of the default-model update unapplied. Align the env-only fallback (and the dead applyProviderFlag fallback) on M3, and update the regression test accordingly. Users who pin a model via env/profile are unaffected. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
d35d4687ad |
fix(ci): build before unit tests in release workflow (#1463)
The release workflow ran `bun test` before `bun run build`, but the bundle regression tests in scripts/missing-module-stub.test.ts read the shipped dist/cli.mjs. On a fresh release-tag checkout dist/ (gitignored) does not exist yet, so both tests threw "dist/cli.mjs not found" and failed the npm publish job. pr-checks.yml already builds first (via `bun run smoke`); reorder release.yml to match. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
690b3f07a4 |
fix(test): prevent providerProfiles config mock from leaking across files (#1432)
The mock.module('./config.js') replacement in providerProfiles.test.ts
returned a partial config object (mockConfigState) with no
autoCompactEnabled field. Because bun's mock.restore() does not revert
mock.module(), this incomplete config leaked into later test files in
the same process, making getGlobalConfig().autoCompactEnabled undefined.
That caused isAutoCompactEnabled() to be falsy and the auto-compact
cooldown safety-net block in query.ts to be skipped, failing 3 tests in
src/query/autoCompactCooldown.test.ts — but only in the full suite, not
in isolation.
Spread the real getGlobalConfig() into the mock so it stays a complete
GlobalConfig and only the provider-profile fields are overridden.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
7c23fb7a05 |
fix(provider): require API key input when adding OpenGateway (#1384)
The OpenGateway preset was missing `apiKeyEnvVars`, so credential resolution fell back to the descriptor's `credentialEnvVars` chain which includes `OPENAI_API_KEY`. If a user had `OPENAI_API_KEY` set for a different provider, the add-provider flow silently pre-populated the draft key and skipped the API key input screen entirely. Add an explicit `apiKeyEnvVars: ['OPENGATEWAY_API_KEY']` to the preset so the UI only auto-fills from the provider-specific env var. The runtime setup/validation still falls back to `OPENAI_API_KEY` for existing configs. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
326f082682 |
feat(xai): add xAI/Grok OAuth provider (browser + device-code) (#1284)
* feat(xai): add xAI/Grok OAuth provider (browser + device-code)
Sign in to xAI with your account instead of an API key. Inference uses
the access token as a Bearer to api.x.ai/v1 (same surface as XAI_API_KEY)
with automatic refresh ~60s before expiry.
CLI: openclaude auth xai {login|device|status|logout}
UI: /login → 3rd-party platform → xAI OAuth (Grok)
Implementation mirrors openclaw's xai-oauth (shared client_id, PKCE,
OIDC discovery against auth.x.ai, trusted-host gating, refresh_token).
The loopback callback server (127.0.0.1:56121) explicitly echoes CORS
preflight for auth.x.ai / accounts.x.ai so xAI's browser-side push
reaches us; if the loopback still fails (firewall, remote host), users
can paste the code shown on xAI's auth page directly into the CLI or
the ProviderManager input.
Also adds grok-code-fast-1 to the xAI catalog and the x-grok-conv-id
prompt-caching header (mirrors hermes-agent).
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(xai): satisfy provider validation with OAuth + drop raw timeout signal
Two P1 review items from the xAI OAuth PR:
1. After signing in with `openclaude auth xai login` (or via the
ProviderManager), the xAI vendor still required XAI_API_KEY because
validation only checked `credentialEnvVars: ['XAI_API_KEY']`. A user
running `openclaude -p` could exit on the missing-key warning before
openaiShim ever resolved the stored OAuth token. Adds a new
`xai-credential` validation kind that accepts, in order:
a. XAI_API_KEY (legacy / explicit override)
b. XAI_CREDENTIAL_SOURCE=oauth env marker (set by the saved
OAuth profile when its env is applied at startup)
c. stored OAuth credentials in secure storage (covers the
first-process gap before applySavedProfile runs)
Resolver (c) is injectable so tests aren't sensitive to the
developer's actual login state. Adds regression tests for all four
paths (API key, env marker, stored creds, none).
2. `fetchXaiOAuthDiscovery` used `AbortSignal.timeout(...)` directly,
which the `scripts/no-raw-abort-signal-timeout.test.ts` repo guard
forbids (raw timeout signals leak timers in Bun). Routes through the
existing `createCombinedAbortSignal` helper with proper cleanup in
`finally`.
Test counts: 27/27 providerValidation (was 22, +5 xAI), 11/11
xaiOAuthCallback, 13/13 xaiOAuthShared. The repo guard now passes.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(xai): let Esc cancel xAI OAuth setup when manual-code input is focused
The manual-code TextInput's child-effect Esc handler ran before the
parent XaiOAuthSetup's `useKeybinding('confirm:no')`, so pressing Esc
triggered "press Esc again to clear input" instead of going back. Set
`disableEscapeDoublePress` so the parent keybinding fires immediately.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(xai): register Esc-to-back at ProviderManager top level
The child-component `useKeybinding('confirm:no', onBack)` in
XaiOAuthSetup wasn't reliably firing while the manual-code TextInput
held the input loop — even with disableEscapeDoublePress, the input's
listener still ran first and the keybinding context resolution lost
the race in practice. Move the binding to the top level of
ProviderManager with `context: 'Settings'` and `isActive: screen ===
'xai-oauth'`, matching the proven preset-api-key pattern (which also
has a TextInput and where Esc works correctly).
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(xai): persist OAuth startup profile with marker so logout cleans it up
`setActiveProviderProfile()` for an xAI OAuth profile (provider='xai',
no API key) was writing the startup file via the generic
`buildOpenAICompatibleStartupEnv` path — producing a plain
profile='openai' file with OPENAI_BASE_URL=https://api.x.ai/v1 and no
credential marker. Two downstream bugs:
- `clearPersistedXaiOAuthProfile()` only matches files with
profile='xai' + XAI_CREDENTIAL_SOURCE='oauth', so the logout
cleanup left this file untouched. Next non-interactive launch
(e.g. `openclaude -p`) re-applied the stale base URL with no
credential and hit the missing-XAI_API_KEY validation warning
even though the user had just logged out.
- Startup validation could not distinguish "OAuth profile, token
will be resolved at request time" from "user just forgot to set
XAI_API_KEY".
`buildStartupProfileFromActiveProfile()` now detects xAI OAuth
profiles (xai vendor + empty apiKey) and writes profile='xai' with
XAI_CREDENTIAL_SOURCE='oauth'. `buildLaunchEnv()`'s xai branch
preserves the marker so it lands in process.env at startup, where
the existing xai-credential validation kind accepts it without
needing XAI_API_KEY.
Regression test: setActiveProviderProfile for an OAuth profile must
write the marker, isPersistedXaiOAuthProfile must recognise it, and
clearPersistedXaiOAuthProfile must remove the file.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(xai): set Access-Control-Allow-Private-Network so browser auto-detect works
Chrome/Edge require Access-Control-Allow-Private-Network: true on the
preflight response when an HTTPS origin (auth.x.ai) fetches a
private-network address (127.0.0.1). Without it the preflight returns
2xx but the actual GET is silently blocked — our loopback never
receives the callback, the CLI promise never resolves, and the user
has to fall back to pasting the code even after a successful sign-in.
Mirror openclaw's CORS setup: static `Allow-Methods: GET, OPTIONS`,
default `Allow-Headers: content-type` when none requested, and the
private-network header on every trusted-origin response.
Regression-locked because the failure mode is silent: the test now
asserts that an OPTIONS preflight from auth.x.ai with
Access-Control-Request-Private-Network: true gets the matching
Access-Control-Allow-Private-Network: true header back.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(xai): update mainLoopModel when xAI OAuth profile is activated
After the OAuth completion handler activated the new xAI profile, it
never wrote the new model back to app state. The chat session kept
sending the previous provider's model name (e.g. kimi-k2.6) against
api.x.ai/v1, yielding 400 "Model not found: kimi-k2.6". Mirrors the
existing activateSelectedProvider / saveAndCloseProvider flows that
set mainLoopModel + clear mainLoopModelForSession.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(xai): pause stdin on cleanup + CLI logout clears startup profile
Two P2 review items from the xAI OAuth PR:
1. `listenForManualCode()` resumed stdin unconditionally and cleanup
only removed the data listener. A resumed stdin keeps a one-shot
CLI process alive even after sign-in succeeds — the user sees
`openclaude auth xai login` complete but the prompt never returns
until they send EOF. Now records the pre-existing paused state and
only resumes (and re-pauses on cleanup) when stdin was paused to
begin with.
2. `openclaude auth xai logout` only cleared secure storage. If the
user had configured xAI OAuth through `/provider`, the
marker-tagged `.openclaude-profile.json` and the provider profile
in global config both survived. Startup validation still accepted
`XAI_CREDENTIAL_SOURCE=oauth`, but openaiShim could no longer
resolve a token — the next non-interactive xAI launch was left
pointed at api.x.ai with no credentials instead of being logged
out cleanly. CLI logout now mirrors the /provider UI logout:
clear secure storage → delete the xAI OAuth provider profile from
global config (matched by canonical name) → remove the
marker-tagged startup file → clear the global startup-provider
override if the active profile changed.
New regression tests in `src/cli/handlers/xaiAuth.test.ts`:
- logout removes the marker-tagged startup profile (the documented
/provider-then-CLI-logout sequence)
- logout is a no-op when no profile is stored
- logout leaves unrelated (non-xAI) startup profiles alone
The tests assert file-level cleanup directly because Bun's
`mock.module(...)` in ProviderManager.test.tsx leaks providerProfiles
stubs across files within the same `bun test` process; in-memory
lookups aren't reliable here, but the startup-file path is what users
actually hit at next launch.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* test(xai): make xaiLogout regression test hermetic in the parallel suite
The test passed in isolated runs but failed in the full `bun test` run
(2812 pass / 1 fail). Two compounding leaks from other test files:
1. ProviderManager.test.tsx / model.test.tsx / others install
`mock.module('../utils/providerProfile.js', ...)` stubs that omit
`clearPersistedXaiOAuthProfile`. Bun's `mock.restore()` only
restores `mock.fn()` mocks; module mocks persist across files in
the same process, so xaiAuth's static import of
`clearPersistedXaiOAuthProfile` resolved to `undefined`.
2. SQLite / knowledgeGraph / paths tests call
`setClaudeConfigHomeDirForTesting(...)` in parallel and don't
always restore it, so the fresh providerProfile module's call to
`getClaudeConfigHomeDir()` returned a leaked override instead of
our CLAUDE_CONFIG_DIR. The real `clearPersistedXaiOAuthProfile`
ran fine, just against the wrong directory.
Refactor `xaiLogout` to accept an optional `XaiLogoutDeps` object so
the test can inject:
- the real `clearPersistedXaiOAuthProfile` (resolved via cache-bust
import) wrapped to pin `configDir: tempConfigDir`, bypassing the
parallel-test override leak
- the real `clearXaiCredentials` / `getProviderProfiles` /
`deleteProviderProfile` / `clearStartupProviderOverrides`,
bypassing the `mock.module` leak
Production callers (just `main.tsx`) omit the argument and pick up the
static imports — behavior unchanged.
Full test count: 2813 pass / 0 fail (was 2812 pass / 1 fail).
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
* fix(xai): isolate OAuth profile from shell OPENAI_API_KEY + close callback on early cancel
Two more review items from the xAI OAuth PR.
P1 — buildLaunchEnv() leaked OPENAI_API_KEY into xAI OAuth profiles.
For a marker-tagged xAI profile, the launch env builder fell back
through processEnv.OPENAI_API_KEY → persistedEnv.OPENAI_API_KEY when
no XAI_API_KEY was set, then handed that key to buildXaiProfileEnv()
which copied it to BOTH OPENAI_API_KEY and XAI_API_KEY. The OAuth
credential-source marker was then dropped (because env.XAI_API_KEY
became truthy), and openaiShim short-circuited on the ambient
OPENAI_API_KEY before ever resolving the stored OAuth token —
sending the user's generic OpenAI key as a bearer to api.x.ai/v1.
Fix: when the persisted profile is OAuth-tagged, build xaiKey only
from explicit XAI_API_KEY env (shell or persisted), never from
OPENAI_API_KEY. Pass a scrubbed processEnv into buildXaiProfileEnv()
so it can't re-introduce the key via its internal fallback. Also
clear OPENAI_API_KEY from the returned launch env (defensive: the
clearManagedProfileEnv step already drops it, but be explicit).
Three regression tests:
- ambient OPENAI_API_KEY does NOT leak into XAI_API_KEY /
OPENAI_API_KEY for an OAuth profile; XAI_CREDENTIAL_SOURCE
survives.
- explicit XAI_API_KEY still overrides OAuth (existing precedence
preserved).
- non-OAuth xAI profile (legacy api-key flow, no marker) still
accepts the OPENAI_API_KEY fallback — backward compat.
P2 — useXaiOAuthFlow leaked the loopback callback server when the
user cancelled mid-start. If unmount/Esc happened while
beginOAuthFlow() was still awaiting discovery or starting the
listener, the cleanup ran before the service had tracked the handle.
beginOAuthFlow() then resolved, the IIFE saw `cancelled === true`
and returned without closing the just-started server — leaving the
fixed 56121 port held. Next OAuth attempt failed with EADDRINUSE.
Fix: track the resolved handle in a closure variable shared between
the IIFE and the cleanup callback. The IIFE calls handle.cancel()
when it observes cancellation after beginOAuthFlow resolved; the
cleanup callback also calls activeHandle?.cancel() to cover the
common "handle exists by unmount" case. handle.cancel() → service
cleanup is idempotent.
New test file `useXaiOAuthFlow.test.tsx` with two cases:
- unmount while beginOAuthFlow is pending → cancel fires once
beginOAuthFlow eventually resolves
- unmount after handle exists → cancel fires immediately
Test count: 2818 pass / 0 fail (was 2813), TS error count unchanged
at 1692.
Co-Authored-By: OpenClaude <openclaude@gitlawb.com>
---------
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
c366f7062b |
fix(grpc): register built-in agents so Agent tool isn't always empty (#1296)
The gRPC entrypoint constructed QueryEngine with `agents: []`, so the moment the model tried to spawn a subagent (e.g. `general-purpose` for project investigation) the Agent tool threw "Agent type 'general-purpose' not found. Available agents: " with nothing after the colon. The CLI entrypoint hydrates these via getBuiltInAgents(); do the same here so gRPC-hosted sessions (playground sandbox, etc.) get parity. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
0fbfc12a99 |
feat(opengateway): require API key on /v1/* and switch to bearer auth (#1322)
Opengateway flipped from zero-auth to per-user API keys (mint at https://gitlawb.com/opengateway/keys). Update the client to match: - Descriptor: setup.requiresAuth=true, authMode='api-key', credentialEnvVars=['OPENGATEWAY_API_KEY','OPENAI_API_KEY'] in both setup (used by the generator + auth-prompt) and validation (used by getProviderValidationError). Added missingCredentialMessage pointing users at the console URL. - Transport: defaultAuthHeader changed from {name:'api-key',scheme:'raw'} to {name:'authorization',scheme:'bearer'} — the gateway only validates Authorization: Bearer ogw_live_..., the previous raw 'api-key' header was a leftover from the direct-Xiaomi era. - Auto-detect: defaultOpengatewayProvider now returns null when no key env var is set instead of unconditionally selecting opengateway — surfaces the missing-credential prompt instead of silently routing to an endpoint that will 401. - Tests: providerValidation.test.ts no-auth tests replaced with positive/negative key cases; providerAutoDetect.test.ts updated four fallback tests to include OPENGATEWAY_API_KEY (or assert null for the empty-env case). - Regenerated integrationArtifacts.generated.ts via integrations:generate. Full test suite: 2783 pass / 0 fail. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
e5535577aa |
docs: add Xiaomi MiMo sponsor (#1213)
* docs: add Xiaomi MiMo sponsor * feat(tips): add Xiaomi MiMo sponsored tips |
||
|
|
2d20109edc | fix(gemini): parse raw tool call text (#1212) | ||
|
|
13a090162f | fix(gemini): preserve tool calls through opengateway (#1204) | ||
|
|
4d04f5bf4f |
feat(opengateway): add Gemini 3.1 Flash Lite + GLM 5.1 FP8 to catalog (#1194)
* feat(opengateway): add Gemini 3.1 Flash Lite + GLM 5.1 FP8 to catalog Opengateway now routes non-Xiaomi models through GMI Cloud (configured in opengateway/src/providers.ts via modelIds: - google/gemini-3.1-flash-lite-preview - zai-org/GLM-5.1-FP8 Adding both as catalog entries on the gitlawb-opengateway gateway descriptor so openclaude users see them in the model picker when the Opengateway preset is active. Each catalog entry reuses the existing upstream model descriptor (`gemini-3.1-flash-lite-preview`, `GLM-5.1`) for capability metadata; the apiName uses the full vendor-prefixed form the gateway routes on. No new model/brand/vendor descriptors needed — only the gateway catalog gets the new IDs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * update opengateway --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
6174d75e98 |
fix(openai-shim): surface in-stream errors and truncation hints (#1174)
Two related fixes to the OpenAI-compatible streaming parser:
1. Detect `data: {"error": {...}}` events inside the stream and throw a
proper APIError. OpenAI sends this when a stream fails after headers
have been sent, and intermediaries (gateways, proxies) use it to
signal structured failures without dropping the TCP connection.
Previously this chunk was silently dropped and the parser kept
waiting for [DONE], producing the confusing "unexpected response"
error after the stream ended without a proper close.
2. When finish_reason is "length" (model hit max_tokens, OR an upstream
watchdog synthesized a graceful end after detecting a stalled
stream), append a visible "[Response truncated]" hint inline. Mirrors
the existing content_filter hint pattern. Users now know to ask the
model to continue rather than wondering why the answer cut off.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
5b5ba8853b |
feat(provider): add Gitlawb Opengateway as default provider with MiMo (#1165)
* feat(provider): add Gitlawb Opengateway as default provider with MiMo Registers https://opengateway.gitlawb.com/v1/xiaomi-mimo as a first-class gateway descriptor in openclaude. Pins it first in the provider picker order (ahead of anthropic) and surfaces it with a green [FREE] badge in the picker UI. Wires detectBestProvider() to fall back to opengateway with mimo-v2.5-pro when no other credentials or local services are detected, making fresh installs work zero-config during the Xiaomi free-inference partnership window. - src/integrations/gateways/gitlawb-opengateway.ts: gateway descriptor, static catalog of all five mimo models, requiresAuth: false - src/integrations/artifactGenerator.ts: sort comparator pins gitlawb-opengateway before anthropic - src/utils/providerAutoDetect.ts: opengateway is the last-resort fallback; OPENGATEWAY_BASE_URL env override for local dev; skipOpengatewayFallback escape hatch for tests - src/components/ProviderManager.tsx: getPresetLabel renders a green [FREE] badge for the opengateway preset, matching the existing [Sponsor] badge pattern used for xiaomi-mimo Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(provider): keep Codex OAuth after DeepSeek with Opengateway pinned Pinning Gitlawb Opengateway at the top of the preset list shifted every subsequent index by 1, which dropped Codex OAuth from "after DeepSeek" to "before DeepSeek" because the splice insertion index was a hardcoded 6. Bumps the insertion index to 7 to keep Codex OAuth in its established position, and updates PRESET_ORDER in ProviderManager.test.tsx so the keyboard-navigation harness lines up with the new layout. Restores 9 ProviderManager tests that were timing out because navigateToPreset() was landing on the wrong row. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
18483e4d96 |
feat(provider): add Xiaomi MiMo integration (#1152)
* feat(provider): add Xiaomi MiMo integration Add Xiaomi MiMo as an official OpenAI-compatible provider with provider-profile persistence, env-only detection, and sponsor labeling in the provider picker. Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> * feat(provider): rebase Xiaomi MiMo as top-level provider Promote Xiaomi MiMo from generic OpenAI-compatible shim route to a first-class top-level provider matching the MiniMax pattern. Includes brand/model descriptors, env-only detection, provider auto-detect, status labels, legacy provider type, model picker, and profile env persistence. Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> * Fix Xiaomi MiMo provider integration Promote Xiaomi MiMo as a first-tier OpenAI-compatible vendor and align its descriptor with the integration guide. Use the resolving Xiaomi MiMo API host while normalizing the stale docs host alias, wire Xiaomi catalog options into /model, and ensure model selection/display uses OPENAI_MODEL for Xiaomi instead of Claude defaults. Update provider profile/startup handling, OpenAI shim detection, docs, generated integration artifacts, and focused regression tests. Verification: bun run integrations:check; focused bun test provider/model suites; bun run build; bun run smoke. * Fix MiMo startup profile base URL normalization --------- Co-authored-by: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> Co-authored-by: JATMN <the@jat.mn> |
||
|
|
2c71e09394 |
chore(build): clean up external dependency validation warnings (#1124)
* chore(build): clean up external dependency validation warnings Remove 2 unused externals (@opentelemetry/sdk-trace-node, ink) and add 12 missing packages to package.json that are dynamically imported at runtime but weren't declared as dependencies. Also remove the unused @opentelemetry/sdk-trace-node dependency. This eliminates all 13 build validation warnings: - 8 missing OTel exporter deps (http, proto, grpc variants + prometheus) - 4 missing AWS SDK deps (bedrock, bedrock-runtime, sts, credential-providers) - 1 missing Azure dep (@azure/identity) - ink external pointed to local reimplementation, not npm package - sdk-trace-node was declared external but never imported Build validation now passes cleanly with 0 warnings. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * chore(build): eliminate external validation warnings Remove unused @opentelemetry/sdk-trace-node from externals and package.json (it's not imported anywhere in src/). Remove ink from SDK_ONLY_EXTERNALS (the project reimplements ink locally at src/ink/). Add OPTIONAL_RUNTIME_EXTERNALS list for packages that are dynamically imported but intentionally not direct deps — OTel protocol exporters and cloud provider SDKs are resolved from transitive deps or installed by users who need them. Validation now passes with 0 warnings instead of 13. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat(telemetry): full OpenTelemetry purge — remove all tracking dependencies Replace all @opentelemetry/* runtime dependencies with no-op stubs, delete OTel-only source modules, and remove 10 @opentelemetry packages from package.json plus @growthbook/growthbook. Key changes: - Delete 5 OTel-only modules (instrumentation, betaSessionTracing, bigqueryExporter, logger, firstPartyEventLoggingExporter) - Replace 9 modules with no-op stubs (sessionTracing, events, telemetryAttributes, firstPartyEventLogger, growthbook, index, sink, datadog, sinkKillswitch, perfettoTracing) - Remove all @opentelemetry/* imports from bootstrap/state.ts, entrypoints/init.ts, and ~20 caller files - Remove all OTel counter types, meter/provider state from state.ts - Clean externals.ts: remove 27 @opentelemetry/* entries - Clean build.ts: remove OTel native-stub namespace exports - Simplify no-telemetry-plugin.ts: remove redundant source-level stubs - Remove 10 @opentelemetry/* + @growthbook/growthbook from package.json - GrowthBook stub reads local ~/.claude/feature-flags.json for overrides Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> * fix format * chore: regenerate lockfile after OTel dependency removal Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> * fix(growthbook): route gate helpers through local flag overrides checkStatsigFeatureGate_CACHED_MAY_BE_STALE() and checkGate_CACHED_OR_BLOCKING() now resolve from ~/.claude/feature-flags.json like getFeatureValue_* does, so gates like tengu_thinkback, tengu_ccr_bridge, and VS Code upsells can be flipped on locally. Security gates (checkSecurityRestrictionGate) remain hard-false. Also adds 5 tests covering gate helper override behavior and unifies JSDoc wording for _getFlagValue-routed functions. Co-Authored-By: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
a4cbb78585 |
feat: add sponsored tips with frequency-gated display (#1140)
Implement sponsored tip system with registry, scheduler integration, and config-gated frequency control. Sponsored tips appear based on startup count frequency (default: 1 per 10 startups) and are tracked separately from regular tip history. Co-authored-by: OpenClaude (zai-org-glm-5-1) <openclaude@gitlawb.com> |
||
|
|
f9621ab575 |
feat(provider): add Venice official provider (#1109)
Adds Venice as a descriptor-backed OpenAI-compatible provider with VENICE_API_KEY support, provider UI metadata, startup persistence, and regression coverage. Co-authored-by: OpenClaude (gpt-5.5) <openclaude@gitlawb.com> |
||
|
|
dea7ef998a |
Feat/xai grok 4 3 default (#1080)
* feat(xai): add Grok 4.3 as default model Add Grok 4.3 to xAI model metadata and make it the provider default while preserving explicit Grok 4 and Grok 3 selections. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(xai): correct Grok 4.3 context window to 1M tokens Grok 4.3 docs list 1,000,000 token context window. The PR had incorrectly registered 2,000,000, which would allow local budgeting to retain ~2x the supported input size before compaction triggers, leading to provider-side failures. - src/integrations/models/xai.ts: set contextWindow to 1_000_000 - src/utils/context.test.ts: update expectation to match 🤖 Generated with [OpenClaude](https://github.com/Gitlawb/openclaude) Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
d19f4d335d | fix flaky test (#1021) | ||
|
|
7b02695b15 |
Feat/codex default provider (#1014)
* chore: add .openclaude/ to gitignore The .openclaude/ directory contains auto-generated project-local files (wiki pages, convention cache, local settings) that should not be committed to the repository. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * feat: make Codex + GPT 5.5 the default provider and model Changes the default provider to Codex and default model to GPT 5.5: - package.json: dev script now uses provider-launch.ts codex - providerRecommendation.ts: getGoalDefaultOpenAIModel returns gpt-5.5 for coding and balanced goals (was gpt-4o) - providerConfig.ts: fallback model changed from gpt-4o to codexplan (resolves to gpt-5.5) - ProviderManager.tsx: Codex OAuth option now shows green "★ Recommended" badge in the provider picker Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix: replace Box with nested Text in Codex label Ink's <Text> component cannot contain <Box>. The label is rendered inside a <Text> parent, so use nested <Text> elements instead. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix: default to Codex when no provider profile is saved When no persisted provider profile exists (fresh install / first run), buildStartupEnvFromProfile now injects Codex + GPT 5.5 env vars instead of returning process.env unchanged. Falls back gracefully — if Codex credentials are available (OAuth or existing), uses those; otherwise injects base URL and model defaults so the provider picker shows GPT 5.5 as the default. This closes the gap where node dist/cli.mjs (production start) would default to firstParty (Anthropic) when no profile or env vars were set. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * chore: resolve stash conflict markers from accidental stash pop Cleans up merge conflict artifacts left by a git stash pop from an unrelated branch (chore/add-atomic-chat-partner). Kept upstream (current branch) version in all cases. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix: restore memoize import and cleanup stash artifacts Restores the memoize import dropped during conflict resolution in modelSupportOverrides.ts. Removes duplicate originalEnv declaration and redundant delete statements in providerValidation.test.ts. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * revert change in package.json * fix broken test * fix color --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
1f66d322ad | fix flaky tests in full test run (#1020) | ||
|
|
95a817fdb0 |
fix(provider): apply Codex OAuth session switch correctly (#974)
* fix(provider): apply Codex OAuth session switch correctly Ensure Codex OAuth activation in an existing session does not briefly apply an empty OpenAI API key, preventing missing Authorization headers until restart. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(provider): preserve explicit env for Codex API profiles Limit the Codex session-switch override to OAuth profiles so explicit OpenAI environment settings keep taking precedence for regular Codex profiles. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(provider): isolate Codex OAuth env from ambient credentials Keep Codex OAuth profile activation from inheriting ambient Codex API credentials so CI and user shells cannot poison the in-session OAuth regression path. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
4eb486ef83 |
Feat/web landing refresh (#958)
* feat(web): openclaude landing — runs anywhere, uses anything A new marketing site for openclaude under web/, plus the minimal root infrastructure to build, ignore, and gate it without affecting the published npm package. Landing page (web/) - Vite + React 19 with monospace gitlawb typography (sf mono / fira code). - Hero: pill, two-line wordmark "runs anywhere. / uses anything.", copy-to-clipboard install command, github cta. - Six feature rows in hermes-style "title — sentence" format on hairline dividers (any model, real tools, profiles per repo, streaming, gateway routing, editor + server modes). - Install block: same copyable command + three numbered steps. - One-line footer with brand, version, gitlawb link, and license. - Light theme is the default with a no-flash bootstrap script and a ☀ / ☾ toggle persisted to localStorage. - New orange terminal-face logo at 36px in the nav. - Body wash: dual orange radial gradients for warmth on both themes. Root infra - web/ excluded from npm publish via .npmignore (belt-and-suspenders alongside the existing files whitelist). - web/ excluded from docker context (.dockerignore). - web:dev / web:build / web:preview / web:typecheck scripts in package.json that delegate via --cwd web (no root deps added). - web typecheck + build added to the pr-checks workflow. - web/dist/ and web/*.tsbuildinfo ignored. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * added vercel in .gitignore --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
c0b5535d86 |
docs: add Atomic Chat partner (#942)
Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
4c93a9f9f1 |
feat: add Opus 4.7 as default model and fix alias/thinking bugs (#928)
- Add CLAUDE_OPUS_4_7_CONFIG and register it in ALL_MODEL_CONFIGS
- Set Opus 4.7 as default for firstParty in getDefaultOpusModel() (3P stays on 4.6 until rollout)
- Fix sonnet[1m] → 404 bug: query.ts was passing raw alias to API without resolving via parseUserSpecifiedModel
- Add opus-4-7 to modelSupportsAdaptiveThinking so it uses { type: 'adaptive' } not { type: 'enabled' }
- Fix duplicate opus47 case and wrong opus46[1m] fallthrough in getPublicModelDisplayName switch
- Update user-facing display strings (picker labels, plan mode description) to reference Opus 4.7
- Add 3P fallback suggestion chain for opus-4-7 → opus-4-6 in validateModel
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
46a9d3eec4 |
chore: rebrand user-facing copy to OpenClaude (#851)
* chore: rebrand user-facing copy to OpenClaude Replace lingering Claude Code branding in CLI, tips, and runtime UI with OpenClaude/openclaude, including the startup tip Gitlawb mention. Co-Authored-By: Claude GPT-5.4 <noreply@openclaude.dev> * chore: address branding-sweep review feedback - PermissionRequest.tsx: rebrand the two remaining "Claude needs your approval/permission" notifications to OpenClaude (review-artifact and generic tool permission paths). - main.tsx, teleport.tsx, session.tsx, WebFetchTool/utils.ts, skills/bundled/{debug,updateConfig}.ts: replace leftover `claude --…` CLI hints and "Claude Code" labels missed by the original sweep. - main.tsx: drop the inline gitlawb.com marketing copy from the stale-prompt tip; keep it a pure rebrand. - auth.ts: finish the half-rename so both `claude setup-token` and `claude auth login` references in the same error block now read `openclaude …`. - mcp/client.ts: keep `name: 'claude-code'` for MCP server allowlist compatibility (now explicit via comment) and replace the "Anthropic's agentic coding tool" description with an OpenClaude one. - MCPSettings.tsx: point the empty-server-list hint at https://github.com/Gitlawb/openclaude instead of code.claude.com. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: replace help link with OpenClaude repo URL Replace https://code.claude.com/docs/en/overview with https://github.com/Gitlawb/openclaude in the help screen. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: Claude GPT-5.4 <noreply@openclaude.dev> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
2586a9cddb |
feat: add xAI as official provider (#865)
* feat: add xAI as official provider - Add xAI preset to ProviderManager (alphabetical order) - Add xAI provider detection via XAI_API_KEY - Add xAI startup screen heuristic (x.ai base URL or grok model) - Add xAI status display properties - Add grok-4 and grok-3 context windows - Add xAI model fallbacks across all tiers - Fix JSDoc priority order in providerAutoDetect Co-Authored-By: Claude Opus 4.6 <noreply@openclaude.dev> * fix(xai): persist relaunch classification for xAI profiles Addresses reviewer feedback on feat/xai-official-provider: - isProcessEnvAlignedWithProfile now validates XAI_API_KEY for x.ai base URLs, mirroring the Bankr pattern. Without this, relaunch skips re-applying the profile, XAI_API_KEY stays unset, and getAPIProvider() falls back to 'openai'. - buildOpenAICompatibleStartupEnv now sets XAI_API_KEY when syncing active xAI profile to the legacy fallback file. - Adds 'xai' to VALID_PROVIDERS and --provider xai CLI flag support. - Adds xAI detection to providerDiscovery label heuristics. - Adds 'xai' to legacy ProviderProfile type/isProviderProfile guard. - Adds targeted tests for relaunch alignment, flag application, and discovery labeling. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@openclaude.dev> Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
818689b2ee |
fix(query): restore system prompt structure and add missing config import (#907)
- import getGlobalConfig — six call sites referenced it without an import;
five short-circuited via feature() gates, but src/query.ts:1896 always
ran and crashed every queryLoop iteration with "getGlobalConfig is not
defined" (e.g. Explore subagent: "Agent failed: getGlobalConfig is not
defined").
- stop coercing SystemPrompt (string[]) into a template-string before
appendSystemContext — that made [...systemPrompt] spread the string
character-by-character, replacing the structured prompt with thousands
of one-char system blocks. Append arcSummary as its own array element
instead.
- gate the finalizeArcTurn call behind feature('CONVERSATION_ARC') so it
matches the rest of the memory-PR call sites and gets dead-code-
eliminated for users without the flag.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
d9ae56bc58 |
fix provider switch not presistingin session (#903)
* fix provider switch not presistingin session * fix broken tests |
||
|
|
64b1014b9a |
Feat/bankr provider (#888)
* feat(provider): add Bankr LLM Gateway support Add Bankr as an OpenAI-compatible provider preset with dedicated env vars: - BNKR_API_KEY, BANKR_BASE_URL, BANKR_MODEL - Uses X-API-Key header instead of Authorization Bearer - Base URL: https://llm.bankr.bot/v1 - Default model: claude-opus-4.6 Changes: - Add 'bankr' to VALID_PROVIDERS and provider flag handling - Add buildBankrProfileEnv() with env key registration - Add Bankr detection in startup screen and provider discovery - Map Bankr env vars to OpenAI-compatible vars in shim - Add Bankr preset to ProviderManager (alphabetical order) - Update PRESET_ORDER test to include Bankr Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fixup(provider): address Bankr PR review feedback 1. Map BNKR_API_KEY → OPENAI_API_KEY in providerFlag.ts so --provider bankr works with BNKR_API_KEY in non-interactive startup. 2. Remove unconditional BANKR_MODEL read from model.ts; it maps to OPENAI_MODEL via providerFlag.ts and openaiShim.ts, preventing cross-provider leakage. 3. Use X-API-Key for Bankr model discovery in openaiModelDiscovery.ts and providerDiscovery.ts, matching chat request auth. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
038f715b7a |
feat(model): add GPT-5.5 support for Codex provider (#880)
- Bump Codex provider defaults from gpt-5.4 to gpt-5.5 across all ModelConfigs - Update codexplan alias to resolve to gpt-5.5 - Add gpt-5.5 and gpt-5.5-mini to model picker with reasoning effort mappings - Add context window and max output token specs for gpt-5.5 family - Add gpt-5.5 entries to COPILOT_MODELS registry - Keep official OpenAI API preset at gpt-5.4 (API availability pending) - Update codexShim tests to expect gpt-5.5 from codexplan alias Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
b694ccfff1 | Add sponsors section to README (#874) | ||
|
|
531e3f1059 |
feat(tools): resilient web search and fetch across all providers (#836)
- Add exponential backoff retry to DuckDuckGo adapter (3 attempts with
jitter) to handle transient rate-limiting and connection errors.
- Add native fetch() fallback in WebFetch when axios hangs with custom
DNS lookup in bundled contexts.
- Prevent broken native-path fallback for web search on OpenAI shim
providers (minimax, moonshot, nvidia-nim, etc.) that do not support
Anthropic's web_search_20250305 tool.
- Cherry-pick existing fixes:
- a48bd56: cover codex/minimax/nvidia-nim in getSmallFastModel()
- 31f0b68: 45s budget + raw-markdown fallback for secondary model
- 446c1e8: sparse Codex /responses payload parsing
-
|
||
|
|
67de6bd2cf |
fix(openai-shim): echo reasoning_content on assistant tool-call messages for Moonshot (#828)
Kimi / Moonshot's chat completions endpoint requires that every assistant
message carrying tool_calls also carry reasoning_content when the
"thinking" feature is active. When an agent sends prior-turn assistant
history back (standard multi-turn / subagent / Explore patterns), the
shim previously stripped the thinking block:
case 'thinking':
case 'redacted_thinking':
// Strip thinking blocks for OpenAI-compatible providers.
break
That's correct for providers that would mis-interpret serialized
<thinking> tags, but Moonshot validates the schema strictly and rejects
with:
API Error: 400 {"error":{"message":"thinking is enabled but
reasoning_content is missing in assistant tool call message at
index N","type":"invalid_request_error"}}
Reproducer: launch with Kimi profile, run any tool-using command
(Explore, Bash, etc.) — every request after the first 400s.
Fix: in convertMessages(), when the per-request flag
preserveReasoningContent is set (only for Moonshot baseUrls today),
attach the original thinking block's text as reasoning_content on the
outgoing OpenAI-shaped assistant message. Other providers continue to
strip (unknown-field rejection risk).
OpenAIMessage type grows a reasoning_content?: string field.
convertMessages() accepts an options object and threads the flag
through; the only call site (_doOpenAIRequest) gates via
isMoonshotBaseUrl(request.baseUrl).
Tests (openaiShim.test.ts):
- Moonshot: echoes reasoning_content on assistant tool-call messages
(regression for the reported 400)
- non-Moonshot providers do NOT receive reasoning_content (guards
against leaking the field to strict-parse endpoints)
Full suite: 1195/1195 pass under --max-concurrency=1. PR scan clean.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
c13842e91c |
fix(test): autoCompact floor assertion is flag-sensitive (#816)
The test "never returns negative even for unknown 3P models (issue #635)" asserted that getEffectiveContextWindowSize() returns >= 33_000 for an unknown 3P model under the OpenAI shim. That specific number assumes reservedTokensForSummary = 20_000 (MAX_OUTPUT_TOKENS_FOR_SUMMARY), which holds only when the tengu_otk_slot_v1 GrowthBook flag is disabled. When the flag is ON — which is the case in CI but not always locally — getMaxOutputTokensForModel() caps the model's default output at CAPPED_DEFAULT_MAX_TOKENS (8_000). Then reservedTokensForSummary = 8_000, floor = 8_000 + 13_000 = 21_000, and the test fails with 21_000 < 33_000. The test reliably passes locally and reliably fails in CI, manifesting as the intermittent PR-check failure. Fix: relax the lower bound to 21_000 (cap-enabled worst case), which is still well above zero — preserving the anti-regression intent of issue #635 (no infinite auto-compact from a negative effective window) without binding the test to GrowthBook flag state. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
458120889f |
fix(model): codex/nvidia-nim/minimax now read OPENAI_MODEL env (#815)
getUserSpecifiedModelSetting() decides which env var to consult based on the active provider. The check included openai and github but omitted codex, nvidia-nim, and minimax — even though all three use the OpenAI shim transport and get their model routing via CLAUDE_CODE_USE_OPENAI=1 + OPENAI_MODEL (set by applyProviderProfileToProcessEnv). Concrete failure: user switches from Moonshot profile (which persisted settings.model='kimi-k2.6') to the Codex profile. The new profile correctly writes OPENAI_MODEL=codexplan + base URL to chatgpt.com/backend-api/codex. Startup banner reflects Codex / gpt-5.4 correctly. But at request time getUserSpecifiedModelSetting() returns early for provider='codex' (not in the env-consult list), falls through to the stale settings.model='kimi-k2.6', and the Codex API rejects: API Error 400: "The 'kimi-k2.6' model is not supported when using Codex with a ChatGPT account." Fix: extract an isOpenAIShimProvider flag covering openai|codex|github| nvidia-nim|minimax — all providers that set OPENAI_MODEL as their model env var. The Gemini and Mistral branches stay as-is (they use GEMINI_MODEL / MISTRAL_MODEL). Five regression tests pin the fix for each OpenAI-shim provider plus guard tests for openai and github that already worked. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
13de4e85df |
fix(provider): saved profile ignored when stale CLAUDE_CODE_USE_* in shell (#807)
* fix(provider): saved profile ignored when stale CLAUDE_CODE_USE_* in shell Users reported "my saved /provider profile isn't picked up at startup — the banner shows gpt-4o / api.openai.com even though I saved Moonshot". Root cause: applyActiveProviderProfileFromConfig() bailed out whenever hasProviderSelectionFlags(processEnv) was true — i.e. whenever ANY CLAUDE_CODE_USE_* flag was present. But a bare `CLAUDE_CODE_USE_OPENAI=1` with no paired OPENAI_BASE_URL / OPENAI_MODEL is almost always a stale shell export left over from a prior manual setup, not genuine startup intent. Respecting it skipped the saved profile and let StartupScreen.ts fall through to the hardcoded `gpt-4o` / `https://api.openai.com/v1` defaults — the exact symptom users see. Fix: narrow the guard from "any flag set" to "flag set AND at least one concrete config value (BASE_URL, MODEL, or API_KEY)". A bare stale flag no longer blocks the saved profile. A real shell selection (flag + URL or flag + model) still wins, preserving the "explicit startup intent overrides saved profile" contract. New helper: hasCompleteProviderSelection(env). Per-provider check for a paired concrete value. Bedrock/Vertex/Foundry keep the flag-alone semantic since they rely on ambient AWS/GCP credentials rather than env config. Three new tests cover the bug and the two counter-cases: - bare USE flag → profile applies (fixes the bug) - USE flag + BASE_URL → profile blocked (preserves explicit intent) - USE flag + MODEL → profile blocked (preserves explicit intent) Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(provider): don't overlay stale legacy profile on plural-managed env Second half of the "saved profile not picked up in banner" bug. The prior commit fixed the guard that prevented applyActiveProviderProfileFromConfig() from firing when a stale CLAUDE_CODE_USE_* flag was in the shell. But even when the plural system applies correctly, buildStartupEnvFromProfile() was then loading the legacy .openclaude-profile.json AND overwriting the plural-managed env with whatever that file contained. addProviderProfile() (the call path the /provider preset picker uses) does NOT sync the legacy file, so a user who went: manual setup: CLAUDE_CODE_USE_OPENAI=1 + OPENAI_MODEL=gpt-4o → writes .openclaude-profile.json as { openai, gpt-4o, ... } /provider: add Moonshot preset, mark active → writes plural config; legacy file UNCHANGED would see startup reliably apply Moonshot env first, then get it clobbered by the stale legacy file. Banner shows gpt-4o / api.openai.com while runtime ends up with the correct env via a different code path — exactly the user-reported symptom. Fix: in buildStartupEnvFromProfile, when the plural system has already set env (CLAUDE_CODE_PROVIDER_PROFILE_ENV_APPLIED === '1'), skip the legacy-file overlay entirely and return processEnv unchanged. Legacy is now strictly a first-run / fallback path for users who haven't adopted the plural system. Also removes the stripped-then-rebuilt env construction that was part of the old overlay path — no longer needed. Test updates: - Replaced "lets saved startup profile override profile-managed env" (encoded the old broken behavior) with a regression test that pins the new semantic: plural env survives when legacy is stale. - Added "falls back to legacy when plural hasn't applied" to pin the first-run path still works. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
a5bfcbbadf |
feat(provider): zero-config autodetection primitive (#784)
First-run users with a credential already exported (ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.) currently still have to navigate the provider picker or set CLAUDE_CODE_USE_* flags manually. Selecting the right provider from ambient state should be automatic. New module src/utils/providerAutoDetect.ts: - detectProviderFromEnv() — synchronous env scan in a deterministic priority order (anthropic → codex → github → openai → gemini → mistral → minimax). Also detects Codex via ~/.codex/auth.json presence. - detectLocalService() — parallel probes for Ollama (:11434) and LM Studio (:1234), with honoring of OLLAMA_BASE_URL / LM_STUDIO_BASE_URL overrides. Short 1.2s default timeout so first-run latency stays low when no local service is running. - detectBestProvider() — orchestrator. Env scan short-circuits the probe; only hits the network when env has nothing. All detection paths are side-effect-free: returns a DetectedProvider descriptor describing what was found and why. Callers decide whether to apply it (gated on hasExplicitProviderSelection() / profile file existence) and how to hydrate the launch env. Codex auth-file check is injectable (hasCodexAuth option) so tests are hermetic from the dev machine's ~/.codex/auth.json state. Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
e908864da7 |
feat(api): smart model routing primitive (cheap-for-simple, strong-for-hard) (#785)
Most everyday turns ("ok", "thanks", "yep go ahead", "what does that do?")
get no measurable quality improvement from Opus-tier models over Haiku-tier,
but cost ~10x more and stream slower. Smart routing opts a user into
automatically routing obviously-simple turns to a cheaper model while
keeping the strong model for anything non-trivial.
New module src/services/api/smartModelRouting.ts:
- routeModel(input, config) → { model, complexity, reason }
- Pure primitive: no env reads, no state, caller supplies everything.
- Config is opt-in (enabled: false by default).
Routes to strong (conservative) when ANY of:
- First turn of session (task-setup is worth the quality)
- Code fence or inline code span present
- Reasoning/planning keyword (plan, design, refactor, debug, architect,
investigate, root cause, etc. — 20+ anchors)
- Multi-paragraph input
- Over char/word cutoff (defaults: 160 chars, 28 words; matches hermes)
Routes to simple only for clearly-trivial chatter.
Decision includes a reason string for a future UI indicator that shows
which tier handled the turn.
Integration into query path is intentionally deferred to a follow-up PR so
the heuristics can be reviewed and tuned in isolation first.
Co-authored-by: OpenClaude <openclaude@gitlawb.com>
|
||
|
|
b95d2221df |
Feat/kimi moonshot support (#805)
* feat(provider): first-class Moonshot (Kimi) direct-API support Moonshot's direct API (api.moonshot.ai/v1) is OpenAI-compatible and works today via the generic OpenAI shim, including the reasoning_content channel that Kimi returns alongside the user-visible content. But the UX was rough: unknown context window triggered the conservative 128k fallback + a warning, and the provider displayed as "Local OpenAI-compatible". Makes Moonshot a recognized provider: - src/utils/model/openaiContextWindows.ts: add the Kimi K2 family and moonshot-v1-* variants to both the context-window and max-output tables. Values from Moonshot's model card — K2.6 and K2-thinking are 256K, K2/K2-instruct are 128K, moonshot-v1 sizes are embedded in the model id. - src/utils/providerDiscovery.ts: recognize the api.moonshot.ai hostname and label it "Moonshot (Kimi)" in the startup banner and provider UI. Users can now launch with: CLAUDE_CODE_USE_OPENAI=1 \ OPENAI_BASE_URL=https://api.moonshot.ai/v1 \ OPENAI_API_KEY=sk-... \ OPENAI_MODEL=kimi-k2.6 \ openclaude and get accurate compaction + correct labeling + correct max_tokens out of the box. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(openai-shim): Moonshot API compatibility — max_tokens + strip store Moonshot's direct API (api.moonshot.ai and api.moonshot.cn) uses the classic OpenAI `max_tokens` parameter, not the newer `max_completion_tokens` that the shim defaults to. It also hasn't published support for `store` and may reject it on strict-parse — same class of error as Gemini's "Unknown name 'store': Cannot find field" 400. - Adds isMoonshotBaseUrl() that recognizes both .ai and .cn hosts. - Converts max_completion_tokens → max_tokens for Moonshot requests (alongside GitHub / Mistral / local providers). - Strips body.store for Moonshot requests (alongside Mistral / Gemini). Two shim tests cover both the .ai and .cn hostnames. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix: null-safe access on getCachedMCConfig() in external builds External builds stub src/services/compact/cachedMicrocompact.ts so getCachedMCConfig() returns null, but two call sites still dereferenced config.supportedModels directly. The ?. operator was in the wrong place (config.supportedModels? instead of config?.supportedModels), so the null config threw "Cannot read properties of null (reading 'supportedModels')" on every request. Reproduces with any external-build provider (notably Kimi/Moonshot just enabled in the sibling commits, but equally DeepSeek, Mistral, Groq, Ollama, etc.): ❯ hey ⏺ Cannot read properties of null (reading 'supportedModels') - prompts.ts: early-return from getFunctionResultClearingSection() when config is null, before touching .supportedModels. - claude.ts: guard the debug-log jsonStringify with ?. so the log line never throws. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * fix(startup): show "Moonshot (Kimi)" on the startup banner The startup-screen provider detector had regex branches for OpenRouter, DeepSeek, Groq, Together, Azure, etc., but nothing for Moonshot. Remote Moonshot sessions fell through to the generic "OpenAI" label — getLocalOpenAICompatibleProviderLabel() only runs for local URLs, and api.moonshot.ai / api.moonshot.cn are not local. Adds a Moonshot branch matching /moonshot/ in the base URL OR /kimi/ in the model id. Now launches with: OPENAI_BASE_URL=https://api.moonshot.ai/v1 OPENAI_MODEL=kimi-k2.6 display the Provider row as "Moonshot (Kimi)" instead of "OpenAI". Co-Authored-By: OpenClaude <openclaude@gitlawb.com> * refactor(provider): sort preset picker alphabetically; Custom at end The /provider preset picker was in ad-hoc order (Anthropic, Ollama, OpenAI, then a jumble of third-party / local / codex / Alibaba / custom / nvidia / minimax). Hard to scan when you know the provider name you want. Sorts the list alphabetically by label A→Z. Pins "Custom" to the end — it's the catch-all / escape hatch so it's scanned last, not shuffled into the alphabetical run where a user looking for a named provider might grab it by mistake. First-run-only "Skip for now" stays at the very bottom, after Custom. Test churn: - ProviderManager.test.tsx: four tests hardcoded press counts (1 or 3 'j' presses) that broke when targets moved. Replaces them with a navigateToPreset(stdin, label) helper driven from a declared PRESET_ORDER array, so future list edits only update the array. - ConsoleOAuthFlow.test.tsx: the 13-row test frame only renders the first ~13 providers. "Ollama", "OpenAI", "LM Studio" sentinels moved below the fold; swap them for alphabetically-early providers still visible in-frame ("Azure OpenAI", "DeepSeek", "Google Gemini"). Test intent (picker opened with providers listed) is preserved. Co-Authored-By: OpenClaude <openclaude@gitlawb.com> --------- Co-authored-by: OpenClaude <openclaude@gitlawb.com> |
||
|
|
336ddcc50d |
fix(api): replace phrase-based reasoning sanitizer with tag-based filter (#779)
Reasoning models (MiniMax M2.7, GLM-4.5/5, DeepSeek, Kimi K2) inline chain-of-thought inside <think>...</think> tags in the content field rather than using the reasoning_content channel. The prior phrase-matching sanitizer (looksLikeLeakedReasoningPrefix) only caught English-prose preambles like "I should"/"the user asked", missed tag-based leaks entirely, and risked false-stripping legitimate assistant output. Replace with a structural tag-based approach (same pattern as hermes-agent): - createThinkTagFilter() — streaming state machine that buffers partial tags across SSE delta boundaries (<th| + |ink>), so tags split mid-chunk still parse correctly. - stripThinkTags() — whole-text cleanup for non-streaming responses and as a safety net. Handles closed pairs, unterminated opens at block boundaries, and orphan tags. - Recognizes think, thinking, reasoning, thought, REASONING_SCRATCHPAD case-insensitively, including tags with attributes. - False-negative bias: flush() discards buffered partial tags at stream end rather than leaking them. Existing phrase-based shim tests updated to exercise the actual <think> tag leak. Added regression tests confirming legitimate prose starting with "I should..." is preserved (the old sanitizer's main false-positive). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
aab489055c | fix: require trusted approval for sandbox override (#778) | ||
|
|
7002cb302b | fix: enforce Bash path constraints after sandbox allow (#777) | ||
|
|
739b8d1f40 | fix: enforce MCP OAuth callback state before errors (#775) | ||
|
|
13e9f22a83 | feat: mask provider api key input (#772) | ||
|
|
f828171ef1 | fix: allow provider recovery during startup (#765) |