Commit Graph
696 Commits
Author SHA1 Message Date
chioarub 8dd7cb066a fix(ollama): cap deepseek v4 pro cloud output tokens (#1348)
* fix(ollama): cap deepseek v4 pro cloud output tokens

* fix(ollama): preserve colon-tagged override prefixes

* fix(ollama): mark curated catalog as hybrid
2026-06-01 06:36:47 +08:00
stamsam cf305ccc29 fix(query): keep tool failure guard across unrelated successes (#1277)
* fix(query): keep tool failure guard across unrelated successes

* fix(query): count repeated path failures across mixed batches
2026-06-01 06:10:36 +08:00
stamsam 64ad44abaf chore(build): reject stale bundled external entries (#1275) 2026-06-01 06:10:04 +08:00
chioarub f3d41c6161 fix(release): verify npm latest tag and document @latest install (#1378)
* fix(release): verify npm latest tag and document @latest install

* fix(auto-updater): use @latest for global installs
2026-06-01 06:08:17 +08:00
Paijoandoyi77 dda5ea31bd fix: third-party provider compat — update, metrics, and refusal message (#1406)
* fix: third-party provider compatibility — update, metrics, and refusal message

Four fixes for third-party provider users:

1. cli/update.ts: Allow self-update for non-Anthropic builds by checking
   PACKAGE_URL instead of blocking all non-firstParty providers.

2. utils/autoUpdater.ts: Same fix for assertMinVersion() — allow version
   checks for builds with custom PACKAGE_URL.

3. api/metricsOptOut.ts: Gate Anthropic metrics endpoint behind firstParty
   check so 3P providers don't hit api.anthropic.com and get auth errors.

4. api/errors.ts: Replace hardcoded 'claude-sonnet-4-20250514' in refusal
   message with getDefaultMainLoopModel() for provider-appropriate suggestion.

* fix(api): gate checkMetricsEnabled before disk cache for 3P providers

Address review feedback: stale first-party metrics cache could leak
enabled:true to third-party sessions. Short-circuit non-firstParty
before reading the shared disk cache.

---------

Co-authored-by: oyi77 <oyi77@users.noreply.github.com>
2026-06-01 06:07:43 +08:00
beardthelion b900364dbe fix(fork): render forked-worker messages, drop unmirrored /fork command (#1451)
* fix(fork): add UserForkBoilerplateMessage, drop unmirrored /fork command

FORK_SUBAGENT ships enabled, which made two missing-module paths live:

1. UserTextMessage renders <UserForkBoilerplateMessage> whenever a user
   message contains <fork-boilerplate> (produced by
   forkSubagent.buildChildMessage). No source file existed, so the build
   stubbed the import to a noop default and the named component was
   undefined, crashing the render of any forked-worker message.
2. The /fork slash command required ./commands/fork/index.js, whose source
   was never mirrored; its noop .default was spread into the command list,
   registering a command with no name/description/call.

Add the component, rendering a compact dimmed marker with just the
directive (parsed off FORK_DIRECTIVE_PREFIX) instead of dumping the verbose
worker rules block into the transcript. Remove the /fork registration:
the implicit-fork machinery (AgentTool/forkSubagent) is present and keeps
working; only the unmirrored slash command is dropped.

Verified: both stubs gone from dist/cli.mjs, the real component is bundled,
smoke passes, and component + commands tests pass.

* docs(fork): align /fork contract with implicit-fork-only behavior

Removing the unmirrored /fork command left two stale contract references:
- forkSubagent.ts claimed '/fork <directive> slash command is available'.
- branch/index.ts dropped /branch's 'fork' alias when FORK_SUBAGENT was on
  (on the assumption a dedicated /fork command existed), so the build had
  neither the command nor the alias and /fork resolved to nothing.

Restore /branch as the unconditional owner of the 'fork' alias (its
historical pre-FORK_SUBAGENT behavior, honoring the original 'always have a
fork entry point' intent), drop the now-unused feature import, and correct
the forkSubagent doc to state the slash command is not in this build and
forking is implicit.
2026-06-01 06:00:50 +08:00
beardthelion 1d48f8e855 test(build): assert WebFetch binds the real SSRF guard in the bundle (#1450)
#1399 already fixed the specifier-collision class by tracking missing
relative imports per importer, which also resolves the WebFetch ssrfGuard
case (the test-file string literal now only stubs the test importer, never
WebFetch). The remaining gap is bundle-level coverage: the existing
security-hardening test reads source only and would pass even if the
shipped CLI bundle had stubbed the guard to a noop.

Rebase onto current main (dropping the now-redundant scanner change) and
add a dist/cli.mjs assertion alongside the /dream regression test: the real
ssrfGuard blocked-address error is present and ssrfGuard is not replaced by
a missing-module stub.
2026-06-01 05:59:39 +08:00
Shannon CodeandClaude Opus 4.8 479b0e8226 fix(sandbox): guard annotateStderrWithSandboxFailures against missing runtime method (fixes Bash on builds without sandbox-runtime) (#1452)
* fix(sandbox): guard annotateStderrWithSandboxFailures against missing runtime method

Fall back to a passthrough when BaseSandboxManager.annotateStderrWithSandboxFailures
is absent, so BashTool no longer throws "is not a function" on every command when the
underlying sandbox-runtime build doesn't provide the method. No behavior change when it
is present.

* fix(sandbox): complete the SDK SandboxManager stubs so they match the CLI's Proxy-noop

The SDK build stubs @anthropic-ai/sandbox-runtime two ways: the native-stub
namespace uses `new Proxy({}, { get: () => noop })` (every access is safe), but
defaultExportOverrides replaces SandboxManager/BaseSandboxManager with hollow
classes that omit annotateStderrWithSandboxFailures. The class form wins in the
SDK bundle, so SDK embedders crash on every Bash command
(`SandboxManager.annotateStderrWithSandboxFailures is not a function`) while the
CLI build — which keeps the Proxy-noop and ships the real native runtime — is
unaffected.

Add a passthrough `annotateStderrWithSandboxFailures` to both stub classes so
they behave like the Proxy form (return stderr unchanged when no real runtime is
present). Combined with the call-site `?? passthrough` guard, the SDK now
degrades gracefully on builds without sandbox-runtime instead of throwing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 05:58:05 +08:00
chioarub 276ec6ab0e fix(ci): scan PR head for intent checks (#1461) 2026-06-01 05:55:29 +08:00
3kin0x 4d26ca7628 Fix on prem keepalive (#1462)
* fix: automatically retry 502/504 responses with keepalive disabled to recover from NAT drops

* fix: remove cooked string from retry pattern
2026-06-01 05:53:26 +08:00
beardthelion f111eaa1b3 feat: enable MCP_SKILLS — discover skill:// resources as invocable skills (#1408)
* feat(mcp-skills): implement MCP skill discovery via skill:// resources

- mcpSkills.ts: fetchMcpSkillsForClient — lists MCP resources, filters skill://
  URIs, reads each via resources/read, parses frontmatter, builds skill commands
  with loadedFrom/source: 'mcp'. Memoized per server name (LRU, size 20).
- isSkillResource: pure helper to detect skill:// URI scheme
- deriveMcpSkillName: namespaced name builder (mcp__<server>__<name>)
- Enable MCP_SKILLS: true in scripts/build.ts

All call sites, cache-invalidation paths, and consumers were already wired
behind feature('MCP_SKILLS'). Only the module itself was missing. Fixes the
"fetchMcpSkillsForClient is not a function" crash (#856) when the flag was
force-enabled without the module present.

* fix(mcp-skills): discard hooks frontmatter from remote MCP skills

A skill:// resource's hooks frontmatter was carried through the
parseSkillFrontmatterFields spread into the Command, and the slash-command
path registered command.hooks as session hooks on invocation. This let any
connected MCP server install local command hooks that later run shell in the
user's workspace, bypassing the loadedFrom === 'mcp' inline-shell guard by
moving the payload into frontmatter hooks instead of the markdown body.

Discard hooks at the MCP construction site so untrusted remote skills can
never become registrable session hooks.

* fix(mcp-skills): discard allowed-tools frontmatter from remote MCP skills

Like hooks, a skill:// resource's allowed-tools frontmatter flowed through
the parseSkillFrontmatterFields spread into the Command. On the user-typed
slash path (/mcp__server__skill) those tools are written into
alwaysAllowRules.command, so a remote MCP server could auto-approve tool
calls (e.g. Bash) that its own skill body then drives the model to make —
with no permission prompt. The inline-shell guard for loadedFrom === 'mcp'
does not cover this.

Discard allowed-tools at the MCP construction site so remote skills can't
auto-grant tools; the model still prompts on each tool use. The model-invoked
SkillTool path already gates non-empty allowedTools via
skillHasOnlySafeProperties, but the slash path bypasses checkPermissions.

* fix(mcp-skills): skip @-mention attachment scanning for remote MCP skill bodies

A skill:// resource's markdown body flows through getMessagesForPromptSlashCommand
into getAttachmentMessages, which scans for @-mentions and MCP resource refs and
reads them before the model continues. skipSkillDiscovery only gates skill
discovery, not @-mention file reads, so a remote skill could embed @~/.ssh/config
or @.env and exfiltrate local file contents into the conversation with no tool
permission prompt — the same class as the already-stripped hooks/allowed-tools.

Gate the scan input on loadedFrom === 'mcp' (new attachmentScanInputForCommand
helper): the body still reaches the model verbatim, but its @-mentions are no
longer auto-read. Thread-level attachments are unaffected (input=null only gates
the user-input branch in getAttachments).
2026-05-31 10:15:07 +08:00
4a4f379b8c Add full access mode and fix bypass commit prompts (Issue 1097) (#1110)
* Add full access permission mode

Introduce a Full Access mode as a second-level dangerous permission option that bypasses normal confirmation prompts and hard safety-check prompts while still preserving deny decisions.

Wire fullAccess through permission mode types, SDK schemas/types, CLI and REPL control paths, settings, mode cycling, spawned teammate inheritance, prompt speculation, and setup safety checks.

Update permission handling so Full Access skips ask rules, requiresUserInteraction prompts, content-specific ask results, safety-check asks, and hook-forced asks while preserving updatedInput from tool permission checks.

Add a separate Full Access warning acknowledgement and render Full Access selections in red to make the higher-risk mode visually distinct.

Allow the project-local .git/OPENCLAUDE_COMMIT_MSG helper file in dangerous modes for /commit while keeping default mode and other .git paths protected by safety prompts.

Add focused regression tests for Full Access prompt bypass behavior, hook ask handling, commit message file permissions, mode cycling, spawned teammate propagation, and SDK permission mappings.

* fix: restore sdk permission fail-closed behavior

Preserve host canUseTool and onPermissionRequest enforcement in fullAccess instead of short-circuiting around SDK policy callbacks.

Keep the default SDK permission path fail-closed when no host callback is configured, while still allowing interactive tools to surface guidance prompts under fullAccess.

Add focused regression coverage for SDK permission routing and fullAccess user-interaction behavior, plus filesystem coverage for the project-local OPENCLAUDE_COMMIT_MSG path.

* fix: complete full access permission mode integrations

- keep Full Access out of persisted default permission mode settings

- sync Full Access to Claude in Chrome skip-all permission mode

- restore Full Access correctly when exiting plan mode

- add regression coverage for settings, Chrome sync, and plan-mode exit

* test: harden dangerous mode startup flow

* feat: add permission mode management tab

Add a dedicated permission mode tab for switching session modes from the permissions UI.

Keep dangerous modes visible when currently active, route dangerous mode changes through the confirmation dialog without exiting settings, and surface availability errors for auto or bypass modes.

Also add focused tests for permission mode option visibility.

* feat: add full access approval flows

Add fullAccess as an approval option across file, shell, skill, monitor, web fetch, fallback, and plan-exit permission prompts.

Introduce a shared dangerous-mode confirmation hook, wire fullAccess session mode updates through the permission handlers, and gate the new option on dangerous-mode availability.

Also fix the plan-exit follow-up review findings by preserving hook order around the dangerous-mode dialog and restoring Shift+Tab to the explicit accept-edits approval path.

Verified with focused permission tests and Bun module import smoke checks.

* Harden dangerous permission mode boundaries

Tighten fullAccess and bypassPermissions entry paths so elevated mode always respects explicit local confirmation and authoritative org policy gates.

This hardens SDK and bridge activation, Chrome integration, session resume and rewind restoration, team and plan mode transitions, and shared permission update handling. It also keeps session dangerous-mode state in sync and adds focused regression coverage for permission setup, killswitch behavior, conversation recovery, and SDK permission flows.

* refactor: centralize permission mode transitions

Route permission mode changes through shared decision and live-transition helpers so dangerous/full-access confirmation, plan/auto side effects, and mode application stay aligned across CLI, REPL, prompts, and swarm surfaces.

Add a shared UI request hook for resolved dangerous-mode confirmations, persist in-session dangerous-mode acceptance, and remove duplicated request/confirm/apply flows from prompt input, teams, plan exit, and permission settings.

Also fix follow-up correctness issues by validating all setMode updates consistently, applying live permission updates before persisting them, and rebasing those live updates on the latest permission context to avoid partial commits or stale-state overwrites.

* refactor: simplify permission request flows

Centralize permission mode changes behind requestPermissionModeChange and reuse it from the CLI, REPL bridge, inbox poller, and UI callers.

Consolidate duplicated permission request behavior by introducing shared shell and simple permission helpers, routing file permission actions through a shared executor, and unifying remote permission queue-item construction.

Add a shared PermissionScaffold for the common dialog frame, remove redundant shell option/helper modules, and keep focused permission mode transition coverage in permissionSetup tests.

* Enable full access from the permissions UI

Expose bypass/full-access modes in the /permissions picker so dangerous modes can be enabled in-session instead of only via launch flags.

Propagate a session-only bypass-enable signal through the permission mode change flow, preserve the existing dangerous-mode confirmation and policy checks, and keep the session marked as bypass-capable after the user enables one of the dangerous modes.

Also add targeted tests covering picker visibility, local session unlock behavior, and the post-enable session state.

* Refine bypass permissions warning copy

* fix(powershell): anchor commit message .git exception to project root

Align the PowerShell .git write safety exception for
.git/OPENCLAUDE_COMMIT_MSG with the shared filesystem permission rule.
The PowerShell helper was resolving the path from the mutable shell cwd,
which made the bypassPermissions and fullAccess cases order-sensitive in
the full test suite. Resolve the exception from getOriginalCwd() instead
so the temp commit message file is only exempted inside the project root
.git directory while other .git writes still require a safety prompt.

Verified with:
- bun test src/tools/PowerShellTool/powershellPermissions.test.ts --max-concurrency=1
- bun test src/utils/permissions/filesystem.test.ts --max-concurrency=1
- bun test src/tools/PowerShellTool src/utils/permissions --max-concurrency=1

* test: fix dangerous mode prompt suite hang

* Fix monitor permission test isolation

* Harden monitor permission state selector

---------

Co-authored-by: JATMN <12479882+jatmn@users.noreply.github.com>
Co-authored-by: TechBrewBoss <dash@hicap.ai>
2026-05-31 09:03:49 +08:00
3kin0x 83abfa506a fix(ink): correct stringWidth JS fallback for symbol characters (#1244) 2026-05-31 09:00:34 +08:00
Cal ac3ae10936 fix(bash): show output for ! shell commands (#1265) (#1395)
* fix(bash): show output for ! shell commands (#1265)

Apply the fix from upstream PR #1270: use raw stdout (with escapeXml)
for normal ! commands, only use processToolResultBlock when output
is persisted or backgrounded. This prevents the model-facing formatter
from silently losing stdout.

Fixes #1265

* fix(bash): address PR review comments

- Remove escapeXml from backgrounded formatter output (trusted XML)
- Force bash routing in tests via mock.module to avoid PowerShellTool
  routing on Windows

* chore(bash): clarify formatter output is trusted in both metadata branches

* fix(bash): decode XML entities in user-visible bash stdout/stderr display

* fix(bash): fix unescapeXml entity order and decode in export path

* fix(bash): only unescape stdout/stderr in exports, not bash-input
2026-05-31 06:38:41 +08:00
chioarub 132539ff79 fix(build): restore /dream slash command in bundled CLI (#1399)
Scope missing-module stubs for relative imports to the importer file so the unmirrored KAIROS dream skill stub no longer replaces the real /dream command module during bundling.
2026-05-31 06:37:05 +08:00
Nik 5247fb8977 fix(teammate-progress): keep cumulative token+tool counts across prompts (#475) (#1402)
The in-process teammate runner re-created the progress tracker on every
prompt iteration, so task.progress.tokenCount and toolUseCount were reset
between leader prompts to the same teammate. TeammateSpinnerLine,
InProcessTeammateDetailDialog and the Spinner aggregate all read these
counters directly, which is why agent-team pills appeared to lose tokens
and tool uses partway through a session.

The Claude API returns input_tokens as cumulative-per-request (each turn
re-sends forkContextMessages history), so latestInputTokens already
captures the running context cost. The fix moves createProgressTracker
out of the while-loop so cumulativeOutputTokens and toolUseCount also
keep their running totals across multiple prompts.

Adds src/tasks/LocalAgentTask/progressTracker.test.ts pinning:
- output tokens accumulate across multiple assistant messages
- cumulative semantic survives a simulated multi-prompt teammate session
- fresh-tracker-per-prompt regression repro (prior outputs + tool uses lost)
- tool use count accumulates
- cache_creation/read input tokens fold into latestInputTokens
- recentActivities stays capped while toolUseCount keeps climbing

bun test (full): 2998/2998 pass. bun run build clean.
2026-05-31 06:34:01 +08:00
5a22d604f8 feat(provider): add OpenCode Zen/Go subscription support (#1350)
* feat(provider): add OpenCode Zen/Go subscription support

Add OpenCode as a first-class provider, enabling users to connect their
Zen (pay-as-you-go) and Go ($10/mo) subscriptions via the /provider command.

New integration descriptors:
- vendors/opencode.ts — OpenCode Zen vendor (41 models)
- gateways/opencode-go.ts — OpenCode Go gateway (12 models)
- brands/opencode.ts — brand descriptor
- models/opencode.ts — full model catalog (GPT, Claude, Gemini, Qwen,
  GLM, Kimi, MiniMax, Grok, DeepSeek, MiMo, Nemotron)

Modified files:
- integrationArtifacts.generated.ts — register descriptors and presets
- providerProfile.ts — add OPENCODE_API_KEY env/secret key, 'opencode'
  profile type, and buildLaunchEnv handler
- providerConfig.ts — add DEFAULT_OPENCODE_BASE_URL constants

Auth: OPENCODE_API_KEY env var or interactive key entry in /provider
Transport: openai-compatible (chat_completions)
Base URLs: https://opencode.ai/zen/v1 (Zen), /zen/go/v1 (Go)

* feat(provider): add [Zen]/[Go] tags to OpenCode preset labels

Add visual tags in the /provider preset selection to distinguish
OpenCode Zen (pay-as-you-go) from OpenCode Go (subscription).

* feat(provider): enable dynamic model discovery for OpenCode

Switch OpenCode vendor and Go gateway from static to hybrid model
catalog with openai-compatible discovery. Models are fetched from
/v1/models on startup and cached for 1 hour. Manual refresh is
supported via the /provider UI.

Static model list is preserved as fallback when discovery fails.

* test(provider): add comprehensive OpenCode Zen/Go test suite

97 tests across 2 files covering:

Integration tests (72 tests):
- Vendor descriptor: id, label, classification, base URL, model, auth,
  transport, preset, validation, catalog, discovery, usage metadata
- Gateway descriptor: id, label, vendorId, category, base URL, model,
  auth, transport, preset, catalog, discovery
- Brand descriptor: id, label, canonicalVendorId, capabilities, modelIds
- Model catalog: registration, vendor/gateway associations, required
  fields, valid classifications, reasoning/coding tags, no duplicates,
  model counts (41 Zen, 12 Go), modelDescriptorId consistency
- Cross-reference: brand↔model, vendor↔model, gateway↔model,
  shared OPENCODE_API_KEY
- Registry validation: no errors, no preset conflicts
- Edge cases: unique ids, unique apiNames, non-empty labels, valid
  contextWindow/maxOutputTokens, valid defaultModel format, validation
  message content, discovery config

Profile tests (25 tests):
- Type guard: isProviderProfile('opencode'), rejects invalid values
- buildLaunchEnv: persisted env, defaults, process env precedence,
  OPENCODE_API_KEY mapping, whitespace/null/undefined/empty handling,
  very long keys, special characters, concurrent access, boundary
  values, no credential leakage

* fix(provider): add per-model endpoint routing (P1)

Add endpointPath field to OpenAIShimTransportConfig so catalog entries
can specify which API path to use per model. This addresses the
maintainer's [P1] finding that all models were routed to
/chat/completions regardless of their upstream endpoint.

Changes:
- descriptors.ts: add endpointPath?: string to OpenAIShimTransportConfig
- openaiShim.ts: buildRequestUrl checks shimConfig.endpointPath first
- vendors/opencode.ts: add transportOverrides to 31 catalog entries
  (GPT→/responses, Claude/Qwen→/messages, Gemini→/models/<id>)
  + switch to source: 'static' to prevent free models from live API
- gateways/opencode-go.ts: add transportOverrides to 4 entries
  (MiniMax/Qwen→/messages) + switch to source: 'static'
- opencode.test.ts: update tests for static source, remove discovery tests

* refactor(opencode): model OpenCode Zen/Go as gateways (P2)

* docs(provider): document OpenCode setup and move badge metadata to descriptors

- Add OpenCode Zen/Go rows to README supported providers table
- Add OpenCode Zen/Go examples and OPENCODE_API_KEY to advanced-setup.md
- Add PresetBadge type to descriptor/manifest with badge propagation in
  artifact generator
- Move 4 hard-coded preset badges ([FREE], [Sponsor], [Zen], [Go]) from
  ProviderManager.tsx into descriptor preset metadata
- Add badge field to providerUiMetadata so UI components read from manifest
- Update integration overview docs to recommend preset.badge for future
  gateways

* fix(provider): match request body to endpoint format for OpenCode /messages and /responses (P1)

Extend the openaiShim transport so that endpointPath overrides select
both the URL and the correct body/response format:

- /responses → OpenAI Responses API body (input, max_output_tokens)
- /messages  → Anthropic Messages API body (content blocks, system, max_tokens)

Also fixes: abort listener leak in SSE passthrough, system prompt
content-block flattening, and removes [Zen]/[Go] badge entries (P3).

Co-Authored-By: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>

* fix(provider): add Google AI SDK body/response format for OpenCode Zen Gemini models (P1)

The three Gemini models in the OpenCode Zen catalog (gemini-3.5-flash,
gemini-3.1-pro, gemini-3-flash) were sending chat-completions body to
the /models/gemini-* endpoint, which expects Google AI SDK format.

- effectiveTransport now detects /models/gemini- endpointPath → 'gemini'
- buildGeminiBody() converts Anthropic messages → Google contents[]
  with role mapping, systemInstruction, generationConfig, functionDeclarations
- geminiSseToAnthropic() parses Google SSE frames → Anthropic stream events
  with text deltas, functionCall tool_use, finishReason mapping
- _convertGeminiToAnthropicResponse() for non-streaming responses
- Streaming/non-streaming routing via URL detection (/models/gemini-)
- serializeBody(), hasToolsPayload, omitGeminiTools all updated

* fix: prevent OpenCode model descriptors from shadowing canonical limits

P1: Prefix all defaultModel values in opencode.ts with 'opencode-'
so the fallback findModelDescriptorForApiName() doesn't match
canonical model names. The OpenCode descriptors are still found
via catalog entry lookup when the OpenCode route is active.

P2: Add 'OpenCode Go' and 'OpenCode Zen' to PRESET_ORDER in
ProviderManager.test.tsx between 'OpenAI' and 'OpenRouter'
so navigateToPreset() sends the correct number of j keypresses.

* fix: align OpenCode Go descriptor metadata with Zen

- category: 'hosted' → 'aggregating' (both are aggregating gateways)
- add validation block with OPENCODE_API_KEY guidance
- update test assertion from 'hosted' to 'aggregating'

* fix: accept OPENAI_API_KEY as fallback in OpenCode validation

When users set up OpenCode Zen/Go via /provider, the key is saved as
OPENAI_API_KEY (via buildCompatibilityProcessEnv). The validation block
only checked OPENCODE_API_KEY, causing a startup warning even though
the runtime auth header had the key it needed.

Add OPENAI_API_KEY to validation.credentialEnvVars for both gateways,
matching the pattern used by Hicap and Gitlawb Opengateway.

* chore: trigger mergeability recheck

---------

Co-authored-by: Gravirei <gravirei@users.noreply.github.com>
Co-authored-by: OpenClaude (mimo-v2.5-pro) <openclaude@gitlawb.com>
2026-05-30 14:45:08 +08:00
JATMN 9190bd0c50 Harden test isolation and smoke checks (#1440)
* fix(test): isolate provider-related attribution and preconnect tests

Remove process-global provider mocks from apiPreconnect tests and exercise real env-based provider resolution with hermetic first-party setup.

Reset bootstrap/settings state around attribution tests and reload the attribution module per test so provider and client state cannot leak across suites.

Verification: bun test --max-concurrency=1 src/utils/apiPreconnect.test.ts src/utils/attribution.test.ts

* Fix full local check failures

Add a check script that runs smoke plus the full single-concurrency Bun test suite, and wire it into CONTRIBUTING, the PR template, and PR checks.

Fix Windows/full-suite failures by preferring Git Bash over the WSL bash launcher, normalizing settings paths before source matching, making path and warning-glyph tests platform-aware, and restoring persistent Bun module mocks for AgentTool and hook-chain tests.

Verified with bun test src\tools\BashTool\BashTool.errorOutput.test.ts --max-concurrency=1 and bun run check.

* fix(test): eliminate mock.module() leaks and platform-specific test failures

## Problem

The full test suite (bun test --max-concurrency=1) had 10 failing tests on
Windows. Investigation revealed 4 distinct root causes, all stemming from
bun's mock.module() not being fully reversible by mock.restore(). When a
test file replaces a shared module via mock.module(), stale bindings persist
in already-imported modules even after mock.restore() is called. This is a
known bun limitation.

The CI (Ubuntu) only showed 1 consistent failure (the attribution test),
but the Windows-local failures exposed real bugs that could surface in CI
under different test ordering.

## Changes

### src/utils/hookChains.integration.test.ts (root polluter)

This file was the biggest source of test pollution with 9 mock.module()
calls replacing shared modules (analytics, growthbook, policyLimits,
teammateMailbox, teammate, AgentTool, replBridge, etc.) with partial
surfaces. For example, the teammateMailbox mock only exported writeToMailbox
but the real module has 20+ exports including isIdleNotification,
createIdleNotification, readMailbox, etc. When mock.restore() didn't fully
undo these mocks, downstream tests got undefined for missing exports.

Fix: Import real modules via cache-busted dynamic imports before setting up
mocks, then spread the real module surface into each mock.module() call.
This way even if the mock leaks, downstream tests see the full module
surface with only the intended overrides. All 9 mock.module calls now
spread their real module counterparts.

Also fixed: the test was failing in isolation with SyntaxError because
attachments.ts transitively imports isIdleNotification from
teammateMailbox.js, which was missing from the partial mock.

### src/utils/settings/changeDetector.test.ts (Windows path normalization)

4 tests failed because getSourceForPath() normalizes paths using
path.normalize() which converts forward slashes to backslashes on Windows.
The test hardcoded Unix-style paths (/tmp/openclaude/user/settings.json)
but path.normalize produces \tmp\openclaude\user\settings.json on
Windows. The path comparison always failed, so handleChange() returned
early without triggering any callbacks or debounce timers.

Fix: Import normalize from 'path' and apply it to all test path constants
(pathsBySource, getManagedSettingsDropInDir). This matches what the
production code does.

### src/utils/exportFormats.test.ts (Windows path separator)

resolveExportFilepath() uses path.join() which produces backslash-separated
paths on Windows. The test expected forward-slash paths.

Fix: Import join from 'path' and use it in the expected value so the
assertion is platform-agnostic.

### src/utils/file.test.ts (growthbook mock leak)

importFileModuleWithKillswitchEnabled() mocked growthbook.js with only
getFeatureValue_CACHED_MAY_BE_STALE: () => killswitchEnabled. When
killswitchEnabled was false, this poisoned isAgentSwarmsEnabled() for all
downstream tests because agentSwarmsEnabled.ts has a static import of
getFeatureValue_CACHED_MAY_BE_STALE that captured the mock binding.

Fix: Import the real growthbook module and spread it into the mock, so
all exports remain available even if the mock leaks.

### src/utils/plugins/officialMarketplaceStartupCheck.test.ts (same pattern)

Same growthbook mock leak pattern. Top-level mock.module with only
getFeatureValue_CACHED_MAY_BE_STALE: () => true.

Fix: Import real growthbook module and spread into mock.

### src/tools/AgentTool/AgentTool.teammateModel.test.ts (transitive mock binding)

4 tests failed with 'Agent Teams is not yet available on your plan' because
isAgentSwarmsEnabled() returned false. The function checks
getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_flint', true) from
growthbook.js, but the static import binding in agentSwarmsEnabled.ts was
captured from a leaked mock that returned false.

Cache-busting the AgentTool.js import doesn't help because
agentSwarmsEnabled.ts is a transitive dependency that keeps its
already-loaded (mocked) growthbook binding.

Fix: Add mock.module for agentSwarmsEnabled.js in importAgentToolWithSpawnMock()
to pin isAgentSwarmsEnabled to true, matching the test's intent (it sets
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1).

## Verification

- bun run smoke: passes
- bun test --max-concurrency=1: 3019 pass, 0 fail (verified twice)
- No skipped tests (test.skip/it.skip/describe.skip), no test.todo,
  no flaky markers, no test exclusions in config

## Known remaining risks

6 test files still have partial mock.module() calls on providers.js
(withRetry, officialRegistry, domainCheck, conversationRecovery, fastMode)
that don't spread the real module. These don't cause failures under current
test ordering but are latent risks if bun changes file execution order.

* Fix remaining provider mock leak risks

Address the known remaining risks from 7583157 by making provider mocks in withRetry, officialRegistry, and fastMode tests spread and restore the real providers module surface.

Verified with the targeted provider-mock test group and bun run check.

* Harden smoke test coverage

Remove the CI-only skip and unrelated error swallowing from the SDK query lifecycle tests so fork/resume behavior is asserted in CI and local runs.

Isolate test-suite global state by disabling built-in SDK agents for the lifecycle test, restoring MACRO presence exactly, clearing the agent cache, restoring axios mocks, and protecting xAI loopback tests from proxy/fetch leakage.

Make the provider API test script run serially to match the shared env/proxy mutation surface.

Validation: bun run check; CI=1 bun test tests\\sdk\\query-lifecycle.test.ts --max-concurrency=1; bun run test:provider; npm run test:provider-recommendation; bun run security:pr-scan -- --base upstream/main; bun run web:typecheck; bun run web:build; python -m pytest -q -p no:cacheprovider python/tests.

* Expose hidden SDK test failures

Tighten SDK test drains so they only suppress expected lifecycle abort errors instead of swallowing arbitrary init and bootstrap failures.

Replace no-op test assertions with real checks and add V2 lifecycle isolation for MACRO, built-in agents, and agent cache state.

Fix SDK V2 sendMessage to fast-exit when the caller-provided AbortController is already aborted, preventing aborted sessions from submitting work and producing result messages.

Validation: bun test scripts\\feature-flags-source-guard.test.ts tests\\sdk\\query-concurrency.test.ts tests\\sdk\\sdk-v2-lifecycle.test.ts --max-concurrency=1; bun test tests\\sdk\\query-concurrency.test.ts tests\\sdk\\query-lifecycle.test.ts tests\\sdk\\sdk-v2-lifecycle.test.ts --max-concurrency=1; bun run check.

* Fix CI smoke test failures

Respect SDK context null session project directories so regenerated SDK sessions do not fall back to global project state.

Isolate attribution tests from CI provider/model environment and replace nondeterministic live query permission checks with direct assertions against the SDK permission machinery.

Validation: bun test src\\utils\\attribution.test.ts tests\\sdk\\query-lifecycle.test.ts tests\\sdk\\permissions.test.ts --max-concurrency=1; bun test tests\\sdk\\sdk-context-isolation.test.ts tests\\sdk\\query-concurrency.test.ts --max-concurrency=1; bun run check.

* Stabilize attribution contract test

Assert that includeCoAuthoredBy emits the default co-author trailer without pinning the active provider's model label, which can legitimately differ in CI provider environments.

Validation: bun test src\\utils\\attribution.test.ts --max-concurrency=1; ANTHROPIC_MODEL=claude-sonnet-4-5-20250929 CLAUDE_CODE_USE_BEDROCK=1 bun test src\\utils\\attribution.test.ts --max-concurrency=1; bun run check.
2026-05-30 14:40:33 +08:00
Madboly 7cc8edaa3c fix(docs): update Xiaomi MiMo API URL in README. (#1424)
- Reason: https://api.xiaomimimo.com/v1 returns 404
2026-05-30 00:11:03 +08:00
stamsam 2e3f7467f9 docs(vertex): clarify Claude on Vertex setup (#1273)
* docs(vertex): clarify Claude on Vertex setup

* docs(vertex): point region overrides at env utils

* docs(vertex): use cli model selector
2026-05-30 00:08:12 +08:00
Kevin CodexandOpenClaude 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>
2026-05-30 00:06:08 +08:00
mee-kunandprofessional-slacker f6d7a5894b feat: set process.title to 'openclaude' (#1425)
Change process.title from 'claude' to 'openclaude' to match the project name.

Benefits:
- Observability: `pgrep openclaude` / `ps aux | grep openclaude` distinguishes from upstream Claude Code
- Log identification: /proc/[pid]/comm shows 'openclaude' for easier debugging
- nproxy consistency: base process.title aligns with the openclaude identity

Includes source-level test verifying the correct title is set.

Ref: professional-slacker/status#13

Co-authored-by: professional-slacker <professional-slacker@users.noreply.github.com>
2026-05-29 23:00:36 +08:00
chioarubandCursor 70b4b07908 fix(repl): show permission prompts while draft input is present (#1393)
Critical permission and hook dialogs were gated behind prompt typing
suppression, so users only saw "Waiting for permission…" until Return
cleared draft input. Resolve critical dialogs before suppression and
drop the redundant waiting placeholder.

Fixes #651

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-28 23:07:03 +08:00
chioarub fabb148fbb Improve warning notice formatting (#1415) 2026-05-28 15:38:59 +08:00
chioarub 11d59ecdcb fix(autocompact): retry circuit breaker after cooldown (#1375)
* fix(autocompact): retry circuit breaker after cooldown

* fix: honor auto-compact cooldown fallback

Derive the circuit breaker retry time from lastFailureAtMs plus the configured cooldown when nextRetryAtMs is missing or invalid. Also clear SDK auto-compact tracking after manual compact boundaries and cover both paths with regressions.

* test: make auto-compact cooldown fixture portable

* test: extend auto-compact cooldown fixture timeout
2026-05-28 09:17:38 +08:00
Kevin CodexandOpenClaude 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>
2026-05-27 09:55:05 +08:00
0xfandom 01ffbb68b8 fix(provider): allow remote Ollama without OPENAI_API_KEY (#952)
Remote Ollama servers (host outside the loopback / RFC1918 range, or
on a domain like ollama.corp.example.com) hit the OPENAI provider
validation gate that requires OPENAI_API_KEY whenever the base URL is
not local. Ollama doesn't need an API key, so the user has to invent a
phantom value to get past startup.

Extend the bypass to recognise likely-Ollama base URLs in addition to
local URLs:
- port 11434 (Ollama default) on any host
- 'ollama' substring in hostname or pathname

isLikelyOllamaEndpoint already encoded these heuristics for tool-call
gating in providerConfig.ts; export it and reuse so the rules stay in
one place.

Fixes #369
2026-05-27 08:36:20 +08:00
Neodiusanddaltoncoder 363583faf5 fix(launcher): route direct Node launch paths through launcher (#1363)
Ensures package.json scripts (dev, start), scripts/provider-launch.ts,
and Dockerfile route node executions through the bin/openclaude launcher
rather than calling node directly on dist/cli.mjs.

This resolves PR feedback:
1. Preserves the robust launcher relaunch guard, GC exposure, and test
   coverage already merged on main (from #1242).
2. Prevents hardcoded heap caps (--max-old-space-size=8192) from overriding
   user-provided NODE_OPTIONS or OPENCLAUDE_NODE_MAX_OLD_SPACE_SIZE_MB
   settings during development, start, or containerized runs.

Co-authored-by: daltoncoder <daltoncoder@example.com>
2026-05-27 08:19:27 +08:00
chioarub 8513178934 fix(thinking): disable thinking for unsupported Ollama models (#1376)
* fix(thinking): disable thinking for unsupported Ollama models

Fixes #1371

- Adds central `shouldUseThinkingForModel` gate that checks the actual route and model descriptor.
- Disables thinking parameters for the Ollama route when the model is unknown or unsupported.
- Updates API requests to evaluate the actual retry model against the capability gate instead of the initial request model.
- Adds targeted tests for Ollama logic and shim payloads.

* test(thinking): cover Ollama thinking gate
2026-05-27 07:55:25 +08:00
github-actions[bot] 670744fc70 chore(main): release 0.15.0 (#1325)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
v0.15.0
2026-05-26 22:11:32 +08:00
chioarub f15d2360ee fix(codex): allow credential storage fallback (#1347)
* fix(codex): allow credential storage fallback

* fix(codex): scope plaintext credential fallback
2026-05-26 21:54:33 +08:00
chioarub 6bc050e621 fix(attribution): make git attribution opt-in by default (#1335) 2026-05-26 21:53:05 +08:00
chioarub 785d3de2cd fix(agent): allow custom model overrides (#1337)
* fix(agent): allow custom model overrides

* fix(agent): enforce model allowlist for overrides

* test(agent): isolate allowlist override regression tests

* fix(agent): validate teammate model overrides
2026-05-26 21:36:47 +08:00
Vasanth TandOpenClaude Worker 3 4ee279e7de ci: retrigger CodeQL after action download outage (#1374)
Co-authored-by: OpenClaude Worker 3 <worker-3@openclaude.local>
2026-05-26 21:31:46 +08:00
chioarub 7419d3800c feat(agents): set active session agent from agents menu (#1349)
* feat(agents): set active session agent from agents menu

* fix agents menu model switching
2026-05-26 20:16:13 +08:00
JATMN 2c87bfe055 fix(model): include profile models in descriptor picker (#1361)
* fix(model): include profile models in descriptor picker

* fix(model): respect active profile model lists
2026-05-26 20:14:47 +08:00
chioarub ed91673f53 fix(watchers): debounce skills and settings reload bursts (#1370) 2026-05-26 20:13:55 +08:00
3kin0x 2f8aa50cf6 feat(query): robust multi-lingual and structural continuation nudge (#1280)
* feat(cli): improve SSH interactivity detection via SSH_TTY and SSH_CONNECTION

* feat(models): add support for Gemma 4 31B

* feat(query): robust multi-lingual and structural continuation nudge

* fix(query): refine continuation nudge logic to avoid false positives
2026-05-25 19:33:13 +08:00
0xfandom 2d26a4673a fix(codex-stream): recover tool args delivered only via done events (#1262)
`codexspark` / `gpt-5.3-codex-spark` deliver the complete function-call
arguments only through the terminal `response.function_call_arguments.done`
event (and sometimes only on `response.output_item.done`), with zero
`response.function_call_arguments.delta` events in between. The Anthropic-
compat stream adapter ignored both `done` channels for arguments, so the
tool_use block closed with `input: {}` and the routed agent's Glob/Bash
call failed validation with "required parameter X is missing" (#1259).

Fixes:

1. Track `emittedArgs` per tool block (initial seeded from
   `output_item.added.item.arguments` when present).
2. Handle `response.function_call_arguments.done` — emit the full
   `arguments` string as an `input_json_delta` if no deltas streamed.
3. Backstop in `response.output_item.done` for backends that skip the
   dedicated arguments-done event entirely.

The `!toolBlock.emittedArgs` guard on both done branches prevents double
emission when deltas were already streamed (the common path).

Tests cover:
- args delivered only via `function_call_arguments.done`
- args delivered only via `output_item.done` (no arguments.done at all)
- delta path still wins; done events do not duplicate the JSON

Closes #1259
2026-05-25 19:28:44 +08:00
JATMNandJATMN d02c10b399 feat: configure API retry backoff (#370) (#1095)
* feat: configure API retry backoff

Add OpenClaude-branded retry controls for retryable API failures.

- Replace the old CLAUDE_CODE_MAX_RETRIES config with OPENCLAUDE_MAX_RETRIES

- Allow OPENCLAUDE_MAX_RETRIES=0 to disable retries after the initial request

- Cap retry attempts at 100 and invalid values fall back to the default of 10

- Add OPENCLAUDE_RETRY_DELAY_MS to configure the exponential backoff base for APIs that omit Retry-After

- Keep Retry-After precedence over configured retry delay

- Document both settings in .env.example and advanced setup docs

- Add focused retry configuration tests for defaults, invalid values, caps, zero retries, configured delay, and Retry-After precedence

Validation:

- bun test src/services/api/withRetry.test.ts

- bun run build

* Honor legacy max retries env var

Add compatibility fallback from CLAUDE_CODE_MAX_RETRIES when OPENCLAUDE_MAX_RETRIES is unset.

Document the deprecated fallback and cover precedence behavior in retry configuration tests.

---------

Co-authored-by: JATMN <12479882+jatmn@users.noreply.github.com>
2026-05-25 19:27:07 +08:00
JATMN b3dc674dbe fix: route MiniMax compacting through Anthropic-compatible API (#1154)
* fix: route MiniMax through Anthropic-compatible API

Switch MiniMax provider setup away from the OpenAI-compatible shim and onto the Anthropic-compatible endpoint. Update env-only, provider flag, and saved profile paths to use ANTHROPIC_* while preserving legacy OPENAI_MODEL as a migration fallback.

Adjust MiniMax M2 context metadata so shared descriptors use the gateway-safe 196608 window and the direct MiniMax catalog overrides to the documented 204800 window. Extend runtime context lookup to anthropic-proxy routes so compact budgeting uses the direct provider metadata.

Update MiniMax client, provider profile, provider flag, context, and auto-compact tests for the Anthropic-compatible route and provider-specific compact limits.

* test: cover MiniMax provider manager paths

Update ProviderManager test fixtures so MiniMax uses the Anthropic-compatible endpoint instead of the old OpenAI-compatible /v1 endpoint.

Add coverage for the /provider add flow to assert MiniMax saves provider=minimax, endpoint https://api.minimax.io/anthropic, and displays the Anthropic-compatible API provider type.

Add edit-flow coverage to ensure existing MiniMax profiles remain on the Anthropic-compatible provider path and continue hiding OpenAI-only advanced fields.

* test: isolate MiniMax env-only coverage

Harden MiniMax client and compact tests against ambient CI provider env such as OPENAI_API_KEY, ANTHROPIC_BASE_URL, and provider-profile markers.

The compact budget test now explicitly clears competing provider flags before asserting direct MiniMax metadata, preventing CI-level OpenAI credentials from masking env-only MiniMax route detection.

* test: reset provider env inside MiniMax client cases

Make each env-only MiniMax client test clear competing provider flags, OpenAI/XAI keys, Anthropic env, and saved-profile markers before setting MINIMAX_API_KEY.

This keeps the Anthropic-compatible MiniMax route assertions independent of CI-level process env that can otherwise mask env-only provider detection in the full serial suite.

* fix: honor explicit MiniMax routing intent

Route MiniMax env-only requests by explicit MiniMax model/base intent even when generic OpenAI-compatible environment variables are present, while preserving non-MiniMax base URL conflicts.

Use the resolved MiniMax env-only path for Anthropic SDK key selection so stale provider classification or Bun module mocks cannot fall back to an Anthropic test key.

Harden MiniMax compact coverage against leaked env overrides and prior autoCompact module mocks, and cover the ambient OpenAI/XAI env regression.

* test: clean up compression autoCompact mocks

Restore Bun module mocks after compression test files so their deterministic autoCompact window does not leak into later compact tests in full-suite order.

Verified the MiniMax compact regression now passes after the compression suites and in the full local test log.

* test: avoid autoCompact module mocks in compression tests

Replace the compression suites' top-level Bun module mocks for autoCompact/config with real test config and env controls. This avoids Bun 1.3.11 leaking a mocked effective context window into the later MiniMax compact test in full-suite order.

Verified compression-before-compact and MiniMax focused suites pass locally.

* test: allow capped MiniMax compact reservation

CI enables the output-token slot-reservation cap, so MiniMax's direct 204,800 context can produce a 196,800 effective compact window instead of the uncapped 184,800. Keep the test focused on direct MiniMax context metadata while accepting either reservation state.

* fix: address MiniMax review findings

Treat env-only provider routes such as direct MiniMax as complete startup provider selections so saved profiles do not override explicit MINIMAX_API_KEY/ANTHROPIC_* env.

Stop advertising direct MiniMax benchmark support through the OpenAI-compatible benchmark path, and add regression coverage for the unsupported direct MiniMax benchmark env.

* fix: classify MiniMax profile startup correctly

Recognize MiniMax when /provider loads it through the Anthropic-compatible env shape using ANTHROPIC_BASE_URL, ANTHROPIC_MODEL, and ANTHROPIC_API_KEY.

Label MiniMax correctly on the startup screen and skip the Anthropic custom-key approval prompt when the resolved provider is not using the Anthropic account flow.

Add regressions for route metadata, legacy provider classification, account-flow bypass, and startup display for Anthropic-compatible MiniMax profiles.

* fix: include Anthropic key in provider secret source

Allow MiniMax profile redaction to include ANTHROPIC_API_KEY in the SecretValueSource type used by sanitizeProviderConfigValue.

This fixes the PR-specific TS2353 reported by review while keeping the MiniMax Anthropic-compatible key alias redacted alongside MINIMAX_API_KEY.

Validation: bun test --max-concurrency=1 src\utils\providerProfiles.test.ts src\utils\providerFlag.test.ts src\utils\model\providers.test.ts src\integrations\routeMetadata.test.ts; bun run typecheck still has existing repo-wide errors, with no providerProfile.ts matches.

* test: stabilize tool history compression smoke

Add a narrow compression-enabled override for tests so the compression suites do not depend on shared global config state from the full Bun runner.

Pass explicit effective context windows in direct compression tests and use catalog-backed models in shim compression tests to avoid env-capped tier drift.

Verified with focused compression tests, full bun test --max-concurrency=1, and bun run build.

* test: stabilize Orama corruption recovery assertion

Verify the quarantined corrupted Orama file from the actual persistence directory returned by getOramaPersistencePath, instead of assuming the config-dir projects root used by the full CI runner.

Verified with the failing KnowledgeGraph stress test, compression smoke suites, full bun test --max-concurrency=1, and bun run build.

* fix: refresh MiniMax compact branch

Merge upstream/main into fix/minimax-compact so PR #1154 is current with the target branch.

Also fix two branch-local FetchType test casts that surfaced during typecheck scanning of the MiniMax/xAI fallback tests.

Validation: bun test --max-concurrency=1 src/utils/providerProfiles.test.ts src/utils/providerFlag.test.ts src/utils/model/providers.test.ts src/integrations/routeMetadata.test.ts src/services/api/client.test.ts src/services/compact/autoCompact.test.ts src/utils/model/benchmark.test.ts; bun run build.
2026-05-25 19:17:45 +08:00
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