Commit Graph
655 Commits
Author SHA1 Message Date
JATMN cb666c85d0 Fix launcher heap setup for long sessions (#1242)
Relaunch the package executable before loading dist/cli.mjs so OpenClaude starts with an effective V8 heap cap instead of setting NODE_OPTIONS after the current process has already started.

The launcher now adds a default 8192 MB max-old-space-size and --expose-gc when they are missing, preserves flags supplied through process.execArgv or NODE_OPTIONS, and provides OPENCLAUDE_DISABLE_HEAP_RELAUNCH plus OPENCLAUDE_NODE_MAX_OLD_SPACE_SIZE_MB escape hatches.

Update the headless loop GC hook to use Node global.gc when the launcher exposed it, while preserving the existing Bun.gc path. Clarify the entrypoint NODE_OPTIONS comment so it reflects child-process propagation rather than current-process heap sizing.

Add scripts/openclaude-bin-heap.test.ts to guard launcher ordering and user override handling.

Validation: bun test scripts/openclaude-bin-heap.test.ts src/entrypoints/cli.test.ts; node bin/openclaude --version returned 0.13.0 (OpenClaude). Earlier full build passed after bun install --frozen-lockfile. bun run typecheck remains blocked by existing repo-wide type errors unrelated to this change.
2026-05-25 19:15:55 +08:00
0xfandom 4e8fa24cce feat(safety): warn at startup when 3P provider + permissive mode skip the AI classifier (#1260)
* feat(safety): warn at startup when 3P provider runs in a permissive mode

Issue #244 finding 1: `modelSupportsAutoMode` returns `false` for every
non-firstParty provider (betas.ts:166), so the AI safety classifier that
reviews tool calls in context never runs for OpenAI/Gemini/Ollama/etc.
users — even when they are in `acceptEdits` or `bypassPermissions` mode,
where the per-tool consent prompt is suppressed. They get the consent
shortcut without the safety net, with no indication that the net is off.

Adds a `thirdPartyPermissiveModeNotice` to `statusNoticeDefinitions`
that fires when:
  - active permission mode ∈ {acceptEdits, bypassPermissions}, AND
  - the active model does NOT support auto-mode (covers all 3P), AND
  - `getAPIProvider() !== 'firstParty'`

Plumbing: `StatusNoticeContext` grows `permissionMode` and `mainLoopModel`
fields, populated in `StatusNotices.tsx` via `useAppState`. The two
existing helpers (`modelSupportsAutoMode`, `getAPIProvider`) are reused —
no new policy logic, just a visible label on an existing gap.

Refs #244

* feat(safety): warn when --dangerously-skip-permissions runs without a sandbox

Issue #244 finding 2: the sandbox gate (Docker/Bubblewrap container +
no internet) that conditions `--dangerously-skip-permissions` is
employee-only (`isAntEmployee()`); external users — every OpenClaude
user — bypass the gate entirely. Combined with finding 1 (no AI
classifier on 3P), the flag becomes "run any command with full internet
access, no consent prompt, no safety net" with zero visible warning.

Adds `dangerouslySkipPermissionsNotice` to the startup notice list. It
fires when either:
  - `process.argv` contains `--dangerously-skip-permissions`, OR
  - the resolved permission mode is `bypassPermissions` (covers
    settings.json `defaultMode` and runtime toggles too)

argv detection means the notice surfaces from the first frame, before
any AppState propagation, so the user sees the warning during the same
session in which they passed the flag — not on the next launch.

This does not change enforcement (that's a policy call for maintainers,
not a fork to ship). It surfaces an existing risk the CLI was silent
about.

Refs #244

* test(safety): cover both 3P-safety status notices

Eight cases fence the new contract:

- 3P + acceptEdits + classifier-off → fire
- 3P + bypassPermissions → fire
- 3P + default mode → suppressed (consent prompt still active)
- firstParty Anthropic + acceptEdits → suppressed (classifier present)
- 3P + acceptEdits + classifier-supported model → suppressed (defensive
  branch in case future 3P models gain classifier support)
- --dangerously-skip-permissions in argv → fire
- bypassPermissions mode (e.g. settings defaultMode) → fire
- default mode without the flag → suppressed

mock.module + nonced re-import isolates the provider/classifier checks
per case so a misbehaving global cannot leak between tests.
2026-05-23 23:31:16 +08:00
0xfandom 07d9b4fec4 fix(json-schema): support top-level non-object roots via wrap/unwrap (#1261)
`--json-schema` failed with "Failed to provide valid structured output
after maximum retries" whenever the schema's root `type` wasn't `object`
(top-level arrays, strings, etc.). Object schemas worked, and arrays
nested inside objects worked — only top-level non-objects broke.

Root cause: the Anthropic tool_use block requires the `input` field to
be a JSON object (the SDK types it as `Record<string, unknown>`). When
SyntheticOutputTool used the user's array schema as `inputJSONSchema`
directly, the model had no valid object shape to emit and returned `{}`,
which failed Ajv validation on every retry until the retry budget ran
out.

Fix: detect non-object root schemas in `buildSyntheticOutputTool` and
wrap them as `{ type: 'object', properties: { result: <orig> },
required: ['result'], additionalProperties: false }`. After validation,
unwrap `input.result` before emitting `structured_output` so the CLI
prints the same array (or string, number, etc.) the user asked for.
Object roots pass through untouched.

Tests cover:
- top-level array root: schema wrapped, output unwrapped to plain array
- top-level string root: same wrap/unwrap path
- object root: pass-through unchanged
- inner-schema violations still raise the schema-mismatch error

Closes #1256
2026-05-23 23:23:09 +08:00
github-actions[bot] 66ed9b61dc chore(main): release 0.14.0 (#1217)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
v0.14.0
2026-05-23 13:16:56 +08:00
chioarub 0aff8de24f feat(diagnostics): show request payload size breakdown (#1237)
* feat(diagnostics): show request payload size breakdown

* fix(diagnostics): clarify request-size estimate semantics
2026-05-23 13:03:12 +08:00
0xfandom a44a83f38c fix(bash): preserve captured stdout in error message on non-zero exit (#1236)
* fix(bash): preserve captured stdout in error message on non-zero exit

Match PowerShellTool's pattern of passing captured output on the stdout
slot of ShellError so getErrorParts() surfaces the command output
alongside "Exit code N". The previous throw buried the merged output in
the stderr slot with stdout=''; the data still reached formatError
through the spread, but the swapped slots made it easy to lose output
if downstream consumers only inspected error.stdout.

Also drops the dead stdoutAccumulator.append("Exit code N") — the throw
above it discards the accumulator and getErrorParts() already prepends
"Exit code N" from error.code.

Adds regression tests covering the failure scenarios from the issue:
captured stdout/stderr appear in the formatted error, command-not-found
messages reach the surface, and empty-output failures still emit the
exit code.

Closes #1231

* test(bash): build full permission context for error-output tests

The hand-rolled `{ mode: 'default' }` context failed inside
`resetCwdIfOutsideProject` (reads `additionalWorkingDirectories`)
before assertions ran, so the regression was not actually exercised.
Use `getEmptyToolPermissionContext()` like the other BashTool tests.

Refs #1231.
2026-05-23 13:02:14 +08:00
0xfandom 0d3c157149 fix(recovery): keep thinking blocks on resume for reasoning-echo providers (#1248)
DeepSeek, Moonshot/Kimi, Z.AI GLM, and Xiaomi MiMo require `reasoning_content`
echoed back on assistant messages in thinking mode (`preserveReasoningContent`
in the openai-shim runtime config). The shim populates that field from the
`thinking` content block on the Anthropic-side message, so stripping those
blocks during 3P resume left no source and the provider 400'd with:

    The `reasoning_content` in the thinking mode must be passed back to the
    API.

Skip the 3P thinking strip when the active route/model resolves to a shim
config with `preserveReasoningContent: true`. Other 3P providers (generic
OpenAI, etc.) keep the original strip from #248 finding 5.

Closes #957.
2026-05-23 12:58:26 +08:00
stamsam 8cd998de68 docs(windows): add npm global path fix (#1272) 2026-05-23 12:57:43 +08:00
stamsam 9b3c90418b fix(compact): clear native tool results after time compaction (#1278) 2026-05-23 12:55:54 +08:00
0xfandom 892c0545ed fix(retry): adjust max_tokens on OpenRouter 402 credit shortfall (#1263)
OpenRouter (and other quota-billed OpenAI-compat gateways) reply with
HTTP 402 when the caller has fewer credits than the requested
max_tokens would consume. The body includes the affordable cap:

  This request requires more credits, or fewer max_tokens. You
  requested up to 32000 tokens, but can only afford 27342.

Previously this surfaced as a fatal API error and the user had to
guess what value to put in `CLAUDE_CODE_MAX_OUTPUT_TOKENS` to make the
request fit. Now `withRetry` parses the affordable number out of the
message and retries once with `maxTokensOverride = affordable` —
mirroring the existing context-overflow retry path. A single stderr
line tells the user output was clamped so they can top up credits if
they want the full budget back.

Single-shot adjustment (gated by `retryContext.maxTokensOverride ===
undefined`) so an unrelated subsequent 402 doesn't loop.

Also fixes pre-existing test-fixture mock leak: the `providers.js`
stub didn't include `isFirstPartyAnthropicBaseUrl` /
`usesAnthropicAccountFlow` / `isGithubNativeAnthropicMode`, so the
entire `withRetry.test.ts` file errored on import.

Closes #1125
2026-05-23 08:50:04 +08:00
JATMN bafc2a1bc5 fix: harden XAA OAuth callback state handling (#1299)
Validate XAA IdP callback state before processing provider errors or authorization codes.

Keep invalid-state callbacks non-terminal so forged error requests cannot close the active local callback server.

Add focused regression coverage for error callbacks without state, provider errors with matching state, and valid authorization codes.
2026-05-23 08:49:05 +08:00
blouflabandblouf 9949ba7e24 Fix : Function call is missing a thought_signature in functionCall parts (#1302)
Co-authored-by: blouf <blouf@blouf.org>
2026-05-23 08:29:20 +08:00
Kevin CodexandOpenClaude 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>
2026-05-23 08:22:57 +08:00
Kevin CodexandClaude Opus 4.7 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>
2026-05-23 07:51:39 +08:00
Kevin CodexandClaude Opus 4.7 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>
2026-05-23 07:49:20 +08:00
zucchiniandDevin 90360d3e38 fix(stdin,mcp): guard rawModeEnabledCount and defer MCP connections to prevent input freeze (#603) (#1268)
Issue #603 reports that the terminal input freezes shortly after startup
when MCP plugins (especially HTTP/SSE servers with OAuth) are enabled.
`--bare` mode, which skips MCP initialization, works around the freeze.

Root cause analysis points to a race between:
1. Ink's `handleSetRawMode` setting up stdin listeners during REPL mount
2. Async MCP connection callbacks triggering React re-renders via `setAppState`

Rapid mount/unmount of `useInput` components during those re-renders can
unbalance `rawModeEnabledCount`, driving it negative. Once negative,
subsequent `setRawMode(true)` calls skip the stdin setup (because the
count is no longer `0`), leaving the terminal with no active input handler
and producing the "frozen" symptom.

Changes:
- **Ink App.tsx**: Guard `rawModeEnabledCount` against negative values on
  both enable and disable paths. If the count is negative on enable, reset
  it to `0` so the setup path runs. If the count is already `<= 0` on
  disable, ignore the call instead of decrementing further.
- **useManageMCPConnections**: Increase `MCP_BATCH_FLUSH_MS` from `16` to
  `100` to coalesce more MCP state updates into fewer React commits.
- **useManageMCPConnections**: Defer `loadAndConnectMcpConfigs()` by one
  `setTimeout(..., 0)` tick so Ink's initial stdin raw-mode setup fully
  commits before any MCP async callback can interleave with it.

Generated with [Devin](https://cli.devin.ai/docs)

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-21 00:26:14 +08:00
Nishant Sarang eca9dba399 fix: treat blank Read.pages as omitted (#1269)
Fixes Gitlawb/openclaude#1264
2026-05-21 00:18:44 +08:00
0xfandom 03f879158c fix(xml): guard escapeXml/escapeXmlAttr against null and undefined (#1250)
Shell-tool outputs (BashTool result `stderr`, ShellError `stdout`/`stderr`)
surface as null when the stream produced nothing. processBashCommand piped
those straight into escapeXml, and the previous `s.replace(...)` body
crashed the REPL session with:

    TypeError: Cannot read properties of null (reading 'replace')
        at escapeXml
        at processBashCommand

Widen the parameter to `string | null | undefined` and short-circuit to `''`.
escapeXmlAttr delegates to escapeXml so it inherits the guard.

Closes #1247.
2026-05-20 02:31:42 +08:00
mee-kunandprofessional-slacker 4897d597b6 fix: allow non-OpenAI providers to skip OPENAI_API_KEY check (#1207)
* fix: allow providers without OPENAI_API_KEY if auth is not required or specific key exists

- Added validation metadata to gitlawb-opengateway to indicate it doesn't require auth.
- Updated getProviderValidationError to respect requiresAuth: false and provider-specific credential env vars.
- This prevents mandatory OPENAI_API_KEY check from blocking other providers when an OpenAI profile is active.

* test: add regression test for opengateway no-auth validation path

Ensures getProviderValidationError returns null when OPENAI_BASE_URL
points at opengateway.gitlawb.com and OPENAI_API_KEY is absent.

* test: add model-specific path test for opengateway

* fix: remove unreachable providerValidation.ts changes per review

The descriptor metadata in gitlawb-opengateway.ts already handles the
no-auth validation. The additional check in providerValidation.ts was
unreachable code.

---------

Co-authored-by: professional-slacker <professional-slacker@users.noreply.github.com>
2026-05-20 02:22:01 +08:00
Meetpatel006andjatmn aab2fbcd7b fix: MiMo remove unsupported body fields and preserve reasoning content (#1253)
* Fix OpenGateway MiMo agent tool history

Stop the Gitlawb Opengateway preset from forcing synthetic empty reasoning_content onto prior assistant tool-call messages. MiMo rejects that shape after Agent/sub-agent tool calls with upstream Param Incorrect errors.

Add an OpenAI shim regression test covering MiMo tool history through https://opengateway.gitlawb.com/v1 while preserving the separate OpenGateway Gemini signature replay behavior.

Validation: bun test src/services/api/openaiShim.test.ts; bun run build

* fix(MiMo): update integration to remove unnecessary body fields and preserve reasoning content

* test(MiMo): enhance tests to verify reasoning_content handling and strip unsupported options

---------

Co-authored-by: jatmn <the@jat.mn>
2026-05-20 01:40:00 +08:00
mee-kunandprofessional-slacker 23254c21fb fix: add 5-minute timeout to QueryGuard to prevent infinite spinner (#1255)
* fix: allow providers without OPENAI_API_KEY if auth is not required or specific key exists

- Added validation metadata to gitlawb-opengateway to indicate it doesn't require auth.
- Updated getProviderValidationError to respect requiresAuth: false and provider-specific credential env vars.
- This prevents mandatory OPENAI_API_KEY check from blocking other providers when an OpenAI profile is active.

* test: add regression test for opengateway no-auth validation path

Ensures getProviderValidationError returns null when OPENAI_BASE_URL
points at opengateway.gitlawb.com and OPENAI_API_KEY is absent.

* test: add model-specific path test for opengateway

* fix: add 5-minute timeout to QueryGuard to prevent infinite spinner loops

When an API call hangs or the response-received state transition fails,
the spinner runs indefinitely consuming memory (observed at 4GB+ after
24h). This adds a watchdog timer that force-ends the query after 5
minutes, resetting the spinner to idle.

Changes:
- QueryGuard.tryStart() now starts a 5-minute watchdog timer
- QueryGuard.end() and forceEnd() clear the timer
- If timeout fires, forceEnd() is called and a console.error is logged
- Added unit tests for timeout behavior

---------

Co-authored-by: professional-slacker <professional-slacker@users.noreply.github.com>
2026-05-20 01:38:35 +08:00
chioarub 1aa8aab84c fix(monitor): close permission dialog after selection (#1225)
* Fix monitor permission dialog lifecycle

* test(monitor): cover persistent allow continuation path
2026-05-20 01:37:09 +08:00
JATMN a9f8642aa8 fix(input): preserve split utf8 keypresses (#1241)
Buffer incomplete UTF-8 byte sequences across stdin parser reads so interactive IME input does not turn split multibyte characters into replacement text.

Keep the existing high-bit meta-key fallback when a lone pending byte is flushed, and continue to route completed text through the existing terminal tokenizer.

Add regression coverage for Vietnamese input arriving one byte at a time, matching the live typing failure mode from issue #1233.

Validation: bun test src\ink\parse-keypress.test.ts; bun test src\components\TextInput.test.tsx src\ink\parse-keypress.test.ts; bun run build; git diff --check.
2026-05-20 01:36:21 +08:00
chioarub f71e769237 fix(query): stop repeated tool-failure loops (#1219) 2026-05-18 07:23:36 +08:00
Evert Junior 0fba1541a8 fix(TaskListV2): revert overflowX hidden that hides task text labels (#1215)
The overflowX="hidden" added in #1211 clips task subject text to
nothing when TaskItems are nested inside MessageResponse (the └
prefix constrains available width). The icon survives at 2 chars
but the text gets fully clipped, leaving orphaned ✓/■ without
any label.

Reverts the overflowX="hidden" portion of #1211.
2026-05-17 14:55:00 +08:00
github-actions[bot] f102b601c5 chore(main): release 0.13.0 (#1208)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
v0.13.0
2026-05-17 11:13:06 +08:00
Kevin Codex e5535577aa docs: add Xiaomi MiMo sponsor (#1213)
* docs: add Xiaomi MiMo sponsor

* feat(tips): add Xiaomi MiMo sponsored tips
2026-05-17 11:01:06 +08:00
Kevin Codex 2d20109edc fix(gemini): parse raw tool call text (#1212) 2026-05-17 10:13:28 +08:00
Nik c53ef18716 fix(bashPermissions): apply MAX_SUBCOMMANDS cap in sandbox auto-allow path (#1057) (#1166)
* fix(bashPermissions): apply MAX_SUBCOMMANDS cap in sandbox auto-allow path (#1057)

The cap from #21405 is enforced in `bashToolHasPermission`, but the
`checkSandboxAutoAllow` shortcut path called `splitCommand` and iterated
`matchingRulesForInput` once per subcommand before the main-path cap got
a chance to run. With auto-allow-bash-if-sandboxed enabled, a crafted
compound command whose legacy `splitCommand` output explodes could
trigger N rule lookups in this path.

Mirror the existing cap (MAX_SUBCOMMANDS_FOR_SECURITY_CHECK = 50) in
`checkSandboxAutoAllow` right after the `splitCommand(command)` call:
log + return `ask` with the same decision-reason shape as the main path.

Regression test in bashPermissions.test.ts exercises sandbox auto-allow
with 60 echo subcommands and asserts the `ask` short-circuit.

* ci: re-trigger checks (likely-flaky failures on unrelated profile/SDK/KG tests)

* fix(bashPermissions): gate sandbox cap on legacy splitter path (CC-643)

Address reviewer feedback on #1057: the previous patch applied
MAX_SUBCOMMANDS_FOR_SECURITY_CHECK in checkSandboxAutoAllow
unconditionally on splitCommand output. The main bashToolHasPermission
path only applies that cap when astSubcommands === null, because the
fanout/ReDoS concern is specific to the legacy splitter — AST-parseable
compound commands (e.g. long echo chains) are already bounded by
structural parse and should not be downgraded to ask.

Thread astSubcommands into checkSandboxAutoAllow and only fire the cap
when AST is unavailable. Export checkSandboxAutoAllow so the symmetric
behavior is directly testable without depending on tree-sitter WASM
availability in the test runtime.

Tests:
- Legacy path (CLAUDE_CODE_DISABLE_COMMAND_INJECTION_CHECK=1 OR
  astSubcommands=null) over 50 subcommands -> ask, cap reason.
- AST-validated path (astSubcommands provided) with 60 subcommands ->
  allow, sandbox auto-allow reason.
2026-05-17 09:55:27 +08:00
chioarub 271bad4209 feat(export): add Markdown and JSON conversation exports (#1193) 2026-05-17 09:54:33 +08:00
JATMN a7f4b02db9 test: add shared mutation lock timeout guard (#1209)
Give acquireSharedMutationLock a default five-minute timeout so missed releases fail with a scoped error instead of hanging the smoke suite indefinitely.

Keep explicit timeout overrides intact and add isolated mutex coverage for default timeout, override timeout, and release handoff behavior.
2026-05-17 09:46:07 +08:00
Evert Junior 8470832e5c fix(spinner): prevent layout shift during thinking and orphaned task icons (#1211)
The spinner row used flexWrap="wrap" which caused the status text
(thinking indicator, timer, token count) to wrap to a new line when
content width hit a boundary condition. This produced a visible
layout jump — especially during thinking transitions when the status
text changes width.

Additionally, TaskItem rendered icons without overflow constraints,
so when text content overflowed the available width, orphaned icon
characters (checkmarks, squares) leaked into visible rows.

Changes:
- Use flexWrap="nowrap" on spinner row containers to keep status on
  one line, relying on the existing progressive width gating to hide
  elements that don't fit
- Replace magic number in availableSpace calculation with a named
  constant for clarity
- Add overflowX="hidden" on TaskItem to clip overflowing content
2026-05-17 09:45:23 +08:00
Nik b3b771476d fix(websearch): surface adapter failure when auto mode falls back to native (#994) (#1168)
* fix(websearch): surface adapter failure when auto mode falls back to native (#994)

When `WEB_SEARCH_PROVIDER=auto` and the configured adapter chain fails
on a recoverable error (DuckDuckGo "rate-limited from this network",
adapter timeout, 5xx, etc.), the tool falls through to the native
Anthropic / Codex web-search path silently. The only signal that the
adapter failed is a `console.error` line — it never reaches the tool
result the user sees. On rate-limit-prone networks (datacenter IPs,
VPNs) this manifests as "no results found" with no actionable hint,
exactly the symptom reported in #994.

This change captures the adapter error in `adapterFallthroughNotice`
inside the catch branch and prepends it to the eventual native / Codex
output via a small pure helper, `withAdapterFallthroughNotice`. The
hits-present and native-error paths are unchanged; the helper only
mutates a shallow copy when a notice is set, and is a no-op otherwise.

Result: users on a rate-limited adapter chain who get native results
also see *why* the adapter failed, and users whose native search also
returns nothing finally get the actionable diagnostic (configure
TAVILY_API_KEY / FIRECRAWL_API_KEY / etc.) instead of a silent empty.

Test coverage in WebSearchTool.test.ts asserts the pure-helper
contract: no-op when notice is undefined, prepend-not-mutate when a
notice is provided.

* fix(websearch): narrow #994 fix to the reachable adapter-failure surface

Address @techbrewboss feedback: the previous patch's
`adapterFallthroughNotice` machinery and the
`withAdapterFallthroughNotice` helper were unreachable under the
current provider selection.

`shouldUseAdapterProvider()` and `hasNativeSearchFallback()` are
mutually exclusive in auto mode — when a native path exists
(firstParty/vertex/foundry/Codex) the adapter is never tried, and when
the adapter IS tried (openai-shim providers) there is no native
fallback. So the assignment at `adapterFallthroughNotice = ...` and
both `withAdapterFallthroughNotice(...)` call sites could never fire.

Narrow the PR to the path that #994 actually hits today: an
openai-shim provider (moonshot/minimax/nvidia-nim/github copilot) where
the adapter fails transiently and there is no native fallback. The
existing throw at that branch already surfaces the underlying adapter
error verbatim; extract `buildAdapterUnavailableError(provider, errMsg)`
so it is directly testable and cannot regress, and replace the dead
notice helper + its tests with focused coverage of the reachable
message.

Drop the no-op shallow-copy `withAdapterFallthroughNotice` helper and
its two tests; keep the descriptive error throw as the single,
reachable surfacing path.
2026-05-17 05:35:28 +08:00
github-actions[bot] ca357cc78d chore(main): release 0.12.1 (#1202)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
v0.12.1
2026-05-17 05:31:30 +08:00
3kin0x bd7d42cb69 fix(ui): prevent prompt layout corruption when renaming session (#1206) 2026-05-17 05:31:10 +08:00
Kevin Codex 13a090162f fix(gemini): preserve tool calls through opengateway (#1204) 2026-05-17 05:12:07 +08:00
ArkhAngelLifeJiggy 4d0603e990 fix(entrypoint): apply --max-old-space-size=8192 universally, not just CCR (#1191)
Closes #402 — JavaScript heap OOM during large tasks.

The CLI entry point only set --max-old-space-size=8192 when
CLAUDE_CODE_REMOTE=true, leaving local users with V8's ~2 GB default
ceiling. Long agentic tasks (multi-file refactors, large prompts, tool
loops) hit that ceiling and abort with:
  FATAL ERROR: Ineffective mark-compacts near heap limit
  Allocation failed - JavaScript heap out of memory

Fix: remove the CCR gate and apply the 8 GB cap unconditionally, with a
user-override guard -- if the runner already set NODE_OPTIONS
--max-old-space-size to an explicit value, their setting is preserved
(no silent clobbering).

Files changed:
- src/entrypoints/cli.tsx — remove CLAUDE_CODE_REMOTE guard, add
  user-override predicate, update comments
- src/entrypoints/cli.test.ts — 6 regression tests (new file)
2026-05-16 22:16:31 +08:00
github-actions[bot] 5959763e48 chore(main): release 0.12.0 (#1173)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
v0.12.0
2026-05-16 15:24:20 +08:00
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>
2026-05-16 15:20:30 +08:00
JATMN f12eb1c9e8 Harden test isolation for smoke stability (#1192)
* Fix flaky smoke build checks

Replace feature-flag build preprocessing with a Bun onLoad transform so smoke/build no longer rewrites tracked src files while tests may be reading them.

Keep telemetry stubs ahead of the feature transform in both CLI and SDK builds, preserve non-empty text token counts in hybrid context splitting, and make the corrupted Orama stress test assert against the actual project directory used by the test.

Verified with bun run smoke, bun test src/utils/hybridContextStrategy.test.ts, bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 3, and bun test --max-concurrency=1.

* Harden KnowledgeGraph smoke stress isolation

Give each KnowledgeGraph stress test its own temporary config directory and remove it during teardown so Orama, SQLite, and corrupted-file state cannot bleed between stress cases or later PR test runs.

Reviewed at least 20 open PRs and found the recurring smoke-and-tests failure cluster is the full unit suite, especially KnowledgeGraph corrupted Orama recovery. Verified with bun test src/utils/knowledgeGraph.stress.test.ts --rerun-each 5, bun test --max-concurrency=1, and bun run smoke.

* Harden smoke test isolation

Audit and harden broad smoke-adjacent test suites for process-global leaks, including env/config restoration, shared registry/module mock cleanup, fetch/axios/mock restoration, and global MACRO/platform/sandbox mutations.

Replace fragile render sleeps in interactive tests with output-driven waits, and isolate provider/model/profile tests behind the shared mutation lock so unrelated PRs do not inherit stale process state.

Make SQLite knowledge graph cleanup clear closed on-disk databases before best-effort file cleanup, with coverage for the stale database reset path.

Verified: bun run smoke; bun test --max-concurrency=1; python -m pytest -q python/tests; bun run security:pr-scan -- --base origin/main; bun run test:provider; npm run test:provider-recommendation.

* Harden test isolation across smoke suite

Guard process-global test mutations with the shared mutation lock across env, module mock, config cache, and storage tests.\n\nDeep-copy global config snapshots, restore transient globals precisely, and make plugin/LSP mocks expose compatible export surfaces so concurrent test loading does not poison unrelated suites.\n\nReplace fixed SDK cleanup sleeps with call polling to remove timing sensitivity.\n\nVerification:\n- bun test --max-concurrency=1\n- bun run smoke\n- python -m pytest -q python/tests\n- bun run test:provider\n- npm run test:provider-recommendation\n- bun run security:pr-scan -- --base origin/main\n- git diff --check

* Close remaining test global-state leaks

Guard remaining cache, plugin, console, and VS Code module-mock tests with the shared mutation lock.\n\nThis follow-up audit covers non-env process-global state that can leak across test files: tool schema cache, cache stats tracker state, plugin loader caches, console.error replacement, and VS Code mock.module usage.\n\nVerification:\n- leak-surface scans for env/global/mock.module/cache outliers\n- duplicate top-level mock collision cluster\n- affected tests cluster\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check

* Guard remaining mock restore cleanup

Lock tests that call bun:test mock.restore without installing module mocks themselves.\n\nmock.restore is process-global, so these cleanup hooks can still tear down another test file's active module mocks when files run concurrently.\n\nVerification:\n- expanded leak scans for env, globals, module mocks, mock.restore, timers, argv, and caches\n- bun test src/components/useCodexOAuthFlow.test.tsx src/services/github/deviceFlow.test.ts\n- bun test --max-concurrency=1\n- bun run smoke\n- git diff --check

* Harden test isolation for smoke stability

Serialize tests that mutate process-global state behind the shared mutation lock, including process.env, transient globals, global config/cache state, storage mocks, and Bun module mocks.

Add isolated env mutex instances for SDK mutex tests so timeout coverage no longer manipulates the live process-global mutex.

Move top-level mock.module setup behind lock acquisition and restore mocks before releasing locks to prevent cross-file leakage under parallel smoke runs.

Verified with: bun test --max-concurrency=1; bun test; bun run smoke.
2026-05-16 15:18:42 +08:00
JATMN 94e8ff3941 chore: centralize Bun version and refresh CI tool pins (#1171)
* chore: centralize Bun version and refresh CI tool pins

- add .bun-version as the shared Bun source of truth for workflows and Docker builds
- update PR and release workflows to read Bun from bun-version-file
- refresh pinned GitHub Actions and Docker action SHAs to newer low-risk releases
- align contributor docs with Bun 1.3.13 guidance

* test: stabilize reset and provider profile persistence

Harden knowledge graph reset behavior across Windows file-lock scenarios by improving SQLite and JSON reset signaling, preserving a safe JSON source of truth when SQLite cannot be cleared, and adding direct storage regression coverage.

Also centralize deterministic config-home handling for tests, tighten provider profile persistence path resolution and cleanup semantics, isolate environment-sensitive suites with the env mutex, and remove flaky external npx dependency from the SDK consumer type test.

* test: fix Codex OAuth callback flake

Investigate the real provider smoke failure from GitHub Actions and fix the root cause instead of patching the symptom.

- make Codex OAuth callback host explicit and consistent across redirect URI generation and listener binding
- allow safe loopback host overrides for localhost, 127.0.0.1, and ::1
- harden Codex OAuth tests with env/fetch isolation so they do not poison neighboring provider suites
- pin the OAuth callback tests to 127.0.0.1 to avoid localhost IPv4/IPv6 family mismatch flakes in CI

Validated with bun test src/services/api/codexOAuth.test.ts, bun test src/services/api/providerConfig.codexSecureStorage.test.ts, and bun run test:provider.

* test: harden Codex OAuth callback tests

Investigate the recurring provider-smoke OAuth failures across multiple PR runs and fix the flaky callback test design at the root.

- remove the free-port reservation race from Codex OAuth tests
- add bounded callback retry only for loopback listener warm-up during the in-process OAuth test flow
- move ephemeral callback port support into an explicit CodexOAuthService test seam instead of widening production env parsing
- keep runtime callback-port semantics unchanged while adding regression coverage for callback host and port parsing

Validated with targeted Codex OAuth tests and repeated provider-bucket reruns to check for recurring flake.

* test: serialize provider shared-state suites

Fix the recurring provider smoke flake at the root cause by serializing test suites that mutate process.env or globalThis.fetch.

Add a shared test mutation lock and wire it into the provider bucket so Codex OAuth no longer races with unrelated provider/config/openai shim tests under Bun's parallel test execution. Cleanup now releases the lock in finally blocks, and the shared lock waits indefinitely by default to avoid timeout-based CI flakes.

* test: fix smoke root causes and noisy suites

Replace the Codex OAuth test's live loopback listener dependency with an injected listener seam, avoid module-mock leakage across provider suites, and clean up the auth-code listener test setup.

Also harden noisy storage and search tests by asserting expected log output, isolating SQLite masterpiece persistence per test cwd, and removing routine benchmark/stress logging from passing runs.

* build: harden Bun version install in Docker

Validate the repo-tracked .bun-version value before using it in the Docker build stage, strip line endings, and install Bun through a quoted semver-only variable instead of raw shell expansion.

* test: replace flaky conversation arc benchmark

Fix the recurring smoke failure caused by an absolute wall-clock assertion in the normal unit suite. Replace the CI-speed-sensitive conversation arc benchmark with deterministic regression coverage that verifies repeated fact extraction, expected entity shapes, bounded graph growth, and populated-summary behavior.

* test: isolate shared-state smoke suites

* test: restore codex credential mocks between suites

* test: fix shared-state and provider init-order flakes

* test: isolate remaining shared-state smoke suites

Serialize the remaining smoke-sensitive suites that mutate process env, CLAUDE_CONFIG_DIR, fetch, or SDK session globals.

Add shared lock coverage to discovery, agent/skills loading, platform storage, and SDK lifecycle/preserved-segment tests. Restore session and cwd state inside the lock boundary so parallel files cannot leak bootstrap state into knowledge graph and SDK isolation tests.

Validated with repeated smoke and full-suite passes:
- bun run smoke (2x)
- bun test
- bun test --max-concurrency=1
- bun run test:provider
- python -m pytest -q python/tests
- npm run test:provider-recommendation
2026-05-15 13:07:39 +08:00
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>
2026-05-15 09:44:50 +08:00
TechBrewBoss c433d20fdc FIX: Reduce stable stringify heap usage (#1104)
* fix stable stringify heap usage

* test stable stringify spacing

* Fix stableStringify spacing guard for fractional values

* fix stable stringify top-level result typing
2026-05-15 00:19:31 +08:00
github-actions[bot] b187fe91b8 chore(main): release 0.11.0 (#1108)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
v0.11.0
2026-05-14 22:44:12 +08:00
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>
2026-05-14 22:24:29 +08:00
Dolph Prefect 74e3947d88 fix(agent): prevent mid-flight peeking and taking over of forks (#1153)
- Added 'Don't take over' rule to AgentTool prompt to prevent models from overriding forks based on partial output.
- Refined 'Don't peek' rule to explicitly direct models to use SendMessage for course-correction instead of Read.
- Clarified that override decisions belong to the Review phase.
2026-05-14 22:20:55 +08:00
0xfandom b8c9d90703 docs(ollama): drop ollama launch openclaude instructions (#1163)
The `ollama launch openclaude` syntax in README and advanced-setup.md
was docs-only (added in #716), gated on the companion
ollama/ollama#15618 integration that has not landed upstream. Users
following these instructions get `Error: unknown integration:
openclaude` (issue #744 originally, #1134 now).

Remove the misleading section + table mention so the env-var setup
above remains the documented path. Can be re-added once the upstream
ollama integration ships.

Closes #1134
2026-05-14 22:00:20 +08:00
JATMN 4d2de51679 Fix provider profile startup precedence (#1157)
Prevent the legacy .openclaude-profile.json fallback from overriding startup env when a modern configured provider profile has already selected a concrete provider configuration.

Thread the configured-profile signal from CLI bootstrap into buildStartupEnvFromProfile(), add a concrete-selection helper for the new guard, and preserve the legacy file as a first-run fallback when startup env is incomplete.

Also fix the follow-up falsey-flag regression so disabled CLAUDE_CODE_USE_* values do not count as active startup selections, and add regression tests covering stale legacy overrides, incomplete startup env, and falsey provider flags.

Verified with: bun test src/utils/providerProfile.test.ts --test-name-pattern " buildStartupEnvFromProfile\
2026-05-14 08:39:47 +08:00
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>
2026-05-14 07:48:26 +08:00
KRATOS cac11dce0d fix(integrations): cap gpt-5.5 context window at Codex effective limit (#1118) (#1141)
The gpt-5.5 model descriptor was set to the OpenAI API ceiling of
1,050,000 tokens, but in this repo gpt-5.5 is primarily routed through
the Codex transport (src/services/api/providerConfig.ts), whose practical
request limit is ~272k. The mismatch caused /context to under-report
usage and auto-compact to fire after the request had already exceeded the
effective window, surfacing as a mid-turn 500 'input exceeds the context
window of this model'.

Pin the descriptor to the Codex limit so /context shows realistic
budgets and auto-compact triggers before the failure. Provider-aware
context windows via the existing route catalog system remain the
correct long-term fix; this is the conservative interim.

Fixes #1118
2026-05-13 22:27:16 +08:00